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