diff --git a/context/open-questions.md b/context/open-questions.md index c0055ac..970949b 100644 --- a/context/open-questions.md +++ b/context/open-questions.md @@ -254,6 +254,19 @@ register. ⚠ The audit also flags the damage-economy item as SELF-CONTRADICTOR (the databinding trap). DORMANT in-game (the review context no longer survives the viewpoint swap -- the #48 teardown fix) but LIVE the moment the mission-review screen actually runs at round end. Bridge all three through btplayer.cpp accessors before enabling the review screen. +- **`PoweredSubsystem` AutoConnect is DEAD, and vtable slot +0x40 is misattributed + (2026-07-25, found while auditing #47's GetStatusFlags change) [T1 decomp].** The binary + (@004b0bd0 tail) calls slot +0x40 with TWO shapes: `(this, 0)` in the outer gate (require + `== 0`) and `(this, candidateGenerator)` inside the roster walk (require `!= 0`). That is a + ONE-ARG voltage query -- "am I unpowered?" / "would THIS source power me?" -- i.e. the + `HasVoltage(source)` family, **not** `GetStatusFlags()`. powersub.cpp:358/372 model both as + the no-arg `GetStatusFlags()`, so the outer gate demands "no flags at all" while the inner + demands "some flag", and nothing between them can change the value -> **the loop body can + never execute and AutoConnect never attaches anything.** Pre-existing (both before and after + the #47 damage-tier fix); deliberately NOT fixed on release eve because it is a live + power-bus behaviour change that deserves its own playtest cycle. Fix = give slot +0x40 its + real signature (`Logical HasVoltage(Subsystem *source = 0)`), restore the two call shapes, + and verify a mode-Auto subsystem re-attaches after its generator drops. - **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` diff --git a/game/reconstructed/heat.cpp b/game/reconstructed/heat.cpp index 301953d..2e09b51 100644 --- a/game/reconstructed/heat.cpp +++ b/game/reconstructed/heat.cpp @@ -222,8 +222,18 @@ Logical // carry these; the heat/power families override them. Base implementations // are the neutral defaults the recovered overrides chain into. // +// (#47 follow-up, 2026-07-25) NOT a neutral default: the binary's +// HeatSink::GetStatusFlags @004add30 OPENS with `call 0x4ac144` -- +// MechSubsystem::GetStatusFlags, the DAMAGE TIER (damageLevel >= 1.0 -> bit 0 +// Destroyed; > 0 -> bit 1 Damaged). There is no zero-returning +// HeatableSubsystem override in the image; this stub silently dropped bits 0/1 +// for the ENTIRE heat branch (every weapon/sensor/generator/emitter), which +// (a) made Destroyed/Damaged unable to reach MechTech's annunciator scan -- +// gutting half the #47 mechalrm table -- and (b) let PoweredSubsystem's +// AutoConnect gate (`GetStatusFlags() == 0`, powersub.cpp:358) treat a DAMAGED +// subsystem as pristine. Chaining the real tier reproduces @004add30 exactly. LWord - HeatableSubsystem::GetStatusFlags() { return 0; } + HeatableSubsystem::GetStatusFlags() { return MechSubsystem::GetStatusFlags(); } Logical HeatableSubsystem::HandleMessage(int) { return False; } void diff --git a/game/reconstructed/mechsub.cpp b/game/reconstructed/mechsub.cpp index 5b3e3e5..846228d 100644 --- a/game/reconstructed/mechsub.cpp +++ b/game/reconstructed/mechsub.cpp @@ -282,8 +282,15 @@ void MechSubsystem::SetSubsystemDamageLevel(Scalar level) // // -// @0x4ac144 -- damage tier from the DamageZone structure level (dz+0x158): -// >= 1.0 -> 1 (intact / nominal), > 0.0 -> 2 (damaged), else 0 (dead). +// @0x4ac144 -- the DAMAGE TIER, read from the zone's damageLevel (dz+0x158): +// >= 1.0 -> bit 0 (DESTROYED), > 0.0 -> bit 1 (DAMAGED), else 0 (pristine). +// These are TechStatusType bit indices 0/1 -- the low two conditions of the +// MechTech annunciator scan (#47). The old comment read the field as a +// STRUCTURE level ("1.0 = intact / 0 = dead") -- backwards: the engine member +// is damageLevel, which ACCUMULATES to 1.0 = destroyed (mechdmg.cpp's crit +// cascade tests `level >= StructureMax` for exactly that). Behaviour was +// always right; the comment was a leftover of the same structureLevel +// misreading that #46 retired. // LWord MechSubsystem::GetStatusFlags()