narejeno
This commit is contained in:
456
src/systems/FogOfWarSystem.js
Normal file
456
src/systems/FogOfWarSystem.js
Normal file
@@ -0,0 +1,456 @@
|
||||
/**
|
||||
* 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: true,
|
||||
fogColor: 0x000000,
|
||||
fogAlpha: 0.8,
|
||||
exploredAlpha: 0.3,
|
||||
visibleRadius: 5, // tiles
|
||||
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
|
||||
*/
|
||||
renderFog() {
|
||||
if (!this.fogGraphics) return;
|
||||
|
||||
this.fogGraphics.clear();
|
||||
|
||||
// Get camera bounds
|
||||
const cam = this.scene.cameras.main;
|
||||
const offsetX = this.scene.terrainOffsetX || 0;
|
||||
const offsetY = this.scene.terrainOffsetY || 0;
|
||||
|
||||
// Render fog tiles
|
||||
for (let y = 0; y < this.gridHeight; y++) {
|
||||
for (let x = 0; x < this.gridWidth; x++) {
|
||||
const state = this.fogGrid[y][x];
|
||||
|
||||
// Convert grid to screen coordinates
|
||||
const iso = this.scene.iso || { toScreen: (x, y) => ({ x: x * 32, y: y * 32 }) };
|
||||
const screenPos = iso.toScreen(x, y);
|
||||
const screenX = screenPos.x + offsetX;
|
||||
const screenY = screenPos.y + offsetY;
|
||||
|
||||
// Only render if on screen
|
||||
if (screenX < cam.scrollX - 100 || screenX > cam.scrollX + cam.width + 100) continue;
|
||||
if (screenY < cam.scrollY - 100 || screenY > cam.scrollY + cam.height + 100) continue;
|
||||
|
||||
if (state === 0) {
|
||||
// Unexplored - full fog
|
||||
this.fogGraphics.fillStyle(this.settings.fogColor, this.settings.fogAlpha);
|
||||
this.fogGraphics.fillRect(screenX - 32, screenY - 16, 64, 32);
|
||||
} else if (state === 1) {
|
||||
// Explored but not visible - light fog
|
||||
this.fogGraphics.fillStyle(this.settings.fogColor, this.settings.exploredAlpha);
|
||||
this.fogGraphics.fillRect(screenX - 32, screenY - 16, 64, 32);
|
||||
}
|
||||
// state === 2 (visible) - no fog
|
||||
}
|
||||
}
|
||||
|
||||
// Smooth edges if enabled
|
||||
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');
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user