/** * 🏞️ LAKE SYSTEM * Generates and manages lakes across the world * - Creates natural lake shapes * - Biome-specific lake placement * - Lake depth and shorelines * - Connects to river system */ export default class LakeSystem { constructor(worldWidth, worldHeight, biomeSystem) { this.worldWidth = worldWidth; this.worldHeight = worldHeight; this.biomeSystem = biomeSystem; // Lake map (stores depth: 0-1) this.lakeMap = new Map(); // Lakes array this.lakes = []; // Lake settings this.lakeCountPerBiome = { grassland: 2, forest: 3, desert: 0, // No lakes in desert (unless oasis) mountain: 2, swamp: 4 // Most lakes in swamp }; //小 pond settings this.pondCount = 15; // Small ponds in grassland console.log(`🏞️ Initializing Lake System (${worldWidth}x${worldHeight})`); } /** * Generate all lakes */ generateLakes(riverSystem = null) { console.log(`🏞️ Generating lakes...`); // 1. Generate major lakes per biome for (const [biomeName, count] of Object.entries(this.lakeCountPerBiome)) { for (let i = 0; i < count; i++) { const lake = this.generateLakeInBiome(biomeName); if (lake) { this.lakes.push(lake); } } } // 2. Generate small ponds for (let i = 0; i < this.pondCount; i++) { const pond = this.generatePond(); if (pond) { this.lakes.push(pond); } } // 3. Generate desert oases (rare) this.generateOases(2); console.log(`✅ Generated ${this.lakes.length} lakes with ${this.lakeMap.size} water tiles`); } /** * Generate a lake in specific biome */ generateLakeInBiome(biomeName) { // Find suitable location const location = this.findLakeLocation(biomeName); if (!location) return null; // Lake size based on biome const size = this.getLakeSizeForBiome(biomeName); // Create lake const lake = { x: location.x, y: location.y, biome: biomeName, size: size, type: 'lake', tiles: [] }; // Generate organic lake shape this.generateLakeShape(lake); return lake; } /** * Find suitable location for lake */ findLakeLocation(biomeName) { const maxAttempts = 50; for (let attempt = 0; attempt < maxAttempts; attempt++) { const x = Math.floor(Math.random() * this.worldWidth); const y = Math.floor(Math.random() * this.worldHeight); const biome = this.biomeSystem.getBiomeAt(x, y); if (biome === biomeName) { // Check not too close to other lakes const tooClose = this.lakes.some(lake => { const dist = Math.sqrt((lake.x - x) ** 2 + (lake.y - y) ** 2); return dist < 50; }); if (!tooClose) { return { x, y }; } } } return null; } /** * Get lake size for biome */ getLakeSizeForBiome(biomeName) { switch (biomeName) { case 'grassland': return 8 + Math.floor(Math.random() * 7); // 8-15 tiles case 'forest': return 10 + Math.floor(Math.random() * 8); // 10-18 tiles case 'mountain': return 6 + Math.floor(Math.random() * 5); // 6-11 tiles case 'swamp': return 12 + Math.floor(Math.random() * 10); // 12-22 tiles default: return 8; } } /** * Generate organic lake shape */ generateLakeShape(lake) { const centerX = lake.x; const centerY = lake.y; const radius = lake.size; // Use cellular automata-like approach for organic shape for (let dy = -radius; dy <= radius; dy++) { for (let dx = -radius; dx <= radius; dx++) { const x = centerX + dx; const y = centerY + dy; if (x < 0 || x >= this.worldWidth || y < 0 || y >= this.worldHeight) { continue; } // Distance from center const dist = Math.sqrt(dx * dx + dy * dy); // Add noise for organic shape const noise = (Math.random() - 0.5) * 2; const threshold = radius + noise; if (dist < threshold) { // Calculate depth (1.0 at center, 0.0 at edge) const depth = 1.0 - (dist / radius); const key = `${x},${y}`; this.lakeMap.set(key, { depth: Math.max(0, Math.min(1, depth)), lakeId: this.lakes.length, biome: lake.biome }); lake.tiles.push({ x, y, depth }); } } } } /** * Generate small pond */ generatePond() { // Ponds only in grassland const location = this.findLakeLocation('grassland'); if (!location) return null; const pond = { x: location.x, y: location.y, biome: 'grassland', size: 3 + Math.floor(Math.random() * 3), // 3-6 tiles type: 'pond', tiles: [] }; this.generateLakeShape(pond); return pond; } /** * Generate desert oases */ generateOases(count) { for (let i = 0; i < count; i++) { const location = this.findLakeLocation('desert'); if (!location) continue; const oasis = { x: location.x, y: location.y, biome: 'desert', size: 4 + Math.floor(Math.random() * 3), // 4-7 tiles type: 'oasis', tiles: [] }; this.generateLakeShape(oasis); this.lakes.push(oasis); } } /** * Check if tile is lake */ isLake(x, y) { const key = `${x},${y}`; return this.lakeMap.has(key); } /** * Get lake data at tile */ getLakeData(x, y) { const key = `${x},${y}`; return this.lakeMap.get(key) || null; } /** * Get lake color based on depth and biome */ getLakeColor(x, y) { const data = this.getLakeData(x, y); if (!data) return 0x4682B4; const biome = data.biome; const depth = data.depth; // Base colors let baseColor; switch (biome) { case 'forest': baseColor = { r: 42, g: 95, b: 79 }; // Dark green break; case 'swamp': baseColor = { r: 61, g: 90, b: 61 }; // Murky break; case 'desert': baseColor = { r: 135, g: 206, b: 235 }; // Oasis blue break; case 'mountain': baseColor = { r: 70, g: 130, b: 180 }; // Mountain blue break; default: baseColor = { r: 30, g: 144, b: 255 }; // Default blue } // Darken based on depth const r = Math.floor(baseColor.r * depth); const g = Math.floor(baseColor.g * depth); const b = Math.floor(baseColor.b * depth); return (r << 16) | (g << 8) | b; } /** * Get statistics */ getStats() { const typeCount = { lake: 0, pond: 0, oasis: 0 }; for (const lake of this.lakes) { typeCount[lake.type]++; } return { totalLakes: this.lakes.length, lakes: typeCount.lake, ponds: typeCount.pond, oases: typeCount.oasis, totalWaterTiles: this.lakeMap.size }; } /** * Export lake data */ exportData() { return { lakes: this.lakes, lakeMap: Array.from(this.lakeMap.entries()) }; } /** * Import lake data */ importData(data) { this.lakes = data.lakes || []; this.lakeMap = new Map(data.lakeMap || []); } }