fix(phys): A6.P3 slice 3 v2 — point-in stickiness (was sphere-overlap)
Slice 3 v1 (8898166) used SphereIntersectsCellBsp for the stickiness
check. User verification showed: ping-pong WAS closed (3 cell-transit
events vs 20+ pre-fix) but user still couldn't walk up out of cellar
because the stickiness was OVER-CORRECTED — the sphere still partially
overlapped the cellar cell at the top of stairs, so stickiness held the
player in the cellar even when the center had transitioned to the
cottage main floor cell.
Fix: switch the stickiness check from SphereIntersectsCellBsp (sphere
overlap) to PointInsideCellBsp (center-in). Matches FindCellList's
own semantics for "which cell are you in." Player stays in fallback
only while center is still inside fallback's BSP volume.
Trade-off:
- More permissive transitions (good — cellar-up works)
- Less aggressive stickiness, so some boundary ping-ponging may return
IF the sphere center oscillates across the boundary (rare; would
require sub-mm Z drift across the boundary line)
If the trade-off bites (ping-pong returns somewhere), the fix is a
small geometric margin around the point-in check — but verify before
adding.
Test suite: 1148 pass + 8 pre-existing fail (baseline maintained).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
88981669fe
commit
3e140cfe71
1 changed files with 24 additions and 20 deletions
|
|
@ -264,39 +264,43 @@ public sealed class PhysicsEngine
|
||||||
|
|
||||||
// ── Cell-stickiness check (A6.P3 slice 3, 2026-05-22) ──
|
// ── Cell-stickiness check (A6.P3 slice 3, 2026-05-22) ──
|
||||||
// Before re-resolving via FindCellList, check if the fallback
|
// Before re-resolving via FindCellList, check if the fallback
|
||||||
// CellId still validly contains the sphere. If yes, prefer it
|
// CellId still validly contains the sphere CENTER (point-in).
|
||||||
// over any FindCellList result that might pick a different
|
// If yes, prefer it over any FindCellList result that might
|
||||||
// overlapping cell — fixes issue #98 (cellar-up stuck at last
|
// pick a different cell whose BSP also contains the point —
|
||||||
// step due to CellId ping-ponging between adjacent cells whose
|
// fixes issue #98 (cellar-up stuck at last step due to CellId
|
||||||
// BSPs all overlap the sphere).
|
// ping-ponging between adjacent cells in iteration-order races).
|
||||||
//
|
//
|
||||||
// Mechanism: when the sphere is on a cell boundary where it
|
// Mechanism: when the sphere is on a cell boundary where the
|
||||||
// overlaps multiple cells, FindCellList's candidate-iteration
|
// CENTER is in multiple cells geometrically (overlapping BSPs),
|
||||||
// order (HashSet, implementation-defined) determines which
|
// FindCellList's candidate-iteration order (HashSet,
|
||||||
// cell wins. That order may shift tick-to-tick → ping-pong.
|
// implementation-defined) determines which cell wins. That
|
||||||
// Stickiness ensures we keep the CURRENT cell as long as the
|
// order may shift tick-to-tick → ping-pong. Stickiness keeps
|
||||||
// sphere still validly overlaps it, only switching when the
|
// the CURRENT cell as long as the sphere center is still
|
||||||
// sphere has truly left.
|
// inside it, only switching when the center has moved out.
|
||||||
|
//
|
||||||
|
// Uses POINT-IN (not sphere-overlap). Sphere-overlap stickiness
|
||||||
|
// (the first slice 3 attempt) over-corrected — held the player
|
||||||
|
// in the fallback cell even when the center had transitioned to
|
||||||
|
// an adjacent cell, blocking legitimate cell transitions at
|
||||||
|
// stair tops + portal exits. Point-in matches FindCellList's
|
||||||
|
// own semantics for "which cell are you in."
|
||||||
//
|
//
|
||||||
// Retail oracle: cell-array hysteresis pattern from
|
// Retail oracle: cell-array hysteresis pattern from
|
||||||
// CObjCell::find_cell_list Position-variant at
|
// CObjCell::find_cell_list Position-variant at
|
||||||
// acclient_2013_pseudo_c.txt:308742-308783. Retail's cell
|
// acclient_2013_pseudo_c.txt:308742-308783.
|
||||||
// resolution preserves cell membership when the sphere is
|
|
||||||
// close to (but slightly past) cell boundaries.
|
|
||||||
//
|
//
|
||||||
// Likely closes/obsoletes:
|
// Likely closes/obsoletes:
|
||||||
// - #98 (cellar ascent stuck at last step) — direct target
|
// - #98 (cellar ascent stuck at last step) — direct target
|
||||||
// - #97 (phantom collisions + fall-through on 2nd floor) —
|
// - #97 (phantom collisions + fall-through on 2nd floor) —
|
||||||
// same instability family hypothesized
|
// same instability family hypothesized
|
||||||
// - #90 (sphere-overlap stickiness workaround in this same
|
// - #90 (sphere-overlap workaround below) — superseded;
|
||||||
// function below) — superseded; can be removed after
|
// can be removed after visual verification (A6.P4)
|
||||||
// visual verification (deferred to A6.P4)
|
|
||||||
var fallbackCell = DataCache.GetCellStruct(fallbackCellId);
|
var fallbackCell = DataCache.GetCellStruct(fallbackCellId);
|
||||||
if (fallbackCell?.CellBSP?.Root is not null)
|
if (fallbackCell?.CellBSP?.Root is not null)
|
||||||
{
|
{
|
||||||
var fallbackLocal = Vector3.Transform(worldPos, fallbackCell.InverseWorldTransform);
|
var fallbackLocal = Vector3.Transform(worldPos, fallbackCell.InverseWorldTransform);
|
||||||
if (BSPQuery.SphereIntersectsCellBsp(fallbackCell.CellBSP.Root, fallbackLocal, sphereRadius))
|
if (BSPQuery.PointInsideCellBsp(fallbackCell.CellBSP.Root, fallbackLocal))
|
||||||
return fallbackCellId; // sphere still overlaps; stick.
|
return fallbackCellId; // center still inside; stick.
|
||||||
}
|
}
|
||||||
|
|
||||||
// Fallback cell no longer valid → re-resolve via portal-graph BFS.
|
// Fallback cell no longer valid → re-resolve via portal-graph BFS.
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue