Splits MotionCommandResolver's single ACE-modern lookup into an IMotionCommandCatalog seam with two implementations: - AceModernCommandCatalog (runtime default): built cleanly from the DatReaderWriter MotionCommand enum (mirrors ACE + local DAT MotionTables) with a documented class-priority tiebreak. The old blind 0x016E-0x0197 per-range override is DELETED — verified the ACE matrix (LifestoneRecall 0x0153 to 0x10000153, MarketplaceRecall 0x0166, AllegianceHometownRecall 0x0171, OffhandSlashHigh 0x0173) resolves correctly straight from the enum with no override. Stale shift-start comment corrected to SnowAngelState (0x43000115 to 0x43000118), not AllegianceHometownRecall. - Retail2013CommandCatalog (conformance/reference): full verbatim extraction of command_ids[0x198] at 0x007c73e8 (acclient_2013_pseudo_c.txt:1017259-1017667), direct wire-low to full index lookup. 408 entries, anchors verified against source ([0x150]=0x10000150, [0x153]=0x09000153, [0x197]=0x10000197). MotionCommandResolver.ReconstructFullCommand stays a static facade delegating to an AceModern singleton — all ~10 runtime callers unchanged. Removing the override exposed a pre-existing bug: CombatAnimationPlanner's late-combat command block uses 2013 numbering, not ACE/DRW. Corrected the one catalog test assertion pinned to the override's output (wire 0x0170 to 0x09000170 IssueSlashCommand, its true DRW identity) and filed the planner bug as #159 rather than silently patching out-of-scope code. Tests: +catalog matrices (ACE/2013), class-priority collisions, boundary cases, real-DAT availability (gap-doc hit counts reproduced). Build + full suite green (Core.Tests 1718, no regressions). Spec: docs/superpowers/specs/2026-06-30-movement-wire-parity-design.md (1) Research: docs/research/2026-06-26-ace-vs-2013-motion-command-gap.md Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
27 lines
1.1 KiB
C#
27 lines
1.1 KiB
C#
namespace AcDream.Core.Physics;
|
|
|
|
/// <summary>
|
|
/// Reconstructs a full 32-bit retail MotionCommand from the 16-bit wire
|
|
/// value broadcast in <c>InterpretedMotionState.Commands[]</c> (the server
|
|
/// truncates the class byte — see <see cref="MotionCommandResolver"/> for
|
|
/// why that byte must be restored before routing).
|
|
///
|
|
/// <para>
|
|
/// Two implementations exist because the wire-numbering used by ACE / the
|
|
/// local DATs and the wire-numbering used by the Sept 2013 EoR retail
|
|
/// decomp diverge for ~130 command names (a contiguous low-word "+3" shift
|
|
/// starting at <c>SnowAngelState</c>). See
|
|
/// <see cref="AceModernCommandCatalog"/> (runtime default) and
|
|
/// <see cref="Retail2013CommandCatalog"/> (conformance/reference), and
|
|
/// <c>docs/research/2026-06-26-ace-vs-2013-motion-command-gap.md</c> for the
|
|
/// full divergence analysis.
|
|
/// </para>
|
|
/// </summary>
|
|
public interface IMotionCommandCatalog
|
|
{
|
|
/// <summary>
|
|
/// Reconstruct the full 32-bit MotionCommand from a 16-bit wire value.
|
|
/// Returns 0 if no entry matches.
|
|
/// </summary>
|
|
uint ReconstructFullCommand(ushort wireCommand);
|
|
}
|