using System.Numerics; using AcDream.App.Physics; using AcDream.Core.Physics; namespace AcDream.App.Tests.Physics; public sealed class RemoteSpawnPlacementSettlerTests { private const uint Landblock = 0xA9B40000u; private const uint Cell = Landblock | 0x0001u; private const float Radius = 0.48f; private const float Height = 1.835f; [Fact] public void FloorWithinFirstGravityFrame_CommitsGroundContactOnce() { PhysicsEngine engine = BuildFlatEngine(); PhysicsBody body = AirborneBody(new Vector3(12f, 12f, 0.25f)); int hitGround = 0; int leaveGround = 0; bool settled = RemoteSpawnPlacementSettler.TrySettle( engine, body, body.Position, Cell, Radius, Height, ObjectInfoState.EdgeSlide, movingEntityId: 0x70000001u, () => hitGround++, () => leaveGround++); Assert.True(settled); Assert.True(body.InContact); Assert.True(body.OnWalkable); Assert.Equal(1, hitGround); Assert.Equal(0, leaveGround); } [Fact] public void MissingCell_CanRetryAfterHydrationWithoutReconstructingBody() { var engine = new PhysicsEngine { DataCache = new PhysicsDataCache() }; PhysicsBody body = AirborneBody(new Vector3(12f, 12f, 0.25f)); Vector3 originalPosition = body.Position; Assert.False(TrySettle(engine, body)); Assert.False(body.InContact); Assert.Equal(originalPosition, body.Position); AddFlatLandblock(engine); Assert.True(TrySettle(engine, body)); Assert.True(body.InContact); Assert.True(body.OnWalkable); } [Fact] public void FloorOutsideSettleDistance_LeavesGenuinelyAirborneBodyUnchanged() { PhysicsEngine engine = BuildFlatEngine(); PhysicsBody body = AirborneBody(new Vector3(12f, 12f, 1.25f)); Vector3 originalPosition = body.Position; Assert.False(TrySettle(engine, body)); Assert.False(body.InContact); Assert.False(body.OnWalkable); Assert.Equal(originalPosition, body.Position); } private static bool TrySettle(PhysicsEngine engine, PhysicsBody body) => RemoteSpawnPlacementSettler.TrySettle( engine, body, body.Position, Cell, Radius, Height, ObjectInfoState.EdgeSlide, movingEntityId: 0x70000001u, static () => { }, static () => { }); private static PhysicsBody AirborneBody(Vector3 position) => new() { Position = position, Orientation = Quaternion.Identity, State = PhysicsStateFlags.Gravity | PhysicsStateFlags.ReportCollisions, TransientState = TransientStateFlags.None, }; private static PhysicsEngine BuildFlatEngine() { var engine = new PhysicsEngine { DataCache = new PhysicsDataCache() }; AddFlatLandblock(engine); return engine; } private static void AddFlatLandblock(PhysicsEngine engine) => engine.AddLandblock( Landblock, new TerrainSurface(new byte[81], new float[256]), Array.Empty(), Array.Empty(), worldOffsetX: 0f, worldOffsetY: 0f); }