using System; using System.IO; using DatReaderWriter; using DatReaderWriter.DBObjs; using DatReaderWriter.Options; using Xunit; using Xunit.Abstractions; using Env = System.Environment; namespace AcDream.Core.Tests.Physics; /// /// #137 corridor-seam inspection (2026-07-05, Facility Hub). Live probe /// evidence (launch-175-verify2.log:42858): crossing corridor cells /// 0x8A02016E → 0x8A02017A at world x≈85.25 records a wall hit with normal /// (−1,0,0) — pointing straight back against the movement — after which the /// stale sliding normal wedges all forward motion (ok=False hit=no, offset /// projected to zero). Question this dump answers: does cell 0x8A02017A's /// PHYSICS polygon set contain a portal-spanning polygon at its entry plane /// (normal ≈ ±X at the portal's local X) — i.e., are portal-sealing polys in /// our collision set where retail filters them? /// public class Issue137CorridorSeamInspectionTests { private readonly ITestOutputHelper _out; public Issue137CorridorSeamInspectionTests(ITestOutputHelper output) => _out = output; [Theory] [InlineData(0x8A02016Eu)] [InlineData(0x8A02017Au)] public void CorridorCell_PhysicsPolysAndPortals_DatInspection(uint envCellId) { var datDir = Env.GetEnvironmentVariable("ACDREAM_DAT_DIR") ?? Path.Combine(Env.GetFolderPath(Env.SpecialFolder.UserProfile), "Documents", "Asheron's Call"); if (!Directory.Exists(datDir)) { _out.WriteLine($"SKIP: dat directory not found at {datDir}"); return; } using var dats = new DatCollection(datDir, DatAccessType.Read); var envCell = dats.Get(envCellId); Assert.NotNull(envCell); _out.WriteLine($"=== EnvCell 0x{envCellId:X8} ==="); _out.WriteLine($" pos=({envCell!.Position.Origin.X:F2},{envCell.Position.Origin.Y:F2},{envCell.Position.Origin.Z:F2}) " + $"rot=({envCell.Position.Orientation.X:F3},{envCell.Position.Orientation.Y:F3},{envCell.Position.Orientation.Z:F3},{envCell.Position.Orientation.W:F3})"); _out.WriteLine($" EnvironmentId=0x{envCell.EnvironmentId:X4} CellStructure={envCell.CellStructure}"); _out.WriteLine($" CellPortals={envCell.CellPortals.Count}"); foreach (var p in envCell.CellPortals) _out.WriteLine($" portal poly={p.PolygonId} other=0x{p.OtherCellId:X4} flags={p.Flags}"); var environment = dats.Get(0x0D000000u | envCell.EnvironmentId); Assert.NotNull(environment); Assert.True(environment!.Cells.TryGetValue(envCell.CellStructure, out var cs)); _out.WriteLine($" PhysicsPolygons={cs!.PhysicsPolygons.Count} (portal-relevant normals below)"); foreach (var (id, poly) in cs.PhysicsPolygons) { // Compute the face normal from the vertex fan (same math as // PhysicsDataCache.ResolvePolygons). var verts = poly.VertexIds; if (verts.Count < 3) continue; if (!cs.VertexArray.Vertices.TryGetValue((ushort)verts[0], out var v0)) continue; if (!cs.VertexArray.Vertices.TryGetValue((ushort)verts[1], out var v1)) continue; if (!cs.VertexArray.Vertices.TryGetValue((ushort)verts[2], out var v2)) continue; var n = System.Numerics.Vector3.Normalize(System.Numerics.Vector3.Cross( v1.Origin - v0.Origin, v2.Origin - v0.Origin)); // Only print near-horizontal-normal polys (walls) — the seam wall // candidates; floors/ceilings are noise here. if (MathF.Abs(n.Z) > 0.3f) continue; _out.WriteLine($" poly {id}: n=({n.X:F2},{n.Y:F2},{n.Z:F2}) v0=({v0.Origin.X:F2},{v0.Origin.Y:F2},{v0.Origin.Z:F2}) verts={verts.Count} sides={poly.SidesType} stip={poly.Stippling}"); } // The portal polygons live in the VISUAL polygon set — print their // ids so overlap with the physics set (same id space?) is visible. _out.WriteLine($" VisualPolygons={cs.Polygons.Count}"); foreach (var p in envCell.CellPortals) { if (cs.Polygons.TryGetValue((ushort)p.PolygonId, out var vp)) { _out.WriteLine($" portal-poly {p.PolygonId} IS in the visual set (verts={vp.VertexIds.Count})"); bool inPhysics = cs.PhysicsPolygons.ContainsKey((ushort)p.PolygonId); _out.WriteLine($" portal-poly {p.PolygonId} in PHYSICS set: {inPhysics}"); } } } }