Delete the callerless portal-BFS research graph and spent renderer probe families while retaining the production RetailFrameWalk path, terrain diagnostics, membership invariant, and walk transcript. Mutation evidence (all restored): 1. Restored PortalVisibilityBuilder type -> AppAssembly_ContainsNoSupersededPortalGraphTypes first failed Assert.Empty with AcDream.App.Rendering.PortalVisibilityBuilder. 2. Restored ACDREAM_PROBE_FACILITY_STAIRS -> ProductionSource_ContainsNoDeletedRendererProbe_AndRetainsWalkTranscriptProof first failed Assert.Empty on RenderingDiagnostics.cs. 3. Added a second RetailFrameWalk field -> WalkFrameOwners_AreUnique first failed Assert.Single with _frameWalk and _mutatedSecondFrameWalk. 4. Added OrderBy to OrderedStream -> OrderedWalkStream_HasNoCrossStreamReorder first failed Assert.DoesNotContain on OrderBy(. 5. Added IDatReaderWriter parameter -> FrameTimeWalkOwners_HaveNoRawDatDependency first failed Assert.Empty on RetailFrameWalk.MutatedRawDatParameter.
78 lines
3.4 KiB
C#
78 lines
3.4 KiB
C#
using AcDream.App.Tests.Rendering;
|
|
using AcDream.App.Rendering.Walk;
|
|
using DatReaderWriter;
|
|
using DatReaderWriter.Options;
|
|
using System.Text;
|
|
|
|
namespace AcDream.App.Tests.Rendering.Walk;
|
|
|
|
/// <summary>
|
|
/// FW1 look-in adjudication instrument: retail's street-outdoor trace
|
|
/// punched look-ins ONLY at buildings a9b4001a (cells 16e,16a,16c) and
|
|
/// a9b40022 (164,162,167,169). This pins the data-side join: those cells
|
|
/// must be reachable through the respective building's portal table, and
|
|
/// every drawing-BSP PortalRef must index INTO that table. A failure
|
|
/// message carries the full dump for adjudication.
|
|
/// </summary>
|
|
[Trait("Lane", "InstalledDat")]
|
|
public sealed class WalkBuildingJoinDiagnosticTests
|
|
{
|
|
[Theory]
|
|
[InlineData(0xA9B4001Au, new uint[] { 0xA9B4016Eu, 0xA9B4016Au, 0xA9B4016Cu })]
|
|
[InlineData(0xA9B40022u, new uint[] { 0xA9B40164u, 0xA9B40162u, 0xA9B40167u, 0xA9B40169u })]
|
|
public void Traced_lookin_cells_are_reachable_through_the_building_portal_table(
|
|
uint buildingCellId, uint[] tracedLookInCells)
|
|
{
|
|
string? datDir = InstalledDatTestPath.Resolve();
|
|
if (datDir is null)
|
|
{
|
|
Assert.Fail("Lane=InstalledDat requires an installed retail DAT directory.");
|
|
}
|
|
using var dats = new DatCollection(datDir!, DatAccessType.Read);
|
|
List<WalkWorldDatAdapter.BuildingEntry> entries =
|
|
WalkWorldDatAdapter.BuildBuildings(dats, 0xA9B40000u);
|
|
WalkBuilding building = Assert.Single(
|
|
entries, e => e.Building.PositionCellId == buildingCellId).Building;
|
|
|
|
var dump = new StringBuilder();
|
|
dump.AppendLine($"building {buildingCellId:x8}: {building.Portals.Length} portals");
|
|
var directCells = new HashSet<uint>();
|
|
for (int i = 0; i < building.Portals.Length; i++)
|
|
{
|
|
ref WalkBldPortal p = ref building.Portals[i];
|
|
directCells.Add(p.OtherCellId);
|
|
dump.AppendLine(
|
|
$" portal[{i}]: side={p.PortalSide} exact={p.ExactMatch} "
|
|
+ $"other={p.OtherCellId:x8} otherPortal={p.OtherPortalId} "
|
|
+ $"stabs=[{string.Join(',', p.StabList.Select(s => s.ToString("x8")))}]");
|
|
}
|
|
var bspJoins = new List<int>();
|
|
CollectPortalIndices(building.DrawingBsp, bspJoins);
|
|
dump.AppendLine($" BSP PortalRef indices: [{string.Join(',', bspJoins)}]");
|
|
|
|
// Every BSP portal ref must join into the portal table.
|
|
Assert.All(bspJoins, i => Assert.InRange(i, 0, building.Portals.Length - 1));
|
|
|
|
// Every traced look-in cell must be directly behind a portal or in a
|
|
// portal's stab list (the flood reaches deeper cells through those).
|
|
var reachable = new HashSet<uint>(directCells);
|
|
foreach (WalkBldPortal p in building.Portals)
|
|
foreach (uint stab in p.StabList)
|
|
reachable.Add(stab);
|
|
foreach (uint traced in tracedLookInCells)
|
|
{
|
|
Assert.True(
|
|
reachable.Contains(traced),
|
|
$"traced look-in cell {traced:x8} is not reachable\n{dump}");
|
|
}
|
|
}
|
|
|
|
private static void CollectPortalIndices(WalkBspNode? node, List<int> into)
|
|
{
|
|
if (node is null) return;
|
|
if (node.InPortals is not null)
|
|
into.AddRange(node.InPortals.Select(p => p.PortalIndex));
|
|
CollectPortalIndices(node.PosNode, into);
|
|
CollectPortalIndices(node.NegNode, into);
|
|
}
|
|
}
|