feat(phys L.2a slice 1): resolver + cell-transit probes (PhysicsDiagnostics)

New static `AcDream.Core.Physics.PhysicsDiagnostics` holds two
runtime-toggleable flags initialized from env vars:

- ACDREAM_PROBE_RESOLVE=1 — emit one [resolve] line per
  PhysicsEngine.ResolveWithTransition call: input/target/output
  position+cell, ok-vs-partial, grounded-in, contact-plane status,
  wall normal if hit, walkable-polygon valid, moving entity id.
- ACDREAM_PROBE_CELL=1 — emit one [cell-transit] line per
  PlayerMovementController.CellId change: old → new cell, current
  world position, reason tag (resolver / teleport).

Both also exposed as runtime-toggleable checkboxes in the DebugPanel
"Diagnostics" section. Unlike the existing four Dump-* checkboxes
(which only mirror sticky-at-startup env vars), the two new ones
forward directly to PhysicsDiagnostics — toggling on/off takes
effect on the next physics resolve, no relaunch.

Why now: L.2's plan-of-record (docs/plans/2026-04-29-movement-collision-
conformance.md) explicitly says "Land L.2a diagnostics first. Do not
make another physics change blind." This slice closes the most-load-
bearing gap in L.2a — a general-purpose probe on the resolver outcome
and a cell-transit log — so that later L.2b/c/d/e physics changes can
be evidence-driven instead of guessed. Foundation for the indoor /
dungeon walking trajectory (G.3 unblock).

Pure additive: when both flags are off (default), the probes collapse
to a single static-bool read per resolve, zero log cost. PlayerMovement
Controller's two CellId-mutation sites are now routed through a
private UpdateCellId(reason) helper for diag chokepoint.

Build green, 1032/1040 unit tests pass. The 8 failing tests are
pre-existing on the branch base (verified by stash-and-rerun);
none touch resolver or cell-transit code; all fail identically with
this slice stashed. Investigation deferred to a follow-up.

Refs: docs/plans/2026-04-29-movement-collision-conformance.md (L.2a
shipped-slice note added in same commit).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
Erik 2026-05-12 17:19:05 +02:00
parent eab347d7e4
commit ebef82034e
6 changed files with 151 additions and 10 deletions

View file

@ -199,15 +199,21 @@ public sealed class DebugPanel : IPanel
{
if (!r.CollapsingHeader("Diagnostics", defaultOpen: true)) return;
bool dumpMotion = _vm.DumpMotion;
bool dumpVitals = _vm.DumpVitals;
bool dumpOpcodes = _vm.DumpOpcodes;
bool dumpSky = _vm.DumpSky;
bool dumpMotion = _vm.DumpMotion;
bool dumpVitals = _vm.DumpVitals;
bool dumpOpcodes = _vm.DumpOpcodes;
bool dumpSky = _vm.DumpSky;
bool probeResolve = _vm.ProbeResolve;
bool probeCell = _vm.ProbeCell;
if (r.Checkbox("Dump motion (ACDREAM_DUMP_MOTION)", ref dumpMotion)) _vm.DumpMotion = dumpMotion;
if (r.Checkbox("Dump vitals (ACDREAM_DUMP_VITALS)", ref dumpVitals)) _vm.DumpVitals = dumpVitals;
if (r.Checkbox("Dump opcodes (ACDREAM_DUMP_OPCODES)", ref dumpOpcodes)) _vm.DumpOpcodes = dumpOpcodes;
if (r.Checkbox("Dump sky (ACDREAM_DUMP_SKY)", ref dumpSky)) _vm.DumpSky = dumpSky;
if (r.Checkbox("Dump motion (ACDREAM_DUMP_MOTION)", ref dumpMotion)) _vm.DumpMotion = dumpMotion;
if (r.Checkbox("Dump vitals (ACDREAM_DUMP_VITALS)", ref dumpVitals)) _vm.DumpVitals = dumpVitals;
if (r.Checkbox("Dump opcodes (ACDREAM_DUMP_OPCODES)", ref dumpOpcodes)) _vm.DumpOpcodes = dumpOpcodes;
if (r.Checkbox("Dump sky (ACDREAM_DUMP_SKY)", ref dumpSky)) _vm.DumpSky = dumpSky;
// L.2a slice 1 (2026-05-12): unlike the four above, these
// forward to PhysicsDiagnostics so a toggle takes effect live.
if (r.Checkbox("Probe resolve (ACDREAM_PROBE_RESOLVE)", ref probeResolve)) _vm.ProbeResolve = probeResolve;
if (r.Checkbox("Probe cell-transit (ACDREAM_PROBE_CELL)",ref probeCell)) _vm.ProbeCell = probeCell;
r.Spacing();

View file

@ -1,5 +1,6 @@
using System.Numerics;
using AcDream.Core.Combat;
using AcDream.Core.Physics;
namespace AcDream.UI.Abstractions.Panels.Debug;
@ -234,6 +235,32 @@ public sealed class DebugVM
/// <summary>Mirror of <c>ACDREAM_DUMP_SKY</c>.</summary>
public bool DumpSky { get; set; }
// L.2a slice 1 (2026-05-12): unlike DumpMotion/Vitals/Opcodes/Sky
// above (which are display-only mirrors of sticky-at-startup env
// vars), these forward directly to the PhysicsDiagnostics statics,
// so checkbox toggles take effect on the next physics resolve.
/// <summary>
/// Runtime mirror of <c>PhysicsDiagnostics.ProbeResolveEnabled</c>
/// (env var <c>ACDREAM_PROBE_RESOLVE</c>). Toggling here flips the
/// resolver probe live — no relaunch required.
/// </summary>
public bool ProbeResolve
{
get => PhysicsDiagnostics.ProbeResolveEnabled;
set => PhysicsDiagnostics.ProbeResolveEnabled = value;
}
/// <summary>
/// Runtime mirror of <c>PhysicsDiagnostics.ProbeCellEnabled</c>
/// (env var <c>ACDREAM_PROBE_CELL</c>). Toggling here flips the
/// cell-transit probe live.
/// </summary>
public bool ProbeCell
{
get => PhysicsDiagnostics.ProbeCellEnabled;
set => PhysicsDiagnostics.ProbeCellEnabled = value;
}
// ── Action hooks invoked by panel buttons ──────────────────────────
/// <summary>