// ChunkManager - Handles chunk-based terrain loading for large maps class ChunkManager { constructor(scene, chunkSize = 50) { this.scene = scene; this.chunkSize = chunkSize; // 50x50 tiles per chunk this.biomeSystem = null; // Will be set by GameScene // Active chunks (currently loaded) this.activeChunks = new Map(); // Key: "chunkX,chunkY", Value: chunk data // Chunk load radius (how many chunks around player) this.loadRadius = 1; // Load 3x3 = 9 chunks at once // Player position tracking this.lastPlayerChunkX = -1; this.lastPlayerChunkY = -1; console.log(`💾 ChunkManager initialized (chunk size: ${chunkSize}x${chunkSize})`); } // Get chunk coordinates from world coordinates worldToChunk(worldX, worldY) { return { chunkX: Math.floor(worldX / this.chunkSize), chunkY: Math.floor(worldY / this.chunkSize) }; } // Get chunk key string getChunkKey(chunkX, chunkY) { return `${chunkX},${chunkY}`; } // Check if chunk is loaded isChunkLoaded(chunkX, chunkY) { return this.activeChunks.has(this.getChunkKey(chunkX, chunkY)); } // Load a single chunk loadChunk(chunkX, chunkY) { const key = this.getChunkKey(chunkX, chunkY); // Already loaded if (this.activeChunks.has(key)) { return this.activeChunks.get(key); } console.log(`📦 Loading chunk (${chunkX}, ${chunkY})`); // Create chunk data const chunk = { chunkX, chunkY, key, tiles: [], objects: [], // Trees, rocks, decorations sprites: [] // Phaser sprites for this chunk }; // Generate or load chunk tiles const startX = chunkX * this.chunkSize; const startY = chunkY * this.chunkSize; for (let y = 0; y < this.chunkSize; y++) { for (let x = 0; x < this.chunkSize; x++) { const worldX = startX + x; const worldY = startY + y; // Get biome for this tile let biomeId = 'grassland'; if (this.biomeSystem) { biomeId = this.biomeSystem.getBiomeAt(worldX, worldY); } chunk.tiles.push({ x: worldX, y: worldY, biome: biomeId }); } } // Store chunk this.activeChunks.set(key, chunk); // Render chunk (if terrain system available) if (this.scene.terrainSystem && this.scene.terrainSystem.renderChunk) { this.scene.terrainSystem.renderChunk(chunk); } return chunk; } // Unload a single chunk unloadChunk(chunkX, chunkY) { const key = this.getChunkKey(chunkX, chunkY); if (!this.activeChunks.has(key)) return; console.log(`📤 Unloading chunk (${chunkX}, ${chunkY})`); const chunk = this.activeChunks.get(key); // Destroy all sprites in chunk if (chunk.sprites) { chunk.sprites.forEach(sprite => { if (sprite && sprite.destroy) { sprite.destroy(); } }); } // Remove chunk this.activeChunks.delete(key); } // Update active chunks based on player position updateActiveChunks(playerX, playerY) { const { chunkX, chunkY } = this.worldToChunk(playerX, playerY); // Player hasn't changed chunks if (chunkX === this.lastPlayerChunkX && chunkY === this.lastPlayerChunkY) { return; } console.log(`🔄 Player moved to chunk (${chunkX}, ${chunkY})`); this.lastPlayerChunkX = chunkX; this.lastPlayerChunkY = chunkY; // Determine which chunks should be loaded const chunksToLoad = new Set(); for (let dy = -this.loadRadius; dy <= this.loadRadius; dy++) { for (let dx = -this.loadRadius; dx <= this.loadRadius; dx++) { const targetChunkX = chunkX + dx; const targetChunkY = chunkY + dy; chunksToLoad.add(this.getChunkKey(targetChunkX, targetChunkY)); } } // Unload chunks that are too far const chunksToUnload = []; for (const [key, chunk] of this.activeChunks) { if (!chunksToLoad.has(key)) { chunksToUnload.push({ x: chunk.chunkX, y: chunk.chunkY }); } } chunksToUnload.forEach(({ x, y }) => this.unloadChunk(x, y)); // Load new chunks for (let dy = -this.loadRadius; dy <= this.loadRadius; dy++) { for (let dx = -this.loadRadius; dx <= this.loadRadius; dx++) { const targetChunkX = chunkX + dx; const targetChunkY = chunkY + dy; if (!this.isChunkLoaded(targetChunkX, targetChunkY)) { this.loadChunk(targetChunkX, targetChunkY); } } } } // Force reload all chunks (for debugging) reloadAllChunks() { console.log('🔄 Reloading all chunks...'); const chunksToReload = []; for (const [key, chunk] of this.activeChunks) { chunksToReload.push({ x: chunk.chunkX, y: chunk.chunkY }); } // Unload all chunksToReload.forEach(({ x, y }) => this.unloadChunk(x, y)); // Reload based on player position if (this.scene.player) { const pos = this.scene.player.getPosition(); this.updateActiveChunks(pos.x, pos.y); } } // Get chunk at world position getChunkAt(worldX, worldY) { const { chunkX, chunkY } = this.worldToChunk(worldX, worldY); return this.activeChunks.get(this.getChunkKey(chunkX, chunkY)); } // Get statistics getStats() { return { activeChunks: this.activeChunks.size, chunkSize: this.chunkSize, loadRadius: this.loadRadius, maxChunks: Math.pow((this.loadRadius * 2 + 1), 2), totalTilesLoaded: this.activeChunks.size * this.chunkSize * this.chunkSize }; } // Destroy all chunks destroy() { console.log('💾 ChunkManager destroying all chunks...'); for (const [key, chunk] of this.activeChunks) { if (chunk.sprites) { chunk.sprites.forEach(sprite => { if (sprite && sprite.destroy) sprite.destroy(); }); } } this.activeChunks.clear(); console.log('💾 ChunkManager destroyed'); } }