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:
parent
790938392f
commit
b1cf01029a
6 changed files with 280 additions and 199 deletions
|
|
@ -5403,27 +5403,30 @@ public sealed class GameWindow : IDisposable
|
|||
{
|
||||
rmState.Airborne = false;
|
||||
rmState.Body.Velocity = System.Numerics.Vector3.Zero;
|
||||
rmState.Body.State &= ~AcDream.Core.Physics.PhysicsStateFlags.Gravity;
|
||||
rmState.Body.TransientState |= AcDream.Core.Physics.TransientStateFlags.Contact
|
||||
| AcDream.Core.Physics.TransientStateFlags.OnWalkable;
|
||||
rmState.Interp.Clear();
|
||||
rmState.Body.Position = worldPos;
|
||||
|
||||
// Reset the sequencer out of Falling — see matching block in
|
||||
// TickAnimations Step 5 (env-var path) for rationale.
|
||||
// #161: retail landing = MovementManager::HitGround
|
||||
// (minterp → moveto, 0x00524300) with the Gravity state
|
||||
// bit STILL SET (CMotionInterp::HitGround gates on
|
||||
// state&0x400). The re-apply dispatches the PRESERVED
|
||||
// pre-fall forward command → landing link → cycle. This
|
||||
// replaces the forced SetCycle, which read the
|
||||
// then-clobbered ForwardCommand (Falling) and re-set the
|
||||
// pose it meant to clear. See the twin block in
|
||||
// TickAnimations (VU.land).
|
||||
if (_animatedEntities.TryGetValue(entity.Id, out var aeForLand)
|
||||
&& aeForLand.Sequencer is not null)
|
||||
{
|
||||
uint style = aeForLand.Sequencer.CurrentStyle != 0
|
||||
? aeForLand.Sequencer.CurrentStyle
|
||||
: 0x8000003Du;
|
||||
uint landingCmd = rmState.Motion.InterpretedState.ForwardCommand;
|
||||
if (landingCmd == 0)
|
||||
landingCmd = AcDream.Core.Physics.MotionCommand.Ready;
|
||||
float landingSpeed = rmState.Motion.InterpretedState.ForwardSpeed;
|
||||
if (landingSpeed <= 0f) landingSpeed = 1f;
|
||||
aeForLand.Sequencer.SetCycle(style, landingCmd, landingSpeed);
|
||||
EnsureRemoteMotionBindings(rmState, aeForLand, update.Guid);
|
||||
}
|
||||
rmState.Motion.HitGround();
|
||||
rmState.MoveTo?.HitGround();
|
||||
// DR bookkeeping only (partner of the jump-start
|
||||
// `State |= Gravity`).
|
||||
rmState.Body.State &= ~AcDream.Core.Physics.PhysicsStateFlags.Gravity;
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -10045,9 +10048,22 @@ public sealed class GameWindow : IDisposable
|
|||
rm.Airborne = false;
|
||||
rm.Body.TransientState |= AcDream.Core.Physics.TransientStateFlags.Contact
|
||||
| AcDream.Core.Physics.TransientStateFlags.OnWalkable;
|
||||
rm.Body.State &= ~AcDream.Core.Physics.PhysicsStateFlags.Gravity;
|
||||
rm.Body.Velocity = new System.Numerics.Vector3(
|
||||
rm.Body.Velocity.X, rm.Body.Velocity.Y, 0f);
|
||||
// #161: HitGround MUST run with the Gravity state
|
||||
// bit still set — CMotionInterp::HitGround
|
||||
// (0x00528ac0) gates on state&0x400 (retail never
|
||||
// clears GRAVITY on landing; it's a persistent
|
||||
// object property). Clearing it first made this
|
||||
// re-apply a silent no-op, which is why the
|
||||
// falling pose never exited. The re-apply
|
||||
// dispatches the PRESERVED pre-fall forward
|
||||
// command through the funnel → the motion table
|
||||
// plays the Falling→X landing link. (The old
|
||||
// K-fix17 forced SetCycle is deleted: it read the
|
||||
// then-clobbered InterpretedState.ForwardCommand
|
||||
// — 0x40000015 — and re-set the very Falling
|
||||
// cycle it meant to clear.)
|
||||
rm.Motion.HitGround();
|
||||
// R4-V5 (closes the V4 wiring-contract gap the
|
||||
// adversarial review caught): retail order —
|
||||
|
|
@ -10057,34 +10073,10 @@ public sealed class GameWindow : IDisposable
|
|||
// without it a chasing NPC that lands stalls
|
||||
// until ACE's ~1 Hz re-emit.
|
||||
rm.MoveTo?.HitGround();
|
||||
|
||||
// K-fix17 (2026-04-26): reset the sequencer cycle
|
||||
// from Falling back to whatever the interpreted
|
||||
// motion state says they should be doing now.
|
||||
// Without this, the remote stays in the Falling
|
||||
// pose forever (legs folded) until the next
|
||||
// server-sent UpdateMotion arrives. Use the
|
||||
// sequencer's current style (preserved across
|
||||
// jump) and pick the cycle from
|
||||
// InterpretedState.ForwardCommand: Ready
|
||||
// (idle), WalkForward, RunForward, WalkBackward.
|
||||
// SideStep / Turn aren't strict locomotion
|
||||
// priorities — the next UM the server sends will
|
||||
// refine the cycle if the player is mid-strafe
|
||||
// when they land; this just gets the legs out
|
||||
// of Falling immediately.
|
||||
if (ae.Sequencer is not null)
|
||||
{
|
||||
uint style = ae.Sequencer.CurrentStyle != 0
|
||||
? ae.Sequencer.CurrentStyle
|
||||
: 0x8000003Du;
|
||||
uint landingCmd = rm.Motion.InterpretedState.ForwardCommand;
|
||||
if (landingCmd == 0)
|
||||
landingCmd = AcDream.Core.Physics.MotionCommand.Ready;
|
||||
float landingSpeed = rm.Motion.InterpretedState.ForwardSpeed;
|
||||
if (landingSpeed <= 0f) landingSpeed = 1f;
|
||||
ae.Sequencer.SetCycle(style, landingCmd, landingSpeed);
|
||||
}
|
||||
// DR bookkeeping only (partner of the jump-start
|
||||
// `State |= Gravity`): stops the per-tick gravity
|
||||
// integration for the grounded body.
|
||||
rm.Body.State &= ~AcDream.Core.Physics.PhysicsStateFlags.Gravity;
|
||||
|
||||
if (Environment.GetEnvironmentVariable("ACDREAM_DUMP_MOTION") == "1")
|
||||
Console.WriteLine($"VU.land guid=0x{serverGuid:X8} Z={rm.Body.Position.Z:F2}");
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue