Searchlight + ThermalSight ToggleLamp WIRED (#61) -- and a swapped table attribution ROOT-CAUSED, retracting a false "1995 latent bug"
Both classes' Receiver::MessageHandlerSet were default-constructed blackholes (input-path audit systemic cause #1): entryCount 0, no parent chain, Find() returns NullHandler for every id, Receive drops silently. The new unhandled- message trace printed it on the first press: [btntest] PRESS 0x14 at poll 400 [msg] UNHANDLED: Searchlight has no handler for message id 3 Each class now has a GetMessageHandlers() function-local static chained to PowerWatcher's (which name-resolves through HeatWatcher/MechSubsystem to Receiver's empty ROOT set, so id 3 does NOT collide with HeatSink's ToggleCooling), plus a correctly-typed forwarder -- Receiver::Handler is void(const Message*) while the decomp shape is Logical(Message&), so a cast would be UB that merely happens to work on x86 __thiscall. DefaultData re-pointed off the dead empty sets. MessageArg now reads the NAMED ReceiverDataMessageOf<int>::dataContents with a static_assert locking it to the binary's message+0xC (was a raw offset read -- databinding rule). ROOT CAUSE of a long-standing misattribution: @004b860c is **ThermalSight's** ToggleLamp (table @0x51120C), NOT Searchlight's. The two TUs emit parallel shared-data blocks with identical stride (msg entry, +0x5C attrs, +0x84 Performance triple); what pins each entry to its TU is that "ToggleLamp" is NOT pooled across them -- two copies exist, each emitted immediately before its own class-name string ("Searchlight"@0x51144B, "ThermalSight"@0x511475). [T1: reference/decomp/section_dump.txt:69661-69711] Searchlight's own handler is @004b838c, which sits in a Ghidra EXPORT GAP (#60). RECOVERED by raw disassembly of content/BTL4OPT.EXE (scratchpad/dis838c.py, the EjectAmmo technique) -- and it corrected two things I had inferred wrong: * NO ControlsAllowLights/+0x25C novice gate. Searchlight tests the press alone; that lock is ThermalSight-only. A NOVICE pilot CAN work the lamp. * `or word ptr [this+0x18],1` sits AT the jle target -> the graphics-dirty bit (updateModel == ForceUpdate(), per #59) is raised UNCONDITIONALLY. Consequence: the "ORIGINAL 1995 LATENT BUG -- the searchlight can never light" claim is RETRACTED. It compared Searchlight's Performance (@004b841c, reads requestedOn@0x1E0) against ThermalSight's toggle (0x1DC) -- two different classes -- and so invented a missing 0x1DC->0x1E0 bridge. Every sibling toggles the field its own Performance reads. The searchlight DID light in the arcade, the searchlight->fog swap was NOT inert there, and building PullFogRenderable is FAITHFUL rather than a designer-intent deviation (the 2026-07-13 "left as-is" decision is void; the sim needs no repair). Verified live, real click seam (BT_BTNTEST): 0x14 -> [light] requested ON -> reported lightState 0 -> 1 (lamp lights) 0x12 -> [light] thermal sight requested ON -> thermalActive 0 -> 1 UNHANDLED lines for both classes gone; 40 LNK2019 unchanged (the pre-existing CreateStreamedSubsystem + Entity__SharedData::DefaultData families only). Swept the misattribution out of searchlight.cpp/.hpp, thermalsight.cpp/.hpp, hud.cpp:282 (it had claimed @00511180/@004b838c as HUD's), btplayer.cpp's +0x25C consumer list, context/{decomp-reference,subsystems,rendering,open-questions, pod-hardware,experience-levels}.md, docs/{GLASS_COCKPIT,INPUT_PATH_AUDIT}.md. checkctx.py CLEAN. Still open (documented, not fixed): ThermalSight has no visible IR effect (ToggleGlobalThermalVision is a marked no-op, pvision unported, IsLocallyViewed returns False); ThermalSight publishes "LightState"->0x1D8 where the binary has "LightOn"->0x1D8 and "LightState"->0x1E0; Searchlight's commandedOn@0x1DC has no identified role and measured 21 live, hinting our SubsystemResource +0x28 differs from the binary's. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5
parent
62513b2f8a
commit
f3bdb3b85a
@@ -263,9 +263,24 @@ From the weapon `.SUB` records + the charge-curve `.data` constants (PE-parsed a
|
||||
template: `Condenser::MoveValveMessageHandler` (id 4, same novice guard). See [[experience-levels]].
|
||||
- **Generator** @0x50FB90: {4, "ToggleGeneratorOnOff"→@004b1ed0} (streamed buttons 0x1A-0x1D →
|
||||
GeneratorA-D — unreconstructed).
|
||||
- **Searchlight** @0x51120C: {3, "ToggleLamp"→@004b860c}; PowerWatcher/ThermalSight-side variant
|
||||
@0x51117C: {3, "ToggleLamp"→@004b838c}. (Reconstruction has Searchlight::ToggleLamp but the
|
||||
MessageHandlers set is default-constructed EMPTY — unwired.)
|
||||
- **Searchlight** @0x51117C: {3, "ToggleLamp"→**@004b838c**}; **ThermalSight** @0x51120C: {3,
|
||||
"ToggleLamp"→**@004b860c**}. ⚠ **CORRECTED 2026-07-25 (#61)** — these were swapped here (and in
|
||||
`searchlight.cpp:51`, `hud.cpp:282`), which propagated a false "1995 latent bug" across four
|
||||
context files; see the WAVE-4 note in [[subsystems]]. The two TUs emit PARALLEL shared-data blocks
|
||||
with identical stride — msg entry, +0x5C attribute entries, +0x84 Performance triple:
|
||||
| TU | msg entry | handler | attributes | Performance |
|
||||
|---|---|---|---|---|
|
||||
| Searchlight | @0x51117C {3,"ToggleLamp"@0x511440} | @004b838c | LightOn→0x1D8, LightState→0x1E4 | @004b841c (reads **0x1E0**) |
|
||||
| ThermalSight | @0x51120C {3,"ToggleLamp"@0x51146A} | @004b860c | LightOn→0x1D8, LightState→0x1E0 | @004b8648 (reads **0x1DC**) |
|
||||
What pins each entry to its TU: **"ToggleLamp" is NOT pooled across the two** (two copies exist)
|
||||
and each sits immediately before its own class-name string — `"Searchlight"@0x51144B` and
|
||||
`"ThermalSight"@0x511475`. [T1: `reference/decomp/section_dump.txt:69661-69711`]
|
||||
**@004b838c is a Ghidra EXPORT GAP (#60 list)** — `functions_index.tsv` jumps straight to 4b83b8.
|
||||
**RECOVERED 2026-07-25 by raw disasm** of `content/BTL4OPT.EXE` (`scratchpad/dis838c.py`, the
|
||||
EjectAmmo technique) [T1]: `if (msg+0xC > 0) [this+0x1E0] = ([this+0x1E0]==0);` then
|
||||
`or word ptr [this+0x18],1` **unconditionally** (== `ForceUpdate()`, per #59). Note it has **NO**
|
||||
novice/+0x25C gate — that is ThermalSight-only; see [[experience-levels]].
|
||||
**Both sets WIRED 2026-07-25 (#61)**; both were default-constructed blackholes before.
|
||||
- **Myomers-class** @0x51158C: {9, "ToggleSeekVoltage"→@004b8a48}; **energy-weapon** variant
|
||||
@0x511DB8: {0xb, "ToggleSeekVoltage"→@004ba478}; **ammo-weapon** @0x512210: {0xb,
|
||||
"EjectAmmo"→@004bb9b8}. **Both ToggleSeekVoltage handlers WIRED 2026-07-21 (issue #19)**:
|
||||
|
||||
@@ -43,8 +43,12 @@ then unconditionally `+0x264 = +0x268 = mission->advancedDamageOn(+0xf0)` (both
|
||||
## What each flag gates (binary consumers, all reached via `mech+0x190` → BTPlayer) [T1]
|
||||
|
||||
- **+0x25c — "sim live", off only for novice.** Consumers: ballistic jam roll `CheckForJam`
|
||||
@4bbfcc early-returns NO-JAM when 0 (projweap.cpp calls this `LiveFireEnabled`); Searchlight
|
||||
`ToggleLamp` @4b860c (searchlight.cpp `ControlsAllowLights`); PoweredSubsystem message handler
|
||||
@4bbfcc early-returns NO-JAM when 0 (projweap.cpp calls this `LiveFireEnabled`); **ThermalSight**
|
||||
`ToggleLamp` @4b860c (thermalsight.hpp `ControlsAllowLights`) — ⚠ **CORRECTED 2026-07-25 (#61):
|
||||
this was listed as *Searchlight's* gate. It is not. Raw disassembly of Searchlight's real handler
|
||||
@004b838c (a Ghidra export gap) shows it gates on the PRESS ALONE and never reads +0x25C, so a
|
||||
NOVICE pilot CAN work the searchlight; only the thermal sight is locked** [T1, `scratchpad/dis838c.py`];
|
||||
PoweredSubsystem message handler
|
||||
@4b0efc (powersub.cpp's "message-manager short-event flag" — same flag, misattributed); the mech
|
||||
combat-ineffective evaluation (part_012.c:9364: mech state ∈ {3,4} && novice is an extra OR-term
|
||||
setting `mech+0x414`). The `#if 0` block in surviving PPC.CPP:63-95 (novice PPCs deal no damage)
|
||||
@@ -105,8 +109,9 @@ The wiring landed (uncommitted during the coordination pause; issue #2):
|
||||
consumers route mech→`GetPlayerLink()`→named member; NULL player reads permissive [T3].
|
||||
- **Gates unified**: `HeatSink::HeatModelActive()` (heat.hpp; also serves mislanch/projweap/
|
||||
heatfamily call sites) + `Myomers::OwnerAdvancedDamage()` (myomers.hpp) → the +0x260 bridge;
|
||||
`ProjectileWeapon::LiveFireEnabled()` (projweap.cpp) + `Searchlight::ControlsAllowLights()`
|
||||
(searchlight.hpp) → the +0x25c bridge; `PoweredSubsystem::HandleMessage` @4b0efc (powersub.cpp)
|
||||
`ProjectileWeapon::LiveFireEnabled()` (projweap.cpp) + `ThermalSight::ControlsAllowLights()`
|
||||
(thermalsight.hpp) → the +0x25c bridge (**not** `Searchlight::ControlsAllowLights()`, which #61
|
||||
showed is unused — its handler has no novice gate; the accessor is retained but dead); `PoweredSubsystem::HandleMessage` @4b0efc (powersub.cpp)
|
||||
→ not-novice + the +0x25c bridge (the old "message-manager short-event flag" misattribution and
|
||||
its dead `BT_GetMessageManager` guard are gone).
|
||||
- **FUN_004ac9c8 family corrected**: `MechSubsystem::IsDamaged` (mechsub.cpp — was a live
|
||||
|
||||
+20
-18
@@ -180,24 +180,26 @@ register. ⚠ The audit also flags the damage-economy item as SELF-CONTRADICTOR
|
||||
@004bb9b8 (msg 0xb ammo weapons), and MechRIOMapper's own Keypress @004d2514 (id 0x19).
|
||||
Filed as the Gitea "glass panel: unreconstructed button handlers backlog" issue; tables in
|
||||
[[decomp-reference]] §2026-07-20; census in `docs/GLASS_COCKPIT.md`.
|
||||
- **Searchlight-driven fog swap — DEFERRED, and it is an ORIGINAL 1995 LATENT BUG (task #63,
|
||||
2026-07-13) [T1 decomp].** The arcade swaps fog between `fog=` (lights on) and `nosearchlightfog=`
|
||||
(off) via `PullFogRenderable` watching the Searchlight's `lightState`. It never fires — in the PORT
|
||||
because `PullFogRenderable` is never constructed, AND in the BINARY ITSELF because the Searchlight
|
||||
never lights: `ToggleLamp` (@004b860c) toggles `commandedOn` @0x1DC but `SearchlightSimulation`
|
||||
(@004b841c) reads `requestedOn` @0x1E0, which is zeroed by the ctor and never written — no
|
||||
0x1DC→0x1E0 bridge exists (self-consistent sibling ThermalSight reads the field it toggles, 0x1DC).
|
||||
So `lightState` is perpetually 0. The port reproduces this faithfully (searchlight.cpp:189).
|
||||
**DECISION: left as-is + documented (faithful to the buggy original); port keeps the static
|
||||
lights-ON `fog=`.** A working swap is a DESIGNER-INTENT deviation, fully scoped if ever wanted:
|
||||
(1) sim reads `commandedOn` (mirror ThermalSight); (2) construct `PullFogRenderable` at
|
||||
btl4vid.cpp `MakeMechRenderables` reticle-build/inside pass (== arcade part_014.c:5173, Dynamic,
|
||||
bound per Searchlight `lightState` via a new `LightStatePtr()` accessor); (3) a toggle input (the
|
||||
authentic cockpit button-5→`ToggleLamp` dispatch is a controls-family reconstruction; a dev key
|
||||
stands in). `ControlsAllowLights()` (searchlight.hpp) is WIRED since issue #2 to the
|
||||
`player+0x25c` not-novice experience flag via the BTPlayerExperienceSimLive bridge
|
||||
([[experience-levels]]); the consumer (ToggleLamp) still needs the cockpit button dispatch.
|
||||
See [[rendering]] fog section. Verified inert live: BT_FOG_LOG shows zero `SetFogStyle(2/3)`.
|
||||
- **Searchlight-driven fog swap — STILL DEFERRED, but the "ORIGINAL 1995 LATENT BUG" premise is
|
||||
❌ RETRACTED (2026-07-25, #61).** The arcade swaps fog between `fog=` (lights on) and
|
||||
`nosearchlightfog=` (off) via `PullFogRenderable` watching the Searchlight's `lightState`.
|
||||
**The old entry claimed the 1995 binary itself could never light the lamp. That was wrong** — it
|
||||
compared Searchlight's Performance (@004b841c, reads `requestedOn`@0x1E0) against **ThermalSight's**
|
||||
`ToggleLamp` (@004b860c → `0x1DC`), i.e. two different classes, and so invented a missing
|
||||
"0x1DC→0x1E0 bridge". Searchlight's real handler is **@004b838c** (table @0x51117C) and toggles
|
||||
`0x1E0`, the field its own Performance reads. [T1 attribution: `section_dump.txt:69661-69711`,
|
||||
un-pooled `"ToggleLamp"` strings adjacent to their own class names. Body: @004b838c sits in a Ghidra
|
||||
export gap (#60) but was **recovered by raw disasm** (`scratchpad/dis838c.py`) — it toggles 0x1E0 and
|
||||
carries no novice gate. All T1.]
|
||||
Searchlight's handler set is now WIRED and **verified live**: pad `0x14` → `requestedOn 0→1` →
|
||||
`lightState 0→1`. So the sim needs **no repair** and the previous "DECISION: faithful to the buggy
|
||||
original" is void — a working fog swap is now plain FAITHFUL reconstruction. Remaining work is a
|
||||
single item: construct `PullFogRenderable` at btl4vid.cpp `MakeMechRenderables` reticle-build/inside
|
||||
pass (== arcade part_014.c:5173, Dynamic, bound per Searchlight `lightState` via a new
|
||||
`LightStatePtr()` accessor). `ControlsAllowLights()` is WIRED since issue #2 to the `player+0x25c`
|
||||
not-novice experience flag via the BTPlayerExperienceSimLive bridge ([[experience-levels]]).
|
||||
See [[rendering]] fog section. (The pre-#61 "verified inert live: BT_FOG_LOG zero `SetFogStyle(2/3)`"
|
||||
observation still holds — reason (1), the un-constructed renderable, remains.)
|
||||
- **Factory capability-roster loops 2-4 are STILL DEAD (task #57 discovery).** mech.cpp's
|
||||
post-roster loops add to `heatableSubsystems`(0x51155c)/`weaponRoster`(0x511830)/
|
||||
`damageableSubsystems`(0x50e4fc) through the local `SubProxy` stub whose `IsDerivedFrom`
|
||||
|
||||
@@ -55,9 +55,13 @@ the per-condenser VALVE buttons** (MoveValve, Cond1-6 — work), **0x1A-0x1D = G
|
||||
ON/OFF** (`ToggleGeneratorOnOff` id 4, binary table @0050fb90 fn @004b1ed0 — DEAD until
|
||||
reconstructed). Newly decoded from the binary message tables: **0x13 → Mech `DuckRequest`
|
||||
(0x1a @0049fa00 — the manual's CROUCH button)**, **0x28 → Mech `BalanceCoolant` (0x16
|
||||
@0049f728)**, 0x12 → ThermalSight toggle (id 3), **0x14 → Searchlight + Searchlight2
|
||||
`ToggleLamp` (id 3 @004b860c; body reconstructed but the handler-set is default-constructed
|
||||
EMPTY — unwired)** — all currently dead; an unhandled message is SILENTLY ignored
|
||||
@0049f728)**, **0x12 → ThermalSight `ToggleLamp` (id 3, table @0x51120C fn @004b860c)** and **0x14 →
|
||||
Searchlight + Searchlight2 `ToggleLamp` (id 3, table @0x51117C fn @004b838c)** — ✅ **BOTH WIRED
|
||||
2026-07-25 (#61)**, previously default-constructed blackholes; verified live (0x14 →
|
||||
`lightState 0→1`, 0x12 → `thermalActive 0→1`). ⚠ The fn addresses were **swapped** in this file
|
||||
before #61 (@004b860c is ThermalSight's) — see [[decomp-reference]] for the table evidence — and
|
||||
that swap had propagated a false "1995 latent bug" (retracted, [[subsystems]] WAVE 4).
|
||||
An unhandled message is SILENTLY ignored
|
||||
(Receiver::Receive finds no handler and does nothing — dead buttons produce NO log).
|
||||
0x10/0x11 = map ZoomIn/Out, 0x15 = CycleDisplayMode, 0x18 = CycleControlMode (#6). Buttons
|
||||
with NO streamed mapping authored (authentically inert): 0x16/0x17/0x19/0x1E/0x1F/0x38-0x3E.
|
||||
|
||||
+17
-12
@@ -73,18 +73,23 @@ DECLOUDS must stay in the sky pass). Authored TEXTURE **SCROLL** (BMF TEXTURE ta
|
||||
`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.
|
||||
(2) ~~**Original 1995 LATENT BUG**~~ — ❌ **RETRACTED 2026-07-25 (#61). There is no 1995 bug.** The
|
||||
claim rested on a **swapped table attribution**: @004b860c is **ThermalSight's** `ToggleLamp`
|
||||
(table @0x51120C), not Searchlight's, so pitting its `0x1DC` write against Searchlight's
|
||||
`SearchlightSimulation` (@004b841c, reads `requestedOn`@0x1E0) compared two different classes and
|
||||
manufactured a missing "0x1DC→0x1E0 bridge". Searchlight's own handler is **@004b838c** (table
|
||||
@0x51117C) and toggles the field its own Performance reads, `0x1E0` — exactly as self-consistently as
|
||||
ThermalSight does with `0x1DC`. [T1 attribution: un-pooled `"ToggleLamp"` strings sit adjacent to
|
||||
their own class names, `section_dump.txt:69661-69711`. Body: @004b838c sits in a Ghidra export gap
|
||||
(#60) but was **recovered by raw disasm** (`scratchpad/dis838c.py`) — it toggles 0x1E0. All T1.]
|
||||
**The searchlight DID light in the arcade, and the fog swap was NOT inert there.** Searchlight's
|
||||
handler is now wired (#61) and **verified live**: pad `0x14` → `requestedOn 0→1` → `lightState 0→1`.
|
||||
So the ONLY remaining blocker is (1), the un-constructed `PullFogRenderable` — and building it is
|
||||
**FAITHFUL, not a designer-intent deviation** (the old "DECISION 2026-07-13: left as-is" is void;
|
||||
the sim needs NO repair). Remaining work: construct `PullFogRenderable` at btl4vid.cpp
|
||||
`MakeMechRenderables` (the reticle-build/inside pass, == arcade part_014.c:5173, ExecutionType
|
||||
Dynamic, bound to each Searchlight `lightState`). 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
|
||||
|
||||
+23
-5
@@ -41,11 +41,29 @@ Making a base byte-exact GROWS every subclass — they must be re-based TOGETHER
|
||||
- **WAVE 3** — power bus (Generator/PoweredSubsystem) + Emitter/PPC fire-path (end-to-end fire; heat
|
||||
conducts to the central sink via the linked-sink roster).
|
||||
- **WAVE 4** — standalone readouts: Sensor/Searchlight/ThermalSight/AmmoBin (de-shim, gate fixes).
|
||||
- ⚠ **Searchlight has an ORIGINAL latent bug [T1, task #63]:** `SearchlightSimulation` (@004b841c)
|
||||
reads `requestedOn`@0x1E0 (never written) while `ToggleLamp` (@004b860c) toggles `commandedOn`@0x1DC
|
||||
— no bridge, so `lightState`@0x1D8 is perpetually 0 (lamp never lights). Present in the 1995 binary
|
||||
(sibling ThermalSight reads the field it toggles, 0x1DC); the port reproduces it faithfully. Left
|
||||
as-is by decision; it makes the searchlight→fog swap inert. See [[open-questions]] + [[rendering]] fog.
|
||||
- ✅ **Searchlight + ThermalSight buttons WIRED 2026-07-25 (#61)** — both classes' handler sets were
|
||||
default-constructed blackholes (systemic cause #1, `docs/INPUT_PATH_AUDIT.md`). Each now chains
|
||||
`PowerWatcher::GetMessageHandlers()` with its own id-3 `ToggleLamp`. **Verified live**: pad `0x14`
|
||||
→ `requestedOn 0→1` → `lightState 0→1`; pad `0x12` → `requestedOn 0→1` → `thermalActive 0→1`.
|
||||
- ❌ **RETRACTED: the "ORIGINAL 1995 latent bug" (old task #63) NEVER EXISTED.** It was an artifact of
|
||||
a **swapped table attribution**: the claim compared Searchlight's Performance (@004b841c, reads
|
||||
`requestedOn`@0x1E0) against **ThermalSight's** `ToggleLamp` (@004b860c, toggles `0x1DC`) and
|
||||
concluded "no 0x1DC→0x1E0 bridge, so the lamp can never light". @004b860c is not Searchlight's.
|
||||
Searchlight's own handler is **@004b838c** (table @0x51117C) and — like every sibling — toggles the
|
||||
field its OWN Performance reads, `requestedOn`@0x1E0. Each class is internally self-consistent.
|
||||
[T1 throughout. Attribution: un-pooled `"ToggleLamp"` strings adjacent to their own class names,
|
||||
`section_dump.txt:69661-69711`. Body: @004b838c is a Ghidra EXPORT GAP (#60) but was **recovered by
|
||||
raw disasm** of `content/BTL4OPT.EXE` (`scratchpad/dis838c.py`) — it toggles 0x1E0, raises
|
||||
`updateModel` (+0x18) unconditionally, and has **NO novice gate** (ThermalSight-only).] Consequence: the searchlight→fog swap was **NOT** inert in the arcade, and
|
||||
making it work in the port is FAITHFUL, not a designer-intent deviation. See [[open-questions]] +
|
||||
[[rendering]] fog, both corrected.
|
||||
- ⚠ Still open on Searchlight: `commandedOn`@0x1DC (ctor-seeded from subsystem resource +0x28,
|
||||
@004b84dc) has **no identified role** — it is neither the toggle target nor read by @004b841c, and
|
||||
it measured **21** live (not a boolean), which hints our SubsystemResource +0x28 ≠ the binary's.
|
||||
- ⚠ ThermalSight's published attributes are mis-named: we publish `"LightState"→thermalActive@0x1D8`,
|
||||
but the binary's table @0x511268 has **`"LightOn"`(id 3)→0x1D8** and `"LightState"`(id 4)→**0x1E0**
|
||||
(the alarm). A gauge binding `ThermalSight/LightOn` currently misses. Not yet changed (gauge-binding
|
||||
change needs its own verification pass).
|
||||
- **WAVE 5** — Torso (aim twist/elevation; joint-I/O reconstructed; the BLH record disables torso →
|
||||
faithfully no visible twist).
|
||||
- **WAVE 6** — Myomers (mover-coupled; structural un-stub is INERT — no live mover/heat coupling).
|
||||
|
||||
Reference in New Issue
Block a user