fix(combat): #159 — derive CombatAnimationMotionCommands from the DRW enum
CombatAnimationPlanner.CombatAnimationMotionCommands hand-transcribed the combat MotionCommand constants from the Sept 2013 EoR decomp. The whole late-combat block (Offhand* / Attack4-6 / Punch*) is numbered +3 lower there than in ACE/DatReaderWriter — a contiguous low-word shift that begins around SnowAngelState (0x115). So against a live ACE server every one of those 40 commands misclassified: the resolver returned the correct ACE value (e.g. wire 0x0173 -> 0x10000173 OffhandSlashHigh) but the planner's constant held the 2013 value (0x10000170), so no switch case matched. Reload was worse — hardcoded 0x100000D4, a value absent from DRW entirely; the real ACE/DRW Reload is the 0x40000016 SubState. Instead of re-transcribing 40 hex constants (exactly how the drift crept in), derive each constant directly from DatReaderWriter.Enums.MotionCommand by name: `= (uint)Drw.Name`. The value IS the oracle by construction, can never drift from the wire again, and ~80 magic numbers are gone. Ground truth was taken by reflecting over the same DatReaderWriter 2.1.7 assembly the runtime binds (409 enum values). Blocks 1-2 (stances + single melee, 0x3C-0x12A) were already correct and are unchanged in value. Tests: new PlanFromWireCommand_LateCombatBlock_UsesAceDrwNumbering pins the full wire -> resolve -> classify pipeline for 14 late-block ACE wire values (both full value and kind), plus a Reload SubState parity fact. Fixed two rows in ClassifyMotionCommand_RecognisesRetailCombatCommands whose literal values change meaning under DRW numbering (0x1000018E is AttackLow6/CreatureAttack, not PunchFastLow; Reload moves to 0x40000016). Core suite 2499 green. Note: CombatAnimationPlanner is not yet wired into the runtime dispatch (the live path is AnimationCommandRouter -> MotionInterpreter/AnimationSequencer), so this is a latent-correctness fix — it does not by itself change the #170 Mite Scamp symptom (whose attacks 0x62-0x64 live in the already-correct block). Ref: docs/research/2026-06-26-ace-vs-2013-motion-command-gap.md Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
parent
e4155cd962
commit
2de5a011b4
2 changed files with 191 additions and 128 deletions
|
|
@ -1,4 +1,5 @@
|
|||
using AcDream.Core.Physics;
|
||||
using Drw = DatReaderWriter.Enums.MotionCommand;
|
||||
|
||||
namespace AcDream.Core.Combat;
|
||||
|
||||
|
|
@ -200,109 +201,146 @@ public enum CombatAnimationKind
|
|||
Death,
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The full 32-bit retail MotionCommand values the planner classifies, each
|
||||
/// derived DIRECTLY from <see cref="Drw"/> (Chorizite.DatReaderWriter — the
|
||||
/// same enum ACE broadcasts and that the local DAT MotionTables key on) so
|
||||
/// the values can never drift from the wire again.
|
||||
///
|
||||
/// <para>
|
||||
/// History (ISSUES #159): these were previously hand-transcribed from the
|
||||
/// Sept 2013 EoR decomp's <c>command_ids</c> table, which numbers the whole
|
||||
/// late-combat block (<c>Offhand*</c> / <c>Attack4-6</c> / <c>Punch*</c>)
|
||||
/// THREE lower than ACE/DRW — a contiguous low-word "+3" shift that begins
|
||||
/// around <c>SnowAngelState</c> (0x115). Against a live ACE server those
|
||||
/// commands silently misclassified: the resolver returned the correct ACE
|
||||
/// value (e.g. wire 0x0173 -> <c>0x10000173</c> <c>OffhandSlashHigh</c>),
|
||||
/// but the planner's constant held the 2013 value (<c>0x10000170</c>), so no
|
||||
/// match. <c>Reload</c> was worse — hardcoded <c>0x100000D4</c>, a value
|
||||
/// absent from DRW entirely; the real ACE/DRW <c>Reload</c> is the
|
||||
/// <c>0x40000016</c> SubState. Deriving from the enum by name fixes the whole
|
||||
/// block at once and drops ~80 magic numbers.
|
||||
/// See <c>docs/research/2026-06-26-ace-vs-2013-motion-command-gap.md</c>.
|
||||
/// </para>
|
||||
/// </summary>
|
||||
internal static class CombatAnimationMotionCommands
|
||||
{
|
||||
public const uint HandCombat = 0x8000003Cu;
|
||||
public const uint SwordCombat = 0x8000003Eu;
|
||||
public const uint BowCombat = 0x8000003Fu;
|
||||
public const uint SwordShieldCombat = 0x80000040u;
|
||||
public const uint CrossbowCombat = 0x80000041u;
|
||||
public const uint SlingCombat = 0x80000043u;
|
||||
public const uint TwoHandedSwordCombat = 0x80000044u;
|
||||
public const uint TwoHandedStaffCombat = 0x80000045u;
|
||||
public const uint DualWieldCombat = 0x80000046u;
|
||||
public const uint ThrownWeaponCombat = 0x80000047u;
|
||||
public const uint Magic = 0x80000049u;
|
||||
public const uint AtlatlCombat = 0x8000013Bu;
|
||||
public const uint ThrownShieldCombat = 0x8000013Cu;
|
||||
// Combat stances (Style class 0x80). Already correct before #159.
|
||||
public const uint HandCombat = (uint)Drw.HandCombat;
|
||||
public const uint SwordCombat = (uint)Drw.SwordCombat;
|
||||
public const uint BowCombat = (uint)Drw.BowCombat;
|
||||
public const uint SwordShieldCombat = (uint)Drw.SwordShieldCombat;
|
||||
public const uint CrossbowCombat = (uint)Drw.CrossbowCombat;
|
||||
public const uint SlingCombat = (uint)Drw.SlingCombat;
|
||||
public const uint TwoHandedSwordCombat = (uint)Drw.TwoHandedSwordCombat;
|
||||
public const uint TwoHandedStaffCombat = (uint)Drw.TwoHandedStaffCombat;
|
||||
public const uint DualWieldCombat = (uint)Drw.DualWieldCombat;
|
||||
public const uint ThrownWeaponCombat = (uint)Drw.ThrownWeaponCombat;
|
||||
public const uint Magic = (uint)Drw.Magic;
|
||||
public const uint AtlatlCombat = (uint)Drw.AtlatlCombat;
|
||||
public const uint ThrownShieldCombat = (uint)Drw.ThrownShieldCombat;
|
||||
|
||||
public const uint FallDown = 0x10000050u;
|
||||
public const uint Twitch1 = 0x10000051u;
|
||||
public const uint Twitch2 = 0x10000052u;
|
||||
public const uint Twitch3 = 0x10000053u;
|
||||
public const uint Twitch4 = 0x10000054u;
|
||||
public const uint StaggerBackward = 0x10000055u;
|
||||
public const uint StaggerForward = 0x10000056u;
|
||||
public const uint Sanctuary = 0x10000057u;
|
||||
public const uint ThrustMed = 0x10000058u;
|
||||
public const uint ThrustLow = 0x10000059u;
|
||||
public const uint ThrustHigh = 0x1000005Au;
|
||||
public const uint SlashHigh = 0x1000005Bu;
|
||||
public const uint SlashMed = 0x1000005Cu;
|
||||
public const uint SlashLow = 0x1000005Du;
|
||||
public const uint BackhandHigh = 0x1000005Eu;
|
||||
public const uint BackhandMed = 0x1000005Fu;
|
||||
public const uint BackhandLow = 0x10000060u;
|
||||
public const uint Shoot = 0x10000061u;
|
||||
public const uint AttackHigh1 = 0x10000062u;
|
||||
public const uint AttackMed1 = 0x10000063u;
|
||||
public const uint AttackLow1 = 0x10000064u;
|
||||
public const uint AttackHigh2 = 0x10000065u;
|
||||
public const uint AttackMed2 = 0x10000066u;
|
||||
public const uint AttackLow2 = 0x10000067u;
|
||||
public const uint AttackHigh3 = 0x10000068u;
|
||||
public const uint AttackMed3 = 0x10000069u;
|
||||
public const uint AttackLow3 = 0x1000006Au;
|
||||
// Hit reactions / staggers (Action class 0x10, unshifted).
|
||||
public const uint FallDown = (uint)Drw.FallDown;
|
||||
public const uint Twitch1 = (uint)Drw.Twitch1;
|
||||
public const uint Twitch2 = (uint)Drw.Twitch2;
|
||||
public const uint Twitch3 = (uint)Drw.Twitch3;
|
||||
public const uint Twitch4 = (uint)Drw.Twitch4;
|
||||
public const uint StaggerBackward = (uint)Drw.StaggerBackward;
|
||||
public const uint StaggerForward = (uint)Drw.StaggerForward;
|
||||
public const uint Sanctuary = (uint)Drw.Sanctuary;
|
||||
|
||||
public const uint MissileAttack1 = 0x100000D0u;
|
||||
public const uint MissileAttack2 = 0x100000D1u;
|
||||
public const uint MissileAttack3 = 0x100000D2u;
|
||||
public const uint CastSpell = 0x400000D3u;
|
||||
public const uint Reload = 0x100000D4u;
|
||||
public const uint UseMagicStaff = 0x400000E0u;
|
||||
public const uint UseMagicWand = 0x400000E1u;
|
||||
// Single melee attacks (Action class 0x10, unshifted).
|
||||
public const uint ThrustMed = (uint)Drw.ThrustMed;
|
||||
public const uint ThrustLow = (uint)Drw.ThrustLow;
|
||||
public const uint ThrustHigh = (uint)Drw.ThrustHigh;
|
||||
public const uint SlashHigh = (uint)Drw.SlashHigh;
|
||||
public const uint SlashMed = (uint)Drw.SlashMed;
|
||||
public const uint SlashLow = (uint)Drw.SlashLow;
|
||||
public const uint BackhandHigh = (uint)Drw.BackhandHigh;
|
||||
public const uint BackhandMed = (uint)Drw.BackhandMed;
|
||||
public const uint BackhandLow = (uint)Drw.BackhandLow;
|
||||
|
||||
public const uint DoubleSlashLow = 0x1000011Fu;
|
||||
public const uint DoubleSlashMed = 0x10000120u;
|
||||
public const uint DoubleSlashHigh = 0x10000121u;
|
||||
public const uint TripleSlashLow = 0x10000122u;
|
||||
public const uint TripleSlashMed = 0x10000123u;
|
||||
public const uint TripleSlashHigh = 0x10000124u;
|
||||
public const uint DoubleThrustLow = 0x10000125u;
|
||||
public const uint DoubleThrustMed = 0x10000126u;
|
||||
public const uint DoubleThrustHigh = 0x10000127u;
|
||||
public const uint TripleThrustLow = 0x10000128u;
|
||||
public const uint TripleThrustMed = 0x10000129u;
|
||||
public const uint TripleThrustHigh = 0x1000012Au;
|
||||
// Missile attacks (Action 0x10) + reload (SubState 0x40, NOT the dead
|
||||
// 2013 0x100000D4 — the real DRW Reload is 0x40000016).
|
||||
public const uint Shoot = (uint)Drw.Shoot;
|
||||
public const uint MissileAttack1 = (uint)Drw.MissileAttack1;
|
||||
public const uint MissileAttack2 = (uint)Drw.MissileAttack2;
|
||||
public const uint MissileAttack3 = (uint)Drw.MissileAttack3;
|
||||
public const uint Reload = (uint)Drw.Reload;
|
||||
|
||||
public const uint OffhandSlashHigh = 0x10000170u;
|
||||
public const uint OffhandSlashMed = 0x10000171u;
|
||||
public const uint OffhandSlashLow = 0x10000172u;
|
||||
public const uint OffhandThrustHigh = 0x10000173u;
|
||||
public const uint OffhandThrustMed = 0x10000174u;
|
||||
public const uint OffhandThrustLow = 0x10000175u;
|
||||
public const uint OffhandDoubleSlashLow = 0x10000176u;
|
||||
public const uint OffhandDoubleSlashMed = 0x10000177u;
|
||||
public const uint OffhandDoubleSlashHigh = 0x10000178u;
|
||||
public const uint OffhandTripleSlashLow = 0x10000179u;
|
||||
public const uint OffhandTripleSlashMed = 0x1000017Au;
|
||||
public const uint OffhandTripleSlashHigh = 0x1000017Bu;
|
||||
public const uint OffhandDoubleThrustLow = 0x1000017Cu;
|
||||
public const uint OffhandDoubleThrustMed = 0x1000017Du;
|
||||
public const uint OffhandDoubleThrustHigh = 0x1000017Eu;
|
||||
public const uint OffhandTripleThrustLow = 0x1000017Fu;
|
||||
public const uint OffhandTripleThrustMed = 0x10000180u;
|
||||
public const uint OffhandTripleThrustHigh = 0x10000181u;
|
||||
public const uint OffhandKick = 0x10000182u;
|
||||
public const uint AttackHigh4 = 0x10000183u;
|
||||
public const uint AttackMed4 = 0x10000184u;
|
||||
public const uint AttackLow4 = 0x10000185u;
|
||||
public const uint AttackHigh5 = 0x10000186u;
|
||||
public const uint AttackMed5 = 0x10000187u;
|
||||
public const uint AttackLow5 = 0x10000188u;
|
||||
public const uint AttackHigh6 = 0x10000189u;
|
||||
public const uint AttackMed6 = 0x1000018Au;
|
||||
public const uint AttackLow6 = 0x1000018Bu;
|
||||
public const uint PunchFastHigh = 0x1000018Cu;
|
||||
public const uint PunchFastMed = 0x1000018Du;
|
||||
public const uint PunchFastLow = 0x1000018Eu;
|
||||
public const uint PunchSlowHigh = 0x1000018Fu;
|
||||
public const uint PunchSlowMed = 0x10000190u;
|
||||
public const uint PunchSlowLow = 0x10000191u;
|
||||
public const uint OffhandPunchFastHigh = 0x10000192u;
|
||||
public const uint OffhandPunchFastMed = 0x10000193u;
|
||||
public const uint OffhandPunchFastLow = 0x10000194u;
|
||||
public const uint OffhandPunchSlowHigh = 0x10000195u;
|
||||
public const uint OffhandPunchSlowMed = 0x10000196u;
|
||||
public const uint OffhandPunchSlowLow = 0x10000197u;
|
||||
// Creature attacks 1-6 (Action class 0x10). 1-3 are the unshifted low
|
||||
// block (0x62-0x6A); 4-6 live in the shifted late block (0x186+).
|
||||
public const uint AttackHigh1 = (uint)Drw.AttackHigh1;
|
||||
public const uint AttackMed1 = (uint)Drw.AttackMed1;
|
||||
public const uint AttackLow1 = (uint)Drw.AttackLow1;
|
||||
public const uint AttackHigh2 = (uint)Drw.AttackHigh2;
|
||||
public const uint AttackMed2 = (uint)Drw.AttackMed2;
|
||||
public const uint AttackLow2 = (uint)Drw.AttackLow2;
|
||||
public const uint AttackHigh3 = (uint)Drw.AttackHigh3;
|
||||
public const uint AttackMed3 = (uint)Drw.AttackMed3;
|
||||
public const uint AttackLow3 = (uint)Drw.AttackLow3;
|
||||
public const uint AttackHigh4 = (uint)Drw.AttackHigh4;
|
||||
public const uint AttackMed4 = (uint)Drw.AttackMed4;
|
||||
public const uint AttackLow4 = (uint)Drw.AttackLow4;
|
||||
public const uint AttackHigh5 = (uint)Drw.AttackHigh5;
|
||||
public const uint AttackMed5 = (uint)Drw.AttackMed5;
|
||||
public const uint AttackLow5 = (uint)Drw.AttackLow5;
|
||||
public const uint AttackHigh6 = (uint)Drw.AttackHigh6;
|
||||
public const uint AttackMed6 = (uint)Drw.AttackMed6;
|
||||
public const uint AttackLow6 = (uint)Drw.AttackLow6;
|
||||
|
||||
// Spell casts (SubState class 0x40).
|
||||
public const uint CastSpell = (uint)Drw.CastSpell;
|
||||
public const uint UseMagicStaff = (uint)Drw.UseMagicStaff;
|
||||
public const uint UseMagicWand = (uint)Drw.UseMagicWand;
|
||||
|
||||
// Multi-strike melee (Action class 0x10, unshifted 0x11F-0x12A).
|
||||
public const uint DoubleSlashLow = (uint)Drw.DoubleSlashLow;
|
||||
public const uint DoubleSlashMed = (uint)Drw.DoubleSlashMed;
|
||||
public const uint DoubleSlashHigh = (uint)Drw.DoubleSlashHigh;
|
||||
public const uint TripleSlashLow = (uint)Drw.TripleSlashLow;
|
||||
public const uint TripleSlashMed = (uint)Drw.TripleSlashMed;
|
||||
public const uint TripleSlashHigh = (uint)Drw.TripleSlashHigh;
|
||||
public const uint DoubleThrustLow = (uint)Drw.DoubleThrustLow;
|
||||
public const uint DoubleThrustMed = (uint)Drw.DoubleThrustMed;
|
||||
public const uint DoubleThrustHigh = (uint)Drw.DoubleThrustHigh;
|
||||
public const uint TripleThrustLow = (uint)Drw.TripleThrustLow;
|
||||
public const uint TripleThrustMed = (uint)Drw.TripleThrustMed;
|
||||
public const uint TripleThrustHigh = (uint)Drw.TripleThrustHigh;
|
||||
|
||||
// Offhand strikes (Action class 0x10, shifted late block 0x173+).
|
||||
public const uint OffhandSlashHigh = (uint)Drw.OffhandSlashHigh;
|
||||
public const uint OffhandSlashMed = (uint)Drw.OffhandSlashMed;
|
||||
public const uint OffhandSlashLow = (uint)Drw.OffhandSlashLow;
|
||||
public const uint OffhandThrustHigh = (uint)Drw.OffhandThrustHigh;
|
||||
public const uint OffhandThrustMed = (uint)Drw.OffhandThrustMed;
|
||||
public const uint OffhandThrustLow = (uint)Drw.OffhandThrustLow;
|
||||
public const uint OffhandDoubleSlashLow = (uint)Drw.OffhandDoubleSlashLow;
|
||||
public const uint OffhandDoubleSlashMed = (uint)Drw.OffhandDoubleSlashMed;
|
||||
public const uint OffhandDoubleSlashHigh = (uint)Drw.OffhandDoubleSlashHigh;
|
||||
public const uint OffhandTripleSlashLow = (uint)Drw.OffhandTripleSlashLow;
|
||||
public const uint OffhandTripleSlashMed = (uint)Drw.OffhandTripleSlashMed;
|
||||
public const uint OffhandTripleSlashHigh = (uint)Drw.OffhandTripleSlashHigh;
|
||||
public const uint OffhandDoubleThrustLow = (uint)Drw.OffhandDoubleThrustLow;
|
||||
public const uint OffhandDoubleThrustMed = (uint)Drw.OffhandDoubleThrustMed;
|
||||
public const uint OffhandDoubleThrustHigh = (uint)Drw.OffhandDoubleThrustHigh;
|
||||
public const uint OffhandTripleThrustLow = (uint)Drw.OffhandTripleThrustLow;
|
||||
public const uint OffhandTripleThrustMed = (uint)Drw.OffhandTripleThrustMed;
|
||||
public const uint OffhandTripleThrustHigh = (uint)Drw.OffhandTripleThrustHigh;
|
||||
public const uint OffhandKick = (uint)Drw.OffhandKick;
|
||||
|
||||
// Punches (Action class 0x10, shifted late block).
|
||||
public const uint PunchFastHigh = (uint)Drw.PunchFastHigh;
|
||||
public const uint PunchFastMed = (uint)Drw.PunchFastMed;
|
||||
public const uint PunchFastLow = (uint)Drw.PunchFastLow;
|
||||
public const uint PunchSlowHigh = (uint)Drw.PunchSlowHigh;
|
||||
public const uint PunchSlowMed = (uint)Drw.PunchSlowMed;
|
||||
public const uint PunchSlowLow = (uint)Drw.PunchSlowLow;
|
||||
public const uint OffhandPunchFastHigh = (uint)Drw.OffhandPunchFastHigh;
|
||||
public const uint OffhandPunchFastMed = (uint)Drw.OffhandPunchFastMed;
|
||||
public const uint OffhandPunchFastLow = (uint)Drw.OffhandPunchFastLow;
|
||||
public const uint OffhandPunchSlowHigh = (uint)Drw.OffhandPunchSlowHigh;
|
||||
public const uint OffhandPunchSlowMed = (uint)Drw.OffhandPunchSlowMed;
|
||||
public const uint OffhandPunchSlowLow = (uint)Drw.OffhandPunchSlowLow;
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue