diff --git a/src/AcDream.Core/Physics/PhysicsEngine.cs b/src/AcDream.Core/Physics/PhysicsEngine.cs index 49c6e89..f966d10 100644 --- a/src/AcDream.Core/Physics/PhysicsEngine.cs +++ b/src/AcDream.Core/Physics/PhysicsEngine.cs @@ -329,18 +329,26 @@ public sealed class PhysicsEngine bool ok = transition.FindTransitionalPosition(this); + var sp = transition.SpherePath; + var ci = transition.CollisionInfo; + if (ok) { - var sp = transition.SpherePath; - var ci = transition.CollisionInfo; - bool onGround = ci.ContactPlaneValid || transition.ObjectInfo.State.HasFlag(ObjectInfoState.OnWalkable); return new ResolveResult(sp.CheckPos, sp.CheckCellId, onGround); } - // Transition failed — fall back to simple resolve. - return Resolve(currentPos, cellId, targetPos - currentPos, stepUpHeight); + // Transition failed (e.g., stuck in corner, too many steps). + // Use whatever position the transition reached (partial movement) + // instead of falling back to the no-collision Resolve. + // If CheckPos hasn't moved from CurPos, the player stays put — + // this is correct behavior when completely blocked. + bool partialOnGround = ci.ContactPlaneValid + || transition.ObjectInfo.State.HasFlag(ObjectInfoState.OnWalkable) + || isOnGround; + + return new ResolveResult(sp.CheckPos, sp.CheckCellId != 0 ? sp.CheckCellId : cellId, partialOnGround); } }