fix #137 (window climb): the player's collision capsule topped out at 1.2m — head sphere 0.63m too low

The 'run into the last corridor window and pop up through its roof'
report: the live callers passed sphereHeight: 1.2f into
SpherePath.InitPath, whose head-sphere formula (height - radius) put the
head sphere center at 0.72 - the capsule top at 1.2m. The top 0.63m of a
1.83m character had NO collision, so at the corridor-end window alcove
(0x8A020179 -> 0x8A02017E: 0.70m sill face, 1.3m opening, sloped funnel
behind) the step-up's placement never saw the head overlapping the
lintel solids and let the player climb in head-through-roof.

Dat truth (HumanSetup_CollisionSpheres_DatTruth): Setup 0x02000001
spheres = (0,0,0.475) r=0.48 and (0,0,1.350) r=0.48 - capsule top 1.83 =
Setup.Height 1.835. Retail collides with that sphere list verbatim
(CPhysicsObj::transition 0x00512dc0 -> init_sphere(GetNumSphere,
GetSphere, m_scale)).

Fix: PlayerMovementController + the GameWindow remote resolve now pass
sphereHeight: 1.835f (capsule top; head center 1.355 vs dat 1.350).
InitPath unchanged - captured-input replay fixtures (recorded 1.2
inputs) stay byte-identical. Register TS-46: the (radius, capsule-top)
scalar approximation of the Setup sphere list (5mm foot/head offsets;
remotes use human dims) with the retire path (plumb the sphere list).

Pins: WindowOpening_HeadCannotFit_EntryBlocked (22-frame walked approach
wall-slides at the sill, never enters 0x8A02017E) +
WindowAlcove_RaisedPlacement_HeadInLintelSolid_Collides (Path-1
placement rejects the raised head in the lintel solids) + the
WindowShaft_FullPolyDump / HumanSetup dat inspections.

Suites: Core 2562 / App 713 / UI 425 / Net 385, 0 failures.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
Erik 2026-07-05 19:57:11 +02:00
parent 1494f10cb5
commit aa96d7ad77
6 changed files with 285 additions and 13 deletions

View file

@ -882,8 +882,17 @@ public sealed class PlayerMovementController
// Falls back to simple Z-snap if transition fails.
var resolveResult = _physics.ResolveWithTransition(
preIntegratePos, postIntegratePos, CellId,
sphereRadius: 0.48f, // human player radius from Setup
sphereHeight: 1.2f, // human player height from Setup
sphereRadius: 0.48f, // human Setup 0x02000001 sphere radius (dat: 0.480)
// #137 window climb (2026-07-06): sphereHeight is the CAPSULE TOP
// (InitPath places the head sphere center at height radius). The
// dat human Setup 0x02000001 has Spheres[1].Origin.Z = 1.350
// (top 1.830) and Height = 1.835 — retail collides with that
// sphere list verbatim (CPhysicsObj::transition 0x00512dc0 →
// init_sphere(GetNumSphere, GetSphere, scale)). The old 1.2f put
// the head sphere center at 0.72 — the top 0.63 m of the
// character had NO collision, letting the player climb into a
// 1.3 m window alcove head-through-lintel. Register TS-46.
sphereHeight: 1.835f,
stepUpHeight: StepUpHeight,
stepDownHeight: StepDownHeight, // L.2.3a: from Setup.StepDownHeight
isOnGround: _body.OnWalkable,