Files
novafarma/EMERGENCY_SYSTEMS_RECOVERY/FogOfWarSystem.js
2026-01-16 02:43:46 +01:00

474 lines
13 KiB
JavaScript

/**
* FOG OF WAR SYSTEM
* Exploration and visibility system for unexplored areas
*/
class FogOfWarSystem {
constructor(scene) {
this.scene = scene;
this.enabled = true;
// Fog grid (matches terrain grid)
this.gridWidth = 100;
this.gridHeight = 100;
this.tileSize = 64;
// Fog states: 0 = unexplored, 1 = explored, 2 = visible
this.fogGrid = [];
this.fogSprites = new Map();
// Settings
this.settings = {
enabled: false, // DISABLED - use weather fog instead
fogColor: 0x000000,
fogAlpha: 0.15, // Very light animated fog
exploredAlpha: 0.05, // Almost invisible for explored areas
visibleRadius: 8, // tiles (increased more)
smoothEdges: true,
persistMemory: true,
refogDungeons: true
};
// Fog layer
this.fogLayer = null;
this.fogGraphics = null;
// Memory of explored areas
this.exploredAreas = new Set();
this.loadSettings();
this.init();
console.log('✅ Fog of War System initialized');
}
init() {
if (!this.settings.enabled) return;
// Initialize fog grid
this.initFogGrid();
// Create fog layer
this.createFogLayer();
// Load explored areas from memory
this.loadExploredAreas();
console.log('🌫️ Fog of War active');
}
/**
* Initialize fog grid
*/
initFogGrid() {
for (let y = 0; y < this.gridHeight; y++) {
this.fogGrid[y] = [];
for (let x = 0; x < this.gridWidth; x++) {
this.fogGrid[y][x] = 0; // Unexplored
}
}
}
/**
* Create fog layer
*/
createFogLayer() {
// Create layer for fog
this.fogLayer = this.scene.add.layer();
this.fogLayer.setDepth(9000); // Above game, below UI
// Create fog graphics
this.fogGraphics = this.scene.add.graphics();
this.fogGraphics.setDepth(9001);
// Initial full fog
this.renderFullFog();
}
/**
* Render full fog (all unexplored)
*/
renderFullFog() {
if (!this.fogGraphics) return;
this.fogGraphics.clear();
this.fogGraphics.fillStyle(this.settings.fogColor, this.settings.fogAlpha);
// Cover entire map
const width = this.scene.cameras.main.width;
const height = this.scene.cameras.main.height;
this.fogGraphics.fillRect(
-width,
-height,
width * 3,
height * 3
);
}
/**
* Update fog based on player position
*/
updateFog(playerX, playerY) {
if (!this.settings.enabled) return;
const gridX = Math.floor(playerX);
const gridY = Math.floor(playerY);
// Reveal area around player
this.revealArea(gridX, gridY, this.settings.visibleRadius);
// Update fog rendering
this.renderFog();
}
/**
* Reveal area around a point
*/
revealArea(centerX, centerY, radius) {
const radiusSq = radius * radius;
for (let y = centerY - radius; y <= centerY + radius; y++) {
for (let x = centerX - radius; x <= centerX + radius; x++) {
// Check if within grid bounds
if (x < 0 || x >= this.gridWidth || y < 0 || y >= this.gridHeight) {
continue;
}
// Check if within circular radius
const dx = x - centerX;
const dy = y - centerY;
const distSq = dx * dx + dy * dy;
if (distSq <= radiusSq) {
// Mark as explored
if (this.fogGrid[y][x] === 0) {
this.fogGrid[y][x] = 1; // Explored
this.exploredAreas.add(`${x},${y}`);
}
// Mark as currently visible
this.fogGrid[y][x] = 2; // Visible
}
}
}
// Save explored areas
if (this.settings.persistMemory) {
this.saveExploredAreas();
}
}
/**
* Render fog based on current state - ANIMATED FOG EFFECT
*/
renderFog() {
if (!this.fogGraphics) return;
this.fogGraphics.clear();
// Get camera bounds
const cam = this.scene.cameras.main;
const time = this.scene.time.now * 0.0005; // Slow animation
// Render fog as smooth animated overlay
for (let y = 0; y < this.gridHeight; y++) {
for (let x = 0; x < this.gridWidth; x++) {
const state = this.fogGrid[y][x];
// Skip if visible
if (state === 2) continue;
// Simple 2D tile coordinates
const screenX = x * this.tileSize;
const screenY = y * this.tileSize;
// Only render if on screen (with margin)
if (screenX < cam.scrollX - 100 || screenX > cam.scrollX + cam.width + 100) continue;
if (screenY < cam.scrollY - 100 || screenY > cam.scrollY + cam.height + 100) continue;
// Animated fog effect - wave pattern
const waveX = Math.sin(time + x * 0.1) * 5;
const waveY = Math.cos(time + y * 0.1) * 5;
if (state === 0) {
// Unexplored - animated fog particles
const alpha = this.settings.fogAlpha + Math.sin(time + x + y) * 0.05;
this.fogGraphics.fillStyle(this.settings.fogColor, alpha);
// Draw multiple overlapping circles for smooth fog
this.fogGraphics.fillCircle(
screenX + this.tileSize / 2 + waveX,
screenY + this.tileSize / 2 + waveY,
this.tileSize * 0.8
);
} else if (state === 1) {
// Explored but not visible - very light animated fog
const alpha = this.settings.exploredAlpha + Math.sin(time + x + y) * 0.02;
this.fogGraphics.fillStyle(this.settings.fogColor, alpha);
// Smaller, lighter circles
this.fogGraphics.fillCircle(
screenX + this.tileSize / 2 + waveX,
screenY + this.tileSize / 2 + waveY,
this.tileSize * 0.6
);
}
}
}
// Apply blur effect for smooth fog
if (this.settings.smoothEdges) {
this.fogGraphics.setBlendMode(Phaser.BlendModes.NORMAL);
}
}
/**
* Reset fog in area (for dungeons/caves)
*/
refogArea(x, y, width, height) {
if (!this.settings.refogDungeons) return;
for (let gy = y; gy < y + height; gy++) {
for (let gx = x; gx < x + width; gx++) {
if (gx >= 0 && gx < this.gridWidth && gy >= 0 && gy < this.gridHeight) {
this.fogGrid[gy][gx] = 0; // Back to unexplored
this.exploredAreas.delete(`${gx},${gy}`);
}
}
}
this.renderFog();
}
/**
* Clear all fog (reveal entire map)
*/
revealAll() {
for (let y = 0; y < this.gridHeight; y++) {
for (let x = 0; x < this.gridWidth; x++) {
this.fogGrid[y][x] = 2; // All visible
this.exploredAreas.add(`${x},${y}`);
}
}
this.renderFog();
}
/**
* Reset all fog (hide entire map)
*/
resetAll() {
for (let y = 0; y < this.gridHeight; y++) {
for (let x = 0; x < this.gridWidth; x++) {
this.fogGrid[y][x] = 0; // All unexplored
}
}
this.exploredAreas.clear();
this.renderFullFog();
}
/**
* Check if area is explored
*/
isExplored(x, y) {
if (x < 0 || x >= this.gridWidth || y < 0 || y >= this.gridHeight) {
return false;
}
return this.fogGrid[y][x] > 0;
}
/**
* Check if area is visible
*/
isVisible(x, y) {
if (x < 0 || x >= this.gridWidth || y < 0 || y >= this.gridHeight) {
return false;
}
return this.fogGrid[y][x] === 2;
}
/**
* Get exploration percentage
*/
getExplorationPercentage() {
let explored = 0;
const total = this.gridWidth * this.gridHeight;
for (let y = 0; y < this.gridHeight; y++) {
for (let x = 0; x < this.gridWidth; x++) {
if (this.fogGrid[y][x] > 0) {
explored++;
}
}
}
return (explored / total) * 100;
}
/**
* Enable fog of war
*/
enable() {
this.settings.enabled = true;
if (!this.fogLayer) {
this.createFogLayer();
}
this.fogLayer.setVisible(true);
this.saveSettings();
}
/**
* Disable fog of war
*/
disable() {
this.settings.enabled = false;
if (this.fogLayer) {
this.fogLayer.setVisible(false);
}
this.saveSettings();
}
/**
* Set visible radius
*/
setVisibleRadius(radius) {
this.settings.visibleRadius = Math.max(1, Math.min(20, radius));
this.saveSettings();
}
/**
* Set fog color
*/
setFogColor(color) {
this.settings.fogColor = color;
this.renderFog();
this.saveSettings();
}
/**
* Set fog alpha
*/
setFogAlpha(alpha) {
this.settings.fogAlpha = Phaser.Math.Clamp(alpha, 0, 1);
this.renderFog();
this.saveSettings();
}
/**
* Set explored alpha
*/
setExploredAlpha(alpha) {
this.settings.exploredAlpha = Phaser.Math.Clamp(alpha, 0, 1);
this.renderFog();
this.saveSettings();
}
/**
* Update (called every frame)
*/
update() {
if (!this.settings.enabled) return;
// Update fog based on player position
if (this.scene.player) {
const pos = this.scene.player.getPosition();
this.updateFog(pos.x, pos.y);
}
// Fade out visible areas that are no longer in range
this.fadeDistantAreas();
}
/**
* Fade distant areas back to explored state
*/
fadeDistantAreas() {
if (!this.scene.player) return;
const pos = this.scene.player.getPosition();
const playerX = Math.floor(pos.x);
const playerY = Math.floor(pos.y);
const radius = this.settings.visibleRadius;
const radiusSq = radius * radius;
for (let y = 0; y < this.gridHeight; y++) {
for (let x = 0; x < this.gridWidth; x++) {
if (this.fogGrid[y][x] === 2) {
// Check if still in visible range
const dx = x - playerX;
const dy = y - playerY;
const distSq = dx * dx + dy * dy;
if (distSq > radiusSq) {
// Fade back to explored
this.fogGrid[y][x] = 1;
}
}
}
}
}
/**
* Save explored areas to localStorage
*/
saveExploredAreas() {
if (!this.settings.persistMemory) return;
const data = Array.from(this.exploredAreas);
localStorage.setItem('novafarma_explored_areas', JSON.stringify(data));
}
/**
* Load explored areas from localStorage
*/
loadExploredAreas() {
if (!this.settings.persistMemory) return;
const saved = localStorage.getItem('novafarma_explored_areas');
if (saved) {
try {
const data = JSON.parse(saved);
this.exploredAreas = new Set(data);
// Apply to fog grid
for (const key of this.exploredAreas) {
const [x, y] = key.split(',').map(Number);
if (x >= 0 && x < this.gridWidth && y >= 0 && y < this.gridHeight) {
this.fogGrid[y][x] = 1; // Explored
}
}
console.log(`🗺️ Loaded ${this.exploredAreas.size} explored tiles`);
} catch (error) {
console.error('Failed to load explored areas:', error);
}
}
}
/**
* Save settings
*/
saveSettings() {
localStorage.setItem('novafarma_fog_of_war', JSON.stringify(this.settings));
}
/**
* Load settings
*/
loadSettings() {
const saved = localStorage.getItem('novafarma_fog_of_war');
if (saved) {
this.settings = { ...this.settings, ...JSON.parse(saved) };
}
}
/**
* Destroy system
*/
destroy() {
if (this.fogLayer) this.fogLayer.destroy();
if (this.fogGraphics) this.fogGraphics.destroy();
this.saveExploredAreas();
console.log('🌫️ Fog of War System destroyed');
}
}