diff --git a/context/decomp-reference.md b/context/decomp-reference.md index 02d88bb..58f2368 100644 --- a/context/decomp-reference.md +++ b/context/decomp-reference.md @@ -292,7 +292,10 @@ From the weapon `.SUB` records + the charge-curve `.data` constants (PE-parsed a MechWeapon defaults destination=&fireImpulse@0x31C per @004b99a8). Subsystem+0x110: 0/-1 config-session flag written by handler 9 (reader unknown). - ConfigMapGauge @004c6d80/@004c6f1c: table DAT_00518eb4 = {y,btn} {0x0d,0x45}{0x25,0x46}{0x3d,0x40} - {0x55,0x47}, blit x=0xc; SetColor @004c6ee0 has NO caller (gauge dormant in the shipped build). + {0x55,0x47}, blit x=0xc; @004c6ee0 = the virtual GaugeBase::LinkToEntity override (vtbl @0051a1b8 + slot 9 +0x24; @0x94 = linkedEntity, the Execute gate), broadcast at viewpoint bind (APP.cpp:1277 → + GAUGREND.cpp:3011) — the gauge IS live in the shipped build (old "SetColor/no-caller/dormant" claim + was wrong; corrected 2026-07-21). ## 6. Env-var gates (runtime; default OFF unless noted) diff --git a/context/gauges-hud.md b/context/gauges-hud.md index a8987b8..8a19c7c 100644 --- a/context/gauges-hud.md +++ b/context/gauges-hud.md @@ -328,15 +328,22 @@ authentically ANIMATES 0→1 through each reload. (The census's four other writ @004b9c9c is the MechWeapon/projectile base body.) The launcher panel's other live indicators: AMMO DIGITS (AmmoBin::ammoCount via the complete-type bridges), jam/fire lamps, eject wipe. -## ConfigMapGauge (the weapon panel's regroup lamp column) — authentically DORMANT +## ConfigMapGauge (the weapon panel's trigger-config joystick) — LIVE via LinkToEntity (2026-07-21) The per-weapon btjoy.pcc joystick image + 4 cm_* state lamps (off/other/only/both) showing, for each mappable fire button (Pinky/ThumbLow/Trigger/ThumbHigh), whether THIS panel's weapon -is bound to it. **The shipped binary never enables it [T1, task #6]:** no caller of SetColor -@004c6ee0 exists, so color==0 and Execute early-outs. The state loop is reconstructed -(btl4gau2.cpp; table DAT_00518eb4 PE-recovered; sampler = LBE4ControlsManager:: -buttonGroup[btn].GetMapState — NOT the ModeManager, the old guard note was wrong) behind the -PORT dev enable `BT_CONFIGMAP=1`. The regroup MECHANISM itself (ConfigureMappables/ -ChooseButton, task #6) is live regardless of the gauge. +is bound to it. **CORRECTION — the old "authentically DORMANT" claim [task #6] was WRONG.** +@004c6ee0 is NOT an uncalled `SetColor(int)`: it is the virtual **`GaugeBase::LinkToEntity` +override** (vtbl slot 9, +0x24 — verified against the binary vtable @0051a1b8 and the T0 +GaugeBase virtual roster, GAUGE.h), and `@0x94` is the **linkedEntity**, not a colour. The +engine broadcasts `LinkToEntity(viewpointEntity)` to every gauge when the viewpoint binds +(APP.cpp:1277 → GaugeRenderer::LinkToEntity GAUGREND.cpp:3011), arming the Execute gate — the +joystick DOES render in the shipped game (matches the DOSBox reference: solid circle = the +mapped trigger, hollow = unmapped). The "no caller" analysis failed twice: it looked for +direct calls to a virtual, and its slot math used the wrong vtable copy. Port fix: renamed +SetColor→LinkToEntity / color→linkedEntity, deleted the `BT_CONFIGMAP` dev enable (no longer +needed — the authentic path lights it). Sampler = LBE4ControlsManager:: +buttonGroup[btn].GetMapState (table DAT_00518eb4 PE-recovered); the regroup MECHANISM +(ConfigureMappables/ChooseButton, task #6) was always live. ## Authentically-static (do NOT "fix") Degradation/Failure temps are fixed markers; AmbientTemperature 300; CoolantMassLeakRate 0 on a diff --git a/context/open-questions.md b/context/open-questions.md index 61832c3..90e7548 100644 --- a/context/open-questions.md +++ b/context/open-questions.md @@ -341,13 +341,15 @@ register. ⚠ The audit also flags the damage-economy item as SELF-CONTRADICTOR old hostEntity/subsystemId2 labels were wrong; MechWeapon ctor defaults the destination to &fireImpulse per @004b99a8); dev harness = HOLD 'G' (BT_CONFIG_SLOT selects the weapon; BT_CONFIG_TEST scripts a headless session). - * **ConfigMapGauge finding [T1]**: the shipped binary NEVER enables it — no caller of - SetColor @004c6ee0 exists (the only child accesses are BecameActive/SetEnable forwards), - so color stays 0 and Execute early-outs: the gauge is authentically DORMANT in BTL4OPT. - The state loop is now reconstructed anyway (@004c6f1c: 4 {y,button} pairs from - DAT_00518eb4 = {13,Pinky}{37,ThumbLow}{61,Trigger}{85,ThumbHigh}, x=0xc; - buttonGroup[btn].GetMapState(controlDestination, subsys, controlMessageID, 0x10000) → - cm_off/cm_other/cm_only/cm_both), behind the PORT dev enable BT_CONFIGMAP=1. + * **ConfigMapGauge finding [T1] — CORRECTED 2026-07-21**: the old "authentically DORMANT / + no SetColor caller" claim was WRONG. @004c6ee0 is the virtual GaugeBase::LinkToEntity + override (vtbl @0051a1b8 slot 9 +0x24; @0x94 = linkedEntity, the Execute gate), and the + engine broadcasts LinkToEntity(viewpointEntity) at viewpoint bind (APP.cpp:1277 → + GAUGREND.cpp:3011) — the gauge IS live in the shipped game. The state loop + (@004c6f1c: 4 {y,button} pairs from DAT_00518eb4 = {13,Pinky}{37,ThumbLow}{61,Trigger} + {85,ThumbHigh}, x=0xc; buttonGroup[btn].GetMapState(controlDestination, subsys, + controlMessageID, 0x10000) → cm_off/cm_other/cm_only/cm_both) now runs on the authentic + LinkToEntity path; the BT_CONFIGMAP dev enable was deleted. * **Bonus [T1]**: the binary MechWeapon ATTRIBUTE table @0x511890 has ELEVEN entries (0x12 PercentDone .. 0x1C WeaponState) — future gauge-wave feeds; the "TriggerState is the only MechWeapon attribute" claim was wrong (fixed in mechweap.hpp). diff --git a/docs/GAUGE_COMPOSITE.md b/docs/GAUGE_COMPOSITE.md index 8cf8c4f..5707257 100644 --- a/docs/GAUGE_COMPOSITE.md +++ b/docs/GAUGE_COMPOSITE.md @@ -676,7 +676,7 @@ reconfigure/externalConfigure); parse-skip list EMPTY ([gskip]=0), all 50 attr b | 12 | Quad mini-panels: temp bars / cooling-loop / power-bus lamps / evolt bar / cmode lamp | SubsystemCluster children (@004c8140 map) reading InputVoltage/HeatSink/temps | VEHICLE_SUBSYSTEMS.md phase 2; LIVE: panels render per-subsystem, [vss] roster matches authored aux screens 1-10 | T2 | CORRECT | | 13 | Quad: recharge dial (SegmentArc270) | PercentDone → rechargeLevel@0x320 | ~~writer census: Emitter::ComputeOutputVoltage @004ba738 only — projectile dial full-once~~ **CORRECTED (Gitea #12, 2026-07-19): the census missed vtbl slot 17 @004b9c9c** — `rechargeLevel=(rechargeRate−recoil)/rechargeRate`, called per frame by the recovered @004bbd04 Loading/state-7 cases: the projectile dial authentically ANIMATES through reload (port fix pending — the port sim never calls slot 17) | T1 | CORRECT (emitters) + **WRONG-STATIC (projectile; fix pending)** | | 14 | Quad: ammo count (ammoCountA) | AmmoBin::ammoCount via BTAmmoBinCountPtr bridge | AFC100 fix 2026-07-12; LIVE: 24 fresh (audit4) vs 19 after missile autofire (audit3) — counter moves | T2 | CORRECT | -| 15 | Quad: ConfigMap regroup lamps | LBE4ControlsManager buttonGroup map state | task #6: NO SetColor caller in the binary → color=0, Execute early-outs | T1 | AUTH-STATIC (dormant; BT_CONFIGMAP dev enable) | +| 15 | Quad: ConfigMap regroup lamps | LBE4ControlsManager buttonGroup map state | CORRECTED 2026-07-21: @004c6ee0 is the LinkToEntity override (not SetColor); armed at viewpoint bind → LIVE | T1 | LIVE (authentic LinkToEntity path; BT_CONFIGMAP deleted) | | 16 | Eng pages: prepEngr SYSTEM NN + subsystem label + class cells | roster walk, sub+0x1dc auxScreen == screen (BTGetSubsystemAuxScreen) | FUN_004c7e48 matched line-by-line; [vss] dump: scr1/2/4=PPC/Strk6/ERMed, 5-8=Sens/Myo/ERMed/ERMed, 9/10=Strk6/PPC — page headers+labels CORRECT | T1/T2 | CORRECT | | 17 | Eng ballistic page: ammoCountB / jam / fire / reload numeric / destroyed lamp | bin count bridge + weaponAlarm@0x364 (MechWeapon layout static_assert-locked) | ctor @004c9558 coords matched; LIVE renders | T2 | CORRECT | | 18 | Eng ballistic page: eject wipe (BitMapInverseWipeScalar @004c61c8) | subsys+0x3f8 eject timer | port tracked NULL (class not reconstructed) | T3 | DEFERRED-FEED | diff --git a/docs/VEHICLE_SUBSYSTEMS.md b/docs/VEHICLE_SUBSYSTEMS.md index 0554bc0..64e860f 100644 --- a/docs/VEHICLE_SUBSYSTEMS.md +++ b/docs/VEHICLE_SUBSYSTEMS.md @@ -152,8 +152,9 @@ the resource parse, verifying combat/heat un-regressed. **This is the final, sig live in btl4gau2.cpp (recovered `DAT_00518eb4` table; `buttonGroup[btn].GetMapState(...,0x10000)` via the complete-type bridge `BTSubsystemControlFeed`; `MechSubsystem` +0xE8/+0xEC fixed to controlDestination/controlMessageID, MechWeapon ctor defaults the destination to `&fireImpulse`). - The gauge stays authentically DORMANT (the shipped binary never calls SetColor `0x4c6ee0` — no caller - exists); `BT_CONFIGMAP=1` is the port's dev enable. + CORRECTED 2026-07-21: the "authentically DORMANT" claim was wrong — `0x4c6ee0` is the virtual + GaugeBase::LinkToEntity override (not SetColor), broadcast at viewpoint bind (APP.cpp:1277), so the + gauge IS live in the shipped game; the `BT_CONFIGMAP` dev enable was deleted (see context/gauges-hud.md). - The **separate** dev-gauge window (960×384) didn't appear in the headless test run (the panels render in `BT_DEV_GAUGES_DOCK=1` docked mode); the separate-window path is existing dev-gauge infra, unrelated to this reconstruction. diff --git a/game/reconstructed/btl4gau2.cpp b/game/reconstructed/btl4gau2.cpp index ca926f2..87f3037 100644 --- a/game/reconstructed/btl4gau2.cpp +++ b/game/reconstructed/btl4gau2.cpp @@ -544,7 +544,7 @@ void SeekVoltageGraph::Execute() // @004c6d80 -- ctor (vtable PTR_FUN_0051a1b8). GraphicGauge base; interns the // joystick base bitmap "btjoy.pcc" (BitMapCache AddRef) + the four config-state // pixmaps cm_off/cm_other/cm_only/cm_both.pcc (PixMapCache AddRef); sets the port -// origin (x,y); subsystem = param_9; color = 0. +// origin (x,y); subsystem = param_9; linkedEntity = 0. // ConfigMapGauge::ConfigMapGauge( GaugeRate rate, @@ -567,14 +567,14 @@ ConfigMapGauge::ConfigMapGauge( localView.SetOrigin(x, y); // vtbl+0x10 (this+0x48) - color = 0; // @0x94 this[0x25] - // task #6 finding [T1]: NO caller of SetColor (@004c6ee0) exists in the - // shipped binary -- the only accesses to the WeaponCluster's configMap - // child (@0xD4) are BecameActive forwards and SetEnable -- so the gauge is - // authentically DORMANT in BTL4OPT (color stays 0; Execute early-outs). - // BT_CONFIGMAP=1 is the PORT's dev enable to watch the state loop live. - if (getenv("BT_CONFIGMAP")) - color = 0xff; + linkedEntity = 0; // @0x94 this[0x25] + // CORRECTION (2026-07-21, retires the task #6 "dormant" claim): @0x94 is NOT + // a colour and @004c6ee0 is NOT an uncalled SetColor -- it is the virtual + // GaugeBase::LinkToEntity override (vtbl slot 9, +0x24; verified against the + // binary vtable @0051a1b8 + the engine GaugeBase roster). The engine + // broadcasts LinkToEntity(viewpointEntity) to every gauge when the viewpoint + // binds (APP.cpp:1277 -> GAUGREND.cpp:3011), which arms this gate -- the + // trigger-config joystick DOES render in the shipped game. subsystem = subsystem_in; // @0x90 this[0x24] L4Warehouse *warehouse = (L4Warehouse *)renderer_in->warehousePointer; @@ -601,12 +601,14 @@ Logical } // -// @004c6ee0 -- SetColor: color (this[0x25]) = the passed value. +// @004c6ee0 -- LinkToEntity (GaugeBase vtbl slot 9): linkedEntity (this[0x25]) = +// the viewpoint entity. Broadcast by GaugeRenderer::LinkToEntity when the +// viewpoint binds; arms the Execute gate. // void - ConfigMapGauge::SetColor(int color_in) // @004c6ee0 + ConfigMapGauge::LinkToEntity(Entity *entity) // @004c6ee0 { - color = color_in; + linkedEntity = entity; } // @@ -621,7 +623,7 @@ void } // -// @004c6f1c -- Execute. When enabled (color != 0): on the dirty flag, blit the +// @004c6f1c -- Execute. Once linked (linkedEntity != 0): on the dirty flag, blit the // base joystick bitmap; then for each of the 4 config-map slots read the fire // button's map state and, if changed, blit the matching cm_* pixmap. // @@ -644,7 +646,7 @@ extern void BTSubsystemControlFeed(void *subsystem_in, void **destination_out, void ConfigMapGauge::Execute() { - if (color == 0) + if (linkedEntity == 0) // @004c6f28: not yet linked to the viewpoint return; L4Warehouse *warehouse = (L4Warehouse *)renderer->warehousePointer; diff --git a/game/reconstructed/btl4gau2.hpp b/game/reconstructed/btl4gau2.hpp index a464378..ecb9eda 100644 --- a/game/reconstructed/btl4gau2.hpp +++ b/game/reconstructed/btl4gau2.hpp @@ -162,12 +162,18 @@ int x, int y, Entity *subsystem, const char *identification_string); ~ConfigMapGauge(); // @004c6e54 Logical TestInstance() const; - void SetColor(int color); // @004c6ee0 this[0x25] + // @004c6ee0 -- the GaugeBase::LinkToEntity OVERRIDE (vtbl slot 9, +0x24), + // NOT a "SetColor": it stores the viewpoint entity into @0x94 and Execute + // gates on it. GaugeRenderer::LinkToEntity (GAUGREND.cpp:3011) broadcasts + // it to every gauge when the viewpoint binds (APP.cpp:1277) -- THAT is + // what lights the trigger-config joystick in the shipped game. (The old + // "SetColor, no callers -> authentically dormant" claim was a mislabel.) + void LinkToEntity(Entity *entity); // @004c6ee0 writes this[0x25] void BecameActive(); // @004c6ef4 void Execute(); // @004c6f1c protected: Entity *subsystem; // @0x90 this[0x24] - int color; // @0x94 this[0x25] + Entity *linkedEntity; // @0x94 this[0x25] (Execute gate; was mislabelled 'color') char *joystickImage; // @0x98 this[0x26] "btjoy.pcc" char *stateImage[4]; // @0x9C this[0x27..0x2A] int previousState[4]; // @0xAC this[0x2B..0x2E]