fix(physics): preserve retail edge-slide stop semantics

This commit is contained in:
Erik 2026-07-31 13:03:46 +02:00
parent c559c48d80
commit 4fbd93ecdb
3 changed files with 427 additions and 77 deletions

View file

@ -1758,11 +1758,12 @@ public sealed class Transition
return (probeHeight * 0.5f, 2);
}
internal TransitionState EdgeSlideAfterStepDownFailedForTest(
internal bool EdgeSlideAfterStepDownFailedForTest(
PhysicsEngine engine,
float stepDownHeight,
float zVal)
=> EdgeSlideAfterStepDownFailed(engine, stepDownHeight, zVal);
float zVal,
out TransitionState result)
=> EdgeSlideAfterStepDownFailed(engine, stepDownHeight, zVal, out result);
internal TransitionState CliffSlideForTest(Plane contactPlane)
=> CliffSlide(contactPlane);
@ -2087,7 +2088,14 @@ public sealed class Transition
// merely the EdgeSlide flag.
DumpEdgeSlideStepDownFailed(stepDownHeight, zVal);
var edgeState = EdgeSlideAfterStepDownFailed(engine, stepDownHeight, zVal);
bool stop = EdgeSlideAfterStepDownFailed(
engine,
stepDownHeight,
zVal,
out TransitionState edgeState);
if (stop)
return edgeState;
if (edgeState == TransitionState.Slid)
{
transitState = edgeState;
@ -2104,7 +2112,12 @@ public sealed class Transition
continue;
}
return edgeState;
// Retail edge_slide has a bool return separate from its out
// TransitionState. In particular, a degenerate CliffSlide
// writes OK_TS but returns false, so transitional_insert must
// continue its outer retry rather than treating OK as a stop.
transitState = edgeState;
continue;
}
return TransitionState.OK;
@ -2212,10 +2225,11 @@ public sealed class Transition
? hook(this, phase, cellId, actual)
: actual;
private TransitionState EdgeSlideAfterStepDownFailed(
private bool EdgeSlideAfterStepDownFailed(
PhysicsEngine engine,
float stepDownHeight,
float zVal)
float zVal,
out TransitionState result)
{
var sp = SpherePath;
var ci = CollisionInfo;
@ -2231,7 +2245,8 @@ public sealed class Transition
sp.RestoreCheckPos();
ci.ContactPlaneValid = false;
ci.ContactPlaneIsWater = false;
return TransitionState.OK;
result = TransitionState.OK;
return true;
}
if (ci.ContactPlaneValid && ci.ContactPlane.Normal.Z < zVal)
@ -2242,7 +2257,8 @@ public sealed class Transition
sp.RestoreCheckPos();
ci.ContactPlaneValid = false;
ci.ContactPlaneIsWater = false;
return CliffSlide(cliffPlane);
result = CliffSlide(cliffPlane);
return false;
}
// Retail tests only SPHEREPATH::walkable here. When the failed
@ -2263,7 +2279,8 @@ public sealed class Transition
sp.RestoreCheckPos();
ci.ContactPlaneValid = false;
ci.ContactPlaneIsWater = false;
return sp.PrecipiceSlide(this);
result = sp.PrecipiceSlide(this);
return result == TransitionState.Collided;
}
if (ci.ContactPlaneValid)
@ -2273,7 +2290,8 @@ public sealed class Transition
sp.RestoreCheckPos();
ci.ContactPlaneValid = false;
ci.ContactPlaneIsWater = false;
return TransitionState.OK;
result = TransitionState.OK;
return true;
}
// Retail back-probes from the current sphere center to rediscover the
@ -2317,10 +2335,14 @@ public sealed class Transition
// Retail returns Collided when the back-probe found no walkable.
// In particular, it does not substitute a retained earlier polygon.
if (sp.HasWalkablePolygon)
return sp.PrecipiceSlide(this);
{
result = sp.PrecipiceSlide(this);
return result == TransitionState.Collided;
}
sp.ClearWalkable();
return TransitionState.Collided;
result = TransitionState.Collided;
return true;
}
private TransitionState CliffSlide(Plane contactPlane)