fix(R4): #161 remote landing stuck in falling pose - apply-pass params decode

Retail's apply_interpreted_movement (0x00528600) does NOT dispatch with
ctor-default MovementParameters: the BN decomp smears the bitfield store
into the mush expression at raw 305778 - (word & 0x37ff) | cancelMoveTo<<15
| disableJump<<17 - which CLEARS SetHoldKey/ModifyInterpretedState/
CancelMoveTo. ACE MotionInterp.cs:444-449 confirms independently. Three
legs fixed, all retail decode, no adaptations added:

1. ApplyInterpretedMovement now builds the pass params retail actually
   uses (ModifyInterpretedState=false is load-bearing): no dispatch in the
   pass writes InterpretedState, so the airborne Falling substitution
   PRESERVES the wire's forward command and HitGround's re-apply
   dispatches it - the motion table plays the Falling->X landing link.
   The W6 entry-cache (built on the wrong "retail self-heals via hoisted
   registers" theory) is deleted; live reads are retail semantics. Raw
   arg3 decoded as DisableJumpDuringLink -> every (N,0) caller means
   allowJump=true; all 9 caller polarities fixed. copy_movement_from's
   current_style copy (raw 0051e757) added to the UM flat-copy.

2. Both GameWindow landing blocks cleared the Gravity state bit BEFORE
   Motion.HitGround(), whose verbatim state&0x400 gate then no-opped the
   whole retail re-apply; the UP-driven landing block never called
   HitGround at all. Both now run HitGround (minterp then moveto,
   MovementManager::HitGround 0x00524300 order) with Gravity still set,
   then do the DR bookkeeping clear (register row AP-81 added for the
   remaining flag dance, retire in R6).

3. K-fix17's forced SetCycle (both copies) deleted: it executed every
   landing but read the leg-1-clobbered ForwardCommand (0x40000015) and
   re-set the very Falling cycle it meant to clear.

Tests: new HitGround_AfterFall_RedispatchesPreservedForward_ExitsFalling
lifecycle pin; AirborneBody state assertion flipped (it had pinned the
bug value). Suite 3,964 green incl. the 183-case retail-observer trace.
Filed #164 (action-replay Autonomous bit, no current consumer).

Awaiting live verify: stand-still landing must exit the falling pose with
zero wire input.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
Erik 2026-07-03 17:00:03 +02:00
parent 790938392f
commit b1cf01029a
6 changed files with 280 additions and 199 deletions

View file

@ -73,31 +73,60 @@ actually steps at (Player_Move.cs / Creature GetRunRate usage).
## #161 — Remote jump landing holds the falling pose (no landing anim)
**Status:** OPEN (2026-07-03; retested after TS-40/`41006e79` — still
broken; user: stuck in falling until the char moves or the window loses
focus).
**Evidence (launch.log, session bklydthrr, guid 0x50000001):** `VU.land`
fires; Falling completions (0x40000015) flow; but the FIRST post-landing
UM still reports `seq now motion=0x40000015` — the landing block's reset
(rm.Motion.HitGround() apply + K-fix17 SetCycle) did NOT move the cycle
off Falling. Recovery happened only via SUBSEQUENT wire UMs (the mover
pressed keys); a stand-still landing gets none → stuck forever. The
focus-change unstick hints a frame-advance/big-dt interaction as well.
**Prime suspect:** the funnel's airborne branch dispatches Falling via
DoInterpretedMotion with ctor-default ModifyInterpretedState=true →
InterpretedState.ForwardCommand becomes 0x40000015; HitGround's
apply_current_movement (interpreted branch for remotes) then re-applies
the interpreted state — i.e. re-dispatches FALLING — instead of the
pre-fall wire command. Check retail: does apply_interpreted_movement's
falling dispatch (raw ~305730, fresh local var_2c params) really write
interpreted fwd (bit 0x4000 in the local's 0x1EE0F default) — and if so,
what exits Falling on a retail REMOTE landing (HitGround raw @305949
apply_current_movement(this, 1, ...) — the arg2=1 semantics)? Also
verify K-fix17's SetCycle actually executes there (add a probe) and
whether it lost a fight with the same-frame HitGround apply ordering.
**Where:** GameWindow remote landing block (`VU.land` region);
MotionInterpreter.ApplyInterpretedMovement falling branch;
HitGround/apply_current_movement.
**Status:** FIX LANDED (2026-07-03) — awaiting user live verify.
**Root cause (three legs, all retail-decode):**
1. **Apply-pass params (the big one):** retail's `apply_interpreted_movement`
runs ALL its dispatches with `ModifyInterpretedState=false` — the ctor
default 0x1EE0F is REWRITTEN by a bitfield store the BN decomp smears
into the mush expression at raw 305778 (`(word & 0x37ff) |
cancelMoveTo<<15 | disableJump<<17`; 0x37ff clears SetHoldKey/
ModifyInterpretedState/CancelMoveTo). ACE MotionInterp.cs:444-449
confirms. Our pass dispatched with ctor defaults (modify=true), so the
airborne Falling substitution CLOBBERED `InterpretedState.ForwardCommand`
→ HitGround's re-apply re-dispatched Falling instead of the preserved
pre-fall wire command. (The handoff's "HitGround raw @305949" was a
mislabel — 305949 is inside `move_to_interpreted_state`; the real
HitGround is 0x00528ac0 → `apply_current_movement(0,0)`.) Bonus decode:
raw arg3 = DisableJumpDuringLink, so every `(N, 0)` caller means
allowJump=TRUE — all caller polarities fixed. The W6 entry-cache (whose
"retail self-heals via hoisted registers" theory was this same
misreading) is deleted; live field reads are retail semantics now that
the pass cannot write state.
2. **Landing-order:** both GameWindow landing blocks cleared the Gravity
state bit BEFORE `rm.Motion.HitGround()`, whose verbatim `state & 0x400`
gate then no-opped the entire retail re-apply (retail never clears
GRAVITY on landing). The UP-driven landing block never called HitGround
at all. Both now: transients → `Motion.HitGround()``MoveTo.HitGround()`
→ THEN the DR bookkeeping gravity-clear.
3. **K-fix17 answered (user Q3):** its SetCycle DID execute — and re-set
the Falling cycle, because it read the leg-1-clobbered ForwardCommand
(0x40000015 ≠ 0, so the Ready fallback never fired). Both SetCycle
adaptation blocks deleted — superseded by the retail HitGround path,
which also plays the proper Falling→X landing LINK animation SetCycle
never did. Also fixed en route: `copy_movement_from` omitted
`current_style` (raw 0051e757 copies it first) — the landing re-apply's
style dispatch read a stale style.
**Tests:** `MotionInterpreterFunnelTests.HitGround_AfterFall_
RedispatchesPreservedForward_ExitsFalling` (full lifecycle) + the flipped
`AirborneBody_NoCycleDispatches_OnlyTurnStop` state assertion (it had
PINNED the bug value 0x40000015 — noted in-file). Suite 3,964 green.
**Verify:** retail mover jumps in place near acdream observer → lands →
legs exit falling pose into idle WITHOUT pressing any key; jump while
running → lands into run cycle.
## #164 — UM action-replay dispatches drop the per-action Autonomous bit
**Status:** OPEN (2026-07-03, filed during #161)
**Finding:** retail's `move_to_interpreted_state` action loop sets the
dispatch params' Autonomous bit (0x1000) from each action's autonomy flag
(raw 305982: `var_28 ^= ((autonomous << 0xc) ^ var_28) & 0x1000`), which
flows into `InterpretedMotionState::AddAction`'s stored node. Our
`MoveToInterpretedState` action loop dispatches with `new
MovementParameters { Speed }` — Autonomous stays false. No observed
symptom today (stored-node autonomy has no current consumer); fix when
R5/R6 touches the action list. **Where:**
`MotionInterpreter.MoveToInterpretedState` action loop /
`DispatchInterpretedMotion` (doc comment marks the gap).
## #162 — Observer-side moveto cancelled by ACE's autonomous MTS reflections (the "glide" class)