diff --git a/src/AcDream.App/Rendering/Walk/RetailFrameWalk.cs b/src/AcDream.App/Rendering/Walk/RetailFrameWalk.cs
index 8ee37b49..57a4dabe 100644
--- a/src/AcDream.App/Rendering/Walk/RetailFrameWalk.cs
+++ b/src/AcDream.App/Rendering/Walk/RetailFrameWalk.cs
@@ -44,7 +44,11 @@ public interface IRetailFrameWalkContext : IWalkBuildingFrameContext
public sealed class RetailFrameWalk
{
private readonly WalkPView _interiorPView = new();
- private readonly WalkPView _outdoorPView = new();
+ // The outdoor pview never draws the landscape through its look-in
+ // floods (the landscape is already drawn when buildings punch), so its
+ // draw_landscape is FALSE and look-in DCs always read ov=0 — exactly
+ // the pv=…/ov=0 pattern of every look-in in the oracle traces.
+ private readonly WalkPView _outdoorPView = new() { DrawLandscape = false };
private readonly WalkPortalView _defaultView = new();
public WalkPView InteriorPView => _interiorPView;
diff --git a/tests/AcDream.App.Tests/Rendering/Walk/WalkLandscapeDatBuilder.cs b/tests/AcDream.App.Tests/Rendering/Walk/WalkLandscapeDatBuilder.cs
index f858150c..57367bce 100644
--- a/tests/AcDream.App.Tests/Rendering/Walk/WalkLandscapeDatBuilder.cs
+++ b/tests/AcDream.App.Tests/Rendering/Walk/WalkLandscapeDatBuilder.cs
@@ -33,7 +33,25 @@ public static class WalkLandscapeDatBuilder
private static int SideCellCountForRing(int ring)
=> ring <= 1 ? 8 : ring == 2 ? 4 : ring <= 4 ? 2 : 1;
- public static BuiltWorld Build(DatCollection dats, uint cameraCellId)
+ /// Per-frame viewer placement for moving replays (the camera
+ /// stays within the anchor block in every moving fixture).
+ public static void SetViewer(WalkLandscape landscape, uint cameraCellId, Vector3 cameraOrigin)
+ {
+ uint low = cameraCellId & 0xFFFFu;
+ if (low >= 1 && low <= 0x40)
+ {
+ int cellIndex = (int)low - 1;
+ landscape.ViewerCellX = cellIndex / 8;
+ landscape.ViewerCellY = cellIndex % 8;
+ }
+ else
+ {
+ landscape.ViewerCellX = Math.Clamp((int)MathF.Floor(cameraOrigin.X / 24f), 0, 7);
+ landscape.ViewerCellY = Math.Clamp((int)MathF.Floor(cameraOrigin.Y / 24f), 0, 7);
+ }
+ }
+
+ public static BuiltWorld Build(DatCollection dats, uint cameraCellId, Vector3 cameraOrigin = default)
{
Region region = (Region)dats.Get(0x13000000u)!;
float[] heightTable = region.LandDefs.LandHeightTable;
@@ -57,6 +75,15 @@ public static class WalkLandscapeDatBuilder
landscape.ViewerCellX = cellIndex / 8;
landscape.ViewerCellY = cellIndex % 8;
}
+ else
+ {
+ // Interior camera: retail keys the landscape order off the
+ // OUTSIDE-projected landcell (Position::get_outside_cell_id via
+ // SmartBox::RenderNormalMode seen_outside arm) - derive it from
+ // the camera origin (block-local, 24 m cells).
+ landscape.ViewerCellX = Math.Clamp((int)MathF.Floor(cameraOrigin.X / 24f), 0, 7);
+ landscape.ViewerCellY = Math.Clamp((int)MathF.Floor(cameraOrigin.Y / 24f), 0, 7);
+ }
var cells = new Dictionary();
var buildings = new Dictionary();
diff --git a/tests/AcDream.App.Tests/Rendering/Walk/WalkLookInGateSweepTests.cs b/tests/AcDream.App.Tests/Rendering/Walk/WalkLookInGateSweepTests.cs
index f8cbf2b6..c798d628 100644
--- a/tests/AcDream.App.Tests/Rendering/Walk/WalkLookInGateSweepTests.cs
+++ b/tests/AcDream.App.Tests/Rendering/Walk/WalkLookInGateSweepTests.cs
@@ -66,7 +66,7 @@ public sealed class WalkLookInGateSweepTests
try
{
WalkLandscapeDatBuilder.BuiltWorld world =
- WalkLandscapeDatBuilder.Build(dats, frame.Pose!.CellId);
+ WalkLandscapeDatBuilder.Build(dats, frame.Pose!.CellId, frame.Pose.Origin);
var ctx = new WalkTraceReplayContext(frame.Pose, world.Cells)
{
Buildings = world.Buildings,
diff --git a/tests/AcDream.App.Tests/Rendering/Walk/WalkPortalGateDumpTests.cs b/tests/AcDream.App.Tests/Rendering/Walk/WalkPortalGateDumpTests.cs
index 2049bfb0..06127420 100644
--- a/tests/AcDream.App.Tests/Rendering/Walk/WalkPortalGateDumpTests.cs
+++ b/tests/AcDream.App.Tests/Rendering/Walk/WalkPortalGateDumpTests.cs
@@ -31,7 +31,7 @@ public sealed class WalkPortalGateDumpTests
Assert.NotNull(frame.Pose);
using var dats = new DatCollection(datDir!, DatAccessType.Read);
WalkLandscapeDatBuilder.BuiltWorld world =
- WalkLandscapeDatBuilder.Build(dats, frame.Pose!.CellId);
+ WalkLandscapeDatBuilder.Build(dats, frame.Pose!.CellId, frame.Pose.Origin);
var ctx = new WalkTraceReplayContext(frame.Pose, world.Cells)
{
Buildings = world.Buildings,
diff --git a/tests/AcDream.App.Tests/Rendering/Walk/WalkTraceConformanceTests.cs b/tests/AcDream.App.Tests/Rendering/Walk/WalkTraceConformanceTests.cs
index cd372fbd..ae4032c0 100644
--- a/tests/AcDream.App.Tests/Rendering/Walk/WalkTraceConformanceTests.cs
+++ b/tests/AcDream.App.Tests/Rendering/Walk/WalkTraceConformanceTests.cs
@@ -32,14 +32,7 @@ public sealed class WalkTraceConformanceTests
return new DatCollection(datDir!, DatAccessType.Read);
}
- [Fact(Skip = "FW1 outdoor adjudication (2026-08-30 state): with the pinned "
- + "camera basis the BLD roster+order match retail EXACTLY except one "
- + "ring-1 boundary pair (aab50002 extra / a9b3003c missing). The "
- + "look-in punches fire at the wrong buildings under BOTH GfxObj "
- + "plane-winding conventions (retail: only 001a and 0022, cells "
- + "a9b4016x) — adjudicate the PortalRef.PortalIndex join and the "
- + "BuildingPortal side-flag decode with a per-building dump next. "
- + "Re-enable as the outdoor gate when adjudicated.")]
+ [Fact]
public void Street_outdoor_first_frame_diff()
{
// The first landscape-involving conformance case: diff-first (the
@@ -51,7 +44,7 @@ public sealed class WalkTraceConformanceTests
WalkOracleFrame frame = frames[1]; // frame 2: pose settled (marker timing)
Assert.NotNull(frame.Pose);
WalkLandscapeDatBuilder.BuiltWorld world =
- WalkLandscapeDatBuilder.Build(dats, frame.Pose!.CellId);
+ WalkLandscapeDatBuilder.Build(dats, frame.Pose!.CellId, frame.Pose.Origin);
var ctx = new WalkTraceReplayContext(frame.Pose, world.Cells)
{
Buildings = world.Buildings,
@@ -68,6 +61,123 @@ public sealed class WalkTraceConformanceTests
$"walk diverged from retail\nEXPECTED: {expected}\nACTUAL: {actual}");
}
+ [Fact]
+ public void Doorway_still_first_frame_diff()
+ {
+ // Interior flood adjudication: DI + DC(ov=2, n=3) + the landscape
+ // through two exit views — tests the flood depth in isolation from
+ // the building look-in machinery.
+ IReadOnlyList frames =
+ WalkOracleTrace.Load("posed/holtburg-doorway-still");
+ Assert.NotEmpty(frames);
+ using DatCollection dats = OpenDats();
+ WalkOracleFrame frame = frames[1];
+ Assert.NotNull(frame.Pose);
+ WalkLandscapeDatBuilder.BuiltWorld world =
+ WalkLandscapeDatBuilder.Build(dats, frame.Pose!.CellId, frame.Pose.Origin);
+ var ctx = new WalkTraceReplayContext(frame.Pose, world.Cells)
+ {
+ Buildings = world.Buildings,
+ };
+ WalkCell camera = Assert.Contains(frame.Pose.CellId, world.Cells);
+ var walk = new RetailFrameWalk();
+ var recorder = new Recorder();
+
+ walk.WalkFrame(frame.Pose.CellId, camera, world.Landscape, ctx, recorder);
+
+ string expected = WalkTraceReplayContext.Signature(frame);
+ string actual = WalkTraceReplayContext.Signature(recorder.Events);
+ Assert.True(
+ expected == actual,
+ $"walk diverged from retail\nEXPECTED: {expected}\nACTUAL: {actual}");
+ }
+
+ [Theory]
+ [InlineData("posed/terrace-center")]
+ [InlineData("posed/terrace-edge")]
+ [InlineData("posed/cathedral-arrival")]
+ public void Still_fixture_first_frame_reproduces_exactly(string fixture)
+ {
+ IReadOnlyList frames = WalkOracleTrace.Load(fixture);
+ Assert.NotEmpty(frames);
+ using DatCollection dats = OpenDats();
+ WalkOracleFrame frame = frames[1];
+ Assert.NotNull(frame.Pose);
+ WalkLandscapeDatBuilder.BuiltWorld world =
+ WalkLandscapeDatBuilder.Build(dats, frame.Pose!.CellId, frame.Pose.Origin);
+ var ctx = new WalkTraceReplayContext(frame.Pose, world.Cells)
+ {
+ Buildings = world.Buildings,
+ };
+ WalkCell? camera = (frame.Pose.CellId & 0xFFFFu) >= 0x100
+ ? Assert.Contains(frame.Pose.CellId, world.Cells)
+ : null;
+ var walk = new RetailFrameWalk();
+ var recorder = new Recorder();
+
+ walk.WalkFrame(frame.Pose.CellId, camera, world.Landscape, ctx, recorder);
+
+ string expected = WalkTraceReplayContext.Signature(frame);
+ string actual = WalkTraceReplayContext.Signature(recorder.Events);
+ Assert.True(
+ expected == actual,
+ $"walk diverged from retail ({fixture})\nEXPECTED: {expected}\nACTUAL: {actual}");
+ }
+
+ [Theory(Skip = "FW1 moving-fixture tail (2026-08-30): the six STILL fixtures are "
+ + "fully conformant; the moving four diverge only at punch-edge frames "
+ + "(walkout/transitions F2: retail punches 001e where the replay does "
+ + "not; walkabout diverges at F9, foundry-entry at F67) - pose-timing/"
+ + "threshold sensitivity to adjudicate next (the exact pose-application "
+ + "point within the frame).")]
+ [InlineData("posed/holtburg-walkout")]
+ [InlineData("posed/holtburg-transitions")]
+ [InlineData("posed/holtburg-walkabout")]
+ [InlineData("posed/foundry-entry")]
+ public void Moving_fixture_reproduces_every_pairable_frame(string fixture)
+ {
+ // Marker timing: the pose stamped at frame N+1 is the camera state
+ // frame N drew with (fixture README) — pair events(N) with
+ // pose(N+1). One walk + one landscape carry state across frames,
+ // reproducing retail's cross-frame caches (the stale cell-order
+ // quirk included).
+ IReadOnlyList frames = WalkOracleTrace.Load(fixture);
+ Assert.True(frames.Count >= 3);
+ using DatCollection dats = OpenDats();
+ WalkOraclePose anchor = frames[1].Pose!;
+ WalkLandscapeDatBuilder.BuiltWorld world =
+ WalkLandscapeDatBuilder.Build(dats, anchor.CellId, anchor.Origin);
+ var walk = new RetailFrameWalk();
+
+ for (int n = 1; n < frames.Count - 1; n++)
+ {
+ WalkOracleFrame frame = frames[n];
+ WalkOraclePose pose = frames[n + 1].Pose!;
+ Assert.NotNull(pose);
+ WalkLandscapeDatBuilder.SetViewer(world.Landscape, pose.CellId, pose.Origin);
+ var ctx = new WalkTraceReplayContext(pose, world.Cells)
+ {
+ Buildings = world.Buildings,
+ };
+ WalkCell? camera = null;
+ if ((pose.CellId & 0xFFFFu) >= 0x100)
+ {
+ Assert.True(
+ world.Cells.TryGetValue(pose.CellId, out camera),
+ $"frame {frame.Number}: interior camera cell {pose.CellId:x8} not loaded");
+ }
+ var recorder = new Recorder();
+
+ walk.WalkFrame(pose.CellId, camera, world.Landscape, ctx, recorder);
+
+ string expected = WalkTraceReplayContext.Signature(frame);
+ string actual = WalkTraceReplayContext.Signature(recorder.Events);
+ Assert.True(
+ expected == actual,
+ $"frame {frame.Number} diverged ({fixture})\nEXPECTED: {expected}\nACTUAL: {actual}");
+ }
+ }
+
[Fact]
public void Foundry_deep_reproduces_every_complete_frame_exactly()
{
diff --git a/tests/AcDream.App.Tests/Rendering/Walk/WalkWorldDatAdapter.cs b/tests/AcDream.App.Tests/Rendering/Walk/WalkWorldDatAdapter.cs
index 88b46fe3..b0ee798a 100644
--- a/tests/AcDream.App.Tests/Rendering/Walk/WalkWorldDatAdapter.cs
+++ b/tests/AcDream.App.Tests/Rendering/Walk/WalkWorldDatAdapter.cs
@@ -63,7 +63,10 @@ public static class WalkWorldDatAdapter
? 0xFFFFFFFFu
: lbMask | portal.OtherCellId,
PolygonIndex = i,
- PortalSide = ((ushort)portal.Flags & 0x2) != 0 ? 1 : 0,
+ // Data-pinned 2026-08-30 (the doorway-still flood): cell
+ // portal_side is the INVERSE of the 0x2 bit — the same
+ // convention the building-portal sweep pinned.
+ PortalSide = ((ushort)portal.Flags & 0x2) != 0 ? 0 : 1,
ExactMatch = ((ushort)portal.Flags & 0x1) != 0,
OtherPortalId = unchecked((short)portal.OtherPortalId),
};