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); }