using System.Collections.Generic;
using AcDream.Core.Physics;
using DatReaderWriter.Enums;
using DatReaderWriter.Types;
using Xunit;
namespace AcDream.Core.Tests.Physics;
///
/// Issue #101 (2026-05-25) — phantom-stair fix. Retail's
/// CPartArray::InitParts emits collision shapes only from
/// Setup-level CylSpheres/Spheres or per-Part
/// PhysicsBSP. There is NO synthesis from visual mesh AABB.
/// Acdream's mesh-aabb-fallback path at
/// GameWindow.cs:6127 previously fired for ANY entity that
/// reached it with entityBsp==0 && entityCyl==0,
/// including GfxObj-only stabs whose GfxObj has
/// HasPhysics=False. This produced the 10 phantom 0.80 m
/// cylinders that block the Holtburg upper-floor staircase (see
/// docs/research/2026-05-25-a6-stairs-cyl-retail-investigation.md).
///
///
/// captures the
/// retail rule as a predicate: "the entity's source is a GfxObj
/// (high byte 0x01) AND the cache has no
/// entry for it." When this returns true, the caller suppresses the
/// fallback synthesis.
///
///
public class PhysicsDataCachePhantomSourceTests
{
[Fact]
public void IsPhantomGfxObjSource_SetupHighByte_ReturnsFalse()
{
// Setup source (high byte 0x02) is never the GfxObj-phantom case.
// The existing isPhantomSetup check at GameWindow.cs:6090 handles
// the Setup-side phantom. This predicate is scoped to GfxObj only.
var cache = new PhysicsDataCache();
Assert.False(cache.IsPhantomGfxObjSource(0x020019FFu)); // door setup
Assert.False(cache.IsPhantomGfxObjSource(0x02000266u)); // some setup
}
[Fact]
public void IsPhantomGfxObjSource_GfxObjUncached_ReturnsTrue()
{
// GfxObj source (high byte 0x01) with NO cached GfxObjPhysics
// = the phantom case. The stair-step GfxObj 0x0100081A from
// issue #101's broken-stairs capture has HasPhysics=False and
// does not enter the cache. Acdream should treat it as phantom.
var cache = new PhysicsDataCache();
Assert.True(cache.IsPhantomGfxObjSource(0x0100081Au));
}
[Fact]
public void IsPhantomGfxObjSource_GfxObjCached_ReturnsFalse()
{
// GfxObj source (high byte 0x01) WITH a cached GfxObjPhysics
// (i.e. the GfxObj's HasPhysics flag was set and its PhysicsBSP
// root is non-null) is NOT phantom. The staircase BSP entity
// 0x40B50089 from issue #101's capture, backed by GfxObj
// 0x01000C16 with hasPhys=True, is this case.
var cache = new PhysicsDataCache();
var leaf = new PhysicsBSPNode { Type = BSPNodeType.Leaf };
var fakePhysics = new GfxObjPhysics
{
BSP = new PhysicsBSPTree { Root = leaf },
PhysicsPolygons = new Dictionary(),
Vertices = new VertexArray(),
Resolved = new Dictionary(),
};
cache.RegisterGfxObjForTest(0x01000C16u, fakePhysics);
Assert.False(cache.IsPhantomGfxObjSource(0x01000C16u));
}
}