using System.Numerics; using AcDream.Core.Physics; using DatReaderWriter; using DatReaderWriter.Options; using Xunit; using Xunit.Abstractions; namespace AcDream.Core.Tests.Conformance; /// /// #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. /// 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); } }