feat(physics): P5 commit 2 - arm the ConstraintManager leash on accepted positions (#167)
Wire ConstraintManager.ConstrainTo at every current acdream inbound-position acceptance seam, matching retail SmartBox::HandleReceivedPosition (0x00453fd0): - Remote (player + NPC): LiveEntityNetworkUpdateController arms right after the hard-teleport branch (remotePlacementRequired) returns - reaching that point already means MoveOrTeleport did NOT hard-place - anchored to the object's own live IPhysicsObjHost.Position. - Local player teleport: PlayerMovementController.SetPositionCore now runs UnConstrain (retail teleport_hook 0x00514ed0, previously a no-op because nothing armed the leash) then re-arms anchored to the just-snapped position, composing with the existing StopCompletelyAtPhysicsObjectBoundary velocity zero rather than duplicating it. CommitPreparedPosition mirrors the same pair for the deferred player-mode-entry commit path. - Local player ForcePosition: PlayerMovementController.BlipPosition arms with NO preceding UnConstrain (retail BlipPlayer/SetPositionSimple survives motion/velocity/stick, and the leash is no different). Push PhysicsBody.IsFullyConstrained from PositionManager.IsFullyConstrained at the SAME per-tick chokepoint each pump already runs AdjustOffset (PlayerMovementController.Update, RuntimeRemotePhysicsUpdater.Tick/TickHidden) so TS-35's read gate in jump_is_allowed sees live state instead of a stub that is never written. Tests: local-player arm/teardown/rearm/taper/jump-refusal (Runtime.Tests, PlayerMovementControllerTests), remote-tick IsFullyConstrained push (Runtime.Tests, RuntimePhysicsStateTests). Full Core/Runtime/App suites green with no regressions.
This commit is contained in:
parent
378d0b6ca0
commit
7719d25bc5
5 changed files with 330 additions and 3 deletions
|
|
@ -1288,6 +1288,31 @@ public sealed class PlayerMovementController
|
|||
{
|
||||
_physics.UpdatePlayerCurrCell(CellId);
|
||||
PositionManager?.UnStick();
|
||||
// #167 (Campaign P P5): mirrors the SetPositionCore teleport_hook
|
||||
// teardown+rearm below — see that comment for the retail citation.
|
||||
RearmConstraintLeashAtCurrentPosition();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// #167 (Campaign P P5): retail <c>SmartBox::HandleReceivedPosition</c>
|
||||
/// (0x00453fd0) "Player, teleport-newer" branch re-arms the leash
|
||||
/// immediately after <c>TeleportPlayer</c>'s teardown, anchored to the
|
||||
/// RECEIVED position (here, the body's just-snapped current position).
|
||||
/// Shared by the teleport path (after UnConstrain) and the deferred
|
||||
/// player-mode-entry commit path (<see cref="CommitPreparedPosition"/>),
|
||||
/// which never ran UnConstrain because nothing could have armed the
|
||||
/// leash before the controller had a <see cref="PositionManager"/>.
|
||||
/// docs/research/2026-07-30-constraint-leash-constants.md §2/§3.2.
|
||||
/// </summary>
|
||||
private void RearmConstraintLeashAtCurrentPosition()
|
||||
{
|
||||
if (PositionManager is not { } positionManager)
|
||||
return;
|
||||
AcDream.Core.Physics.Position anchor = _body.CellPosition;
|
||||
positionManager.ConstrainTo(
|
||||
anchor,
|
||||
AcDream.Core.Physics.Motion.ConstraintDistance.GetStartConstraintDistance(anchor.ObjCellId),
|
||||
AcDream.Core.Physics.Motion.ConstraintDistance.GetMaxConstraintDistance(anchor.ObjCellId));
|
||||
}
|
||||
|
||||
private void SetPositionCore(
|
||||
|
|
@ -1332,11 +1357,23 @@ public sealed class PlayerMovementController
|
|||
_mouseMovementEventPending = false;
|
||||
// R5-V3 (#171): retail teleport_hook (0x00514ed0) — PositionManager::
|
||||
// UnStick (@0x00514eee) right after the moveto cancel: a teleport
|
||||
// tears down any active stick. (StopInterpolating/UnConstrain have no
|
||||
// armed acdream counterparts — no local-player InterpolationManager,
|
||||
// constraint leash unarmed per #167.)
|
||||
// tears down any active stick. (StopInterpolating has no armed
|
||||
// acdream counterpart — no local-player InterpolationManager.)
|
||||
// #167 (Campaign P P5): teleport_hook's UnConstrain (@0x00514f02) runs
|
||||
// right after UnStick — previously a no-op because nothing armed the
|
||||
// leash. Now that inbound positions arm it (both remote UpdatePosition
|
||||
// and this player teleport/blip path), the teardown must actually run
|
||||
// so a teleport doesn't inherit a stale leash from wherever the player
|
||||
// was constrained before. Retail's "Player, teleport-newer" branch
|
||||
// then immediately RE-arms the leash anchored to the new (received)
|
||||
// position (SmartBox::HandleReceivedPosition 0x00453fd0) — velocity is
|
||||
// already zeroed above by StopCompletelyAtPhysicsObjectBoundary.
|
||||
if (publishSharedState)
|
||||
{
|
||||
PositionManager?.UnStick();
|
||||
PositionManager?.UnConstrain();
|
||||
RearmConstraintLeashAtCurrentPosition();
|
||||
}
|
||||
// Reset the edge tracker: the stop wiped the motion state, so keys
|
||||
// still physically held must re-fire as press edges on the next
|
||||
// Update (matches the pre-W6 level-triggered behavior of walking
|
||||
|
|
@ -1367,6 +1404,13 @@ public sealed class PlayerMovementController
|
|||
_prevPhysicsPos = pos;
|
||||
_currPhysicsPos = pos;
|
||||
UpdateCellId(_body.CellPosition.ObjCellId, "force-position");
|
||||
// #167 (Campaign P P5): retail "Player, normal" branch of
|
||||
// SmartBox::HandleReceivedPosition (0x00453fd0) — ConstrainTo anchored
|
||||
// to the received position, with NO teardown call (this is the
|
||||
// BlipPlayer path: motion, velocity, and PositionManager stick
|
||||
// relationships all deliberately survive the blip per the class
|
||||
// comment above, and the leash is no different).
|
||||
RearmConstraintLeashAtCurrentPosition();
|
||||
}
|
||||
|
||||
private Vector3 ComputeRenderPosition()
|
||||
|
|
@ -1840,6 +1884,12 @@ public sealed class PlayerMovementController
|
|||
// complete Frame after PartArray. Interpolation may replace it;
|
||||
// Sticky/Constraint then compose according to their retail rules.
|
||||
PositionManager?.AdjustOffset(pmDelta, tickDt);
|
||||
// #167 (Campaign P P5): push the read side of TS-35's
|
||||
// jump_is_allowed gate. MotionInterpreter only has a PhysicsBody
|
||||
// (no host reference), so the per-tick pump — the single owner of
|
||||
// this write, right beside the taper call it mirrors — is the seam
|
||||
// that keeps the stub property current for the local player.
|
||||
_body.IsFullyConstrained = PositionManager?.IsFullyConstrained() ?? false;
|
||||
if (pmDelta.Origin != Vector3.Zero)
|
||||
_body.Position += Vector3.Transform(pmDelta.Origin, _body.Orientation);
|
||||
if (!pmDelta.Orientation.IsIdentity)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue