Campaign P Slice P2 step 1 (docs/research/2026-07-30-response-layer-edge-family-pseudocode.md §2, §6 Step 1/2). The TS-1 register row (retail-divergence-register.md:238) described work that is already done: SpherePath.PrecipiceSlide, Transition.CliffSlide, and Transition.EdgeSlideAfterStepDownFailed are real, tested ports of retail's edge_slide -> precipice_slide/cliff_slide chain (pc:274316, pc:272397, pc:273001-273090). Its cited :1254 line was stale stepping-loop code the file moved past. The one real remaining gap (the back-probe fallback skipping retail's walkable_check_pos/localspace_sphere recache before its second precipice_slide call, pc:274318-274326 / 0050b4e0-0050b507) needed no production code change: a fresh read of SPHEREPATH::get_walkable_pos (0050a8f0), cache_localspace_sphere (0050c9d0), and set_walkable_check_pos (00509ce0) shows that machinery exists to re-project a sphere across retail's PER-CELL local coordinate frames. acdream's SpherePath.WalkableVertices and GlobalSphere are populated in UNIFIED WORLD SPACE at assignment time (SetWalkable/SetWalkableTransformed, SetCheckPos/RestoreCheckPos), so both operands BSPQuery.FindCrossedEdge compares are already commensurable -- retail's recache is a no-op correction under this architecture, and FindCrossedEdge never reads a sphere radius, so retail's walkable_scale radius correction has no acdream counterpart either. Documented in-code at the back-probe site with full citations, and pinned with EdgeSlideBackProbePrecipiceSlideTests: a walkable polygon rediscovered near GlobalCurrCenter, tested against GlobalSphere[0] restored to the original failed target, crosses the edge and slides -- it does not wedge into Collided (and the inverse case, standing inside the polygon with no edge crossed, correctly still returns Collided matching retail's own precipice_slide on a false find_crossed_edge). TS-1's other two flagged gaps are real acdream-only compensating branches, not retail reads, and get their own rows rather than being silently retired alongside it: - AD-53: CliffSlide's three-source reference-normal fallback chain (LastWalkablePlane -> LastKnownContactPlane -> world-up) vs retail's direct last_known_contact_plane.N use. A fresh read of last_known_contact_plane's maintenance (pc:272659-272668) confirms retail overwrites it unconditionally every validate_transition pass, including with a steep plane -- so the fallback chain compensates for AP-4's incomplete OnWalkable bookkeeping, not a retail-matching read. - AD-54: the walkable-steepness reroute to CliffSlide before PrecipiceSlide when the stored walkable polygon itself is steeper than FloorZ. Retail's raw edge_slide has no such branch; the permissive LandingZ acceptance that makes this state reachable IS retail-faithful (TS-4's own BSPTREE::find_collisions citation), but whether retail's outer transitional_insert retry loop absorbs the resulting COLLIDED_TS some other way is not yet independently verified -- flagged open in the row. Physics test suite: 1836 passed, 1 skipped (D4, unrelated to this change). Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
151 lines
6.7 KiB
C#
151 lines
6.7 KiB
C#
using System.Numerics;
|
|
using AcDream.Core.Physics;
|
|
using Xunit;
|
|
using Plane = System.Numerics.Plane;
|
|
|
|
namespace AcDream.Core.Tests.Physics;
|
|
|
|
/// <summary>
|
|
/// Campaign P Slice P2, TS-1 gap #1 (2026-07-30 research pass,
|
|
/// <c>docs/research/2026-07-30-response-layer-edge-family-pseudocode.md</c>
|
|
/// §2). Pins the specific state <c>Transition.EdgeSlideAfterStepDownFailed</c>'s
|
|
/// back-probe fallback (<c>TransitionTypes.cs:2015-2031</c>) hands to
|
|
/// <see cref="SpherePath.PrecipiceSlide"/>: a walkable polygon rediscovered
|
|
/// near <c>GlobalCurrCenter</c> (the last-known-good grounded position),
|
|
/// tested against <c>GlobalSphere[0]</c> restored to the ORIGINAL failed
|
|
/// move target (off the polygon's edge) after <c>RestoreCheckPos()</c>.
|
|
///
|
|
/// <para>
|
|
/// Retail's <c>SPHEREPATH::edge_slide</c> back-probe branch
|
|
/// (<c>acclient_2013_pseudo_c.txt:274316-274326</c>, <c>0050b4e0-0050b507</c>)
|
|
/// re-caches <c>walkable_check_pos</c>/<c>localspace_sphere</c> from
|
|
/// <c>SPHEREPATH::get_walkable_pos</c> (<c>0050a8f0</c>) via
|
|
/// <c>SPHEREPATH::cache_localspace_sphere</c> (<c>0050c9d0</c>) and
|
|
/// <c>SPHEREPATH::set_walkable_check_pos</c> (<c>00509ce0</c>) before calling
|
|
/// <c>precipice_slide</c> a second time. That machinery exists to solve a
|
|
/// coordinate-FRAME problem: retail's <c>walkable</c> polygon and
|
|
/// <c>check_pos</c> are each expressed relative to a PER-CELL local frame
|
|
/// (<c>cache_localspace_sphere</c> re-projects one into the other's frame via
|
|
/// <c>Position::localtolocal</c>, and <c>precipice_slide</c> itself applies a
|
|
/// <c>LandDefs::get_block_offset</c> landblock correction, pc:274341).
|
|
/// </para>
|
|
///
|
|
/// <para>
|
|
/// acdream's <see cref="SpherePath.WalkableVertices"/>/<see cref="SpherePath.WalkablePlane"/>
|
|
/// are populated in UNIFIED WORLD SPACE at assignment time (see
|
|
/// <c>SpherePath.SetWalkable</c>/<c>SetWalkableTransformed</c>,
|
|
/// <c>TransitionTypes.cs:667-739</c>, which bake <c>worldOrigin</c> and
|
|
/// <c>scale</c> in immediately), and <see cref="SpherePath.GlobalSphere"/> is
|
|
/// likewise always world-space (<c>SpherePath.SetCheckPos</c>/
|
|
/// <c>RestoreCheckPos</c>, <c>TransitionTypes.cs:621-650</c>). Both operands
|
|
/// <see cref="BSPQuery.FindCrossedEdge"/> compares are therefore ALREADY
|
|
/// commensurable without any recache step — retail's local-frame
|
|
/// re-projection is a no-op correction in acdream's flat-world-space design.
|
|
/// <see cref="BSPQuery.FindCrossedEdge"/> also never reads a sphere radius
|
|
/// (only <c>sphereCenter</c>), so retail's radius/<c>walkable_scale</c>
|
|
/// correction has no acdream counterpart to begin with.
|
|
/// </para>
|
|
///
|
|
/// <para>
|
|
/// This test does not add a recache step (there is nothing for it to
|
|
/// correct in this architecture); it instead PINS the claim: given exactly
|
|
/// the field values the back-probe fallback produces (a world-space walkable
|
|
/// polygon near <c>GlobalCurrCenter</c>, a restored <c>GlobalSphere[0]</c> at
|
|
/// the original off-edge target), <c>PrecipiceSlide</c> must find the
|
|
/// crossed edge and slide — not wedge into <c>Collided</c>.
|
|
/// </para>
|
|
/// </summary>
|
|
public class EdgeSlideBackProbePrecipiceSlideTests
|
|
{
|
|
/// <summary>
|
|
/// Back-probe re-discovers a flat platform's edge polygon near the last
|
|
/// known good center; the restored (failed) target sphere sits just past
|
|
/// the +X edge. PrecipiceSlide must cross that edge and slide, matching
|
|
/// retail's post-recache precipice_slide result (ADJUSTED_TS/SLID_TS,
|
|
/// never a stuck COLLIDED_TS).
|
|
/// </summary>
|
|
[Fact]
|
|
public void PrecipiceSlide_BackProbeState_CrossesEdge_DoesNotWedge()
|
|
{
|
|
var transition = new Transition();
|
|
var sp = transition.SpherePath;
|
|
|
|
// Last known good grounded center — where the back-probe offset
|
|
// points back toward (retail: global_curr_center).
|
|
sp.GlobalCurrCenter[0].Origin = new Vector3(0f, 0f, 1f);
|
|
sp.GlobalCurrCenter[0].Radius = 0.5f;
|
|
|
|
// The walkable polygon the back-probe's DoStepDown rediscovered near
|
|
// that center: a flat 2x2 platform, top face at Z=1.
|
|
var plane = new Plane(Vector3.UnitZ, -1f);
|
|
sp.SetWalkable(
|
|
plane,
|
|
new[]
|
|
{
|
|
new Vector3(-1f, -1f, 1f),
|
|
new Vector3(1f, -1f, 1f),
|
|
new Vector3(1f, 1f, 1f),
|
|
new Vector3(-1f, 1f, 1f),
|
|
},
|
|
Vector3.UnitZ);
|
|
|
|
// GlobalSphere[0] after RestoreCheckPos(): the ORIGINAL failed move
|
|
// target, just past the platform's +X edge (off the polygon, over
|
|
// open air) — exactly what the back-probe fallback hands to
|
|
// PrecipiceSlide once retail's edge_slide restores check_pos.
|
|
sp.CheckPos = new Vector3(1.2f, 0f, 1f);
|
|
sp.GlobalSphere[0].Origin = sp.CheckPos;
|
|
sp.GlobalSphere[0].Radius = 0.5f;
|
|
|
|
var result = sp.PrecipiceSlide(transition);
|
|
|
|
Assert.NotEqual(TransitionState.Collided, result);
|
|
Assert.True(
|
|
result is TransitionState.Slid or TransitionState.Adjusted or TransitionState.OK,
|
|
$"Back-probe PrecipiceSlide must slide/adjust across the found edge, not wedge; got {result}.");
|
|
|
|
// The walkable context is consumed (retail: this->walkable = nullptr
|
|
// inside precipice_slide before slide_sphere runs).
|
|
Assert.False(sp.WalkableValid);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Sanity inverse: when the restored target sphere is still WELL INSIDE
|
|
/// the rediscovered polygon (no edge crossed — e.g. the back-probe found
|
|
/// the same ground the mover is standing on), retail's raw
|
|
/// <c>precipice_slide</c> returns COLLIDED_TS
|
|
/// (<c>acclient_2013_pseudo_c.txt:274322-274326</c>: <c>eax==0 → walkable
|
|
/// = nullptr; return 2</c>). Confirms the "no wedge" claim above is about
|
|
/// the edge-crossing case specifically, not a blanket "never Collided."
|
|
/// </summary>
|
|
[Fact]
|
|
public void PrecipiceSlide_NoEdgeCrossed_ReturnsCollided_MatchingRetail()
|
|
{
|
|
var transition = new Transition();
|
|
var sp = transition.SpherePath;
|
|
|
|
sp.GlobalCurrCenter[0].Origin = new Vector3(0f, 0f, 1f);
|
|
sp.GlobalCurrCenter[0].Radius = 0.5f;
|
|
|
|
var plane = new Plane(Vector3.UnitZ, -1f);
|
|
sp.SetWalkable(
|
|
plane,
|
|
new[]
|
|
{
|
|
new Vector3(-1f, -1f, 1f),
|
|
new Vector3(1f, -1f, 1f),
|
|
new Vector3(1f, 1f, 1f),
|
|
new Vector3(-1f, 1f, 1f),
|
|
},
|
|
Vector3.UnitZ);
|
|
|
|
// Restored target well inside the polygon — no edge crossed.
|
|
sp.CheckPos = new Vector3(0.1f, 0f, 1f);
|
|
sp.GlobalSphere[0].Origin = sp.CheckPos;
|
|
sp.GlobalSphere[0].Radius = 0.5f;
|
|
|
|
var result = sp.PrecipiceSlide(transition);
|
|
|
|
Assert.Equal(TransitionState.Collided, result);
|
|
}
|
|
}
|