From 5aba071aec795d3ce9d5324fd5aaedfeb0dc3ea9 Mon Sep 17 00:00:00 2001 From: Erik Date: Fri, 22 May 2026 08:58:03 +0200 Subject: [PATCH] =?UTF-8?q?feat(phys):=20A6.P3=20slice=201=20step=201=20?= =?UTF-8?q?=E2=80=94=20add=20Mechanism=20B=20(LKCP=20restore)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Restores CollisionInfo.ContactPlane from LastKnownContactPlane when: - LKCP is valid - the sphere's current center is geometrically close to the LKCP plane (|dot(global_curr_center, N) + d| <= radius + EPSILON) Matches retail's validate_transition LKCP-restore at acclient_2013_pseudo_c.txt:272577 (CTransition::validate_transition, address 0050aa70, lines 272565-272582). Slice 1 step 1 of the A6.P3 indoor CP retention fix. Step 2 (Task 5) strips the TryFindIndoorWalkablePlane synthesis from FindEnvCollisions. Also fixes the proximity-check sphere: was using sp.GlobalSphere[0].Origin (start sphere); now uses sp.GlobalCurrCenter[0].Origin (current center) per retail (acclient_2013_pseudo_c.txt:272568). Tests: 1147 pass, 9 fail (8 pre-existing + 1 IndoorContactPlaneRetention from T3 — expected; T5 lands the actual synthesis-strip fix). Co-Authored-By: Claude Opus 4.7 (1M context) --- src/AcDream.Core/Physics/TransitionTypes.cs | 27 ++++++++++++++++++++- 1 file changed, 26 insertions(+), 1 deletion(-) diff --git a/src/AcDream.Core/Physics/TransitionTypes.cs b/src/AcDream.Core/Physics/TransitionTypes.cs index 3b8257c..2bc4041 100644 --- a/src/AcDream.Core/Physics/TransitionTypes.cs +++ b/src/AcDream.Core/Physics/TransitionTypes.cs @@ -2849,13 +2849,38 @@ public sealed class Transition // // Matches ACE PhysicsObj's pre-reuse check on the last-known // plane and retail's CPhysicsObj::get_object_info logic. - var sphereCenter = sp.GlobalSphere[0].Origin; + // A6.P3 slice 1 (2026-05-21). Retail uses global_curr_center (NOT + // global_sphere->center) for this proximity check — see + // acclient_2013_pseudo_c.txt:272568. global_sphere is the START + // sphere of the transition; global_curr_center is the CURRENT center + // after sub-step accumulation. Using the wrong one made the proximity + // guard fire on the wrong reference point. + var sphereCenter = sp.GlobalCurrCenter[0].Origin; var radius = sp.GlobalSphere[0].Radius; float angle = Vector3.Dot(ci.LastKnownContactPlane.Normal, sphereCenter) + ci.LastKnownContactPlane.D; if (radius + PhysicsGlobals.EPSILON > MathF.Abs(angle)) { + // ── Mechanism B — restore CP from LKCP per retail ──────────────── + // A6.P3 slice 1 (2026-05-21). Retail oracle: + // acclient_2013_pseudo_c.txt:272577 (inside CTransition::validate_transition + // at line 272547). When the sphere is geometrically close to the + // LastKnownContactPlane, retail restores CP from LKCP via + // set_contact_plane(&collision_info, &last_known_contact_plane, + // last_known_contact_plane_is_water). This closes the gap that the + // stripped TryFindIndoorWalkablePlane synthesis path used to fill — + // when no fresh Path-6 CP write lands in this transition, CP is + // retained from the previous frame instead of being re-synthesized. + // + // NOTE: SetContactPlane also re-latches LKCP fields + // (TransitionTypes.cs:258-261), which is a no-op here since we + // pass LKCP as the source. + ci.SetContactPlane( + ci.LastKnownContactPlane, + ci.LastKnownContactPlaneCellId, + ci.LastKnownContactPlaneIsWater); + // Still close enough to the last-known plane — preserve // grounded state. L.2.3i FloorZ test for OnWalkable. oi.State |= ObjectInfoState.Contact;