acdream/tests/AcDream.Core.Tests/Physics/Motion/MovementStructWideningTests.cs
Erik e0d2492cbb feat(R4-V1): command-selection family + state widening (closes M2-mechanics, M11, M12, M15)
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>
2026-07-03 11:13:15 +02:00

108 lines
3.1 KiB
C#

using System.Numerics;
using AcDream.Core.Physics;
using AcDream.Core.Physics.Motion;
using Xunit;
namespace AcDream.Core.Tests.Physics.Motion;
/// <summary>
/// R4-V1 — <see cref="MovementStruct"/> widening per r4-moveto-decomp.md §0
/// (acclient.h:38069, struct #4067):
/// <code>
/// struct __cppobj MovementStruct
/// {
/// MovementTypes::Type type;
/// unsigned int motion; // types 1-4 only
/// unsigned int object_id; // types 6, 8
/// unsigned int top_level_id; // types 6, 8
/// Position pos; // type 7
/// float radius; // type 6
/// float height; // type 6
/// MovementParameters *params; // types 1-4, 6-9
/// };
/// </code>
/// Additive-only (M11, mechanical) — no consumer wires these fields yet;
/// this test just pins the shape exists and round-trips.
/// </summary>
public sealed class MovementStructWideningTests
{
[Fact]
public void ObjectId_TopLevelId_RoundTrip()
{
var mvs = new MovementStruct
{
Type = MovementType.MoveToObject,
ObjectId = 0x50001234u,
TopLevelId = 0x50005678u,
};
Assert.Equal(0x50001234u, mvs.ObjectId);
Assert.Equal(0x50005678u, mvs.TopLevelId);
}
[Fact]
public void Pos_RoundTrips_WorldPositionAndCell()
{
var pos = new Position(0x12340001u, new Vector3(10f, 20f, 3f), Quaternion.Identity);
var mvs = new MovementStruct
{
Type = MovementType.MoveToPosition,
Pos = pos,
};
Assert.Equal(pos, mvs.Pos);
Assert.Equal(0x12340001u, mvs.Pos.ObjCellId);
Assert.Equal(new Vector3(10f, 20f, 3f), mvs.Pos.Frame.Origin);
}
[Fact]
public void Radius_Height_RoundTrip()
{
var mvs = new MovementStruct
{
Type = MovementType.MoveToObject,
Radius = 0.75f,
Height = 1.8f,
};
Assert.Equal(0.75f, mvs.Radius);
Assert.Equal(1.8f, mvs.Height);
}
[Fact]
public void Params_HoldsMovementParametersReference()
{
var p = new MovementParameters { CanCharge = true };
var mvs = new MovementStruct
{
Type = MovementType.TurnToHeading,
Params = p,
};
Assert.Same(p, mvs.Params);
}
[Fact]
public void ExistingFields_Type_Motion_StillPresent_NoRegression()
{
// The pre-R4 fields (Type/Motion/Speed/Autonomous/ModifyInterpretedState/
// ModifyRawState) must survive the widening untouched — R4 is
// additive-only per the plan (M11, "no consumer changes").
var mvs = new MovementStruct
{
Type = MovementType.RawCommand,
Motion = 0x45000005u,
Speed = 1.5f,
Autonomous = true,
ModifyInterpretedState = true,
ModifyRawState = false,
};
Assert.Equal(MovementType.RawCommand, mvs.Type);
Assert.Equal(0x45000005u, mvs.Motion);
Assert.Equal(1.5f, mvs.Speed);
Assert.True(mvs.Autonomous);
Assert.True(mvs.ModifyInterpretedState);
Assert.False(mvs.ModifyRawState);
}
}