feat(motion): MoveOrTeleport routing in OnLivePositionUpdated (L.3.1 Task 4)
Wraps the legacy hard-snap path in ACDREAM_INTERP_MANAGER=1 env-var guard. When set, runs retail-faithful routing (acclient!CPhysicsObj:: MoveOrTeleport @ 0x00516330): - distance > 96m → hard-snap (SetPositionSimple equivalent) - distance ≤ 96m → Interp.Enqueue (queue for adjust_offset to walk to) - teleport flag → hard-snap (default false until sequence plumbing) - has_contact false → no-op (default true until parser plumbing) Existing hard-snap behavior preserved when flag unset (default). Old path will be removed in cleanup commit (Task 8) after visual verification. Helper: ExtractYawFromQuaternion (inverse of GameWindow.YawToAcQuaternion). TODO followups (filed as plan known-limitations): - IsStaleSequence (uint16 wrap-aware compare on 4 sequence counters) - HasContact wire field (CreateObject.ServerPosition gap) - Teleport-sequence comparison Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
parent
517a3ce89c
commit
062e19f463
1 changed files with 64 additions and 0 deletions
|
|
@ -3241,6 +3241,56 @@ public sealed class GameWindow : IDisposable
|
|||
// slerp doesn't visibly rotate from Identity to truth.
|
||||
rmState.Body.Orientation = rot;
|
||||
}
|
||||
|
||||
// L.3.1 Task 4: env-var gated retail-faithful MoveOrTeleport routing.
|
||||
// Mirrors CPhysicsObj::MoveOrTeleport (acclient @ 0x00516330).
|
||||
// Enabled only when ACDREAM_INTERP_MANAGER=1 to keep default behavior
|
||||
// identical to before this commit. Legacy hard-snap path remains below.
|
||||
if (System.Environment.GetEnvironmentVariable("ACDREAM_INTERP_MANAGER") == "1")
|
||||
{
|
||||
// CPhysicsObj::MoveOrTeleport router (acclient @ 0x00516330):
|
||||
// - stale instance/position seq → ignore (TODO: IsStaleSequence not yet plumbed)
|
||||
// - teleport-seq newer or no-cell → SetPosition (hard-snap)
|
||||
// - has_contact false → no-op (TODO: HasContact not on wire — default true for L.3.1)
|
||||
// - has_contact && distance ≤ 96 → InterpolationManager.Enqueue (queue)
|
||||
// - has_contact && distance > 96 → SetPositionSimple (slide-snap)
|
||||
|
||||
const float MaxPhysicsDistance = 96f;
|
||||
System.Numerics.Vector3 localPlayerPos =
|
||||
_playerController?.Position ?? System.Numerics.Vector3.Zero;
|
||||
float dist = System.Numerics.Vector3.Distance(worldPos, localPlayerPos);
|
||||
|
||||
// Default-false: teleport flag not plumbed until sequence comparison lands (Task 5+).
|
||||
bool teleportFlag = false;
|
||||
// Default-true: HasContact not on wire yet (CreateObject.ServerPosition gap).
|
||||
// bool hasContact = true; (implicit — only the teleport and distance branches below)
|
||||
|
||||
if (teleportFlag)
|
||||
{
|
||||
// SetPosition equivalent: hard-snap position + orientation, clear interp queue.
|
||||
rmState.Body.Position = worldPos;
|
||||
rmState.Body.Orientation = rot;
|
||||
rmState.Interp.Clear();
|
||||
}
|
||||
else if (dist > MaxPhysicsDistance)
|
||||
{
|
||||
// SetPositionSimple equivalent: slide-snap (clear queue, then hard-snap).
|
||||
rmState.Interp.Clear();
|
||||
rmState.Body.Position = worldPos;
|
||||
rmState.Body.Orientation = rot;
|
||||
}
|
||||
else
|
||||
{
|
||||
// InterpolationManager.Enqueue equivalent: queue for adjust_offset to walk to.
|
||||
// NOTE: do NOT touch rmState.Body.Position here — adjust_offset (Task 5) owns it.
|
||||
float headingFromQuat = ExtractYawFromQuaternion(rot);
|
||||
rmState.Interp.Enqueue(worldPos, headingFromQuat, isMovingTo: false);
|
||||
}
|
||||
|
||||
// Skip the legacy hard-snap path below.
|
||||
return;
|
||||
}
|
||||
|
||||
double nowSec = (now - System.DateTime.UnixEpoch).TotalSeconds;
|
||||
System.Numerics.Vector3? serverVelocity = update.Velocity;
|
||||
if (serverVelocity is null
|
||||
|
|
@ -5131,6 +5181,20 @@ public sealed class GameWindow : IDisposable
|
|||
return new System.Numerics.Quaternion(0f, 0f, z, w);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Inverse of <see cref="YawToAcQuaternion"/>: extracts the local yaw (rotation
|
||||
/// about the Z axis, in radians) from an AC wire quaternion.
|
||||
/// Yaw=0 faces +X (East). Used by the L.3.1 InterpolationManager routing to
|
||||
/// convert server orientation into the heading expected by <c>InterpolationManager.Enqueue</c>.
|
||||
/// Standard formula: atan2( 2(wz + xy), 1 − 2(y² + z²) ).
|
||||
/// </summary>
|
||||
private static float ExtractYawFromQuaternion(System.Numerics.Quaternion q)
|
||||
{
|
||||
return MathF.Atan2(
|
||||
2f * (q.W * q.Z + q.X * q.Y),
|
||||
1f - 2f * (q.Y * q.Y + q.Z * q.Z));
|
||||
}
|
||||
|
||||
private void OnCameraModeChanged(bool _modeBool)
|
||||
{
|
||||
if (_input is null) return;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue