fix(physics): TS-46 - seed the sweep from the Setup's own sphere list
Campaign P Slice P3 item 1. Retail CPhysicsObj::transition (0x00512dc0) seeds the collision sweep from CPartArray::GetSphere (the Setup's own <=2-sphere list, each origin+radius scaled by m_scale) via SPHEREPATH::init_sphere (0x0050c670) -- not from a symmetric two-scalar (radius, height) capsule reconstruction. The human Setup 0x02000001's authored spheres are (0,0,0.475) r=.48 and (0,0,1.350) r=.48; the old reconstruction from (0.48, 1.835) produced (0,0,0.48) + (0,0,1.355), a 5 mm head-center offset the TS-46 register row documented as a residual. Port: - SpherePath.InitPath gains a sphere-list overload (ImmutableArray< FlatCollisionSphere>, scale) sharing a new InitPathCore with the existing (radius, height) overload, which is now the degenerate 2-scalar case of the same code -- byte-for-byte unchanged, so every captured-fixture replay (CellarUpTrajectoryReplayTests, DoorBugTrajectoryReplayTests, CellarLipWedgeTests) keeps passing unmodified. - PhysicsEngine.ResolveWithTransition gains optional sphereList/ sphereScale parameters; empty/default preserves the legacy scalar path for every pre-existing caller. - LiveEntityMotionRuntimeController.GetSetupMoverShape is a new sibling of GetSetupCylinder (left untouched) that resolves the Setup's own sphere list plus Setup-derived step-up/step-down (CPartArray::GetStepUpHeight/GetStepDownHeight, 0x005180d0/0x005180f0, x ObjScale, 0.4 m fallback matching the pre-existing literal). - Threaded through PlayerMovementController (both resolve call sites, new SphereList property set by PlayerModeController.ApplyStepHeights and the Headless world projection), RuntimeRemotePhysicsUpdater (Tick + TickHidden), and RuntimeOrdinaryPhysicsUpdater.TryBegin. Remote/ordinary step heights are now Setup-derived instead of a hardcoded 0.4f literal. Projectile and camera-probe sweeps are untouched (already single-sphere-exact). - PlayerModeController.ApplyStepHeights also now applies the x ObjScale multiply to the player's own step heights (previously only the remote/ordinary paths did), closing an adjacent gap the P3 research flagged. Ts46SphereListConformanceTests proves the sphere-list overload sees the exact dat spheres (not the reconstruction), that the scalar overload is unchanged, and that ResolveWithTransition's sphereList parameter actually drives the sweep (a decoy-scalar control pair using a head-height obstacle sphere). Register: TS-46 retired (both residuals it named are closed); header count corrected to 40 active TS rows. dotnet build + dotnet test (Core.Tests 3991/2 skip, Runtime.Tests 425/0, App.Tests 3968/3 skip, complete solution build) all green. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
parent
3dc10accb0
commit
dae5b1ea68
21 changed files with 648 additions and 50 deletions
|
|
@ -163,6 +163,21 @@ public sealed class PlayerMovementController
|
|||
/// </summary>
|
||||
public float StepDownHeight { get; set; } = 0.4f;
|
||||
|
||||
/// <summary>
|
||||
/// TS-46 (2026-07-30): the player's own Setup ≤2-sphere list (dat
|
||||
/// <c>CSphere</c> Origin+Radius), verbatim per retail
|
||||
/// <c>CPhysicsObj::transition</c> (0x00512dc0) →
|
||||
/// <c>SPHEREPATH::init_sphere</c> (0x0050c670). Set at world-entry by
|
||||
/// <c>PlayerModeController.ApplyStepHeights</c> alongside
|
||||
/// <see cref="StepUpHeight"/>/<see cref="StepDownHeight"/>. Default
|
||||
/// (empty) falls back to <c>ResolveWithTransition</c>'s legacy
|
||||
/// (0.48, 1.835) two-scalar capsule reconstruction — the human Setup
|
||||
/// 0x02000001's authored spheres are (0,0,0.475) r=.48 and
|
||||
/// (0,0,1.350) r=.48, a 5 mm improvement over the reconstruction's
|
||||
/// (0,0,0.48) + (0,0,1.355).
|
||||
/// </summary>
|
||||
public System.Collections.Immutable.ImmutableArray<FlatCollisionSphere> SphereList { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Retail <c>CPhysicsObj::m_scale</c>. Grounded CSequence root
|
||||
/// displacement is multiplied by this value before PositionManager
|
||||
|
|
@ -1404,7 +1419,12 @@ public sealed class PlayerMovementController
|
|||
isOnGround: previousOnWalkable,
|
||||
body: _body,
|
||||
moverFlags: ObjectInfoState.IsPlayer | ObjectInfoState.EdgeSlide,
|
||||
movingEntityId: LocalEntityId);
|
||||
movingEntityId: LocalEntityId,
|
||||
// TS-46: the player's own Setup sphere list, scaled by
|
||||
// ObjectScale. Empty falls back to the 0.48/1.835
|
||||
// reconstruction above.
|
||||
sphereList: SphereList,
|
||||
sphereScale: ObjectScale);
|
||||
_body.CommitTransitionPosition(resolved.CellId, resolved.Position);
|
||||
PhysicsObjUpdate.CommitSetPositionTransition(
|
||||
_body,
|
||||
|
|
@ -1849,7 +1869,14 @@ public sealed class PlayerMovementController
|
|||
// when the local player entity spawns (or stays 0 in tests, in
|
||||
// which case there's no registered ShadowEntry to collide with
|
||||
// anyway).
|
||||
movingEntityId: LocalEntityId);
|
||||
movingEntityId: LocalEntityId,
|
||||
// TS-46 (2026-07-30): the player's own Setup sphere list
|
||||
// (0x02000001: (0,0,0.475) r=.48 + (0,0,1.350) r=.48),
|
||||
// scaled by ObjectScale. Empty (unset/no Setup resolved yet)
|
||||
// falls back to the sphereRadius/sphereHeight reconstruction
|
||||
// above.
|
||||
sphereList: SphereList,
|
||||
sphereScale: ObjectScale);
|
||||
|
||||
// L.4-diag (2026-04-30): trace position transitions so we can see
|
||||
// whether the body is actually moving frame-to-frame on the steep
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue