MovementParameters gains the verbatim selection family: GetCommand 0x0052aa00 (the walk-vs-run cascade INCLUDING the CanCharge 0x10 fast-path ACE dropped - retail's default can_charge=false + the fast-path present, the A13+A15 canceling-pair trap avoided; inclusive threshold edge per the raw), TowardsAndAway, GetDesiredHeading per the live Ghidra decompile (fwd-towards 0 / fwd-away 180 / back-towards 180 / back-away 0), FromWire/FromWireTurnTo (UnPackNet semantics, all 18 A4 masks round-tripped). New MoveToMath: HeadingDiff per the live Ghidra decompile of 0x00528fb0 (the 360-diff NOT-TurnRight mirror + F_EPSILON 0.000199999995f - the BN "arg unused" artifact corrected), HeadingGreater (the visible TurnRight idiom), PositionHeading/Get/SetHeading reusing the codebase's single yaw-heading convention (P5), CylinderDistance (PDB arg order; planar-minus-radii shape documented as the interpretation - the raw's x87 body is garbled; seam noted). MovementType gains Invalid + retail 6/7/8/9; MovementStruct widened (ObjectId/TopLevelId/Pos/Radius/Height/Params, additive); WeenieError += 0x0B/0x36/0x37/0x38/0x3D with retail-meaning doc comments. 148 new conformance tests. Full suite: 3,860 passed. Implemented by a dedicated agent against the V0-pinned spec; scope + suite independently verified. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
82 lines
2.8 KiB
C#
82 lines
2.8 KiB
C#
using AcDream.Core.Physics;
|
|
using AcDream.Core.Physics.Motion;
|
|
using Xunit;
|
|
|
|
namespace AcDream.Core.Tests.Physics.Motion;
|
|
|
|
/// <summary>
|
|
/// R4-V1 — <c>heading_greater</c> (<c>00528f60</c>, raw 306281-306323), per
|
|
/// r4-moveto-decomp.md §5f:
|
|
/// <code>
|
|
/// if (fabs(a - b) > 180) greater = (b > a); // wrapped case: compare flipped
|
|
/// else greater = (a > b);
|
|
/// if (turnCmd == TurnRight) return greater;
|
|
/// return !greater; // TurnLeft: inverted
|
|
/// </code>
|
|
/// "Has the turn passed the target heading" — direction-aware, wrap-aware.
|
|
/// </summary>
|
|
public sealed class MoveToMathHeadingGreaterTests
|
|
{
|
|
private const uint TurnRight = MotionCommand.TurnRight;
|
|
private const uint TurnLeft = MotionCommand.TurnLeft;
|
|
|
|
[Fact]
|
|
public void TurnRight_UnwrappedCase_SimpleGreater()
|
|
{
|
|
Assert.True(MoveToMath.HeadingGreater(90f, 30f, TurnRight));
|
|
Assert.False(MoveToMath.HeadingGreater(30f, 90f, TurnRight));
|
|
}
|
|
|
|
[Fact]
|
|
public void TurnRight_WrappedCase_ComparisonFlips()
|
|
{
|
|
// |350 - 10| = 340 > 180 → wrapped: greater = (b > a) = (10 > 350) = false
|
|
Assert.False(MoveToMath.HeadingGreater(350f, 10f, TurnRight));
|
|
// |10 - 350| = 340 > 180 → wrapped: greater = (b > a) = (350 > 10) = true
|
|
Assert.True(MoveToMath.HeadingGreater(10f, 350f, TurnRight));
|
|
}
|
|
|
|
[Fact]
|
|
public void TurnRight_ExactlyAt180Delta_UnwrappedBranch()
|
|
{
|
|
// fabs(a-b) > 180 is STRICT — exactly 180 uses the unwrapped branch.
|
|
// a=200,b=20: fabs=180, not >180 → greater = (a>b) = true
|
|
Assert.True(MoveToMath.HeadingGreater(200f, 20f, TurnRight));
|
|
}
|
|
|
|
[Fact]
|
|
public void TurnLeft_InvertsTheUnwrappedResult()
|
|
{
|
|
Assert.False(MoveToMath.HeadingGreater(90f, 30f, TurnLeft));
|
|
Assert.True(MoveToMath.HeadingGreater(30f, 90f, TurnLeft));
|
|
}
|
|
|
|
[Fact]
|
|
public void TurnLeft_InvertsTheWrappedResult()
|
|
{
|
|
Assert.True(MoveToMath.HeadingGreater(350f, 10f, TurnLeft));
|
|
Assert.False(MoveToMath.HeadingGreater(10f, 350f, TurnLeft));
|
|
}
|
|
|
|
[Fact]
|
|
public void EqualHeadings_NotGreater_TurnRight()
|
|
{
|
|
Assert.False(MoveToMath.HeadingGreater(45f, 45f, TurnRight));
|
|
}
|
|
|
|
[Fact]
|
|
public void EqualHeadings_InvertedToTrue_TurnLeft()
|
|
{
|
|
// greater=false for equal headings; TurnLeft inverts → true.
|
|
Assert.True(MoveToMath.HeadingGreater(45f, 45f, TurnLeft));
|
|
}
|
|
|
|
[Fact]
|
|
public void AnyNonTurnRightCommand_AlsoInverts()
|
|
{
|
|
// The retail gate is `== TurnRight` (not `!= TurnRight` as in
|
|
// heading_diff) — every OTHER command, not just TurnLeft, inverts.
|
|
Assert.False(MoveToMath.HeadingGreater(90f, 30f, 0u));
|
|
Assert.False(MoveToMath.HeadingGreater(90f, 30f, MotionCommand.WalkForward));
|
|
}
|
|
}
|