acdream/tests/AcDream.Core.Tests/Physics/WeenieErrorCodeTableTests.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

136 lines
5.3 KiB
C#

using AcDream.Core.Physics;
using Xunit;
namespace AcDream.Core.Tests.Physics;
/// <summary>
/// R3-W1 — pins <see cref="WeenieError"/>'s numeric values against the
/// definitive retail table in
/// docs/research/2026-07-02-r3-motioninterp/W0-pins.md §A10 (an exhaustive
/// sweep of every <c>return &lt;code&gt;</c> site across raw 304908-306277 +
/// 300150-300540: 19 return sites + 1 store site, all attributed). These
/// codes are local-only (never on the wire), so the renumber from the
/// pre-R3 shuffled names is a safe rename — this test is the single
/// source of truth that a future edit can't silently un-pin.
/// </summary>
public sealed class WeenieErrorCodeTableTests
{
[Fact]
public void None_Is0x00()
=> Assert.Equal(0x00u, (uint)WeenieError.None);
[Fact]
public void NoPhysicsObject_Is0x08()
=> Assert.Equal(0x08u, (uint)WeenieError.NoPhysicsObject);
/// <summary>
/// 0x0B — NoMotionInterpreter. R4-V1 addition (M12), per
/// docs/research/2026-07-03-r4-moveto/r4-moveto-decomp.md §12 constants
/// inventory row (<c>8, 0xb, 0x36, 0x37, 0x38, 0x3d, 0x47</c>).
/// </summary>
[Fact]
public void NoMotionInterpreter_Is0x0B()
=> Assert.Equal(0x0Bu, (uint)WeenieError.NoMotionInterpreter);
[Fact]
public void NotGrounded_Is0x24()
=> Assert.Equal(0x24u, (uint)WeenieError.NotGrounded);
[Fact]
public void CrouchInCombatStance_Is0x3f()
=> Assert.Equal(0x3fu, (uint)WeenieError.CrouchInCombatStance);
[Fact]
public void SitInCombatStance_Is0x40()
=> Assert.Equal(0x40u, (uint)WeenieError.SitInCombatStance);
[Fact]
public void SleepInCombatStance_Is0x41()
=> Assert.Equal(0x41u, (uint)WeenieError.SleepInCombatStance);
[Fact]
public void ChatEmoteOutsideNonCombat_Is0x42()
=> Assert.Equal(0x42u, (uint)WeenieError.ChatEmoteOutsideNonCombat);
/// <summary>
/// 0x36 — ActionCancelled. R4-V1 addition (M12). Site:
/// <c>MoveToManager::PerformMovement</c> (§3a @0052a901) — every new
/// moveto cancels the previous one with this code before dispatching;
/// also <c>CPhysicsObj::interrupt_current_movement</c>'s
/// <c>MovementManager::CancelMoveTo(0x36)</c> call (§9e). Per §7c, the
/// arg is NEVER READ inside <c>MoveToManager::CancelMoveTo</c>'s body in
/// this build — kept for parity/logging, not behavior.
/// </summary>
[Fact]
public void ActionCancelled_Is0x36()
=> Assert.Equal(0x36u, (uint)WeenieError.ActionCancelled);
/// <summary>
/// 0x37 — ObjectGone. R4-V1 addition (M12). Site:
/// <c>MoveToManager::HandleUpdateTarget</c> (§6d @307866-307867) —
/// retarget delivery with a non-OK target status.
/// </summary>
[Fact]
public void ObjectGone_Is0x37()
=> Assert.Equal(0x37u, (uint)WeenieError.ObjectGone);
/// <summary>
/// 0x38 — NoObject. R4-V1 addition (M12). Site:
/// <c>MoveToManager::HandleUpdateTarget</c> (§6d @307857-307858) — the
/// FIRST target callback arrives with a non-OK status (target never
/// resolved).
/// </summary>
[Fact]
public void NoObject_Is0x38()
=> Assert.Equal(0x38u, (uint)WeenieError.NoObject);
[Fact]
public void ActionDepthExceeded_Is0x45()
=> Assert.Equal(0x45u, (uint)WeenieError.ActionDepthExceeded);
[Fact]
public void GeneralMovementFailure_Is0x47()
=> Assert.Equal(0x47u, (uint)WeenieError.GeneralMovementFailure);
[Fact]
public void YouCantJumpFromThisPosition_Is0x48()
=> Assert.Equal(0x48u, (uint)WeenieError.YouCantJumpFromThisPosition);
[Fact]
public void CantJumpLoadedDown_Is0x49()
=> Assert.Equal(0x49u, (uint)WeenieError.CantJumpLoadedDown);
/// <summary>
/// 0x3D — YouChargedTooFar. R4-V1 addition (M12). Site:
/// <c>MoveToManager::HandleMoveToPosition</c> Phase 2 arrival check —
/// <c>fail_distance</c> exceeded (r4-moveto-decomp.md §6b).
/// </summary>
[Fact]
public void YouChargedTooFar_Is0x3D()
=> Assert.Equal(0x3Du, (uint)WeenieError.YouChargedTooFar);
/// <summary>
/// Every code in the A10 table in one pass — guards against a
/// future partial edit desyncing an individual test above from the
/// enum declaration.
/// </summary>
[Theory]
[InlineData(WeenieError.None, 0x00u)]
[InlineData(WeenieError.NoPhysicsObject, 0x08u)]
[InlineData(WeenieError.NoMotionInterpreter, 0x0Bu)]
[InlineData(WeenieError.NotGrounded, 0x24u)]
[InlineData(WeenieError.ActionCancelled, 0x36u)]
[InlineData(WeenieError.ObjectGone, 0x37u)]
[InlineData(WeenieError.NoObject, 0x38u)]
[InlineData(WeenieError.CrouchInCombatStance, 0x3fu)]
[InlineData(WeenieError.SitInCombatStance, 0x40u)]
[InlineData(WeenieError.SleepInCombatStance, 0x41u)]
[InlineData(WeenieError.ChatEmoteOutsideNonCombat, 0x42u)]
[InlineData(WeenieError.ActionDepthExceeded, 0x45u)]
[InlineData(WeenieError.GeneralMovementFailure, 0x47u)]
[InlineData(WeenieError.YouCantJumpFromThisPosition, 0x48u)]
[InlineData(WeenieError.CantJumpLoadedDown, 0x49u)]
[InlineData(WeenieError.YouChargedTooFar, 0x3Du)]
public void A10Table_EveryCode_MatchesRetailNumericValue(WeenieError code, uint expected)
=> Assert.Equal(expected, (uint)code);
}