using System.Numerics;
using AcDream.Core.Physics;
using Xunit;
using Plane = System.Numerics.Plane;
namespace AcDream.Core.Tests.Physics;
///
/// Campaign P Slice P2, TS-1 gap #1 (2026-07-30 research pass,
/// docs/research/2026-07-30-response-layer-edge-family-pseudocode.md
/// §2). Pins the specific state Transition.EdgeSlideAfterStepDownFailed's
/// back-probe fallback (TransitionTypes.cs:2015-2031) hands to
/// : a walkable polygon rediscovered
/// near GlobalCurrCenter (the last-known-good grounded position),
/// tested against GlobalSphere[0] restored to the ORIGINAL failed
/// move target (off the polygon's edge) after RestoreCheckPos().
///
///
/// Retail's SPHEREPATH::edge_slide back-probe branch
/// (acclient_2013_pseudo_c.txt:274316-274326, 0050b4e0-0050b507)
/// re-caches walkable_check_pos/localspace_sphere from
/// SPHEREPATH::get_walkable_pos (0050a8f0) via
/// SPHEREPATH::cache_localspace_sphere (0050c9d0) and
/// SPHEREPATH::set_walkable_check_pos (00509ce0) before calling
/// precipice_slide a second time. That machinery exists to solve a
/// coordinate-FRAME problem: retail's walkable polygon and
/// check_pos are each expressed relative to a PER-CELL local frame
/// (cache_localspace_sphere re-projects one into the other's frame via
/// Position::localtolocal, and precipice_slide itself applies a
/// LandDefs::get_block_offset landblock correction, pc:274341).
///
///
///
/// acdream's /
/// are populated in UNIFIED WORLD SPACE at assignment time (see
/// SpherePath.SetWalkable/SetWalkableTransformed,
/// TransitionTypes.cs:667-739, which bake worldOrigin and
/// scale in immediately), and is
/// likewise always world-space (SpherePath.SetCheckPos/
/// RestoreCheckPos, TransitionTypes.cs:621-650). Both operands
/// 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.
/// also never reads a sphere radius
/// (only sphereCenter), so retail's radius/walkable_scale
/// correction has no acdream counterpart to begin with.
///
///
///
/// 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 GlobalCurrCenter, a restored GlobalSphere[0] at
/// the original off-edge target), PrecipiceSlide must find the
/// crossed edge and slide — not wedge into Collided.
///
///
public class EdgeSlideBackProbePrecipiceSlideTests
{
///
/// 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).
///
[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);
}
///
/// 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
/// precipice_slide returns COLLIDED_TS
/// (acclient_2013_pseudo_c.txt:274322-274326: eax==0 → walkable
/// = nullptr; return 2). Confirms the "no wedge" claim above is about
/// the edge-crossing case specifically, not a blanket "never Collided."
///
[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);
}
}