using System.Numerics;
namespace AcDream.Core.Physics.Motion;
///
/// Mutable stand-in for retail's Frame when it is used as the per-tick
/// delta accumulator that PositionManager::adjust_offset and its
/// three sub-managers (Interpolation / Sticky / Constraint) write into
/// (retail arg2, e.g. StickyManager::adjust_offset 0x00555430,
/// ConstraintManager::adjust_offset 0x00556180). acdream's
/// is an immutable record — retail's per-tick math
/// mutates m_fOrigin 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.
///
/// = retail m_fOrigin (the accumulated
/// position delta, in the mover's LOCAL frame after
/// Position::globaltolocalvec). is the full
/// relative quaternion carried by retail Frame; the heading accessors
/// are narrow helpers for managers that explicitly call
/// Frame::set_heading.
///
public sealed class MotionDeltaFrame
{
/// Retail m_fOrigin — the accumulated per-tick position
/// delta (mover-local frame).
public Vector3 Origin;
/// Retail Frame's complete relative rotation.
public Quaternion Orientation = Quaternion.Identity;
///
/// Restore retail's identity Frame: zero translation and identity
/// orientation. Object ticks reuse these accumulators to avoid allocating
/// one transform per entity per render frame.
///
public void Reset()
{
Origin = Vector3.Zero;
Orientation = Quaternion.Identity;
}
///
/// Retail Frame::combine (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 m_scale operation performed by
/// CPhysicsObj::UpdatePositionInternal.
///
public void Combine(
Vector3 localOrigin,
Quaternion localOrientation,
float originScale = 1f)
{
Origin += Vector3.Transform(localOrigin * originScale, Orientation);
Orientation = FrameOps.SetRotate(
Origin,
Orientation,
Orientation * localOrientation);
}
///
/// Compose another complete delta frame using retail
/// Frame::combine semantics.
///
public void Combine(MotionDeltaFrame delta, float originScale = 1f)
{
ArgumentNullException.ThrowIfNull(delta);
Combine(delta.Origin, delta.Orientation, originScale);
}
/// Retail Frame::get_heading (P5 compass degrees).
public float GetHeading() => MoveToMath.GetHeading(Orientation);
/// Retail Frame::set_heading(headingDeg) — pure
/// yaw-about-Z setter (P5 compass degrees).
public void SetHeading(float headingDeg) =>
Orientation = MoveToMath.SetHeading(Orientation, headingDeg);
}