@ -4185,7 +4185,8 @@
// Fog of War state variables (must be declared before use)
let fogCanvas = null;
let fogCtx = null;
let playerRevealRadius = 800;
let playerRevealRadius = 800; // Homebase reveal radius (meters)
let playerExploreRadius = 50; // Player's personal reveal radius when exploring (meters)
// Initialize fog of war canvas (directly in map container for simple viewport alignment)
function initFogOfWar() {
@ -4260,52 +4261,96 @@
fogCtx.fillStyle = 'rgba(0, 0, 0, 0.6)';
fogCtx.fillRect(0, 0, width, height);
// If no homebase set, full fog
if (!playerStats || playerStats.homeBaseLat == null || playerStats.homeBaseLng == null) {
return;
// Homebase reveal (only if homebase exists)
if (playerStats & & playerStats.homeBaseLat != null & & playerStats.homeBaseLng != null) {
// Calculate reveal circle center using container coordinates (viewport-relative)
const homeLatLng = L.latLng(playerStats.homeBaseLat, playerStats.homeBaseLng);
const centerPoint = map.latLngToContainerPoint(homeLatLng);
// Calculate radius in pixels using map projection
const edgeLatLng = destinationPoint(homeLatLng, playerRevealRadius, 90);
const edgePoint = map.latLngToContainerPoint(edgeLatLng);
const radiusPixels = Math.abs(edgePoint.x - centerPoint.x);
// Cut out revealed area using composite operation
fogCtx.save();
fogCtx.globalCompositeOperation = 'destination-out';
// Create gradient for soft edge
const gradient = fogCtx.createRadialGradient(
centerPoint.x, centerPoint.y, radiusPixels * 0.85,
centerPoint.x, centerPoint.y, radiusPixels
);
gradient.addColorStop(0, 'rgba(0, 0, 0, 1)');
gradient.addColorStop(1, 'rgba(0, 0, 0, 0)');
fogCtx.beginPath();
fogCtx.arc(centerPoint.x, centerPoint.y, radiusPixels, 0, Math.PI * 2);
fogCtx.fillStyle = gradient;
fogCtx.fill();
fogCtx.restore();
}
// Calculate reveal circle center using container coordinates (viewport-relative)
const homeLatLng = L.latLng(playerStats.homeBaseLat, playerStats.homeBaseLng);
const centerPoint = map.latLngToContainerPoint(homeLatLng);
// Secondary reveal: Player's explore radius when outside homebase
if (userLocation) {
const playerLatLng = L.latLng(userLocation.lat, userLocation.lng);
// Check if player is outside homebase radius (or no homebase)
let isOutsideHomebase = true;
if (playerStats & & playerStats.homeBaseLat != null & & playerStats.homeBaseLng != null) {
const homeLatLng = L.latLng(playerStats.homeBaseLat, playerStats.homeBaseLng);
const distToHome = playerLatLng.distanceTo(homeLatLng);
isOutsideHomebase = distToHome > playerRevealRadius;
}
// Calculate radius in pixels using map projection
const edgeLatLng = destinationPoint(homeLatLng, playerRevealRadius, 90);
const edgePoint = map.latLngToContainerPoint(edgeLatLng);
const radiusPixels = Math.abs(edgePoint.x - centerPoint.x);
if (isOutsideHomebase) {
const playerPoint = map.latLngToContainerPoint(playerLatLng);
const playerEdge = destinationPoint(playerLatLng, playerExploreRadius, 90);
const playerEdgePoint = map.latLngToContainerPoint(playerEdge);
const playerRadiusPixels = Math.abs(playerEdgePoint.x - playerPoint.x);
// Cut out revealed area using composite operation
fogCtx.save();
fogCtx.globalCompositeOperation = 'destination-out';
fogCtx.save();
fogCtx.globalCompositeOperation = 'destination-out';
// Create gradient for soft edge
const gradient = fogCtx.createRadialGradient(
centerPoint.x, centerPoint.y, radiusPixels * 0.85,
centerPoint.x, centerPoint.y, radiusPixels
);
gradient.addColorStop(0, 'rgba(0, 0, 0, 1)');
gradient.addColorStop(1, 'rgba(0, 0, 0, 0)');
const playerGradient = fogCtx.createRadialGradient(
playerPoint.x, playerPoint.y, playerRadiusPixels * 0.85,
playerPoint.x, playerPoint.y, playerRadiusPixels
);
playerGradient.addColorStop(0, 'rgba(0, 0, 0, 1)');
playerGradient.addColorStop(1, 'rgba(0, 0, 0, 0)');
fogCtx.beginPath();
fogCtx.arc(centerPoint.x, centerPoint.y, radiusPixels, 0, Math.PI * 2);
fogCtx.fillStyle = gradient;
fogCtx.fill();
fogCtx.beginPath();
fogCtx.arc(playerPoint.x, playerPoint.y, playerR adiusPixels, 0, Math.PI * 2);
fogCtx.fillStyle = playerG radient;
fogCtx.fill();
fogCtx.restore();
fogCtx.restore();
}
}
}
// Check if a location is within the revealed area
// Check if a location is within the revealed area (homebase or player explore radius)
function isInRevealedArea(lat, lng) {
// If no homebase, nothing is revealed
if (!playerStats || playerStats.homeBaseLat == null || playerStats.homeBaseLng == null) {
return false;
const checkPoint = L.latLng(lat, lng);
// Check homebase radius
if (playerStats & & playerStats.homeBaseLat != null & & playerStats.homeBaseLng != null) {
const homeLatLng = L.latLng(playerStats.homeBaseLat, playerStats.homeBaseLng);
if (homeLatLng.distanceTo(checkPoint) < = playerRevealRadius) {
return true;
}
}
// Calculate distance from homebase
const distance = L.latLng(playerStats.homeBaseLat, playerStats.homeBaseLng)
.distanceTo(L.latLng(lat, lng));
// Check player's explore radius
if (userLocation) {
const playerLatLng = L.latLng(userLocation.lat, userLocation.lng);
if (playerLatLng.distanceTo(checkPoint) < = playerExploreRadius) {
return true;
}
}
return distance < = playerRevealRadius;
return false ;
}
// Hook fog updates to map events (fog will be initialized lazily)
@ -5586,9 +5631,10 @@
// Check for monster clear at home base
checkHomeBaseMonsterClear();
// Update geocache visibility based on new location
// Update geocache visibility and fog of war based on new location
if (navMode) {
updateGeocacheVisibility();
updateFogOfWar();
}
// Update or create marker
@ -15680,6 +15726,9 @@
statsSyncState.inCombat = false;
flushStatsSync();
// Update fog of war to reflect current position after combat
updateFogOfWar();
// If victory music isn't playing, switch to appropriate ambient music
if (gameMusic.currentTrack !== 'victory' || gameMusic.victory.paused) {
const distToHome = getDistanceToHome();