the visible limp (#78): there was never a jump-jet clip set -- the 'Airborne' drivers ARE the gimp gait machines
The port had FUN_004a5bf8/71f4 fully reconstructed as AdvanceBody/Leg-
AnimationAirborne, gated on (MovementMode()==3||4) && jumpCapable@0x580 and
believed dead ("the test mech never jumps"). The binary says otherwise:
mech+0x40 in that gate is the graphicAlarm LEVEL (3=left-leg gimp, 4=right)
and +0x580 is hasGimpClips, set by the conditional loader block that probes
'wgl' and fills clip slots 22-27 (wgl/wgr/ggr/ggl/gsl/gsr) plus the four
measurements at 0x53c-0x548 (wg entry strides = speed caps, gg cycle strides).
Renamed the five jump* members + both drivers accordingly.
New: GimpBodyClipFinished @004a6344 / GimpLegClipFinished @004a7970 -- the
gimp transition machines, branched from the normal finished-callbacks. Phase-
correct limp entry (left-gimp enters 0x16/wgl only from a RIGHT step, right-
gimp 0x17/wgr from a LEFT step), gg cycles at gimp cadence, gs exits, and the
demand clamp to the gimped side's speed cap (leg cb writes it back into the
mapper -- the binary's authentic slowdown; the T3 x0.5 stand-in in mechmppr is
retired, BT_GIMP_SPEED now defaults 1.0). The binary's gimp machines have no
reverse entry -- the "reverse disabled" behavior is now binary-proven.
Reviving the dead drivers replayed two port-glue bugs (gotcha #24): the raw
*(controlSource) mapper read (null -> crash at first engagement) and the
missing alarm->member state re-sync (machine pinned in one run state). Both
fixed; bench harness gained BT_SELF_DAMAGE_TICKS=<n> to hold a zone past
LegHalfStructure without destroying it.
Bench-verified (madcat, novice, zone 16): crossing -> alarm 4 -> wgr entry
from a left step -> 8k+ frames stable in the ggl limp cycle at cadence 14.77
(vs 18.5 walk / 22+ run) with raw demand still 50. [T2]
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Fable 5
parent
520f6eecd3
commit
36f68718c2
@@ -475,7 +475,7 @@ default-ON (`'0'` disables).
|
||||
| `BT_AUDIO_SOURCES=<n>` | request `n` OpenAL mono sources instead of the driver default (~256). **Opt-in on purpose** — the cap doubles as a governor, and with EFX reverb live a higher ceiling means more simultaneous voices mixing during heavy combat. Measure frame time. See [[wintesla-port]] |
|
||||
| `BT_SELF_DAMAGE_ZONE=<n>` | aim the BT_SELF_DAMAGE harness at an explicit zone (-1/unset = the lottery) -- ramps one zone deterministically past thresholds (#78 bench) |
|
||||
| `BT_DRIVE_LOG` | the demand-site drive scale: `[drive] n/drive/mm/dmd/mech` ~1Hz (drive = myomers speedEffect x gimp; mm = the gimp level via BTMechGimpLevel) |
|
||||
| `BT_GIMP_SPEED=<0..1>` | override the gimped speed factor (default 0.5 [T3 VGL Lynx]) |
|
||||
| `BT_GIMP_SPEED=<0..1>` | EXTRA gimp demand multiplier (default 1.0 = authentic since 2026-07-30: the real slowdown is the gimp gait machines' clamp to the wg clip speed — see [[locomotion]] §visible limp; the old 0.5 T3 stand-in is retired) |
|
||||
| `BT_PICK_LOG` | #73 aimed-pick diagnostics: `[segpick]` the segment→zone map at tree build (index, name, zone, sphere), `[pickwin]` the winning zone/score/t per pick (score ~0 = threading the part core, ~1 = envelope graze) |
|
||||
| `BT_CRIT_LOG` | #80 crit diagnostics: `[subarmor]` per-subsystem armour/scales/critBonus at ctor (proves the resource keys parsed + the zone got REAL scales), `[critroll]` per landed crit (zone, subsystem, its resulting own-zone level). NB the type-0x1e loader's `[crit]` tag is a different, older log |
|
||||
| `BT_DEVICELOST_TEST=<frame>[,crashrepro]` | #35 bench hook. `<frame>` forces the D3D9 DEVICELOST branch at that render frame (+600/+1200 = 3 cycles), driving the REAL `BTResetLostDevice` recovery. `,crashrepro` runs the field null-teardown shape (double `ParticleEngine::Destroy`) — pre-fix this reproduced the field crash byte-for-byte (`Destroy +0x11`, `target=0x0`); post-fix it must log `SURVIVED`. See [[wintesla-port]] §Device-loss |
|
||||
|
||||
+42
-14
@@ -254,21 +254,49 @@ Check `[shadowobj]` tag lines and `[sync]`/`BT_SYNC_LOG` before touching bias/ti
|
||||
- Detail: `docs/P3_LOCOMOTION.md`. Uses: [[asset-formats]] (SKL/ANI), [[decomp-reference]] (offsets).
|
||||
- Feeds: [[combat-damage]] (collision→damage), [[rendering]] (shadow/visual-conform).
|
||||
|
||||
## Myomer drive + the GIMP (limp) chain — IMPLEMENTED 2026-07-30 (#75/#78) [T2 bench]
|
||||
The speed-demand site (`mechmppr.cpp` InterpretControls) now applies the **drive scale**:
|
||||
`speedDemand *= myomers.speedEffect × gimpFactor`. The myomers factor is the wrapper's live 0..1
|
||||
## Myomer drive + the GIMP (limp) chain — COMPLETE 2026-07-30 (#75/#78)
|
||||
The speed-demand site (`mechmppr.cpp` InterpretControls) applies the **drive scale**:
|
||||
`speedDemand *= myomers.speedEffect`. The myomers factor is the wrapper's live 0..1
|
||||
output (gear/thermal/1−damage, via `BTMyomersDriveOf`); the authentic coupling attached
|
||||
`&speedEffect` into the mover's feed roster, which the 2007 engine lacks — the multiplication at
|
||||
the demand site is the port equivalent. The GIMP chain: `mechdmg` raises **graphicAlarm 3 (left)
|
||||
/ 4 (right)** when a leg zone (the `LegDamageZone`-flagged zones; MadCat: 3/5/8 left, 10/16/19
|
||||
right) crosses `LegHalfStructure` (0.5); while gimped the demand additionally multiplies by
|
||||
**0.5 [T3 — VGL Lynx's "roughly 50%", `BT_GIMP_SPEED` overrides]** and **reverse input is
|
||||
refused** ("reverse disabled", `[gimp]` log; the pod's audio cue rides the alarm's watchers if
|
||||
audio rows exist). Measured composed and exact: `dmd 44.837 → 6.726 = 44.837 × 0.5 × 0.3` (a
|
||||
crit-damaged myomers at 0.3 during the same ramp). ⚠ Cross-TU reads of the gimp level MUST use
|
||||
`BTMechGimpLevel` (mechdmg.cpp) — see [[reconstruction-gotchas]] §23 (the AlarmIndicator typedef
|
||||
split). Open on #78: the **Gimp animation clips** (`Left/RightGimpAnimation` + transitions —
|
||||
authored KEYS in the binary's model-record parser; mech2's enum names states 0x12-0x17 for them
|
||||
but mech3's reverse-fix reassigned those slots to the reverse figures — reconcile before wiring
|
||||
clips) and the audio cue binding. Harness: `BT_SELF_DAMAGE_ZONE=<n>` ramps one zone
|
||||
deterministically; `BT_DRIVE_LOG` prints `[drive] n/drive/mm/dmd/mech`.
|
||||
right) crosses `LegHalfStructure` (0.5), and **reverse input is refused** ("reverse disabled",
|
||||
`[gimp]` log — and the binary agrees: the gimp gait machines have NO standing→reverse entry).
|
||||
⚠ Cross-TU reads of the gimp level MUST use `BTMechGimpLevel` (mechdmg.cpp) — see
|
||||
[[reconstruction-gotchas]] §23 (the AlarmIndicator typedef split). Harness:
|
||||
`BT_SELF_DAMAGE_ZONE=<n>` ramps one zone deterministically; `BT_DRIVE_LOG` prints
|
||||
`[drive] n/drive/mm/dmd/mech`.
|
||||
|
||||
### The VISIBLE limp — the gimp gait machines (reconstructed + bench-verified 2026-07-30) [T2]
|
||||
Bench (madcat, novice, `BT_SELF_DAMAGE=60 BT_SELF_DAMAGE_ZONE=16 BT_SELF_DAMAGE_TICKS=2`,
|
||||
`BT_GOTO` walk): right-leg crossing → alarm 4 → body enters 0x17/wgr FROM A LEFT STEP
|
||||
(phase-correct) → both channels settle in 0x19/ggl and hold it 8k+ frames at the gimp cadence
|
||||
cap (cycle 14.77 = the wgr entry stride, vs 18.5 walk / 22+ run) while raw demand stays 50 —
|
||||
the slowdown IS the gait machine, no demand multiply. Two revive-bugs fixed en route: see
|
||||
[[reconstruction-gotchas]] §24.
|
||||
The binary's limp was hiding behind a **"jump-jet" misread**: the port had `FUN_004a5bf8` /
|
||||
`FUN_004a71f4` reconstructed as `AdvanceBody/LegAnimationAirborne`, gated on
|
||||
`(MovementMode()==3||4) && jumpCapable@0x580` and thought DEAD (mechs never jump). Truth
|
||||
[T1, raw part_012.c]: **mech+0x40 there is the graphicAlarm level (3=left gimp, 4=right)**
|
||||
and **+0x580 is `hasGimpClips`** — set by the conditional loader block that probes `wgl` and
|
||||
loads clip slots 22-27 (`wgl wgr ggr ggl gsl gsr`, +0x624..0x638) plus four measurements:
|
||||
`gimpLeft/RightSpeedMax`@0x53c/0x540 (the wg entry clips' final keyframe strides) and
|
||||
`gimpLeft/RightStrideLength`@0x544/0x548 (MeasureClipStride over the gg cycles). The five
|
||||
`jump*`-named members were renamed accordingly (mech.hpp; there is no jump clip set).
|
||||
Machinery (all four reconstructed in mech2.cpp, gates in mech4.cpp):
|
||||
- **Drivers** `AdvanceBodyAnimationGimp`@004a5bf8 / `AdvanceLegAnimationGimp`@004a71f4 —
|
||||
selected per-frame when `(gimpLevel 3|4) && hasGimpClips`; clamp the demand (body:
|
||||
`bodyTargetSpeed`; leg: the LIVE mapper `speedDemand`, written back) to the gimped side's
|
||||
speed cap and advance the gg cycles at gimp cadence. **This clamp IS the authentic gimp
|
||||
slowdown** — ground speed is clip travel, so the limp cadence bounds it; the earlier T3
|
||||
`×0.5` stand-in in mechmppr is retired (`BT_GIMP_SPEED` remains as an override, default 1.0).
|
||||
- **Finished-callbacks** `GimpBodyClipFinished`@004a6344 / `GimpLegClipFinished`@004a7970 —
|
||||
branched to from the tops of the normal `Body/LegClipFinished`. **Phase-correct entry**:
|
||||
left-gimp enters 0x16/wgl only from a RIGHT step (walk-R cases 5/6/0xe), right-gimp enters
|
||||
0x17/wgr from a LEFT step (7/0xf) — the machine forces one extra normal step if mid-wrong-foot.
|
||||
Cycles: left = 0x18 (ggr clip, stride @0x544), right = 0x19 (ggl, @0x548); exits through the gs
|
||||
transitions 0x1a/0x1b when demand dies. No reverse entry exists while gimped.
|
||||
Open: the load-time per-clip callbacks `PTR_LAB_0050d738/744` on the wg clips (uncarved LABs,
|
||||
inert `Recon` placeholders in the port — likely footstep/cycle events, transitions work without
|
||||
them); the gimp audio cue binding.
|
||||
|
||||
@@ -647,3 +647,20 @@ type, and which member traffic crosses families — is an open work item; the sa
|
||||
explains why `MovementMode()` (the engine `simulationState`) and the KB's "movementMode IS the
|
||||
graphicAlarm level" (task #1) never actually met in the port: they are two different cells, both
|
||||
alive, written by different subsystems.
|
||||
|
||||
## 24. Reviving DEAD reconstructed code replays every port-glue fix it predates (2026-07-30)
|
||||
The gimp gait drivers (`AdvanceBody/LegAnimationGimp`, ex-"Airborne") sat reconstructed-but-dead
|
||||
for weeks while their gate read the wrong cell (gotcha #23's split-brain). The moment the gate
|
||||
was fixed and they ran for the first time, they reproduced — within seconds of engagement — TWO
|
||||
bugs the LIVE drivers had each been cured of long before:
|
||||
1. **the raw `*(this->controlSource)` mapper read** (`controlSource@0x128` is never wired in the
|
||||
port; the live drivers were converted to `MappingMapper()` — the dead one crashed at
|
||||
`AdvanceLegAnimationGimp+0x16`, null deref, first live engagement);
|
||||
2. **the missing alarm→member state re-sync** (`bodyAnimationState = bodyStateAlarm.GetLevel()`;
|
||||
the binary has ONE cell, the recon has two — the ground drivers re-sync at :831/:1181; the
|
||||
dead driver didn't, so the state member froze at its pre-gimp value and the machine pinned in
|
||||
one run state for 8000+ frames).
|
||||
**Rule:** before wiring a long-dead reconstructed function into a live path, diff it against its
|
||||
LIVE sibling for the port-glue idioms (mapper access via `MappingMapper()`, alarm→member
|
||||
re-syncs, null guards, `BTEnvOn` gates). The binary-faithful parts transplant cleanly; it is the
|
||||
RECON-side glue that will be missing, because every glue fix landed only where code was running.
|
||||
|
||||
Reference in New Issue
Block a user