#82 ROOT FIX: reconstruct the crash SELF-DAMAGE -- wall-grinding now hurts,

the knockdown storm self-limits, peers keep the limp

The 'peers see a limping mech skating' report decomposed into a wall-grind
KNOCKDOWN STORM: a gimped mech held against a blocking solid re-crashes each
time its gg cycle rebuilds speed (the gg ceiling is authentically the clip's
natural speed -- bhk1 ~14.9 u/s, ceiling+divisor @0x544/0x548, loader formula
byte-matched -- so iv2 ~222 >> the 40.0 threshold @0x4ab184), and every
type-5 KNOCKDOWN broadcast floors all peers' replicants into knockdown/trn
churn: gliding + leg-lifts = skating.  Turning and crushable cars were
A/B-exonerated (grass circling: 198 replicant gimp entries, 17 crunches,
zero broadcasts).

What the binary has that the port lacked -- recovered from the EXPORT GAP by
raw disasm (scratchpad/night6/gap_4a9770.txt, @4aa89f-4aaab4) -- is the crash
branch's tail: fallDirection = worldToLocal(damageForce) (FUN_0040879c, M^T v,
un-normalized), fallScalar = -(fallDirection . localVelocity.linearMotion),
and a self-dispatched TakeDamageMessage{0x64, zone=-1, the engine-computed
collision Damage verbatim}.  A wall crash COSTS ARMOR, so the grind degrades
the mech instead of looping forever.  That dispatch was the long-standing
'DEFERRED' note in the response policy; now reconstructed.

Falsified en route (comments + KB corrected, do not revive): the stagger's
FUN_004a4c54(1)/(0x20) 'action-request flags' feed NO drive suppressor --
image-wide sweep + full gap disasm show word[this+0x18] is read only by the
net record emitter; the bmp clip applies NO root motion (adv=0 measured); the
'gimp cap 5.8' figure was the BACK-cycle stride printed under a misleading
label (now ggCapL/R; @0x52c has no binary consumer).

Verified (mp_gimpAB rigs): arena1 wall-grind now 4 bounded strikes, limper
dies of its crashes and respawns cleanly twice (START->RESET, no strand);
grass circling unchanged-clean (198 replicant gimp states, 0 knocks).
Rig: pid capture race fixed (a missed winpid left a stale node holding the
-net port -> null runs), per-config sweep added.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
Joe DiPrima
2026-07-30 16:18:28 -05:00
co-authored by Claude Fable 5
parent 19fe1e5fa2
commit 9c83a468db
8 changed files with 353 additions and 36 deletions
+27 -16
View File
@@ -212,22 +212,33 @@ knockdown must stagger BOTH channels (`SetBodyAnimation(0x20)` + `SetLegAnimatio
stops the knockdown re-firing on sustained/glancing contact. `localVelocity` is zeroed while crashed
(spec-faithful) so a knocked mech can't keep advancing. [T2]
**⚠ The WALL-GRIND KNOCKDOWN STORM = the surviving "#82 peers see skating" (2026-07-30) [T2].**
Each stagger bind also broadcasts `ForceUpdate(0x20)` the **type-5 KNOCKDOWN record** — and the
replicant handler (`mech.cpp` case 5) plays the bmp clip AND zeroes `projectedVelocity`. The bmp
clip carries ~6.5 u/s of root motion pressing back into the wall (6.5² ≈ 42 > the iv²>40 threshold),
so **against a wall the stagger self-retriggers**: a gimped mech grinding a wall re-broadcast 26
knockdowns in one bench run, and every peer's replicant lived in knockdown/recovery churn — never
re-entering the gimp cycle. On the observer that reads as gliding + trn leg-lifting = "skating."
4-config A/B (`scratchpad/night6/mp_gimpAB.sh`, `[animind]` inst=1 verdict): grass straight/circle
**198 gimp-state entries, clean limp** (17 car crunches during it — crushable icons are the
`0.00123f` sentinel path: gyro kick only, NO broadcast, exonerated); arena1 straight = 1 strike,
replicant recovered to gimp (196); arena1 circle (wall grind) = **0 gimp entries, skating**. So:
turning innocent, cars innocent, single strikes recover — the STORM is the whole bug, and it is
field-relevant (legged players hug walls). **The authentic fix is the un-reconstructed consumer of
the stagger's action-request flags** (`FUN_004a4c54(this,1)` + `(this,0x20)` — the drive suppressor
during the stagger; the port's rebind guard at mech4.cpp:6861 is marked bring-up pending exactly
this). Tracked in [[open-questions]]; replication detail in [[multiplayer]].
**⚠ The WALL-GRIND KNOCKDOWN STORM = the surviving "#82 peers see skating" — ROOT-CAUSED + FIXED
(2026-07-30) [T1 disasm / T2 verified].** Each stagger bind also broadcasts `ForceUpdate(0x20)`
the **type-5 KNOCKDOWN record** — and the replicant handler (`mech.cpp` case 5) plays the bmp clip
AND zeroes `projectedVelocity`. A gimped mech held against a wall re-crashes each time its gg cycle
rebuilds speed (the gg ceiling is authentically the CLIP'S NATURAL SPEED — bhk1 ≈ 14.9 u/s, ratio
divisor + accel clamp @0x544/0x548, loader formula byte-matched — so 14.9² ≈ 222 ≫ the 40.0 crash
threshold @0x4ab184): 26 broadcasts in one bench run, every peer's replicant in knockdown/recovery
churn — gliding + trn leg-lifts = "skating". 4-config A/B (`scratchpad/night6/mp_gimpAB.sh`,
`[animind]` inst=1 verdict): grass straight/circle clean (198 gimp entries; **17 car crunches
during it — crushable icons are the `0.00123f` sentinel path, gyro kick only, NO broadcast**);
arena1 single strike recovers (196); arena1 wall-grind = 0 gimp entries.
**What actually bounds the loop in the binary — and what the port was missing — is the crash
SELF-DAMAGE**: the blocking branch (export gap @4aa89f-4aaab4, raw disasm
`scratchpad/night6/gap_4a9770.txt`) fills `throttleState`=surface material,
`fallDirection`=worldToLocal(dmg.damageForce) (FUN_0040879c = Mᵀv, un-normalized),
`fallScalar`=(fallDirection·localVelocity.linearMotion), then **Dispatches
`TakeDamageMessage{0x64, zone=1, the engine-computed collision Damage verbatim}` at its own
mech** — wall crashes hurt, grinding degrades the mech, the storm self-limits. That dispatch was
the port's long-standing "DEFERRED" note; now reconstructed (mech4.cpp blocking branch).
**Falsified en route (do not revive):** (a) "the stagger's action-request flags feed a drive
suppressor" — image-wide + full-gap sweep shows `word[this+0x18]` is read ONLY by the net record
emitter; (b) "the bmp clip's ~6.5 u/s root motion presses back into the wall" — measured `adv=0
cycleSpeed=0` during state 0x20; the re-strike velocity is the REBUILT gg cycle; (c) "the gimp cap
is ±5.8 (so √40 sits just above it)" — that number is `gimpStrideLength`@0x350 (the BACK-cycle
figure, printed under a misleading label since renamed `ggCapL/R`); field @0x52c (`gimpSpeedMax`)
has NO binary consumer. Advance caps, loader fills, crunch path and crash branch are all verified
byte-faithful — the self-damage dispatch was the only gap. Replication detail in [[multiplayer]].
## Controls (`BT_REAL_CONTROLS`, default-on)
`MechControlsMapper` (mechmppr.cpp @004afbe0; btl4mppr.cpp mappers) interprets input → `speedDemand`
+6 -8
View File
@@ -761,14 +761,12 @@ register. ⚠ The audit also flags the damage-economy item as SELF-CONTRADICTOR
numbers no longer point at that code).
## Multiplayer (Phase 7 / P6)
- **Stagger drive-suppressor reconstruction (the #82 wall-grind fix; 2026-07-30).** The binary's
crash/stagger sets action-request flags via `FUN_004a4c54(this,1)` then `(this,0x20)`; their
CONSUMER (almost certainly a drive/steering suppressor while the bmp clip plays) is not
reconstructed, so the clip's ~6.5 u/s root motion re-strikes the wall and the knockdown
broadcast storms (26× one bench run) — peers' replicants churn knockdown/trn = "skating"
([[locomotion]] §Knockdown for the full A/B). Dig: find the actionRequestFlags bit-1/bit-0x20
readers in the decomp (mech performance / drive integrate), reconstruct, then drop the marked
rebind guard at mech4.cpp:6861. Until then the limp bench should circle on GRASS (no walls).
- **#82 wall-grind storm — CLOSED 2026-07-30** (the "stagger drive-suppressor" theory that
briefly lived here was falsified: `word[this+0x18]` has no consumer but the record emitter).
The real missing piece was the crash SELF-DAMAGE dispatch (binary @4aa984-4aaab4), now
reconstructed — see [[locomotion]] §Knockdown. Residue actually still open: the crash's
`throttleState = fall-surface material` fill (binary `(**[mech+0x2f8])+0x24`; payload nicety
for the type-5 reader's surface-specific fall handling — needs the assistant-record map).
- **Cross-fire bench opens (2026-07-30, first real-weapons 4-node runs).** (a) COMBAT STALL: all
firing and dying ceased ~3 min in (run 2, throttled `BT_AF_PERIOD=7`) with everyone alive,