feat(physics): R5-V4 behavioral slice — head stance dispatch (all mt) + #164 autonomy bit + mt-0 wire flags
Three unpack_movement parity items (facade deferred per the handoff's own optional clause — see r5-wiring-handoff §V4 status): 1. HEAD style-on-change (0x00524440 @00524502-0052452c): both GameWindow routing heads (remote + player) now dispatch DoMotion(style, ctor defaults) when the UM's stance differs from the interp's current style, BEFORE the movement-type routing — for EVERY type. Previously style applied only through the mt-0 funnel copy, so a chase/turn UM (mt 6-9) carrying a stance change started the move in the OLD stance (a monster charged in NonCombat posture until the next mt-0). The RetailObserverTraceConformanceTests exclusion note updated — the trace filter stays (head calls can't appear in a MoveToInterpretedState replay) but the production gap it pointed at is closed. 2. #164 (closes): the action-replay loop threads each action's autonomy into the dispatch params (Autonomous = the 0x1000 splice, raw 305982) — replayed actions enter the interpreted actions list with their real autonomy instead of ctor-default false. 3. mt-0 wire flags consumed (UpdateMotion parsed them since R4-V3): 0x1 StickToObject → CPhysicsObj::stick_to_object port (0x005127e0: resolve target, PartArray radii — 0 when shapeless, guid-as-is for acdream's flat entity table — → PositionManager.StickTo; unresolvable target → no stick), at BOTH case-0 tails in retail order (@00524583-0052458e: funnel apply → stick → longjump flag); 0x2 StandingLongJump → Motion.StandingLongJump, UNCONDITIONAL write (absent flag clears — retail @0052458e). ServerMotionState gains the StandingLongJump field. Conformance: ChaseArm_WithStanceChange_AppliesStanceBeforeTheChase (harness mirrors the routing-head dispatch) + Actions_ReplayCarriesAutonomyIntoTheInterpretedList. Suite 4041 green. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
parent
530453067a
commit
f423884bd1
9 changed files with 213 additions and 20 deletions
|
|
@ -4474,6 +4474,27 @@ public sealed class GameWindow : IDisposable
|
|||
+ delta.GetHeading());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// R5-V4: retail <c>CPhysicsObj::stick_to_object</c> (0x005127e0) — the
|
||||
/// mt-0 WIRE sticky (unpack_movement case 0 @00524589, gated on the
|
||||
/// motionFlags 0x1 trailer guid): resolve the target, read its PartArray
|
||||
/// radius/height (0 when shapeless — retail's null-PartArray arm), then
|
||||
/// <c>PositionManager::StickTo</c>. Unresolvable target → no stick at
|
||||
/// all (retail's GetObjectA-null path). Retail resolves the target's
|
||||
/// top-level PARENT first (<c>parent ?? self</c>); acdream's entity
|
||||
/// table is flat (no client-side parenting), so the guid is used as-is —
|
||||
/// the same convention as RouteServerMoveTo's TopLevelId.
|
||||
/// </summary>
|
||||
private void StickToObjectFromWire(EntityPhysicsHost? host, uint targetGuid)
|
||||
{
|
||||
if (host is null)
|
||||
return;
|
||||
if (!_entitiesByServerGuid.TryGetValue(targetGuid, out var tgtEnt))
|
||||
return;
|
||||
var (radius, height) = GetSetupCylinder(targetGuid, tgtEnt);
|
||||
host.PositionManager.StickTo(targetGuid, radius, height);
|
||||
}
|
||||
|
||||
private bool RemoveLiveEntityByServerGuid(uint serverGuid)
|
||||
{
|
||||
if (!_entitiesByServerGuid.TryGetValue(serverGuid, out var existingEntity))
|
||||
|
|
@ -4936,6 +4957,24 @@ public sealed class GameWindow : IDisposable
|
|||
_playerController.Motion.InterruptCurrentMovement?.Invoke();
|
||||
_playerController.Motion.UnstickFromObject?.Invoke();
|
||||
|
||||
// R5-V4a: unpack_movement HEAD style-on-change
|
||||
// (0x00524440 @00524502-0052452c): wire style index →
|
||||
// command word (command_ids[]; style-class indices map to
|
||||
// 0x80000000|index — the funnel's S0-verified conversion)
|
||||
// and `if (current style != wire style) DoMotion(style,
|
||||
// ctor defaults)` BEFORE the movement-type switch — for
|
||||
// EVERY type. acdream previously applied style only via
|
||||
// the mt-0 funnel copy, so a chase/turn UM (mt 6-9)
|
||||
// carrying a stance change started the move in the OLD
|
||||
// stance (the RetailObserverTraceConformanceTests "S3
|
||||
// wires the unpack-level style-on-change" exclusion —
|
||||
// this is that wiring).
|
||||
uint wireStylePlayer = stance != 0
|
||||
? (0x80000000u | (uint)stance) : 0x8000003Du;
|
||||
if (_playerController.Motion.InterpretedState.CurrentStyle != wireStylePlayer)
|
||||
_playerController.Motion.DoMotion(wireStylePlayer,
|
||||
new AcDream.Core.Physics.Motion.MovementParameters());
|
||||
|
||||
if (_playerController.MoveTo is { } playerMoveTo
|
||||
&& RouteServerMoveTo(playerMoveTo, _playerController.Motion,
|
||||
_playerController.CellId, update))
|
||||
|
|
@ -4947,6 +4986,23 @@ public sealed class GameWindow : IDisposable
|
|||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// R5-V4: retail unpack_movement case-0 TAIL for the local
|
||||
// player (@00524583-0052458e) — stick_to_object when the
|
||||
// motionFlags 0x1 trailer carried a guid, then
|
||||
// standing_longjump ← motionFlags 0x2 (UNCONDITIONAL —
|
||||
// an absent flag clears it). The interpreted-state COPY
|
||||
// stays skipped for the local player (the
|
||||
// sequencer-authority posture above); these two writes
|
||||
// are the only case-0 effects that apply here.
|
||||
if (update.MotionState.MovementType == 0)
|
||||
{
|
||||
if (update.MotionState.StickyObjectGuid is { } playerSticky
|
||||
&& playerSticky != 0)
|
||||
StickToObjectFromWire(_playerHost, playerSticky);
|
||||
_playerController.Motion.StandingLongJump =
|
||||
update.MotionState.StandingLongJump;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
|
|
@ -4996,6 +5052,18 @@ public sealed class GameWindow : IDisposable
|
|||
remoteMot.Motion.InterruptCurrentMovement?.Invoke();
|
||||
remoteMot.Motion.UnstickFromObject?.Invoke();
|
||||
|
||||
// R5-V4a: unpack_movement head style-on-change — see the
|
||||
// player-side comment (@00524502-0052452c). Runs BEFORE
|
||||
// the type routing for every movement type; the mt-0
|
||||
// funnel below still performs its own full style
|
||||
// adoption (retail has BOTH — the head fires on CHANGE
|
||||
// only, so an unchanged stance is a no-op here).
|
||||
uint wireStyleRemote = stance != 0
|
||||
? (0x80000000u | (uint)stance) : 0x8000003Du;
|
||||
if (remoteMot.Motion.InterpretedState.CurrentStyle != wireStyleRemote)
|
||||
remoteMot.Motion.DoMotion(wireStyleRemote,
|
||||
new AcDream.Core.Physics.Motion.MovementParameters());
|
||||
|
||||
// R4-V5: the type-6..9 routing body is shared with the
|
||||
// local player (RouteServerMoveTo) — behavior identical
|
||||
// to the R4-V4 inline blocks it was extracted from.
|
||||
|
|
@ -5086,6 +5154,18 @@ public sealed class GameWindow : IDisposable
|
|||
// motion-table stack (GetObjectSequence + is_allowed
|
||||
// decide) — mt-0 only post-V4 (types 6-9 returned above).
|
||||
remoteMot.Motion.MoveToInterpretedState(ims, sink);
|
||||
|
||||
// R5-V4: retail unpack_movement case-0 TAIL order
|
||||
// (@00524583-0052458e): move_to_interpreted_state FIRST
|
||||
// (above), THEN stick_to_object when the motionFlags 0x1
|
||||
// trailer carried a guid, THEN standing_longjump ←
|
||||
// motionFlags 0x2 (UNCONDITIONAL — an absent flag
|
||||
// CLEARS it).
|
||||
if (update.MotionState.StickyObjectGuid is { } stickyGuid
|
||||
&& stickyGuid != 0)
|
||||
StickToObjectFromWire(remoteMot.Host, stickyGuid);
|
||||
remoteMot.Motion.StandingLongJump =
|
||||
update.MotionState.StandingLongJump;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue