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