114 lines
5.7 KiB
C#
114 lines
5.7 KiB
C#
using System.Numerics;
|
|
|
|
namespace AcDream.Core.Physics.Motion;
|
|
|
|
/// <summary>
|
|
/// R5 seam — the acdream stand-in for retail's <c>CPhysicsObj</c> as seen BY
|
|
/// its owned managers. Retail's <c>StickyManager</c> / <c>ConstraintManager</c>
|
|
/// / <c>TargetManager</c> each hold a raw <c>physics_obj</c> pointer and call
|
|
/// back through it (position/velocity/radius accessors, target-tracking
|
|
/// registration, the <c>HandleUpdateTarget</c> fan-out) and — for the voyeur
|
|
/// system — resolve OTHER physics objects via <c>CObjectMaint::GetObjectA</c>
|
|
/// and drive their <c>add_voyeur</c> / <c>receive_target_update</c> /
|
|
/// <c>remove_voyeur</c> entry points. This interface is that back-pointer.
|
|
///
|
|
/// <para>The App layer implements one host per entity (a remote
|
|
/// <c>RemoteMotion</c> or the local player), wiring the accessors to the live
|
|
/// <see cref="PhysicsBody"/> and the <see cref="MoveToManager"/> /
|
|
/// <see cref="PositionManager"/> / <see cref="TargetManager"/> it owns.
|
|
/// <see cref="GetObjectA"/> is backed by the App's canonical
|
|
/// <c>LiveEntityRuntime</c> records, giving the voyeur round-trip its
|
|
/// cross-entity delivery path without a second GUID index.</para>
|
|
/// </summary>
|
|
public interface IPhysicsObjHost
|
|
{
|
|
/// <summary>Retail <c>physics_obj->id</c> — this object's guid.</summary>
|
|
uint Id { get; }
|
|
|
|
/// <summary>Retail <c>physics_obj->m_position</c> — world-space cell +
|
|
/// frame (acdream seams carry WORLD space; see the MoveToManager binding
|
|
/// note).</summary>
|
|
Position Position { get; }
|
|
|
|
/// <summary>Retail <c>CPhysicsObj::get_velocity</c>.</summary>
|
|
Vector3 Velocity { get; }
|
|
|
|
/// <summary>Retail <c>CPhysicsObj::GetRadius</c> — the mover's cylinder
|
|
/// radius.</summary>
|
|
float Radius { get; }
|
|
|
|
/// <summary>Retail <c>physics_obj->transient_state & 1</c> — the
|
|
/// CONTACT bit (ConstraintManager's grounded gate).</summary>
|
|
bool InContact { get; }
|
|
|
|
/// <summary>Retail <c>CPhysicsObj::get_minterp()->get_max_speed()</c> —
|
|
/// the mover's max locomotion speed, or <c>null</c> if it has no motion
|
|
/// interpreter yet (StickyManager falls back to a 15.0 constant).</summary>
|
|
float? MinterpMaxSpeed { get; }
|
|
|
|
/// <summary>Retail <c>Timer::cur_time</c> — the wall/game clock (seconds).
|
|
/// Drives the sticky 1 s timeout and target 10 s staleness deadlines.</summary>
|
|
double CurTime { get; }
|
|
|
|
/// <summary>Retail <c>PhysicsTimer::curr_time</c> — the physics-tick clock
|
|
/// (seconds). Drives <c>TargetManager::HandleTargetting</c>'s 0.5 s
|
|
/// throttle. Retail uses a DIFFERENT clock here than <see cref="CurTime"/>;
|
|
/// acdream may bind both to the same source.</summary>
|
|
double PhysicsTimerTime { get; }
|
|
|
|
/// <summary>Retail <c>CObjectMaint::GetObjectA(id)</c> — resolve another
|
|
/// physics object by guid from the object table. The App additionally
|
|
/// withholds Hidden objects from ordinary relationship creation through
|
|
/// its registered TS-49 DetectionManager adaptation. This is the
|
|
/// cross-entity seam for the voyeur round-trip and sticky live-target
|
|
/// resolve.</summary>
|
|
IPhysicsObjHost? GetObjectA(uint id);
|
|
|
|
/// <summary>
|
|
/// Returns the exact target incarnation captured by this host's current
|
|
/// TargetManager relationship. App teardown can overlap a newer
|
|
/// INSTANCE_TS with the same GUID, so StickyManager must not re-resolve the
|
|
/// relationship through the active GUID table.
|
|
/// </summary>
|
|
IPhysicsObjHost? GetRelationshipTarget(uint objectId);
|
|
|
|
/// <summary>Retail <c>CPhysicsObj::HandleUpdateTarget</c> — fans a
|
|
/// <see cref="TargetInfo"/> to this host's <see cref="MoveToManager"/>
|
|
/// (move-to steering) AND <see cref="PositionManager"/> (sticky follow).
|
|
/// Called from <see cref="TargetManager.ReceiveUpdate"/> and the timeout
|
|
/// path.</summary>
|
|
void HandleUpdateTarget(TargetInfo info);
|
|
|
|
/// <summary>Retail <c>CPhysicsObj::interrupt_current_movement</c> →
|
|
/// <c>MovementManager::CancelMoveTo(0x36)</c>.</summary>
|
|
void InterruptCurrentMovement();
|
|
|
|
/// <summary>Retail <c>CPhysicsObj::set_target(ctx, objId, radius,
|
|
/// quantum)</c> → <see cref="TargetManager.SetTarget"/> (lazily creating
|
|
/// the TargetManager). Called by <c>StickyManager::StickTo</c> and
|
|
/// <c>MoveToManager</c>'s object-move entry points.</summary>
|
|
void SetTarget(uint contextId, uint objectId, float radius, double quantum);
|
|
|
|
/// <summary>Retail <c>CPhysicsObj::clear_target</c> →
|
|
/// <see cref="TargetManager.ClearTarget"/>.</summary>
|
|
void ClearTarget();
|
|
|
|
/// <summary>Retail <c>CPhysicsObj::receive_target_update</c> →
|
|
/// <see cref="TargetManager.ReceiveUpdate"/>. The inbound side a SENDER's
|
|
/// <c>SendVoyeurUpdate</c> tail-calls on the watcher.</summary>
|
|
void ReceiveTargetUpdate(TargetInfo info, IPhysicsObjHost sender);
|
|
|
|
/// <summary>Retail <c>CPhysicsObj::add_voyeur(id, radius, quantum)</c> →
|
|
/// <see cref="TargetManager.AddVoyeur"/> (lazily creating the
|
|
/// TargetManager). Called on the TARGET when a watcher subscribes. The
|
|
/// exact watcher host preserves retail's unique object-table identity
|
|
/// while App teardown overlaps a newer same-GUID generation.</summary>
|
|
void AddVoyeur(IPhysicsObjHost watcher, float radius, double quantum);
|
|
|
|
/// <summary>Retail <c>CPhysicsObj::remove_voyeur(id)</c> →
|
|
/// <see cref="TargetManager.RemoveVoyeur"/>. Called on the TARGET when a
|
|
/// watcher unsubscribes. The expected watcher identity prevents a retiring
|
|
/// same-GUID incarnation from removing a newer incarnation's subscription
|
|
/// while App teardown callbacks converge.</summary>
|
|
void RemoveVoyeur(uint watcherId, IPhysicsObjHost expectedWatcher);
|
|
}
|