BT410 5.3.97: combat damage now limps the mech -- and a fresh leg hit can no longer one-shot it

Two corrections to the damage model's leg branch, both read straight off the
binary's own control flow (part_012.c:6900-6945, the _DAT_0049c99c/9a0
compares), closing a delta the notes had carried as staged:

  THE KILL GATE WAS WRONG.  Our reconstruction felled the mech whenever a
  leg zone reached 1.0.  The binary only does that when the mech is ALREADY
  LIMPING (mode 3/4): a first leg hit -- even one that takes the zone
  straight to 1.0 -- produces a LIMP, and the fall needs a second blow to
  land while the gait is already gimped.  Two-stage leg death, exactly the
  BattleTech flavour, and measurably more forgiving than what we shipped.

  THE ALARM WAS RIGHT.  The limp/kill signal writes AlarmIndicator::SetLevel
  at mech+0x2c -- the statusAlarm, exactly as our MECHDMG had it (levels 3/4
  limp, 9 down).  Confirmed rather than changed.

AND THE MISSING LINK STAGED: nothing promoted statusAlarm 3/4 into the
movement mode the gait keys on.  The binary's own damage code READS mode 3/4
to decide "already limping", so the promotion demonstrably exists -- its
writer is the master performance (mech4 @004a9b5c, the function Ghidra never
decompiled).  Until that lands, Simulate promotes status 3/4 -> simulation
state 3/4 (guarded so a death mode is never overwritten), marked STAGED with
the evidence.

The chain is now END TO END: enemy fire -> cylinder table -> leg zone
crosses 0.5 -> statusAlarm 3/4 -> movement mode -> the limp advancers
replace the normal gait -> the drag leg shows on the wire -> the demand
clamp caps the throttle.  Live regression: the normal mission's speed
sequence is bit-identical a third time (7.31972, 26.6726, ...); the
promotion block is inert until real leg damage arrives.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
Cyd
2026-08-03 13:27:02 -05:00
co-authored by Claude Fable 5
parent 7c2363d89f
commit aadc372307
2 changed files with 34 additions and 7 deletions
+18
View File
@@ -1261,6 +1261,24 @@ void
// damage model's limp hook (leg zone >= 0.5 -> mode 3/4) is
// reconstructed.
//
//
// STAGED (the master performance's job, mech4 @004a9b5c -- never
// decompiled): promote the damage model's limp signal (statusAlarm level
// 3/4, MECHDMG's leg-half write to mech+0x2c) into the movement mode the
// gait machinery keys on. The binary's damage code READS mode 3/4 to
// decide "already limping", so the promotion demonstrably exists; the
// master perf is the only unaccounted-for writer. Guarded so a death
// (mode 5+) is never overwritten.
//
{
unsigned status = statusAlarm.GetLevel();
int mode = MovementMode();
if ((status == 3 || status == 4) && mode < 3)
{
SetSimulationState(status);
}
}
int limping;
{
int mode = MovementMode();
+16 -7
View File
@@ -394,19 +394,28 @@ void
RecurseSegmentTable(mech);
}
}
else if (damageLevel >= StructureMax)
else if (mech->MovementMode() == 3 || mech->MovementMode() == 4)
{
// leg destroyed -> fall / mech kill
mech->statusAlarm.SetLevel(9);
if (getenv("BT_MECH_LOG"))
//
// ALREADY LIMPING (binary @0x49c88f/@0x49c8c5): only now does a
// DESTROYED leg fell the mech. A first leg hit -- even one that
// takes the zone straight to 1.0 -- limps first; the kill needs
// the second blow to land while the gait is already gimped.
//
if (damageLevel >= StructureMax)
{
DEBUG_STREAM << "[death] LEG zone " << damageZoneIndex
<< " destroyed -- mech down" << endl << flush;
mech->statusAlarm.SetLevel(9);
if (getenv("BT_MECH_LOG"))
{
DEBUG_STREAM << "[death] LEG zone " << damageZoneIndex
<< " destroyed while limping -- mech down"
<< endl << flush;
}
}
}
else if (!mech->IsMechDestroyed())
{
// half-gone leg -> the gimp-gait graphic (right 4, left 3)
// half-gone leg -> the limp gait (right 4, left 3)
if (rightLeg != 0 && damageLevel >= LegHalfStructure)
{
mech->statusAlarm.SetLevel(4);