handoff: M1.5 dungeon support (G.3) grounded — design research + the terrain-less-premise refutation
Adds the 2026-06-13 dungeon-G.3 handoff doc + a dat-probe test that RESOLVED the pivotal design ambiguity. A research agent assumed dungeon landblocks are terrain-less (LandblockLoader.Load returns null -> "rewrite the pipeline for terrain-less landblocks", 13 seams). The dat probe refutes it: dungeon landblock 0x0125 has a flat (all-zero-height) LandBlock record PLUS 71 EnvCells and no buildings/objects -> it streams fine via the existing pipeline as a flat-terrain landblock. The real blocker (#133) is narrow: the teleport-arrival handler (GameWindow.cs:4928) snaps the player via physics.Resolve BEFORE the dungeon landblock streams in -> Resolve falls back to the resident Holtburg landblocks -> places the player at A9B3 ocean. Fix shape: hold-until-hydration (reuse the #107 IsSpawnCellReady gate for the teleport-arrival path) + place into the EnvCell + the retail TeleportAnimState portal-space FSM for the full-G.3 loading screen. ACE confirms dungeons are single-landblock, so "multi-landblock LOD" is moot. The handoff captures: this session's closes (#108-residual/#127/#125 gated, #116 partial), the M1.5 re-open decision, the corrected root cause, the 5-way reference grounding (holtburger/ACE/retail decomp + the dat probe), the design direction, and the open brainstorm questions. Next session: resume the brainstorm at "propose approaches" -> spec -> writing-plans -> implement. Suites green: App 264+1skip / Core 1445+2skip / UI 420 / Net 294. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
parent
9c2ceb2336
commit
90786c19e2
2 changed files with 281 additions and 0 deletions
|
|
@ -0,0 +1,76 @@
|
|||
using System.Linq;
|
||||
using DatReaderWriter;
|
||||
using DatReaderWriter.Options;
|
||||
using DatLandBlock = DatReaderWriter.DBObjs.LandBlock;
|
||||
using DatLandBlockInfo = DatReaderWriter.DBObjs.LandBlockInfo;
|
||||
using DatEnvCell = DatReaderWriter.DBObjs.EnvCell;
|
||||
using Xunit;
|
||||
using Xunit.Abstractions;
|
||||
|
||||
namespace AcDream.Core.Tests.Conformance;
|
||||
|
||||
/// <summary>
|
||||
/// G.3 dungeon-support research probe (2026-06-13): resolve the pivotal
|
||||
/// terrain-less-vs-ocean ambiguity for the meeting-hall dungeon landblock
|
||||
/// 0x0125 (the teleport this session went to cell 0x01250126). Does a dungeon
|
||||
/// landblock have a LandBlock (0xXXYYFFFF) terrain record at all, or only
|
||||
/// LandBlockInfo + EnvCells? Output-only — no assertions.
|
||||
/// </summary>
|
||||
public sealed class DungeonLandblockDatProbeTests
|
||||
{
|
||||
private readonly ITestOutputHelper _out;
|
||||
public DungeonLandblockDatProbeTests(ITestOutputHelper output) => _out = output;
|
||||
|
||||
[Fact]
|
||||
public void Probe_Dungeon0125_vs_Holtburg_A9B4()
|
||||
{
|
||||
var datDir = ConformanceDats.ResolveDatDir();
|
||||
if (datDir is null) { _out.WriteLine("SKIP: dats unavailable"); return; }
|
||||
using var dats = new DatCollection(datDir, DatAccessType.Read);
|
||||
|
||||
foreach (uint lb in new uint[] { 0x0125u, 0xA9B4u })
|
||||
{
|
||||
_out.WriteLine($"=== landblock 0x{lb:X4} ===");
|
||||
|
||||
uint terrainId = (lb << 16) | 0xFFFFu;
|
||||
var block = dats.Get<DatLandBlock>(terrainId);
|
||||
if (block is null)
|
||||
{
|
||||
_out.WriteLine($" LandBlock 0x{terrainId:X8}: NULL (no terrain record)");
|
||||
}
|
||||
else
|
||||
{
|
||||
var heights = block.Height;
|
||||
bool allZero = heights is not null && heights.All(h => h == 0);
|
||||
int distinct = heights is null ? 0 : heights.Distinct().Count();
|
||||
_out.WriteLine($" LandBlock 0x{terrainId:X8}: present, Height[{heights?.Length ?? 0}] allZero={allZero} distinctIndices={distinct} first8=[{(heights is null ? "" : string.Join(",", heights.Take(8)))}]");
|
||||
}
|
||||
|
||||
uint infoId = (lb << 16) | 0xFFFEu;
|
||||
var info = dats.Get<DatLandBlockInfo>(infoId);
|
||||
if (info is null)
|
||||
{
|
||||
_out.WriteLine($" LandBlockInfo 0x{infoId:X8}: NULL");
|
||||
}
|
||||
else
|
||||
{
|
||||
_out.WriteLine($" LandBlockInfo 0x{infoId:X8}: NumCells={info.NumCells} Buildings={info.Buildings?.Count ?? 0} Objects={info.Objects?.Count ?? 0}");
|
||||
}
|
||||
|
||||
// probe the first few EnvCells
|
||||
int found = 0;
|
||||
for (uint low = 0x0100u; low < 0x0110u; low++)
|
||||
{
|
||||
uint cellId = (lb << 16) | low;
|
||||
var cell = dats.Get<DatEnvCell>(cellId);
|
||||
if (cell is not null)
|
||||
{
|
||||
found++;
|
||||
if (found <= 3)
|
||||
_out.WriteLine($" EnvCell 0x{cellId:X8}: present, CellStructure={cell.CellStructure} Portals={cell.CellPortals?.Count ?? 0} pos=({cell.Position.Origin.X:F1},{cell.Position.Origin.Y:F1},{cell.Position.Origin.Z:F1})");
|
||||
}
|
||||
}
|
||||
_out.WriteLine($" EnvCells 0x0100..0x010F present: {found}");
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue