/** * 🗺️ MAP REVEAL SYSTEM * Fog of war system that reveals map as player explores * - Reveals tiles around player * - Persistent (saves discovered areas) * - Minimap integration * - Full map view (M key) */ class MapRevealSystem { constructor(scene) { this.scene = scene; // Revealed tiles (500x500 grid) this.revealed = Array(500).fill(null).map(() => Array(500).fill(false)); // Reveal radius around player this.revealRadius = 15; // tiles // Map UI elements this.mapOpen = false; this.mapContainer = null; // Minimap this.minimap = null; this.minimapSize = 200; this.minimapScale = 2; // pixels per tile on minimap console.log('🗺️ MapRevealSystem initialized'); } // Reveal tiles around player revealArea(playerX, playerY) { const gridX = Math.floor(playerX); const gridY = Math.floor(playerY); let newTilesRevealed = 0; for (let dx = -this.revealRadius; dx <= this.revealRadius; dx++) { for (let dy = -this.revealRadius; dy <= this.revealRadius; dy++) { const x = gridX + dx; const y = gridY + dy; // Check if within world bounds if (x < 0 || x >= 500 || y < 0 || y >= 500) continue; // Check if within circular radius const dist = Math.sqrt(dx * dx + dy * dy); if (dist > this.revealRadius) continue; // Reveal tile if (!this.revealed[y][x]) { this.revealed[y][x] = true; newTilesRevealed++; } } } if (newTilesRevealed > 0) { // Update minimap this.updateMinimap(); } } // Create minimap (bottom-right corner) createMinimap() { if (this.minimap) return; const size = this.minimapSize; const x = this.scene.cameras.main.width - size - 20; const y = this.scene.cameras.main.height - size - 20; // Background this.minimapBg = this.scene.add.rectangle(x, y, size, size, 0x000000, 0.7); this.minimapBg.setOrigin(0); this.minimapBg.setScrollFactor(0); this.minimapBg.setDepth(9000); // Border this.minimapBorder = this.scene.add.rectangle(x, y, size, size); this.minimapBorder.setOrigin(0); this.minimapBorder.setStrokeStyle(2, 0xFFFFFF); this.minimapBorder.setScrollFactor(0); this.minimapBorder.setDepth(9001); // Canvas for map rendering this.minimapTexture = this.scene.add.renderTexture(x, y, size, size); this.minimapTexture.setOrigin(0); this.minimapTexture.setScrollFactor(0); this.minimapTexture.setDepth(9002); // Label this.minimapLabel = this.scene.add.text(x + size / 2, y - 15, 'Map (M)', { fontSize: '14px', color: '#ffffff', backgroundColor: '#000000', padding: { x: 5, y: 2 } }); this.minimapLabel.setOrigin(0.5); this.minimapLabel.setScrollFactor(0); this.minimapLabel.setDepth(9003); this.minimap = { bg: this.minimapBg, border: this.minimapBorder, texture: this.minimapTexture, label: this.minimapLabel, x, y, size }; this.updateMinimap(); } // Update minimap rendering updateMinimap() { if (!this.minimap) return; const player = this.scene.player; if (!player) return; const playerX = Math.floor(player.gridX); const playerY = Math.floor(player.gridY); // Clear this.minimap.texture.clear(); // Calculate view area (centered on player) const viewSize = Math.floor(this.minimap.size / this.minimapScale); const startX = Math.max(0, playerX - viewSize / 2); const startY = Math.max(0, playerY - viewSize / 2); const endX = Math.min(500, startX + viewSize); const endY = Math.min(500, startY + viewSize); // Draw revealed tiles for (let y = startY; y < endY; y++) { for (let x = startX; x < endX; x++) { if (!this.revealed[y] || !this.revealed[y][x]) continue; const screenX = (x - startX) * this.minimapScale; const screenY = (y - startY) * this.minimapScale; // Get biome color const biome = this.scene.biomeSystem ? this.scene.biomeSystem.getBiome(x, y) : 'Grassland'; const color = this.getBiomeMinimapColor(biome); this.minimap.texture.fill(color, 1, screenX, screenY, this.minimapScale, this.minimapScale); } } // Draw player position const playerScreenX = (playerX - startX) * this.minimapScale; const playerScreenY = (playerY - startY) * this.minimapScale; this.minimap.texture.fill(0xFFFF00, 1, playerScreenX - 2, playerScreenY - 2, 4, 4); } // Get minimap color for biome getBiomeMinimapColor(biome) { const colors = { 'Grassland': 0x3CB371, 'Forest': 0x2d5016, 'Desert': 0xd4c4a1, 'Mountain': 0x808080, 'Swamp': 0x3d5a3d }; return colors[biome] || 0x3CB371; } // Toggle full map view (M key) toggleFullMap() { if (this.mapOpen) { this.closeFullMap(); } else { this.openFullMap(); } } // Open full map openFullMap() { if (this.mapOpen) return; this.mapOpen = true; // Semi-transparent background this.fullMapBg = this.scene.add.rectangle( this.scene.cameras.main.centerX, this.scene.cameras.main.centerY, this.scene.cameras.main.width, this.scene.cameras.main.height, 0x000000, 0.9 ); this.fullMapBg.setScrollFactor(0); this.fullMapBg.setDepth(15000); // Map title this.fullMapTitle = this.scene.add.text( this.scene.cameras.main.centerX, 50, 'World Map (Press M to close)', { fontSize: '32px', color: '#FFD700', backgroundColor: '#000000', padding: { x: 20, y: 10 } } ); this.fullMapTitle.setOrigin(0.5); this.fullMapTitle.setScrollFactor(0); this.fullMapTitle.setDepth(15001); // Render full map const mapSize = 600; const mapX = this.scene.cameras.main.centerX - mapSize / 2; const mapY = this.scene.cameras.main.centerY - mapSize / 2; this.fullMapTexture = this.scene.add.renderTexture(mapX, mapY, mapSize, mapSize); this.fullMapTexture.setScrollFactor(0); this.fullMapTexture.setDepth(15002); // Draw entire world const scale = mapSize / 500; for (let y = 0; y < 500; y += 5) { for (let x = 0; x < 500; x += 5) { if (!this.revealed[y] || !this.revealed[y][x]) continue; const biome = this.scene.biomeSystem ? this.scene.biomeSystem.getBiome(x, y) : 'Grassland'; const color = this.getBiomeMinimapColor(biome); this.fullMapTexture.fill(color, 1, x * scale, y * scale, scale * 5, scale * 5); } } // Player position if (this.scene.player) { const px = Math.floor(this.scene.player.gridX) * scale; const py = Math.floor(this.scene.player.gridY) * scale; this.fullMapTexture.fill(0xFFFF00, 1, px - 3, py - 3, 6, 6); } // Stats const exploredTiles = this.revealed.flat().filter(r => r).length; const totalTiles = 500 * 500; const percent = ((exploredTiles / totalTiles) * 100).toFixed(1); this.fullMapStats = this.scene.add.text( this.scene.cameras.main.centerX, mapY + mapSize + 30, `Explored: ${exploredTiles} / ${totalTiles} tiles (${percent}%)`, { fontSize: '20px', color: '#ffffff', backgroundColor: '#000000', padding: { x: 15, y: 8 } } ); this.fullMapStats.setOrigin(0.5); this.fullMapStats.setScrollFactor(0); this.fullMapStats.setDepth(15003); } // Close full map closeFullMap() { if (!this.mapOpen) return; this.mapOpen = false; if (this.fullMapBg) { this.fullMapBg.destroy(); this.fullMapTitle.destroy(); this.fullMapTexture.destroy(); this.fullMapStats.destroy(); } } // Update (called every frame) update() { if (this.scene.player) { this.revealArea(this.scene.player.gridX, this.scene.player.gridY); } } // Get exploration statistics getStats() { const exploredTiles = this.revealed.flat().filter(r => r).length; const totalTiles = 500 * 500; const percent = ((exploredTiles / totalTiles) * 100).toFixed(2); return { explored: exploredTiles, total: totalTiles, percent: parseFloat(percent) }; } // Export/Import for save system exportData() { // Convert 2D array to compressed format const compressed = []; for (let y = 0; y < 500; y++) { for (let x = 0; x < 500; x++) { if (this.revealed[y][x]) { compressed.push(`${x},${y}`); } } } return { revealed: compressed }; } importData(data) { if (!data || !data.revealed) return; // Clear current this.revealed = Array(500).fill(null).map(() => Array(500).fill(false)); // Decompress for (const key of data.revealed) { const [x, y] = key.split(',').map(Number); if (x >= 0 && x < 500 && y >= 0 && y < 500) { this.revealed[y][x] = true; } } } destroy() { this.closeFullMap(); if (this.minimap) { this.minimap.bg.destroy(); this.minimap.border.destroy(); this.minimap.texture.destroy(); this.minimap.label.destroy(); this.minimap = null; } } }