KB: document the searchlight fog-swap latent bug (task #63, left as-is by decision)

Investigation (workflow) found the searchlight-driven fog swap never worked in the
ORIGINAL 1995 binary either -- it's a latent bug, not a port regression:
SearchlightSimulation (@004b841c) reads requestedOn@0x1E0 (never written) while
ToggleLamp (@004b860c) toggles commandedOn@0x1DC; no bridge exists, so lightState
@0x1D8 is perpetually 0 (lamp never lights). The self-consistent sibling
ThermalSight reads the field it toggles (0x1DC) -- the tell. The port reproduces
the bug faithfully (searchlight.cpp:189). Additionally the port never constructs
PullFogRenderable, so even absent the bug the swap wouldn't fire.

Decision: LEAVE AS-IS + document (faithful to the buggy original); the port keeps
the static lights-ON fog= values. A working swap would DEVIATE from the shipped
binary (repair the sim to read commandedOn + construct PullFogRenderable at the
btl4vid MakeMechRenderables inside pass + a toggle input) -- fully scoped in the KB
if ever wanted. No code change this commit.

Documented in rendering.md (fog section), open-questions.md (deferred subsystems),
subsystems.md (Searchlight). Core per-map/time/weather fog (task #63) is unaffected.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
arcattack
2026-07-13 22:04:29 -05:00
co-authored by Claude Opus 4.8
parent 225fa5951b
commit c0a0ec5e69
3 changed files with 39 additions and 9 deletions
+18 -9
View File
@@ -67,15 +67,24 @@ DECLOUDS must stay in the sky pass). Authored TEXTURE **SCROLL** (BMF TEXTURE ta
arcade. Night fog is dark BY DESIGN (distance→murk, not light haze); day pages author light haze. Env:
`BT_FOGMODE=table|vertex|off`, `BT_WEATHER=clear|fog|soup`, `BT_FOG="near far r g b"` (all branches);
`[fog] resolved`/`[fog] model`/`[fogstyle]` (BT_FOG_LOG) log lines. [T1 pipeline / T0 arcade model / T2 fix]
**⚠ SEARCHLIGHT SWAP IS INERT [T1, verified 2026-07-13].** The arcade swaps the active fog between
`fog=` (headlight ON) and `nosearchlightfog=` (OFF, darker/tighter) via `PullFogRenderable::Execute`
(L4VIDRND.cpp:4601, edge-triggered on two light attrs → `SetFogStyle(searchLightOn/OffFogStyle)`). In
the port **`PullFogRenderable` is NEVER CONSTRUCTED** (grep: only its .h decl + .cpp def; no `new`), and
`searchLightOn/Off` is called from nowhere else — so the swap never fires (BT_FOG_LOG: over 22s only
`SetFogStyle(0)`, zero style 2/3). Fog is STATIC at the searchlight-ON (`fog=`) values; the
`nosearchlightfog=` variant is dead. Reconstruction gap: wiring needs the mech's headlight/searchlight
state (Searchlight subsys 3032) bound to a `PullFogRenderable` constructed at viewpoint/mech setup.
Core per-map/time/weather fog is unaffected (uses the lights-ON set, the normal play state).
**⚠ SEARCHLIGHT SWAP IS INERT — and it was inert in the ARCADE too (task #63 workflow, 2026-07-13).**
The arcade swaps the active fog between `fog=` (headlight ON) and `nosearchlightfog=` (OFF,
darker/tighter) via `PullFogRenderable::Execute` (L4VIDRND.cpp:4601, edge-triggered on two light attrs →
`SetFogStyle(searchLightOn/OffFogStyle)`). TWO independent reasons it never fires:
(1) **Port:** `PullFogRenderable` is NEVER CONSTRUCTED (grep: only its .h decl + .cpp def; no `new`);
`searchLightOn/Off` is called from nowhere else (BT_FOG_LOG: over 22s only `SetFogStyle(0)`, zero 2/3).
(2) **Original 1995 LATENT BUG [T1, decomp]:** even if wired, the Searchlight never lights —
`ToggleLamp` (@004b860c) toggles `commandedOn` @0x1DC (part_013.c:6087) but `SearchlightSimulation`
(@004b841c) reads `requestedOn` @0x1E0 (part_013.c:5978), which the ctor zeroes and NOTHING ever writes;
no bridge 0x1DC→0x1E0 exists anywhere in the binary. So `lightState`@0x1D8 is perpetually 0. The
self-consistent sibling **ThermalSight** reads the SAME field it toggles (0x1DC) — the tell that
Searchlight's sim reads the wrong slot. The port reproduces this faithfully (searchlight.cpp:189 reads
requestedOn). **DECISION (2026-07-13): left as-is + documented** (faithful to the buggy original); the
port keeps the static lights-ON `fog=` values. To make the swap WORK is a DESIGNER-INTENT fix that
DEVIATES from the shipped binary — repair the sim to read `commandedOn` (mirror ThermalSight) + construct
`PullFogRenderable` at btl4vid.cpp `MakeMechRenderables` (the reticle-build/inside pass, == arcade
part_014.c:5173, ExecutionType Dynamic, bound to each Searchlight `lightState`) + a toggle input. See
[[open-questions]]. Core per-map/time/weather fog is unaffected.
- **Projection / stencil (task #55):** the BTDPL.INI `viewangle=60` is the **HORIZONTAL** FOV —
authentic 60×47 frustum at 4:3; fovY derived via `BTFovYFromHorizontal` (L4VIDEO.cpp), applied at
all 5 projection sites (it was mis-applied as vertical). The device depth-stencil is now