acdream/tests/AcDream.Core.Tests/Conformance/Issue112MembershipTests.cs
Erik 2d6954ee44 fix(phys): #112 - remove the non-retail escape-hatch demote from the pick; lateral stab-graph recovery + retail keep-curr
Root cause (oracle: CLandCell::point_in_cell :316941 = terrain-poly only;
find_cell_list null-result keep-curr pc:308788-308825; CEnvCell::
check_building_transit :309827 = sphere_intersects_cell per portal-adjacent
cell): retail KEEPS curr_cell when nothing contains the centre — including
inside a house's containment gaps. Our 6dbbf95 escape hatch instead demoted
any hydrated indoor claim the sphere no longer overlaps to the outdoor
column; at the A9B3 hill cottage's real interior gap this stranded the
player outdoor-classified deep indoors, where re-promotion is portal-
adjacent-only (retail-identical) -> the outdoor flood rendered the interior
transparent (the user's "sometimes transparent" walk).

The hatch's actual target - poisoned (cell, position) SAVES - has been
handled at the SNAP by PhysicsEngine.Resolve's AdjustPosition validation
since #107/#111, so the per-tick pick reverts to retail semantics:
1. lateral recovery first - when the sphere no longer overlaps the claim,
   search the claim's stab list for a containing cell (retail
   find_visible_child_cell :311444, the same recovery AdjustPosition uses);
   the #111 adjacent-claim shape now self-heals laterally (dat-backed test:
   pick(seed 0x172) at a 0x171-interior point -> 0x171);
2. else KEEP curr_cell (retail null-result).

Two old tests asserting the hatch demote rewritten to the retail semantics
(tests-can-codify-bugs); P1 retail-golden conformance gates explicitly green
(FindCellListConformance + ThresholdPortalCrossing + CottageDoorway +
CameraCornerSeal = 11/11). New Issue112MembershipTests: the lateral-recovery
fact + a DocumentsResidual fact pinning the remaining at-doorway gap demote
(via the NORMAL outdoor-candidate path; open oracle read = retail's
add_all_outside_cells gate in CEnvCell::find_transit_cells pc:317499 -
sphere-proximity vs graph-reachability). Core 1383 + 4 pre-existing #99
failures + 1 skip.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-06-10 15:03:49 +02:00

86 lines
4 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

using System.Numerics;
using AcDream.Core.Physics;
using DatReaderWriter;
using DatReaderWriter.Options;
using Xunit;
using Xunit.Abstractions;
namespace AcDream.Core.Tests.Conformance;
/// <summary>
/// #112 (2026-06-10): the A9B3 hill cottage has a real containment GAP inside
/// the house (world (184.9, 109.5, 116) / A9B3-local (184.9, 82.5) is in NO
/// interior cell while points ~0.7 m away are inside 0x100/0x103). The
/// 6dbbf95 escape hatch demoted the walker to the outdoor column there,
/// stranding them outdoor-classified deep indoors (no containment-based
/// re-promotion) → the outdoor flood rendered the interior transparent.
/// Retail find_cell_list KEEPS curr_cell when nothing contains the centre
/// (pc:308788-308825). These tests pin the replacement semantics against the
/// real dats.
/// </summary>
public sealed class Issue112MembershipTests
{
private readonly ITestOutputHelper _out;
public Issue112MembershipTests(ITestOutputHelper output) => _out = output;
private const float FootRadius = 0.48f;
private static PhysicsDataCache LoadLandblockInteriors(DatCollection dats, uint lbPrefix)
{
var cache = new PhysicsDataCache();
for (uint low = 0x0100; low <= 0x01FF; low++)
{
try { ConformanceDats.LoadEnvCell(dats, cache, lbPrefix | low); }
catch { }
}
return cache;
}
[Fact]
public void A9B3CottageGap_IndoorSeed_DemotesViaOutdoorCandidates_DocumentsResidual()
{
var datDir = ConformanceDats.ResolveDatDir();
if (datDir is null) { _out.WriteLine("dats unavailable — skipped"); return; }
using var dats = new DatCollection(datDir, DatAccessType.Read);
var cache = LoadLandblockInteriors(dats, 0xA9B30000u);
// The gap point (A9B3-local frame, footcenter height): contained by NO
// interior cell (dat-scan fact from the live capture issue111-verify7).
var gap = new Vector3(184.915f, 82.464f, 116.48f);
uint picked = CellTransit.FindCellList(cache, gap, FootRadius, 0xA9B30104u);
_out.WriteLine($"pick(seed 0x104) at gap -> 0x{picked:X8}");
// DOCUMENTS THE #112 RESIDUAL (flips loudly when fixed): the gap sits
// in the doorway region, so the BFS from 0x104 reaches the exterior
// portal and outdoor cells enter the candidate array → the NORMAL
// outdoorResult path demotes (not the removed escape hatch — its
// removal fixed the deep-room stranding; re-promotion now happens at
// the doorway cells on the way back in). Open question for the fix:
// retail's CEnvCell::find_transit_cells gate for add_all_outside_cells
// (pc:317499 region) — if it requires sphere proximity to the exterior
// portal POLYGON (not just graph reachability), this demote disappears
// and the assert below should become Assert.Equal(0xA9B30104u, ...).
Assert.Equal(0xA9B3003Cu, picked);
}
[Fact]
public void ThresholdCottage_AdjacentClaim_LaterallyRecovers_ViaStabGraph()
{
var datDir = ConformanceDats.ResolveDatDir();
if (datDir is null) { _out.WriteLine("dats unavailable — skipped"); return; }
using var dats = new DatCollection(datDir, DatAccessType.Read);
var cache = LoadLandblockInteriors(dats, 0xA9B40000u);
// The #111 gate shape: claim 0x172 (adjacent room), position deep
// inside 0x171 (the captured spawn). The sphere does not overlap
// 0x172's volume from there → the new lateral recovery searches the
// claim's stab list (retail find_visible_child_cell :311444) and
// self-heals to 0x171 — instead of the old outdoor demote.
var spawnFootCenter = new Vector3(155.390f, 11.020f, 94.0f + FootRadius);
uint picked = CellTransit.FindCellList(cache, spawnFootCenter, FootRadius, 0xA9B40172u);
_out.WriteLine($"pick(seed 0x172) at 0x171-interior -> 0x{picked:X8}");
Assert.Equal(0xA9B40171u, picked);
}
}