fix(R4-V5): remote transition links stripped by the detached-object guard - door swings snapped (adds register TS-40)
Root cause (pinned by the worktree investigation + verified against source): CMotionInterp's dispatch tails strip link animations for DETACHED objects only (retail `if (physics_obj->cell == 0) RemoveLinkAnimations`, raw @305627, three ported sites: DoInterpretedMotion / StopInterpretedMotion / StopCompletely). The R3 port proxied retail's cell pointer with CellPosition.ObjCellId == 0 - but that #145 field is seeded ONLY by SnapToCell, whose single production caller is the LOCAL PLAYER's SetPosition. Every REMOTE body therefore read "detached" forever, and every dispatch stripped the transition link the motion table had just appended: a used door's swing link died the same tick (pose snapped closed<->open - the live symptom), and remote walk<->run link poses have been silently eaten since the guard landed (masked: locomotion cycles resemble each other; a door's two poses don't). The dispatcher itself (GetObjectSequence link path) was verified retail-verbatim end-to-end - not the bug. Fix: PhysicsBody.InWorld - an explicit "placed in the world" flag carrying exactly the guard's retail meaning (register row TS-40, replacing the previously UNREGISTERED ObjCellId proxy). Set by SnapToCell (retail: enter_world/set_cell assigns the cell) and by RemoteMotion construction (a RemoteMotion exists only for a world entity). All three guard sites now test !InWorld. Default false = retail's pre-enter_world detached state, preserving the guard for genuinely unplaced bodies (bare-harness interps unchanged). Tests: InWorldLinkGuardTests pins the polarity at all guard classes (in-world dispatch keeps links; detached strips; StopCompletely same). Full suite green: 3,961. If the door still snaps after this, the open question is DATA (does the door's table author an On<->Off link at all) - next step per the investigation: dump the real table's Links/StyleDefaults via a DoorSetupGfxObjInspectionTests extension. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
parent
006cf6597a
commit
350fb5e3a5
5 changed files with 126 additions and 3 deletions
|
|
@ -1247,7 +1247,14 @@ public sealed class MotionInterpreter : IMotionDoneSink
|
|||
|
||||
AddToQueue(contextId: 0, MotionCommand.Ready, (uint)jumpSnapshot);
|
||||
|
||||
if (PhysicsObj.CellPosition.ObjCellId == 0)
|
||||
// R4-V5 door-swing fix (2026-07-03): retail's guard is
|
||||
// physics_obj->cell == 0 — DETACHED objects only (raw @305627).
|
||||
// The old proxy (CellPosition.ObjCellId == 0, #145 machinery seeded
|
||||
// only by the local player's SnapToCell) read every REMOTE body as
|
||||
// detached, so every dispatch stripped the just-appended transition
|
||||
// link (door swings snapped; remote walk↔run links died) — see
|
||||
// PhysicsBody.InWorld (register row).
|
||||
if (!PhysicsObj.InWorld)
|
||||
RemoveLinkAnimations?.Invoke();
|
||||
|
||||
return WeenieError.None;
|
||||
|
|
@ -3132,7 +3139,14 @@ public sealed class MotionInterpreter : IMotionDoneSink
|
|||
result = WeenieError.NotGrounded; // 0x24 (A10) — blocked action-class motion
|
||||
}
|
||||
|
||||
if (PhysicsObj.CellPosition.ObjCellId == 0)
|
||||
// R4-V5 door-swing fix (2026-07-03): retail's guard is
|
||||
// physics_obj->cell == 0 — DETACHED objects only (raw @305627).
|
||||
// The old proxy (CellPosition.ObjCellId == 0, #145 machinery seeded
|
||||
// only by the local player's SnapToCell) read every REMOTE body as
|
||||
// detached, so every dispatch stripped the just-appended transition
|
||||
// link (door swings snapped; remote walk↔run links died) — see
|
||||
// PhysicsBody.InWorld (register row).
|
||||
if (!PhysicsObj.InWorld)
|
||||
RemoveLinkAnimations?.Invoke();
|
||||
|
||||
return result;
|
||||
|
|
@ -3235,7 +3249,14 @@ public sealed class MotionInterpreter : IMotionDoneSink
|
|||
}
|
||||
}
|
||||
|
||||
if (PhysicsObj.CellPosition.ObjCellId == 0)
|
||||
// R4-V5 door-swing fix (2026-07-03): retail's guard is
|
||||
// physics_obj->cell == 0 — DETACHED objects only (raw @305627).
|
||||
// The old proxy (CellPosition.ObjCellId == 0, #145 machinery seeded
|
||||
// only by the local player's SnapToCell) read every REMOTE body as
|
||||
// detached, so every dispatch stripped the just-appended transition
|
||||
// link (door swings snapped; remote walk↔run links died) — see
|
||||
// PhysicsBody.InWorld (register row).
|
||||
if (!PhysicsObj.InWorld)
|
||||
RemoveLinkAnimations?.Invoke();
|
||||
|
||||
return result;
|
||||
|
|
|
|||
|
|
@ -115,6 +115,22 @@ public sealed class PhysicsBody
|
|||
/// </summary>
|
||||
public Position CellPosition { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// R4-V5 (the door-swing snap fix, 2026-07-03): the retail
|
||||
/// <c>physics_obj->cell != 0</c> "placed in the world" truth (register
|
||||
/// row). CMotionInterp's dispatch tails strip link animations for
|
||||
/// DETACHED objects only (<c>if (cell == 0) RemoveLinkAnimations</c>,
|
||||
/// raw @305627); the previous proxy — <see cref="CellPosition"/>.<c>ObjCellId
|
||||
/// == 0</c> — was seeded ONLY by the local player's <see cref="SnapToCell"/>
|
||||
/// (#145 machinery), so every REMOTE body read "detached" forever and
|
||||
/// every dispatched transition link (door swings, walk↔run links) was
|
||||
/// stripped the same tick it was appended. Set by <see cref="SnapToCell"/>
|
||||
/// (local player placement) and by RemoteMotion construction (remotes
|
||||
/// exist only for world entities). Default false = detached, matching
|
||||
/// retail's pre-enter_world state.
|
||||
/// </summary>
|
||||
public bool InWorld { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Placement: set the world position AND seed <see cref="CellPosition"/> from the
|
||||
/// wire's (cell, local) — NO streaming center, NO delta. For an OUTDOOR cell the
|
||||
|
|
@ -137,6 +153,7 @@ public sealed class PhysicsBody
|
|||
if ((cellId & 0xFFFFu) is >= 1u and <= 0x40u)
|
||||
LandDefs.AdjustToOutside(ref cell, ref local);
|
||||
CellPosition = new Position(cell, new CellFrame(local, Orientation));
|
||||
InWorld = true; // retail: enter_world / set_cell assigns physics_obj->cell
|
||||
}
|
||||
|
||||
// Mirror a world-position translation into the cell-relative frame. Velocity is
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue