using System.Numerics; using AcDream.App.World; using AcDream.Core.Physics; namespace AcDream.App.Physics; /// /// Commits the placement half of retail CPhysicsObj::MoveOrTeleport /// Branch A (0x00516330). The caller runs /// first, then this exact frame/cell snap, /// before considering contact-driven interpolation. /// internal static class RemoteTeleportPlacement { internal static bool Apply( ILiveEntityRemotePlacementRuntime remote, PhysicsBody body, ResolveResult placement, Vector3 cellLocalPosition, Quaternion orientation, double gameTime, bool previousContact, bool previousOnWalkable, Func? isCurrent = null, Func? isVelocityCurrent = null) { ArgumentNullException.ThrowIfNull(remote); ArgumentNullException.ThrowIfNull(body); if (!placement.Ok || !IsFinite(placement.Position) || !IsFinite(cellLocalPosition) || !PositionFrameValidation.IsValid( placement.CellId, cellLocalPosition, orientation) || !double.IsFinite(gameTime)) { throw new ArgumentOutOfRangeException( nameof(placement), "A remote teleport placement requires an accepted finite frame, clock, and nonzero cell."); } body.Orientation = orientation; body.SnapToCell(placement.CellId, placement.Position, cellLocalPosition); body.LastUpdateTime = gameTime; body.ContactPlaneValid = placement.InContact; if (placement.InContact) { body.ContactPlane = placement.ContactPlane; body.ContactPlaneCellId = placement.ContactPlaneCellId; body.ContactPlaneIsWater = placement.ContactPlaneIsWater; } else { body.ContactPlaneCellId = 0u; body.ContactPlaneIsWater = false; } if (!PhysicsObjUpdate.CommitSetPositionTransition( body, placement.InContact, placement.OnWalkable, placement.CollisionNormalValid, placement.CollisionNormal, previousContact, previousOnWalkable, remote.HitGround, remote.LeaveGround, isCurrent, isVelocityCurrent)) { return false; } if (isVelocityCurrent?.Invoke() != false) remote.Airborne = !body.OnWalkable; return isCurrent?.Invoke() ?? true; } private static bool IsFinite(Vector3 value) => float.IsFinite(value.X) && float.IsFinite(value.Y) && float.IsFinite(value.Z); }