using AcDream.Core.Physics; using Xunit; namespace AcDream.Core.Tests.Physics; public class PhysicsEngineIsLandblockLoadedTests { private static TerrainSurface MakeFlatTerrain() { var heights = new byte[81]; Array.Fill(heights, (byte)50); var heightTable = new float[256]; for (int i = 0; i < 256; i++) heightTable[i] = i * 1f; return new TerrainSurface(heights, heightTable); } [Fact] public void IsLandblockLoaded_AbsentId_ReturnsFalse() { var engine = new PhysicsEngine(); Assert.False(engine.IsLandblockLoaded(0xA9B4FFFFu)); } [Fact] public void IsLandblockLoaded_AddedId_ReturnsTrue() { var engine = new PhysicsEngine(); engine.AddLandblock(0xA9B4FFFFu, MakeFlatTerrain(), Array.Empty(), Array.Empty(), worldOffsetX: 0f, worldOffsetY: 0f); Assert.True(engine.IsLandblockLoaded(0xA9B4FFFFu)); } [Fact] public void IsLandblockLoaded_AnotherIdAbsent_ReturnsFalse() { var engine = new PhysicsEngine(); engine.AddLandblock(0xA9B4FFFFu, MakeFlatTerrain(), Array.Empty(), Array.Empty(), worldOffsetX: 0f, worldOffsetY: 0f); Assert.False(engine.IsLandblockLoaded(0xDEADBEEFu)); } [Fact] public void IsLandblockLoaded_AfterRemove_ReturnsFalse() { var engine = new PhysicsEngine(); engine.AddLandblock(0xA9B4FFFFu, MakeFlatTerrain(), Array.Empty(), Array.Empty(), worldOffsetX: 0f, worldOffsetY: 0f); engine.RemoveLandblock(0xA9B4FFFFu); Assert.False(engine.IsLandblockLoaded(0xA9B4FFFFu)); } // Regression for the Slice 2 key-mismatch bug (#145 apparatus re-test, 2026-06-21): // landblocks are stored under the EncodeLandblockId form (low 16 = 0xFFFF), but the // teleport gate (TeleportArrivalReadiness) queries with (destCell & 0xFFFF0000) (low // 16 = 0x0000). A raw ContainsKey never matched, so the outdoor gate was permanently // NotReady and every outdoor teleport timed out at 10 s. IsLandblockLoaded must // normalize so ANY id within the landblock resolves to the stored entry. [Theory] [InlineData(0xA9B40000u)] // landblock-prefix form — exactly what the gate passes (destCell & 0xFFFF0000) [InlineData(0xA9B4000Du)] // an outdoor cell id within the landblock [InlineData(0xA9B40143u)] // an indoor cell id within the landblock [InlineData(0xA9B4FFFFu)] // the dat-id storage form public void IsLandblockLoaded_NormalizesLow16_FindsLandblockForAnyContainedId(uint queryId) { var engine = new PhysicsEngine(); engine.AddLandblock(0xA9B4FFFFu, MakeFlatTerrain(), Array.Empty(), Array.Empty(), worldOffsetX: 0f, worldOffsetY: 0f); Assert.True(engine.IsLandblockLoaded(queryId)); } [Fact] public void IsLandblockLoaded_DifferentLandblockPrefix_ReturnsFalse() { var engine = new PhysicsEngine(); engine.AddLandblock(0xA9B4FFFFu, MakeFlatTerrain(), Array.Empty(), Array.Empty(), worldOffsetX: 0f, worldOffsetY: 0f); // A different landblock prefix must NOT match, even in the 0x..0000 query form. Assert.False(engine.IsLandblockLoaded(0xA9B50000u)); } }