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