114 lines
4.9 KiB
C#
114 lines
4.9 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 gate adjudication sweep: with data joins proven correct,
|
|
/// the wrong-building punches must come from the sidedness gate's two
|
|
/// unvalidated inputs — the GfxObj portal-poly plane sign and the
|
|
/// BuildingPortal side-flag decode. This sweeps all combinations against
|
|
/// the street-outdoor fixture and reports which (if any) reproduces
|
|
/// retail's punch set {001a, 0022}. Diagnostic: always passes; the result
|
|
/// lands in the campaign record via the assert message when no arm matches.
|
|
/// </summary>
|
|
[Trait("Lane", "InstalledDat")]
|
|
public sealed class WalkLookInGateSweepTests
|
|
{
|
|
private sealed class Recorder : IWalkEventSink
|
|
{
|
|
public readonly List<WalkEvent> Events = new();
|
|
public void Emit(in WalkEvent walkEvent) => Events.Add(walkEvent);
|
|
}
|
|
|
|
[Fact(Skip = "FW1 adjudication driver (2026-08-30 state): the degrade-level "
|
|
+ "BSP gate killed every wrong punch — remaining deltas are the near "
|
|
+ "buildings 001a/0022 (50/28 m center distance vs the 48 m level-0 "
|
|
+ "band: port UpdateViewerDistance's sphere-adjusted distance + "
|
|
+ "hysteresis) and the ring-1 frustum boundary pair "
|
|
+ "(aab50002 extra / a9b3003c missing). Re-enable to re-adjudicate.")]
|
|
public void Sweep_the_lookin_gate_decodes_against_the_street_fixture()
|
|
{
|
|
string? datDir = CornerFloodReplayTests.ResolveDatDir();
|
|
if (datDir is null)
|
|
{
|
|
Assert.Fail("Lane=InstalledDat requires an installed retail DAT directory.");
|
|
}
|
|
IReadOnlyList<WalkOracleFrame> frames =
|
|
WalkOracleTrace.Load("posed/holtburg-street-outdoor");
|
|
WalkOracleFrame frame = frames[1];
|
|
Assert.NotNull(frame.Pose);
|
|
|
|
// Retail's ground truth for this frame: which buildings punched, and
|
|
// which cells each punch listed.
|
|
string expectedPunches = PunchSignature(
|
|
frame.Events.Select(e => e.Kind == WalkOracleEventKind.Building
|
|
? ("BLD", e.CellId!.Value, Array.Empty<uint>())
|
|
: e.Kind == WalkOracleEventKind.DrawCells
|
|
? ("DC", 0u, e.Cells.ToArray())
|
|
: ("", 0u, Array.Empty<uint>())));
|
|
|
|
var report = new StringBuilder();
|
|
report.AppendLine($"RETAIL: {expectedPunches}");
|
|
string? winner = null;
|
|
|
|
foreach (bool flipPlanes in new[] { false, true })
|
|
{
|
|
foreach (int sideMode in new[] { 0, 1, 2, 3 })
|
|
{
|
|
// sideMode: 0 = (Flags & 0x2), 1 = inverted 0x2,
|
|
// 2 = (Flags & 0x1), 3 = inverted 0x1.
|
|
using var dats = new DatCollection(datDir!, DatAccessType.Read);
|
|
WalkWorldDatAdapter.FlipGfxPolygonPlanes = flipPlanes;
|
|
WalkWorldDatAdapter.BuildingSideMode = sideMode;
|
|
try
|
|
{
|
|
WalkLandscapeDatBuilder.BuiltWorld world =
|
|
WalkLandscapeDatBuilder.Build(dats, frame.Pose!.CellId);
|
|
var ctx = new WalkTraceReplayContext(frame.Pose, world.Cells)
|
|
{
|
|
Buildings = world.Buildings,
|
|
};
|
|
var walk = new RetailFrameWalk();
|
|
var recorder = new Recorder();
|
|
walk.WalkFrame(frame.Pose.CellId, null, world.Landscape, ctx, recorder);
|
|
string actual = PunchSignature(
|
|
recorder.Events.Select(e => e.Kind == WalkEventKind.Building
|
|
? ("BLD", e.CellId, Array.Empty<uint>())
|
|
: e.Kind == WalkEventKind.DrawCells
|
|
? ("DC", 0u, e.Cells.ToArray())
|
|
: ("", 0u, Array.Empty<uint>())));
|
|
string arm = $"flip={flipPlanes} side={sideMode}";
|
|
report.AppendLine($"{arm}: {actual}");
|
|
if (actual == expectedPunches)
|
|
winner ??= arm;
|
|
}
|
|
finally
|
|
{
|
|
WalkWorldDatAdapter.FlipGfxPolygonPlanes = false;
|
|
WalkWorldDatAdapter.BuildingSideMode = 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
Assert.True(
|
|
winner is not null,
|
|
$"no gate decode reproduces retail's punch pattern\n{report}");
|
|
}
|
|
|
|
private static string PunchSignature(
|
|
IEnumerable<(string Kind, uint Id, uint[] Cells)> events)
|
|
{
|
|
var parts = new List<string>();
|
|
foreach ((string kind, uint id, uint[] cells) in events)
|
|
{
|
|
if (kind == "BLD") parts.Add($"B{id:x8}");
|
|
else if (kind == "DC")
|
|
parts.Add($"D[{string.Join(',', cells.Select(c => (c & 0xFFFF).ToString("x3")))}]");
|
|
}
|
|
return string.Join("|", parts);
|
|
}
|
|
}
|