fix(phys): #111 - ground the validated claim via PHYSICS walkable polygons, not the CellSurface triangle soup

Two grounding selection rules failed against live ACE restores before the
right one: (1) first-hit SampleFloorZ returned 0x171's 99.475 ceiling TOP face
over its 94.0 floor (issue111-verify2.log) - the player committed onto the
roof level, and the session's heartbeats poisoned ACE's save with z=99.475;
(2) nearest-to-reference self-confirmed that poison (the reference SAT on the
ceiling face, issue111-verify3.log). Root insight: ceiling/roof top faces are
upward-facing and XY-projectable - geometrically indistinguishable from
floors in the render-ish CellSurface soup. The PHYSICS walkable set (plane
normal.Z >= PhysicsGlobals.FloorZ over the claim's Resolved cell-local
polygons - retail BSPTREE::find_walkable's filter) contains only real floors:
PhysicsEngine.WalkableFloorZNearest transforms into the cell frame, drops on
each walkable plane under the XY, picks nearest the reference.

Verified live (issue111-verify4.log): ACE restored the roof-poisoned
(0xA9B40171, z=99.475); the snap validated the claim and grounded to
z=94.000 - the first fully clean indoor login of the arc:
[snap] claim=0xA9B40171 VALIDATED -> grounded to its walkable floor z=94.000
[cell-transit] 0x00000000 -> 0xA9B40171 pos=(155.525,12.416,94.000)

Baseline: Core 1381 + 4 pre-existing #99 failures + 1 skip; App/UI/Net green.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
Erik 2026-06-10 14:21:59 +02:00
parent 5f1eb7c4b1
commit 5706e0e10a
2 changed files with 69 additions and 7 deletions

View file

@ -76,6 +76,11 @@ public sealed class CellSurface
/// <summary>
/// Project (worldX, worldY) onto this cell's floor polygons and
/// return the Z. Returns null if outside all floor polygons.
/// ⚠️ Returns the FIRST triangle hit in list order — a cell's triangle
/// soup includes ceiling/roof top faces, so the result can be a surface
/// far above the actual floor (#111: 0xA9B40171 returned its 99.475
/// ceiling over its 94.0 floor). Use <see cref="SampleFloorZNearest"/>
/// when a reference height is known.
/// </summary>
public float? SampleFloorZ(float worldX, float worldY)
{
@ -87,6 +92,11 @@ public sealed class CellSurface
return null;
}
// (#111 note: a SampleFloorZNearest variant was tried and removed — even
// nearest-to-reference lands on ceiling faces when the reference itself
// sits on one. Placement snaps must ground via the PHYSICS walkable
// polygons instead: PhysicsEngine.WalkableFloorZNearest.)
/// <summary>
/// Test if (px, py) falls inside triangle (a, b, c) projected onto
/// the XY plane. If inside, computes the barycentric Z interpolation