diff --git a/src/AcDream.Core/Physics/PhysicsEngine.cs b/src/AcDream.Core/Physics/PhysicsEngine.cs
index 108b9234..292eeec4 100644
--- a/src/AcDream.Core/Physics/PhysicsEngine.cs
+++ b/src/AcDream.Core/Physics/PhysicsEngine.cs
@@ -31,6 +31,20 @@ public sealed class PhysicsEngine
/// Number of registered landblocks (diagnostic).
public int LandblockCount => _landblocks.Count;
+ ///
+ /// True once the landblock covering has had its
+ /// terrain + cells registered via . Accepts a canonical
+ /// (0xFFFF) id, a cell-resolved id, or a bare landblock id — compares on the high 16
+ /// bits. This is the teleport "worldReady" gate (the destination is grounded).
+ ///
+ public bool IsLandblockTerrainResident(uint cellOrLandblockId)
+ {
+ uint prefix = cellOrLandblockId & 0xFFFF0000u;
+ foreach (var key in _landblocks.Keys)
+ if ((key & 0xFFFF0000u) == prefix) return true;
+ return false;
+ }
+
///
/// 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/PhysicsEngineResidencyTests.cs b/tests/AcDream.Core.Tests/Physics/PhysicsEngineResidencyTests.cs
new file mode 100644
index 00000000..3cb17aad
--- /dev/null
+++ b/tests/AcDream.Core.Tests/Physics/PhysicsEngineResidencyTests.cs
@@ -0,0 +1,34 @@
+using System;
+using System.Numerics;
+using AcDream.Core.Physics;
+using Xunit;
+
+namespace AcDream.Core.Tests.Physics;
+
+public class PhysicsEngineResidencyTests
+{
+ [Fact]
+ public void IsLandblockTerrainResident_trueOnlyAfterAddLandblock()
+ {
+ var eng = new PhysicsEngine();
+ uint canonical = 0xA9B4FFFFu; // canonical (streaming) form
+ uint lbId = canonical & 0xFFFF0000u; // AddLandblock id
+
+ Assert.False(eng.IsLandblockTerrainResident(canonical));
+
+ eng.AddLandblock(lbId, terrain: null!, cells: Array.Empty(),
+ portals: Array.Empty(), worldOffsetX: 0f, worldOffsetY: 0f);
+
+ Assert.True(eng.IsLandblockTerrainResident(canonical));
+ }
+
+ [Fact]
+ public void IsLandblockTerrainResident_matchesOnHigh16_acceptsCellResolvedId()
+ {
+ var eng = new PhysicsEngine();
+ eng.AddLandblock(0xA9B40000u, null!, Array.Empty(),
+ Array.Empty(), 0f, 0f);
+ Assert.True(eng.IsLandblockTerrainResident(0xA9B40019u)); // cell-resolved id, same LB
+ Assert.False(eng.IsLandblockTerrainResident(0xC6A90019u)); // different LB
+ }
+}