acdream/src/AcDream.Core/Physics/Motion/MotionDeltaFrame.cs
Erik f961d70023 feat(physics): port retail complete object frame pipeline
Restore the named-retail object update order across local, remote, static, projectile, animation, shadow, teleport, and effect lifetimes. Separate authoritative root commits from spatial rebucketing, preserve per-owner hook/FIFO ordering, and remove update-path allocations with exact lifecycle and residency gates.

Add deterministic conformance, adversarial lifetime, GUID-reuse, pending-cell, quaternion, timestamp, and allocation coverage. Release build is warning-free and all 6,446 tests pass with five intentional skips; retail, architecture, and adversarial reviews are clean.

Co-authored-by: OpenAI Codex <codex@openai.com>
2026-07-20 09:10:31 +02:00

80 lines
3.3 KiB
C#

using System.Numerics;
namespace AcDream.Core.Physics.Motion;
/// <summary>
/// Mutable stand-in for retail's <c>Frame</c> when it is used as the per-tick
/// <b>delta accumulator</b> that <c>PositionManager::adjust_offset</c> and its
/// three sub-managers (Interpolation / Sticky / Constraint) write into
/// (retail arg2, e.g. <c>StickyManager::adjust_offset</c> 0x00555430,
/// <c>ConstraintManager::adjust_offset</c> 0x00556180). acdream's
/// <see cref="CellFrame"/> is an immutable record — retail's per-tick math
/// mutates <c>m_fOrigin</c> in place across the interp→sticky→constraint chain
/// (each sub-manager composes on top of the previous one's write), so the
/// accumulator needs a mutable shape.
///
/// <para><see cref="Origin"/> = retail <c>m_fOrigin</c> (the accumulated
/// position delta, in the mover's LOCAL frame after
/// <c>Position::globaltolocalvec</c>). <see cref="Orientation"/> is the full
/// relative quaternion carried by retail <c>Frame</c>; the heading accessors
/// are narrow helpers for managers that explicitly call
/// <c>Frame::set_heading</c>.</para>
/// </summary>
public sealed class MotionDeltaFrame
{
/// <summary>Retail <c>m_fOrigin</c> — the accumulated per-tick position
/// delta (mover-local frame).</summary>
public Vector3 Origin;
/// <summary>Retail <c>Frame</c>'s complete relative rotation.</summary>
public Quaternion Orientation = Quaternion.Identity;
/// <summary>
/// Restore retail's identity <c>Frame</c>: zero translation and identity
/// orientation. Object ticks reuse these accumulators to avoid allocating
/// one transform per entity per render frame.
/// </summary>
public void Reset()
{
Origin = Vector3.Zero;
Orientation = Quaternion.Identity;
}
/// <summary>
/// Retail <c>Frame::combine</c> (0x005122E0), specialized for the mutable
/// per-tick delta frame. The incoming translation is expressed in this
/// frame's local coordinates, so the current orientation rotates it before
/// addition; the incoming orientation is then post-multiplied. Translation
/// scaling is the separate <c>m_scale</c> operation performed by
/// <c>CPhysicsObj::UpdatePositionInternal</c>.
/// </summary>
public void Combine(
Vector3 localOrigin,
Quaternion localOrientation,
float originScale = 1f)
{
Origin += Vector3.Transform(localOrigin * originScale, Orientation);
Orientation = FrameOps.SetRotate(
Origin,
Orientation,
Orientation * localOrientation);
}
/// <summary>
/// Compose another complete delta frame using retail
/// <c>Frame::combine</c> semantics.
/// </summary>
public void Combine(MotionDeltaFrame delta, float originScale = 1f)
{
ArgumentNullException.ThrowIfNull(delta);
Combine(delta.Origin, delta.Orientation, originScale);
}
/// <summary>Retail <c>Frame::get_heading</c> (P5 compass degrees).</summary>
public float GetHeading() => MoveToMath.GetHeading(Orientation);
/// <summary>Retail <c>Frame::set_heading(headingDeg)</c> — pure
/// yaw-about-Z setter (P5 compass degrees).</summary>
public void SetHeading(float headingDeg) =>
Orientation = MoveToMath.SetHeading(Orientation, headingDeg);
}