using System.Collections.Immutable; using System.Numerics; using AcDream.Core.Physics; namespace AcDream.Core.Tests.Physics; public sealed class ShadowSetPositionCommitTests { private const uint Landblock = 0xA9B40000u; private const uint Cell1 = Landblock | 0x0001u; private const uint Cell9 = Landblock | 0x0009u; [Fact] public void NoneRefreshesFrameWithoutChangingExactMembership() { var registry = RegisteredSingle(); ulong version = registry.GetOwnerVersion(1u); var moved = new Vector3(14f, 12f, 50f); registry.CommitSetPosition( 1u, moved, Quaternion.Identity, Cell1, 0f, 0f, PhysicsShadowCommitAction.None, []); ShadowEntry entry = Assert.Single(registry.GetObjectsInCell(Cell1)); Assert.Equal(moved, entry.Position); Assert.Empty(registry.GetObjectsInCell(Cell9)); Assert.Equal(version + 1UL, registry.GetOwnerVersion(1u)); } [Fact] public void ReplaceDeduplicatesExactCellsAndPreserveRetainsThem() { var registry = RegisteredSingle(); var replaced = new Vector3(36f, 12f, 50f); registry.CommitSetPosition( 1u, replaced, Quaternion.Identity, Cell9, 0f, 0f, PhysicsShadowCommitAction.Replace, ImmutableArray.Create(Cell9, Cell9, 0u)); Assert.Empty(registry.GetObjectsInCell(Cell1)); Assert.Equal(replaced, Assert.Single(registry.GetObjectsInCell(Cell9)).Position); var preserved = new Vector3(38f, 12f, 50f); registry.CommitSetPosition( 1u, preserved, Quaternion.Identity, Cell9, 0f, 0f, PhysicsShadowCommitAction.Preserve, []); Assert.Equal(preserved, Assert.Single(registry.GetObjectsInCell(Cell9)).Position); var emptyReplace = new Vector3(40f, 12f, 50f); registry.CommitSetPosition( 1u, emptyReplace, Quaternion.Identity, Cell9, 0f, 0f, PhysicsShadowCommitAction.Replace, []); Assert.Equal(emptyReplace, Assert.Single(registry.GetObjectsInCell(Cell9)).Position); } [Fact] public void RecalculateUsesCanonicalFloodInsteadOfRetainedCells() { var registry = RegisteredSingle(); var moved = new Vector3(36f, 12f, 50f); registry.CommitSetPosition( 1u, moved, Quaternion.Identity, Cell9, 0f, 0f, PhysicsShadowCommitAction.Recalculate, []); Assert.Empty(registry.GetObjectsInCell(Cell1)); Assert.Equal(moved, Assert.Single(registry.GetObjectsInCell(Cell9)).Position); } [Fact] public void SuspendedPreserveRestoresExactRowsAndConsumesReceipt() { var registry = RegisteredSingle(); Assert.True(registry.Suspend(1u)); Assert.Equal(0, registry.TotalRegistered); Assert.Equal(1, registry.SuspendedRegistrationCount); var moved = new Vector3(15f, 12f, 50f); registry.CommitSetPosition( 1u, moved, Quaternion.Identity, Cell1, 0f, 0f, PhysicsShadowCommitAction.Preserve, []); Assert.Equal(1, registry.TotalRegistered); Assert.Equal(0, registry.SuspendedRegistrationCount); Assert.Equal(moved, Assert.Single(registry.GetObjectsInCell(Cell1)).Position); } [Fact] public void SuspendedReceiptCopiesAcrossCollisionRootAndClearsOnTeardown() { var source = RegisteredSingle(); Assert.True(source.Suspend(1u)); var destination = new ShadowObjectRegistry(); Assert.False(destination.RefreshRetainedOwnerFrom( source, 1u, Landblock, out ulong version)); Assert.Equal(source.GetOwnerVersion(1u), version); Assert.Equal(1, destination.RetainedRegistrationCount); Assert.Equal(1, destination.SuspendedRegistrationCount); destination.CommitSetPosition( 1u, new Vector3(16f, 12f, 50f), Quaternion.Identity, Cell1, 0f, 0f, PhysicsShadowCommitAction.None, []); Assert.Single(destination.GetObjectsInCell(Cell1)); destination.Deregister(1u); Assert.Equal(0, destination.RetainedRegistrationCount); Assert.Equal(0, destination.SuspendedRegistrationCount); destination.Clear(); Assert.Equal(0, destination.TotalRegistered); } [Fact] public void MultiPartNoneRefreshesEachPartFromRootTransform() { var registry = new ShadowObjectRegistry(); IReadOnlyList shapes = [ Shape(0x01000001u, new Vector3(1f, 0f, 0f)), Shape(0x01000002u, new Vector3(0f, 1f, 0f)), ]; registry.RegisterMultiPart( 2u, new Vector3(12f, 12f, 50f), Quaternion.Identity, shapes, 0u, EntityCollisionFlags.None, 0f, 0f, Landblock, Cell1); Quaternion rotation = Quaternion.CreateFromAxisAngle( Vector3.UnitZ, MathF.PI / 2f); var root = new Vector3(14f, 12f, 50f); registry.CommitSetPosition( 2u, root, rotation, Cell1, 0f, 0f, PhysicsShadowCommitAction.None, []); ShadowEntry[] entries = registry.GetObjectsInCell(Cell1) .Where(entry => entry.EntityId == 2u) .OrderBy(entry => entry.GfxObjId) .ToArray(); Assert.Equal(2, entries.Length); Assert.Equal(root + Vector3.Transform(shapes[0].LocalPosition, rotation), entries[0].Position); Assert.Equal(root + Vector3.Transform(shapes[1].LocalPosition, rotation), entries[1].Position); } private static ShadowObjectRegistry RegisteredSingle() { var registry = new ShadowObjectRegistry(); registry.Register( 1u, 0x01000001u, new Vector3(12f, 12f, 50f), Quaternion.Identity, 1f, 0f, 0f, Landblock, seedCellId: Cell1, isStatic: false); return registry; } private static ShadowShape Shape(uint gfxObjId, Vector3 local) => new( gfxObjId, local, Quaternion.Identity, 1f, ShadowCollisionType.BSP, 0.25f, 0f); }