diff --git a/src/game/engine/light-grid.ts b/src/game/engine/light-grid.ts index 7589e37..a809342 100644 --- a/src/game/engine/light-grid.ts +++ b/src/game/engine/light-grid.ts @@ -233,8 +233,8 @@ export class LightGrid { const cellCenterSubX = cx * SUB_TILES_PER_LIGHT_CELL + (SUB_TILES_PER_LIGHT_CELL >> 1) const dSubX = cellCenterSubX - relSubX - // Fast Euclidean distance approximation - const dist = fastDistanceApprox(dSubX, dSubY) + // Fast Euclidean distance approximation (emitter's own cell receives full peak intensity at dist = 0) + const dist = (cx === centerCellX && cy === centerCellY) ? 0 : fastDistanceApprox(dSubX, dSubY) if (dist >= effectiveRadius) continue // Linear falloff: ((R - dist) * IntensitySlope) >> 16 diff --git a/src/render/renderer.ts b/src/render/renderer.ts index 8867f01..09f4563 100644 --- a/src/render/renderer.ts +++ b/src/render/renderer.ts @@ -233,7 +233,8 @@ ${cases.join('\n')} ${paletteLookup}${discardAlpha} if (u_lightMode == 1 && u_blendMode == 0) { vec4 lightSample = texture(u_lightmap, v_lightUv); - vec3 totalLight = clamp(u_ambient.rgb + lightSample.rgb, 0.0, 1.0); + vec3 dynamicLight = lightSample.rgb * (0.22 + 0.78 * lightSample.a); + vec3 totalLight = clamp(u_ambient.rgb + dynamicLight, 0.0, 1.0); outColor = vec4(texel.rgb * totalLight * v_tint.rgb, texel.a * v_tint.a); } else { outColor = texel * v_tint; diff --git a/src/scene/act-scene.ts b/src/scene/act-scene.ts index a6f2bf1..2a95d61 100644 --- a/src/scene/act-scene.ts +++ b/src/scene/act-scene.ts @@ -402,25 +402,25 @@ export const LIGHTING_PRESETS: Readonly 0 ? rawIntensity : 14 - const effR = overrideAmb !== undefined ? overrideAmb.red : (rawIntensity > 0 ? environment.currentRed : 225) - const effG = overrideAmb !== undefined ? overrideAmb.green : (rawIntensity > 0 ? environment.currentGreen : 215) - const effB = overrideAmb !== undefined ? overrideAmb.blue : (rawIntensity > 0 ? environment.currentBlue : 205) + const effectiveAmbIntensity = rawIntensity > 0 ? rawIntensity : 5 + const effR = overrideAmb !== undefined ? overrideAmb.red : (rawIntensity > 0 ? environment.currentRed : 185) + const effG = overrideAmb !== undefined ? overrideAmb.green : (rawIntensity > 0 ? environment.currentGreen : 175) + const effB = overrideAmb !== undefined ? overrideAmb.blue : (rawIntensity > 0 ? environment.currentBlue : 165) const ambNorm = effectiveAmbIntensity / 255 const ambR = (effR / 255) * ambNorm const ambG = (effG / 255) * ambNorm diff --git a/tests/scene-lighting.test.ts b/tests/scene-lighting.test.ts index 9e88e1a..082fc2d 100644 --- a/tests/scene-lighting.test.ts +++ b/tests/scene-lighting.test.ts @@ -397,7 +397,7 @@ describe('Scene Lighting & Environment Integration', () => { expect(normalizeLightingPreset('eclipse')).toBe('eclipse') expect(LIGHTING_PRESETS.noon.overrideAmbient?.intensity).toBe(255) - expect(LIGHTING_PRESETS.cave.overrideAmbient?.intensity).toBe(14) + expect(LIGHTING_PRESETS.cave.overrideAmbient?.intensity).toBe(5) expect(LIGHTING_PRESETS.off.enabled).toBe(false) const actsHtml = fs.readFileSync('acts.html', 'utf8') @@ -405,5 +405,32 @@ describe('Scene Lighting & Environment Integration', () => { for (const preset of Object.keys(LIGHTING_PRESETS)) { expect(actsHtml).toContain(`value="${preset}"`) } + }, 15000) + + it('keeps cave player torch (radius=20 sub-tiles) and wall torches (radius=13 sub-tiles) localized without saturating outer viewport cells', () => { + const grid = new LightGrid() + grid.centerOnSubTile(200, 200) + + // Add calibrated cave player torch at center (200, 200), radius=20 sub-tiles (~240px horizontal radius) + grid.addLight({ + subTileX: 200, + subTileY: 200, + radius: 20, + intensity: 242, + red: 255, + green: 234, + blue: 196, + }) + + const centerIdx = 24 * LIGHT_GRID_SIZE + 24 + // 1 cell away (dx=+1, dy=-1 => 8 sub-tiles each => dist=11, 160px horizontally): illuminated + const nearIdx = 23 * LIGHT_GRID_SIZE + 25 + // 3 cells away (dx=+3, dy=-3 => 24 sub-tiles each => dist=32, 480px horizontally, well within 640px half-screen): completely dark! + const outerScreenIdx = 21 * LIGHT_GRID_SIZE + 27 + + expect(grid.intensity[centerIdx]).toBeGreaterThan(220) + expect(grid.intensity[nearIdx]).toBeGreaterThan(80) + expect(grid.intensity[outerScreenIdx]).toBe(0) }) }) +