fix(phys): A6.P3 slice 3 — cell-resolver stickiness for ping-pong fix
Closes A6.P2 Finding 3 (cell-resolver instability) + issue #98 (cellar ascent stuck at last step) + likely closes #97 (phantom collisions + fall-through on 2nd floor; same instability family). Adds a cell-stickiness check at the top of ResolveCellId's indoor branch: before re-resolving via FindCellList, check if the fallback (previous-tick) CellId's BSP still validly contains the sphere. If yes, return fallbackCellId immediately — preserves cell membership when the sphere is at a boundary where multiple cells overlap. The bug: at cell boundaries (cellar last step, indoor doorways, between two adjacent indoor cells), the sphere overlaps multiple cells geometrically. FindCellList's candidate-iteration order (HashSet, implementation-defined) determines which cell wins. That order may shift tick-to-tick → CellId ping-pong → AdjustOffset operates against a different cell's geometry each tick → player can't accumulate forward motion → stuck. Evidence: scen3_inn_2nd_floor_slice2v2 capture shows the ping-pong chain at the cellar boundary: 0xA9B4014B → 0xA9B4014A → 0xA9B4013F → 0xA9B4014A → 0xA9B4014B (Z stable ~96.4; CellId oscillates every tick; reason=resolver) Retail oracle: cell-array hysteresis pattern from CObjCell::find_cell_list Position-variant at acclient_2013_pseudo_c.txt:308742-308783. Retail preserves cell membership when sphere is close to (but slightly past) cell boundaries. Implementation: 9 lines added (sphere-overlap check against fallbackCellId's CellBSP before falling through to FindCellList). Existing #90 workaround at line 299-300 (post-FindCellList sphere- overlap check) is now redundant in the common case but kept for safety; deferred to A6.P4 removal after visual verification. Test suite: 1148 pass + 8 pre-existing fail (baseline maintained). Visual verification: pending — user happy-test will confirm cellar- up walk succeeds + no ping-pong in cell-transit log. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
d868946537
commit
88981669fe
1 changed files with 39 additions and 0 deletions
|
|
@ -261,6 +261,45 @@ public sealed class PhysicsEngine
|
|||
// Indoor branch needs DataCache to look up cells; outdoor uses
|
||||
// _landblocks (no DataCache dependency).
|
||||
if (DataCache is null) return fallbackCellId;
|
||||
|
||||
// ── Cell-stickiness check (A6.P3 slice 3, 2026-05-22) ──
|
||||
// Before re-resolving via FindCellList, check if the fallback
|
||||
// CellId still validly contains the sphere. If yes, prefer it
|
||||
// over any FindCellList result that might pick a different
|
||||
// overlapping cell — fixes issue #98 (cellar-up stuck at last
|
||||
// step due to CellId ping-ponging between adjacent cells whose
|
||||
// BSPs all overlap the sphere).
|
||||
//
|
||||
// Mechanism: when the sphere is on a cell boundary where it
|
||||
// overlaps multiple cells, FindCellList's candidate-iteration
|
||||
// order (HashSet, implementation-defined) determines which
|
||||
// cell wins. That order may shift tick-to-tick → ping-pong.
|
||||
// Stickiness ensures we keep the CURRENT cell as long as the
|
||||
// sphere still validly overlaps it, only switching when the
|
||||
// sphere has truly left.
|
||||
//
|
||||
// Retail oracle: cell-array hysteresis pattern from
|
||||
// CObjCell::find_cell_list Position-variant at
|
||||
// acclient_2013_pseudo_c.txt:308742-308783. Retail's cell
|
||||
// resolution preserves cell membership when the sphere is
|
||||
// close to (but slightly past) cell boundaries.
|
||||
//
|
||||
// Likely closes/obsoletes:
|
||||
// - #98 (cellar ascent stuck at last step) — direct target
|
||||
// - #97 (phantom collisions + fall-through on 2nd floor) —
|
||||
// same instability family hypothesized
|
||||
// - #90 (sphere-overlap stickiness workaround in this same
|
||||
// function below) — superseded; can be removed after
|
||||
// visual verification (deferred to A6.P4)
|
||||
var fallbackCell = DataCache.GetCellStruct(fallbackCellId);
|
||||
if (fallbackCell?.CellBSP?.Root is not null)
|
||||
{
|
||||
var fallbackLocal = Vector3.Transform(worldPos, fallbackCell.InverseWorldTransform);
|
||||
if (BSPQuery.SphereIntersectsCellBsp(fallbackCell.CellBSP.Root, fallbackLocal, sphereRadius))
|
||||
return fallbackCellId; // sphere still overlaps; stick.
|
||||
}
|
||||
|
||||
// Fallback cell no longer valid → re-resolve via portal-graph BFS.
|
||||
uint indoorResult = CellTransit.FindCellList(DataCache, worldPos, sphereRadius, fallbackCellId);
|
||||
|
||||
// ISSUES #83 / Phase A1.7 (2026-05-21): verify the indoor result
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue