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>
67 lines
2.3 KiB
C#
67 lines
2.3 KiB
C#
using AcDream.Core.Physics;
|
|
using AcDream.Core.Physics.Motion;
|
|
using Xunit;
|
|
|
|
namespace AcDream.Core.Tests.Physics.Motion;
|
|
|
|
/// <summary>
|
|
/// R4-V1 — <c>MovementParameters::get_desired_heading</c> (<c>0x0052aad0</c>),
|
|
/// PINNED by direct Ghidra decompile of <c>patchmem.gpr</c> (see
|
|
/// docs/research/2026-07-03-r4-moveto/ghidra-confirmations.md §P2 — fetched
|
|
/// live during V0, ACE-shaped constants CONFIRMED exact):
|
|
/// <code>
|
|
/// forward|run + towards → 0 forward|run + away → 180
|
|
/// backward + towards → 180 backward + away → 0
|
|
/// any other command → 0
|
|
/// </code>
|
|
/// </summary>
|
|
public sealed class MovementParametersGetDesiredHeadingTests
|
|
{
|
|
[Theory]
|
|
[InlineData(false, 0f)] // RunForward, towards → 0
|
|
[InlineData(true, 180f)] // RunForward, away → 180
|
|
public void RunForward_FourQuadrant(bool movingAway, float expected)
|
|
{
|
|
var p = new MovementParameters();
|
|
float h = p.GetDesiredHeading(MotionCommand.RunForward, movingAway);
|
|
Assert.Equal(expected, h);
|
|
}
|
|
|
|
[Theory]
|
|
[InlineData(false, 0f)] // WalkForward, towards → 0
|
|
[InlineData(true, 180f)] // WalkForward, away → 180
|
|
public void WalkForward_FourQuadrant(bool movingAway, float expected)
|
|
{
|
|
var p = new MovementParameters();
|
|
float h = p.GetDesiredHeading(MotionCommand.WalkForward, movingAway);
|
|
Assert.Equal(expected, h);
|
|
}
|
|
|
|
[Theory]
|
|
[InlineData(false, 180f)] // WalkBackward, towards → 180 (face the target while backing up)
|
|
[InlineData(true, 0f)] // WalkBackward, away → 0
|
|
public void WalkBackward_FourQuadrant(bool movingAway, float expected)
|
|
{
|
|
var p = new MovementParameters();
|
|
float h = p.GetDesiredHeading(MotionCommand.WalkBackward, movingAway);
|
|
Assert.Equal(expected, h);
|
|
}
|
|
|
|
[Theory]
|
|
[InlineData(false)]
|
|
[InlineData(true)]
|
|
public void UnknownCommand_DefaultsToZero(bool movingAway)
|
|
{
|
|
var p = new MovementParameters();
|
|
float h = p.GetDesiredHeading(MotionCommand.TurnRight, movingAway);
|
|
Assert.Equal(0f, h);
|
|
}
|
|
|
|
[Fact]
|
|
public void ZeroCommand_DefaultsToZero()
|
|
{
|
|
var p = new MovementParameters();
|
|
Assert.Equal(0f, p.GetDesiredHeading(0u, movingAway: false));
|
|
Assert.Equal(0f, p.GetDesiredHeading(0u, movingAway: true));
|
|
}
|
|
}
|