diff --git a/tests/AcDream.Core.Tests/Physics/ShadowObjectRegistryMultiPartTests.cs b/tests/AcDream.Core.Tests/Physics/ShadowObjectRegistryMultiPartTests.cs index 1d47a19..303a73e 100644 --- a/tests/AcDream.Core.Tests/Physics/ShadowObjectRegistryMultiPartTests.cs +++ b/tests/AcDream.Core.Tests/Physics/ShadowObjectRegistryMultiPartTests.cs @@ -193,4 +193,28 @@ public class ShadowObjectRegistryMultiPartTests Assert.True(found0 && found1, "Expected both parts at new world positions (50, 10, 50) and (51, 10, 50)"); } + + [Fact] + public void Deregister_ClearsEntityShapesCache_NoStaleUpdatePositionRebuild() + { + // A6.P4 door fix (2026-05-24) regression: after Deregister, a stray + // UpdatePosition with the same entityId must NOT resurrect the entity + // via the _entityShapes path. The Deregister cleanup added in Task 4 + // (which folded Task 6 into the multi-part registration commit) clears + // _entityShapes[entityId] alongside the cell-list cleanup. + var reg = new ShadowObjectRegistry(); + const uint doorEntityId = 0x000F4244u; + reg.RegisterMultiPart(doorEntityId, new Vector3(132.6f, 17.1f, 94.08f), + Quaternion.Identity, DoorShapes(), 0x10008u, + EntityCollisionFlags.None, OffX, OffY, LbId); + + reg.Deregister(doorEntityId); + + // Stray UpdatePosition should be a no-op now (no entry to find AND + // no _entityShapes entry to rebuild from). + reg.UpdatePosition(doorEntityId, new Vector3(200f, 200f, 50f), + Quaternion.Identity, OffX, OffY, LbId); + + Assert.Equal(0, reg.TotalRegistered); + } }