From ad8c24ef8bfae6da725d6cbda57b2b5593034656 Mon Sep 17 00:00:00 2001 From: Erik Date: Sun, 21 Jun 2026 20:10:22 +0200 Subject: [PATCH] =?UTF-8?q?feat(slice2):=20add=20PhysicsEngine.IsLandblock?= =?UTF-8?q?Loaded=20=E2=80=94=20readiness=20gate=20=C2=A73.4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude Opus 4.8 (1M context) --- src/AcDream.Core/Physics/PhysicsEngine.cs | 9 +++ .../PhysicsEngineIsLandblockLoadedTests.cs | 57 +++++++++++++++++++ 2 files changed, 66 insertions(+) create mode 100644 tests/AcDream.Core.Tests/Physics/PhysicsEngineIsLandblockLoadedTests.cs diff --git a/src/AcDream.Core/Physics/PhysicsEngine.cs b/src/AcDream.Core/Physics/PhysicsEngine.cs index 108b9234..05354644 100644 --- a/src/AcDream.Core/Physics/PhysicsEngine.cs +++ b/src/AcDream.Core/Physics/PhysicsEngine.cs @@ -31,6 +31,15 @@ public sealed class PhysicsEngine /// Number of registered landblocks (diagnostic). public int LandblockCount => _landblocks.Count; + /// + /// Returns true iff a landblock with this id has been registered via + /// . Because registers + /// terrain + collision cells + portals atomically, ContainsKey implies the + /// collision mesh is present and resolvable. + /// + public bool IsLandblockLoaded(uint landblockId) => + _landblocks.ContainsKey(landblockId); + /// /// Cell-based spatial index for static object collision. /// Populated during landblock streaming; queried by the Transition system. diff --git a/tests/AcDream.Core.Tests/Physics/PhysicsEngineIsLandblockLoadedTests.cs b/tests/AcDream.Core.Tests/Physics/PhysicsEngineIsLandblockLoadedTests.cs new file mode 100644 index 00000000..727eeccf --- /dev/null +++ b/tests/AcDream.Core.Tests/Physics/PhysicsEngineIsLandblockLoadedTests.cs @@ -0,0 +1,57 @@ +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)); + } +}