From b1b103c9d7198f8e40e889f1288acbad91a9f0de Mon Sep 17 00:00:00 2001 From: jwinterm Date: Mon, 13 Jul 2026 08:24:48 -0400 Subject: [PATCH] fix(fog): fade explored memory to black + unify off-map dark (no map-edge leak) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Explored-but-not-visible tiles rendered at a flat 0.25 brightness — a hard-edged grey block that outlined the explored map (and its edge). Now explored memory dims AND fades toward black with distance from the player, so the remembered area melts into the dark with no revealing edge. Also set the render host background to pure #000 so the area beyond the map edge is the SAME black as an unexplored in-bounds tile (previously #0a0c0f vs #000 — a shade seam that traced the boundary). Currently-visible lighting unchanged. --- html/js/render/sceneModel.js | 16 ++++++++++++++-- html/js/render/spGameRenderer.js | 4 +++- 2 files changed, 17 insertions(+), 3 deletions(-) diff --git a/html/js/render/sceneModel.js b/html/js/render/sceneModel.js index 1b961fa..ce63972 100644 --- a/html/js/render/sceneModel.js +++ b/html/js/render/sceneModel.js @@ -167,6 +167,11 @@ function sceneFromGameState(state, opts) { var rows = state.dungeonRows || visible.length || dims.rows; var cols = state.dungeonCols || dims.cols; + // Player cell — explored MEMORY fades to black with distance from here (see below). + var _pl = state.player || {}; + var _plx = typeof _pl.x === 'number' ? _pl.x : (cols / 2); + var _ply = typeof _pl.y === 'number' ? _pl.y : (rows / 2); + var grid = []; var lightGrid = []; for (var y = 0; y < rows; y++) { @@ -183,11 +188,18 @@ function sceneFromGameState(state, opts) { if (ch === null || ch === undefined || ch === ' ') { row.push('dark'); lrow.push(0); - } else { + } else if (isVisible) { row.push(dungeonTileKind(ch)); // Lighting: 0 = fully lit, higher = darker. Convert to brightness (1 = lit). var la = (lighting[y] && lighting[y][x]) || 0; - lrow.push(isVisible ? Math.max(0.15, 1 - Math.min(la, 0.8)) : 0.25); + lrow.push(Math.max(0.15, 1 - Math.min(la, 0.8))); + } else { + // Explored MEMORY (seen before, not currently visible): dim, and fading toward black + // with distance from the player, so the remembered area melts into the dark instead + // of forming a hard-edged grey block that outlines the map's shape/edge (a FoW leak). + row.push(dungeonTileKind(ch)); + var _dx = x - _plx, _dy = y - _ply; + lrow.push(Math.max(0, 0.26 - Math.sqrt(_dx * _dx + _dy * _dy) * 0.019)); } } grid.push(row); diff --git a/html/js/render/spGameRenderer.js b/html/js/render/spGameRenderer.js index 6754224..2234337 100644 --- a/html/js/render/spGameRenderer.js +++ b/html/js/render/spGameRenderer.js @@ -30,8 +30,10 @@ // (the .container is height:100vh; overflow:hidden), so filling it gives the camera // reliable clientWidth/clientHeight every frame. The earlier `min(78vh)` + translate(-50%) // centering could resolve to a wrong/transient size, stranding the view in a top band. + // Pure-black background so the area BEYOND the map edge is the SAME black as an + // unexplored ('dark') in-bounds tile — otherwise the shade difference outlines the map. host.style.cssText = 'display:none; position:absolute; inset:0;' + - ' overflow:hidden; background:#0a0c0f; z-index:6; touch-action:none; cursor:grab;'; + ' overflow:hidden; background:#000; z-index:6; touch-action:none; cursor:grab;'; gd.appendChild(host); } return host;