using System.Numerics;
using AcDream.Core.Physics;
namespace AcDream.Runtime.Tests.Physics;
///
/// AD-10 — the remote slope projection. Retail projects the per-sub-step
/// movement offset onto collision_info.contact_plane INSIDE the sweep
/// (CTransition::adjust_offset 0x0050a370, pc:272271-272393,
/// called once per step from find_transitional_position
/// 0x0050bdf0). acdream ports that faithfully in
/// Transition.AdjustOffset, and remotes run that sweep — so the extra
/// pre-sweep projection the remote tick used to apply at the combiner boundary
/// was a second copy of the same operation against a single-point terrain
/// sample. It was deleted 2026-08-06 after the measurement these tests carry.
///
/// Every assertion here runs the production
/// tick and
/// takes its expected Z from the fixture's own terrain geometry, never from a
/// re-implementation of the projection formula — so a projection against the
/// WRONG plane produces a wrong answer rather than a self-consistent one.
///
public sealed class RuntimeRemoteSlopeProjectionTests
{
///
/// A comfortably walkable ramp steep enough that a failure to track it is
/// unmistakable: plane normal Z = 1/sqrt(1.36) ≈ 0.8575 (30.96 degrees)
/// against retail's 0.6642 floor_z limit (48.4 degrees). The ramp descends
/// along +Y, so +Y root motion is downhill.
///
private const float WalkableSlopeGradient = 0.6f;
///
/// Body-local root displacement per tick — the locomotion-cycle push that
/// moves a running remote between server position updates. 0.1 m at 30 Hz
/// is a 3 m/s run.
///
private const float RootMotionPerTick = 0.10f;
private const int TrackedTicks = 30;
///
/// How far the body's root may drift from the terrain surface directly
/// below it, relative to where the settled fixture put it. The measured
/// drift on this fixture is under 1e-4 m; 30 unprojected ticks down this
/// ramp accumulate about 1.8 m, so this band is ~350x below the failure it
/// must catch and ~50x above the float noise it must tolerate.
///
private const float SurfaceTrackingToleranceMeters = 0.005f;
///
/// The ramp's own plane, derived from the fixture heightmap rather than
/// from anything the code under test computed. The ramp descends along +Y
/// at gradient, so its plane is gradient*y + z = c and its
/// unit normal is (0, gradient, 1) normalized.
///
private static Vector3 RampNormal(float gradient)
=> Vector3.Normalize(new Vector3(0f, gradient, 1f));
///
/// Fixture validation, run before any motion assertion: the sweep really
/// does report this ramp's own geometric plane, and that plane is walkable.
/// A fixture whose contact plane were flat could not discriminate anything.
///
[Fact]
public void TheFixtureRampIsWalkableAndItsPlaneIsTheGeometricOne()
{
using RemoteRampHarness harness =
RemoteRampHarness.OnRamp(WalkableSlopeGradient);
Assert.True(harness.Remote.Body.OnWalkable);
Assert.True(harness.Remote.Body.ContactPlaneValid);
Vector3 expected = RampNormal(WalkableSlopeGradient);
Vector3 actual = harness.Remote.Body.ContactPlane.Normal;
Assert.True(
Vector3.Distance(expected, actual) < 0.001f,
$"contact plane normal was {actual}, expected the ramp's {expected}");
Assert.True(actual.Z >= PhysicsGlobals.FloorZ);
}
///
/// The artifact the deleted projection existed to remove: a remote running
/// across a slope must have its feet track the ground CONTINUOUSLY between
/// server position updates, not ratchet down in ~5 Hz steps.
///
/// Asserted on every tick rather than at the end, because a
/// start/end comparison passes a staircase that happens to catch up on the
/// final tick.
///
/// Sabotage-verified 2026-08-06: discarding the sweep's answer
/// (rm.Body.Position = postIntegratePos instead of
/// resolveResult.Position) reddens this at tick 1 with the body
/// 0.05999 m off the surface.
///
/// What this test does NOT discriminate, stated so nobody infers
/// it later. Short-circuiting Transition.AdjustOffset to
/// return offset; leaves it GREEN. On terrain the sweep has a
/// second, independent way to put the body on the surface:
/// ValidateWalkable's push-out re-plants the sphere at its natural
/// resting distance from the terrain plane on every sub-step, so the Z
/// outcome survives even with the offset projection gone (what changes is
/// the XY, which adjust_offset shortens). Removing the step-down
/// probe does not change that either — measured. So this test asserts the
/// OUTCOME "a running remote's feet stay on the ground", which is what the
/// deleted projection was there for; it is not a unit test of
/// adjust_offset, and it must not be cited as one.
///
[Fact]
public void TheRemoteTickTracksTheSurfaceWhileRunningDownhill()
{
using RemoteRampHarness harness =
RemoteRampHarness.OnRamp(WalkableSlopeGradient);
AssertTracksSurface(harness);
}
///
/// Anti-vacuity guard for the two tests above: on flat ground they pass
/// without Z ever having to move, so a fixture that quietly flattened
/// would make them meaningless. This asserts the ramp genuinely forces a
/// large Z excursion over the same number of ticks.
///
[Fact]
public void TheTrackingFixtureActuallyRequiresTheBodyToChangeZ()
{
using RemoteRampHarness harness =
RemoteRampHarness.OnRamp(WalkableSlopeGradient);
float startZ = harness.Remote.Body.Position.Z;
harness.Tick(TrackedTicks, new Vector3(0f, RootMotionPerTick, 0f));
float dz = harness.Remote.Body.Position.Z - startZ;
Assert.True(
dz < -1.0f,
$"fixture is not exercising slope descent: dz = {dz:F4} m");
}
private static void AssertTracksSurface(RemoteRampHarness harness)
{
Assert.True(harness.Remote.Body.OnWalkable);
// The settled resting offset between the body's root and the terrain
// directly beneath it. Measured, not assumed: the spawn settle may put
// the root a hair off the sampled surface.
float restingOffset =
harness.Remote.Body.Position.Z - harness.SurfaceZUnderBody();
for (int tick = 1; tick <= TrackedTicks; tick++)
{
harness.Tick(1, new Vector3(0f, RootMotionPerTick, 0f));
float offset =
harness.Remote.Body.Position.Z - harness.SurfaceZUnderBody();
Assert.True(
MathF.Abs(offset - restingOffset) < SurfaceTrackingToleranceMeters,
$"tick {tick}: body root sits {offset:F5} m above the terrain "
+ $"under it, expected {restingOffset:F5} m "
+ $"(pos {harness.Remote.Body.Position})");
}
}
}