feat(physics): re-enable indoor transitions with containment validation
Sprint 2 of the audit remediation plan. Re-enables the outdoor→indoor portal transition in PhysicsEngine with an added containment check: after detecting a portal plane crossing, verify the target cell's floor polygon actually covers the candidate position AND the floor Z is within step height of the player's Z. This prevents the wall-bounce bug (where portal planes on upper floors captured outdoor positions) while allowing genuine doorway transitions. Without full CellBSP, the SampleFloorZ + Z-proximity check is the best available approximation per the indoor transition research (docs/research/acclient_indoor_transitions_pseudocode.md). Source: ACE EnvCell.find_transit_cells validates via sphere_intersects_cell in the target cell's local space. Our SampleFloorZ + Z check is the equivalent without BSP. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
4782532c4b
commit
a722c29759
1 changed files with 25 additions and 3 deletions
|
|
@ -187,7 +187,13 @@ public sealed class PhysicsEngine
|
||||||
|
|
||||||
if (crossedPortal is not null)
|
if (crossedPortal is not null)
|
||||||
{
|
{
|
||||||
// Outdoor → Indoor: enter the OwnerCellId.
|
// Outdoor → Indoor: enter the OwnerCellId IF the target cell
|
||||||
|
// actually contains the candidate position. Without CellBSP,
|
||||||
|
// we verify by checking that SampleFloorZ returns non-null
|
||||||
|
// (position is within the cell's floor polygon bounds) AND the
|
||||||
|
// floor Z is close to the player's current Z (not a basement
|
||||||
|
// 30m below). This prevents the wall-bounce bug where portal
|
||||||
|
// planes on upper floors captured outdoor positions.
|
||||||
uint enterCellIndex = crossedPortal.Value.OwnerCellId & 0xFFFFu;
|
uint enterCellIndex = crossedPortal.Value.OwnerCellId & 0xFFFFu;
|
||||||
CellSurface? enterCell = null;
|
CellSurface? enterCell = null;
|
||||||
foreach (var c in physics.Cells)
|
foreach (var c in physics.Cells)
|
||||||
|
|
@ -195,8 +201,24 @@ public sealed class PhysicsEngine
|
||||||
if ((c.CellId & 0xFFFFu) == enterCellIndex) { enterCell = c; break; }
|
if ((c.CellId & 0xFFFFu) == enterCellIndex) { enterCell = c; break; }
|
||||||
}
|
}
|
||||||
float? enterFloorZ = enterCell?.SampleFloorZ(candidatePos.X, candidatePos.Y);
|
float? enterFloorZ = enterCell?.SampleFloorZ(candidatePos.X, candidatePos.Y);
|
||||||
targetZ = enterFloorZ ?? terrainZ;
|
|
||||||
targetCellId = enterCellIndex;
|
// Validate: floor must exist AND be within step height of current Z.
|
||||||
|
// This rejects transitions to basements, upper floors, and cells
|
||||||
|
// whose floor polygon doesn't actually cover this position.
|
||||||
|
bool validTransition = enterFloorZ is not null
|
||||||
|
&& MathF.Abs(enterFloorZ.Value - currentPos.Z) < stepUpHeight + 2f;
|
||||||
|
|
||||||
|
if (validTransition)
|
||||||
|
{
|
||||||
|
targetZ = enterFloorZ!.Value;
|
||||||
|
targetCellId = enterCellIndex;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Portal crossed but target cell doesn't contain us — stay outdoor.
|
||||||
|
targetZ = terrainZ;
|
||||||
|
targetCellId = physics.Terrain.ComputeOutdoorCellId(localCandX, localCandY);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue