From aadc372307889cec0db745ee36718f5c1e8708e9 Mon Sep 17 00:00:00 2001 From: Cyd Date: Mon, 3 Aug 2026 13:27:02 -0500 Subject: [PATCH] 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 --- restoration/source410/BT/MECH.CPP | 18 ++++++++++++++++++ restoration/source410/BT/MECHDMG.CPP | 23 ++++++++++++++++------- 2 files changed, 34 insertions(+), 7 deletions(-) diff --git a/restoration/source410/BT/MECH.CPP b/restoration/source410/BT/MECH.CPP index a47861e9..bb089aee 100644 --- a/restoration/source410/BT/MECH.CPP +++ b/restoration/source410/BT/MECH.CPP @@ -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(); diff --git a/restoration/source410/BT/MECHDMG.CPP b/restoration/source410/BT/MECHDMG.CPP index 62f09680..a0f43a3c 100644 --- a/restoration/source410/BT/MECHDMG.CPP +++ b/restoration/source410/BT/MECHDMG.CPP @@ -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);