using System.Collections.Immutable; using AcDream.App.World; using AcDream.Core.Physics; using AcDream.Core.Physics.Motion; using AcDream.Core.World; using AcDream.Runtime.Physics; using DatReaderWriter.Types; namespace AcDream.App.Physics; /// /// Projects the presentation-free Runtime result of retail /// CPhysicsObj::UpdateObjectInternal (0x005156B0) into App's /// and spatial buckets. /// internal sealed class LiveEntityOrdinaryPhysicsUpdater { private readonly RuntimeOrdinaryPhysicsUpdater _runtime; private readonly Func _getSetupCylinder; private readonly Func Spheres, float Scale, float StepUpHeight, float StepDownHeight)> _getSetupMoverShape; public LiveEntityOrdinaryPhysicsUpdater( RuntimePhysicsState physics, Func getSetupCylinder, Func Spheres, float Scale, float StepUpHeight, float StepDownHeight)> getSetupMoverShape) { _runtime = new RuntimeOrdinaryPhysicsUpdater( physics ?? throw new ArgumentNullException(nameof(physics))); _getSetupCylinder = getSetupCylinder ?? throw new ArgumentNullException(nameof(getSetupCylinder)); _getSetupMoverShape = getSetupMoverShape ?? throw new ArgumentNullException(nameof(getSetupMoverShape)); } public bool Tick( LiveEntityRuntime runtime, LiveEntityRecord record, WorldEntity entity, Frame rootFrame, float objectScale, float quantum, int liveCenterX, int liveCenterY, ulong objectClockEpoch, AnimationSequencer? sequencer, Action captureAnimationHooks) { ArgumentNullException.ThrowIfNull(runtime); ArgumentNullException.ThrowIfNull(record); ArgumentNullException.ThrowIfNull(entity); ArgumentNullException.ThrowIfNull(rootFrame); ArgumentNullException.ThrowIfNull(captureAnimationHooks); if (record.PhysicsBody is not { } body) return false; var (radius, height) = _getSetupCylinder(record.ServerGuid, entity); var shape = _getSetupMoverShape(record.ServerGuid, entity); bool ExternalOwnerValid() => IsCurrent( runtime, record, entity, body, objectClockEpoch); if (!_runtime.TryBegin( record.Canonical, rootFrame, objectScale, quantum, radius, height, objectClockEpoch, sequencer, captureAnimationHooks, ExternalOwnerValid, out RuntimeOrdinaryPhysicsCommit commit, sphereList: shape.Spheres, sphereScale: shape.Scale, stepUpHeight: shape.StepUpHeight, stepDownHeight: shape.StepDownHeight)) { return false; } return _runtime.Complete( commit, liveCenterX, liveCenterY, snapshot => { if (!ExternalOwnerValid()) return false; entity.SetPosition(snapshot.Position); entity.Rotation = snapshot.Orientation; entity.ParentCellId = snapshot.FullCellId; return ExternalOwnerValid(); }); } private static bool IsCurrent( LiveEntityRuntime runtime, LiveEntityRecord record, WorldEntity entity, PhysicsBody body, ulong objectClockEpoch) => runtime.IsCurrentSpatialRootObject(record) && record.ObjectClockEpoch == objectClockEpoch && ReferenceEquals(record.WorldEntity, entity) && ReferenceEquals(record.PhysicsBody, body) && record.RemoteMotionRuntime is null && record.ProjectileRuntime is null; }