using System.Numerics; namespace AcDream.Core.Physics.Motion; /// /// R5 seam — the acdream stand-in for retail's CPhysicsObj as seen BY /// its owned managers. Retail's StickyManager / ConstraintManager /// / TargetManager each hold a raw physics_obj pointer and call /// back through it (position/velocity/radius accessors, target-tracking /// registration, the HandleUpdateTarget fan-out) and — for the voyeur /// system — resolve OTHER physics objects via CObjectMaint::GetObjectA /// and drive their add_voyeur / receive_target_update / /// remove_voyeur entry points. This interface is that back-pointer. /// /// The App layer implements one host per entity (a remote /// RemoteMotion or the local player), wiring the accessors to the live /// and the / /// / it owns. /// is backed by the App's live entity table /// (_entitiesByServerGuid), giving the voyeur round-trip its /// cross-entity delivery path. /// public interface IPhysicsObjHost { /// Retail physics_obj->id — this object's guid. uint Id { get; } /// Retail physics_obj->m_position — world-space cell + /// frame (acdream seams carry WORLD space; see the MoveToManager binding /// note). Position Position { get; } /// Retail CPhysicsObj::get_velocity. Vector3 Velocity { get; } /// Retail CPhysicsObj::GetRadius — the mover's cylinder /// radius. float Radius { get; } /// Retail physics_obj->transient_state & 1 — the /// CONTACT bit (ConstraintManager's grounded gate). bool InContact { get; } /// Retail CPhysicsObj::get_minterp()->get_max_speed() — /// the mover's max locomotion speed, or null if it has no motion /// interpreter yet (StickyManager falls back to a 15.0 constant). float? MinterpMaxSpeed { get; } /// Retail Timer::cur_time — the wall/game clock (seconds). /// Drives the sticky 1 s timeout and target 10 s staleness deadlines. double CurTime { get; } /// Retail PhysicsTimer::curr_time — the physics-tick clock /// (seconds). Drives TargetManager::HandleTargetting's 0.5 s /// throttle. Retail uses a DIFFERENT clock here than ; /// acdream may bind both to the same source. double PhysicsTimerTime { get; } /// Retail CObjectMaint::GetObjectA(id) — resolve another /// physics object by guid, or null if not currently known/visible. /// The cross-entity seam for the voyeur round-trip and sticky live-target /// resolve. IPhysicsObjHost? GetObjectA(uint id); /// Retail CPhysicsObj::HandleUpdateTarget — fans a /// to this host's /// (move-to steering) AND (sticky follow). /// Called from and the timeout /// path. void HandleUpdateTarget(TargetInfo info); /// Retail CPhysicsObj::interrupt_current_movement → /// MovementManager::CancelMoveTo(0x36). void InterruptCurrentMovement(); /// Retail CPhysicsObj::set_target(ctx, objId, radius, /// quantum) (lazily creating /// the TargetManager). Called by StickyManager::StickTo and /// MoveToManager's object-move entry points. void SetTarget(uint contextId, uint objectId, float radius, double quantum); /// Retail CPhysicsObj::clear_target → /// . void ClearTarget(); /// Retail CPhysicsObj::receive_target_update → /// . The inbound side a SENDER's /// SendVoyeurUpdate tail-calls on the watcher. void ReceiveTargetUpdate(TargetInfo info); /// Retail CPhysicsObj::add_voyeur(id, radius, quantum) → /// (lazily creating the /// TargetManager). Called on the TARGET when a watcher subscribes. void AddVoyeur(uint watcherId, float radius, double quantum); /// Retail CPhysicsObj::remove_voyeur(id) → /// . Called on the TARGET when a /// watcher unsubscribes. void RemoveVoyeur(uint watcherId); }