diff --git a/src/AcDream.Core/Physics/TransitionTypes.cs b/src/AcDream.Core/Physics/TransitionTypes.cs index cc6944a..6494789 100644 --- a/src/AcDream.Core/Physics/TransitionTypes.cs +++ b/src/AcDream.Core/Physics/TransitionTypes.cs @@ -623,7 +623,15 @@ public sealed class Transition if (!ci.ContactPlaneValid && oi.Contact && !sp.StepDown && sp.CheckCellId != 0 && oi.StepDown) { - float zVal = PhysicsGlobals.LandingZ; + // L.2.3i (2026-04-29): retail uses FloorZ when OnWalkable, + // LandingZ when not. acdream was unconditionally LandingZ — + // which let the step-down probe accept steep polygons + // (~85° permissive instead of ~49° strict) as the player's + // new contact, contributing to the "walks up steep roofs" + // bug. Per CTransition::transitional_insert step-down OK + // branch (acclient_2013_pseudo_c.txt:273258-273265) and + // ACE Transition.cs:849-856. + float zVal = oi.GetWalkableZ(); float stepDownHeight = oi.StepDownHeight; sp.WalkableAllowance = zVal; sp.SaveCheckPos(); @@ -1638,7 +1646,14 @@ public sealed class Transition ci.LastKnownContactPlaneIsWater = ci.ContactPlaneIsWater; oi.State |= ObjectInfoState.Contact; - if (ci.ContactPlane.Normal.Z >= PhysicsGlobals.LandingZ) + // L.2.3i (2026-04-29): use FloorZ (~49°) NOT LandingZ (~85°) + // for the OnWalkable test. The previous LandingZ check was + // far too permissive — a 60° roof (normal.Z=0.5) was being + // marked OnWalkable, letting the player walk up steep slopes + // they shouldn't reach. Retail's PhysicsObj::is_valid_walkable + // uses FloorZ unconditionally (acclient_2013_pseudo_c.txt:277180-277193, + // ACE PhysicsObj.cs:2861). + if (ci.ContactPlane.Normal.Z >= PhysicsGlobals.FloorZ) oi.State |= ObjectInfoState.OnWalkable; else oi.State &= ~ObjectInfoState.OnWalkable; @@ -1651,7 +1666,8 @@ public sealed class Transition // last-known plane. Without this, every wall bump dropped the // player into the falling animation for one frame. oi.State |= ObjectInfoState.Contact; - if (ci.LastKnownContactPlane.Normal.Z >= PhysicsGlobals.LandingZ) + // L.2.3i: same FloorZ correction as the live-contact branch. + if (ci.LastKnownContactPlane.Normal.Z >= PhysicsGlobals.FloorZ) oi.State |= ObjectInfoState.OnWalkable; else oi.State &= ~ObjectInfoState.OnWalkable;