fix(physics): TransitionalInsert returns the real exhausted-retry state
Per the TS-4/#116 oracle plan (docs/research/2026-07-30-ts4-116-oracle-plan.md §1.4, §4 item 1): TransitionalInsert's retry loop hardcoded `return TransitionState.Slid;` when the attempt budget exhausted, despite the comment's own claim of returning "whatever the last iteration said." ACE's equivalent (Transition.cs:933, `return transitState;`) and retail's (pc:273363, 0x0050b949, `return edi;`) both reuse one state variable across the composite per-attempt call and return whatever it holds. acdream's per-phase dispatch (env/building/object/other-cells/neg-poly/ step-down) is split across several locals instead of ACE's single composite call, so `transitState` is now re-synced from whichever phase-local variable most recently caused a retry `continue`, and the final return uses that real value instead of the hardcoded constant. Blast radius is zero: ValidateTransition's "not OK" branch treats Collided/Adjusted/Slid identically, and every caller of TransitionalInsert either feeds the result straight into ValidateTransition/ ValidatePlacementTransition (both `== OK` vs. not) or checks `== OK` directly. Full AcDream.Core.Tests suite: 4059 passed / 2 skipped, no change in pass count. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
parent
9e17554ed4
commit
7e1be3def0
1 changed files with 41 additions and 4 deletions
|
|
@ -1700,7 +1700,12 @@ public sealed class Transition
|
||||||
var ci = CollisionInfo;
|
var ci = CollisionInfo;
|
||||||
var oi = ObjectInfo;
|
var oi = ObjectInfo;
|
||||||
|
|
||||||
TransitionState transitState;
|
// Initializer is unreachable in practice (numAttempts > 0 is
|
||||||
|
// guaranteed by the guard above, so the loop below always runs at
|
||||||
|
// least once and assigns transitState before any read) — required
|
||||||
|
// only to satisfy C#'s definite-assignment analysis, which can't
|
||||||
|
// connect the early-return guard to the loop bound.
|
||||||
|
TransitionState transitState = TransitionState.OK;
|
||||||
|
|
||||||
for (int attempt = 0; attempt < numAttempts; attempt++)
|
for (int attempt = 0; attempt < numAttempts; attempt++)
|
||||||
{
|
{
|
||||||
|
|
@ -1742,6 +1747,7 @@ public sealed class Transition
|
||||||
|
|
||||||
if (bldgState == TransitionState.Slid)
|
if (bldgState == TransitionState.Slid)
|
||||||
{
|
{
|
||||||
|
transitState = bldgState;
|
||||||
ci.ContactPlaneValid = false;
|
ci.ContactPlaneValid = false;
|
||||||
ci.ContactPlaneIsWater = false;
|
ci.ContactPlaneIsWater = false;
|
||||||
sp.NegPolyHit = false;
|
sp.NegPolyHit = false;
|
||||||
|
|
@ -1750,6 +1756,7 @@ public sealed class Transition
|
||||||
|
|
||||||
if (bldgState == TransitionState.Adjusted)
|
if (bldgState == TransitionState.Adjusted)
|
||||||
{
|
{
|
||||||
|
transitState = bldgState;
|
||||||
sp.NegPolyHit = false;
|
sp.NegPolyHit = false;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
@ -1771,6 +1778,7 @@ public sealed class Transition
|
||||||
// Object collision applied a push-out and set sliding normal.
|
// Object collision applied a push-out and set sliding normal.
|
||||||
// Retry at the new CheckPos — we may have slid into another
|
// Retry at the new CheckPos — we may have slid into another
|
||||||
// object, or need to re-verify env at the new position.
|
// object, or need to re-verify env at the new position.
|
||||||
|
transitState = objState;
|
||||||
ci.ContactPlaneValid = false;
|
ci.ContactPlaneValid = false;
|
||||||
ci.ContactPlaneIsWater = false;
|
ci.ContactPlaneIsWater = false;
|
||||||
sp.NegPolyHit = false;
|
sp.NegPolyHit = false;
|
||||||
|
|
@ -1781,6 +1789,7 @@ public sealed class Transition
|
||||||
{
|
{
|
||||||
// Object modified CheckPos (e.g. PerfectClip adjust_to_plane).
|
// Object modified CheckPos (e.g. PerfectClip adjust_to_plane).
|
||||||
// Retry at the new position.
|
// Retry at the new position.
|
||||||
|
transitState = objState;
|
||||||
sp.NegPolyHit = false;
|
sp.NegPolyHit = false;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
@ -1805,7 +1814,10 @@ public sealed class Transition
|
||||||
return TransitionState.Collided;
|
return TransitionState.Collided;
|
||||||
|
|
||||||
if (otherState != TransitionState.OK)
|
if (otherState != TransitionState.OK)
|
||||||
|
{
|
||||||
|
transitState = otherState;
|
||||||
continue; // ADJUSTED / SLID → retry the attempt
|
continue; // ADJUSTED / SLID → retry the attempt
|
||||||
|
}
|
||||||
|
|
||||||
// ── Phase 3: both env and objects returned OK ──────────────
|
// ── Phase 3: both env and objects returned OK ──────────────
|
||||||
// Handle Collide flag (BSP path 6 set it on a non-contact hit).
|
// Handle Collide flag (BSP path 6 set it on a non-contact hit).
|
||||||
|
|
@ -1943,6 +1955,7 @@ public sealed class Transition
|
||||||
var stepUpSlideRes = sp.StepUpSlide(this);
|
var stepUpSlideRes = sp.StepUpSlide(this);
|
||||||
if (stepUpSlideRes == TransitionState.Slid)
|
if (stepUpSlideRes == TransitionState.Slid)
|
||||||
{
|
{
|
||||||
|
transitState = stepUpSlideRes;
|
||||||
ci.ContactPlaneValid = false;
|
ci.ContactPlaneValid = false;
|
||||||
ci.ContactPlaneIsWater = false;
|
ci.ContactPlaneIsWater = false;
|
||||||
continue;
|
continue;
|
||||||
|
|
@ -1979,6 +1992,7 @@ public sealed class Transition
|
||||||
return TransitionState.Collided; // degenerate slide → hard stop
|
return TransitionState.Collided; // degenerate slide → hard stop
|
||||||
// Slid / Adjusted / OK → re-test at the (slid) CheckPos, mirroring
|
// Slid / Adjusted / OK → re-test at the (slid) CheckPos, mirroring
|
||||||
// retail's insert-loop continuation after slide_sphere.
|
// retail's insert-loop continuation after slide_sphere.
|
||||||
|
transitState = slideRes;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -2070,6 +2084,7 @@ public sealed class Transition
|
||||||
var edgeState = EdgeSlideAfterStepDownFailed(engine, stepDownHeight, zVal);
|
var edgeState = EdgeSlideAfterStepDownFailed(engine, stepDownHeight, zVal);
|
||||||
if (edgeState == TransitionState.Slid)
|
if (edgeState == TransitionState.Slid)
|
||||||
{
|
{
|
||||||
|
transitState = edgeState;
|
||||||
ci.ContactPlaneValid = false;
|
ci.ContactPlaneValid = false;
|
||||||
ci.ContactPlaneIsWater = false;
|
ci.ContactPlaneIsWater = false;
|
||||||
sp.NegPolyHit = false;
|
sp.NegPolyHit = false;
|
||||||
|
|
@ -2078,6 +2093,7 @@ public sealed class Transition
|
||||||
|
|
||||||
if (edgeState == TransitionState.Adjusted)
|
if (edgeState == TransitionState.Adjusted)
|
||||||
{
|
{
|
||||||
|
transitState = edgeState;
|
||||||
sp.NegPolyHit = false;
|
sp.NegPolyHit = false;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
@ -2088,9 +2104,30 @@ public sealed class Transition
|
||||||
return TransitionState.OK;
|
return TransitionState.OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Exhausted retry attempts — return whatever the last iteration said.
|
// Exhausted retry attempts — return the real last transition state.
|
||||||
// (Defaults to Slid in practice since that's the only case that retries.)
|
//
|
||||||
return TransitionState.Slid;
|
// TS-4/#116 oracle pass (Campaign P final physics slice,
|
||||||
|
// docs/research/2026-07-30-ts4-116-oracle-plan.md §1.4, §4 item 1):
|
||||||
|
// this was hardcoded to TransitionState.Slid despite the comment's
|
||||||
|
// own claim of returning "whatever the last iteration said" — a
|
||||||
|
// real, citable port-accuracy divergence from ACE's
|
||||||
|
// `return transitState;` (Transition.cs:933) and retail's
|
||||||
|
// `return edi;` (acclient_2013_pseudo_c.txt:273363, 0x0050b949),
|
||||||
|
// both of which reuse ONE state variable across the composite
|
||||||
|
// per-attempt call and return whatever it holds when the retry
|
||||||
|
// budget is exhausted. acdream's per-phase dispatch (env/building/
|
||||||
|
// object/other-cells/neg-poly/step-down) is split across several
|
||||||
|
// locals instead of ACE's single composite call, so `transitState`
|
||||||
|
// is explicitly re-synced from whichever phase-local variable most
|
||||||
|
// recently caused a retry `continue` (see the `transitState = ...`
|
||||||
|
// assignments immediately above each `continue` in this loop).
|
||||||
|
// Blast radius: zero. ValidateTransition's "not OK" branch
|
||||||
|
// (`transitionState != TransitionState.Invalid`) treats
|
||||||
|
// Collided/Adjusted/Slid identically — every caller of
|
||||||
|
// TransitionalInsert either feeds its result straight into
|
||||||
|
// ValidateTransition/ValidatePlacementTransition (both of which
|
||||||
|
// only branch on `== OK` vs. not) or only checks `== OK` directly.
|
||||||
|
return transitState;
|
||||||
}
|
}
|
||||||
|
|
||||||
private TransitionState EdgeSlideAfterStepDownFailed(
|
private TransitionState EdgeSlideAfterStepDownFailed(
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue