#47 follow-up: restore the DAMAGE TIER to the heat branch -- HeatableSubsystem::GetStatusFlags was a stub returning 0

Found while auditing today's vtable change before release.  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 anywhere in the image.

Our port routed that call through `HeatableSubsystem::GetStatusFlags()
{ return 0; }` -- a "neutral default" that silently dropped bits 0 and 1 for
the ENTIRE heat branch (every weapon, sensor, emitter, PPC, generator,
powered subsystem).  Two live consequences:

  * Destroyed / Damaged could never reach MechTech's annunciator scan --
    gutting half the shipped mechalrm table (Destroyed -> gotoEngineering +
    engCooling + engBusMode) hours after #47 shipped the flash;
  * PoweredSubsystem's AutoConnect gate (`GetStatusFlags() == 0`,
    powersub.cpp:358) read a DAMAGED subsystem as pristine.

One-line fix: chain the real tier, reproducing @004add30 exactly.  The call
site's own comment already said `// FUN_004ac144` -- the implementation had
simply never matched it.

Also corrected a stale comment on MechSubsystem::GetStatusFlags that read the
field as a STRUCTURE level ("1.0 = intact, 0 = dead") -- backwards.  The
engine member is damageLevel and ACCUMULATES to 1.0 = destroyed (mechdmg's
crit cascade tests `level >= StructureMax` for exactly that), which is what
makes bits 0/1 line up with TechStatusType Destroyed/Damaged.  Behaviour was
always right; the comment was a leftover of the same structureLevel misreading
#46 retired.

VERIFIED (3-pod combat sim, BT_LAMP_LOG): the annunciator is genuinely live --
Jammed x7, AmmoBurning x5, BadPower x20, Overheating x207 (Overheating
correctly maps to NO lamp: the shipped table has no entry for it).  The lamp
resolution is per-subsystem as designed -- different weapons flash their own
panel buttons (lamps 0xf/0x5/0xb/0x3 under distinct mode masks), 32 flash
writes across a whole battle, no thrash.  Zero crashes, zero plane/OOB
tripwire hits across all three pods.

FILED, deliberately NOT fixed tonight (open-questions.md): vtable slot +0x40
is misattributed.  The binary calls it `(this, 0)` in AutoConnect's outer gate
and `(this, candidateGenerator)` in the roster walk -- a one-arg voltage query
(HasVoltage(source)), not GetStatusFlags().  Modelling both as the no-arg
GetStatusFlags makes the outer gate demand "no flags" and the inner "some
flag" with nothing between them, so AutoConnect's loop body can never execute.
Pre-existing before and after this change; it is a live power-bus behaviour
change and deserves its own playtest cycle, not a release-eve patch.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
arcattack
2026-07-25 17:58:20 -05:00
co-authored by Claude Fable 5
parent 1d31af61d7
commit f3179ef020
3 changed files with 33 additions and 3 deletions
+9 -2
View File
@@ -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()