From 1c69670392d5e2f3b1f257dd7716559208515b20 Mon Sep 17 00:00:00 2001 From: Erik Date: Tue, 28 Apr 2026 10:38:58 +0200 Subject: [PATCH] docs(anim): Phase L.1a animation system audit --- docs/plans/2026-04-11-roadmap.md | 43 +++ docs/plans/animation-system-audit.md | 557 +++++++++++++++++++++++++++ 2 files changed, 600 insertions(+) create mode 100644 docs/plans/animation-system-audit.md diff --git a/docs/plans/2026-04-11-roadmap.md b/docs/plans/2026-04-11-roadmap.md index 9435f82..ecff132 100644 --- a/docs/plans/2026-04-11-roadmap.md +++ b/docs/plans/2026-04-11-roadmap.md @@ -305,6 +305,49 @@ with retail's MMB-hold mouse-look. --- +### Phase L.1 — Animation system completion + +**Status:** IN PROGRESS on `feature/animation-system-complete`. + +**Goal:** complete the retail-faithful animation surface beyond the +locomotion/jump K-fix series: combat swings, spell casting, emotes, death, +item-use, NPC/monster special actions, remote observer parity, and the +remaining floating-point polish around style transitions, modifiers, action +queues, speed scaling, and PosFrame root motion. + +**Plan of record:** `docs/plans/animation-system-audit.md`. + +**Sub-pieces:** +- **L.1a — Audit & inventory.** Map retail named-decomp evidence, ACE + cross-references, existing acdream hook points, and current gaps for each + animation category. Output: `docs/plans/animation-system-audit.md`. +- **L.1b — Command router + motion-state cleanup.** Extract tested + `SetCycle` vs `PlayAction` routing, add missing `MotionCommand` constants, + and split death `Sanctuary` action from persistent `Dead` substate. +- **L.1c — Combat animation wiring.** Combat mode tracking, draw/sheath + style transitions, attack swings by stance/power/height, hit reactions, + evades/blocks/parries, and death handoff. +- **L.1d — Spell casting wiring.** Cast command classification, windup, + release, fizzle/interruption, recoil, and school/effect distinctions. +- **L.1e — Emotes + postures.** Outbound slash emotes, inbound + command-list emotes, and persistent sit/lie/kneel/sleep states. +- **L.1f — NPC/monster + item-use coverage.** Scripted gestures, monster + special actions, potion/food/scroll/recall cycles, and remote parity. +- **L.1g — Polish + conformance.** Style-transition chain, durable + modifiers/action queues, root-motion handling, speed scaling, and broad + synthetic MotionTable tests. + +**Acceptance:** +- `dotnet build` and `dotnet test` green at each commit. +- Test count grows by at least 30 with one representative cycle/action test + per major animation category. +- Every AC-specific behavior cites named retail decomp or ACE/holtburger + cross-reference evidence in code comments, tests, or commit notes. +- User visual sign-off for local and remote attack, spell, emote, death, and + item-use animation parity before marking shipped. + +--- + ### Phase J — Long-tail (deferred / low-priority) Not detailed here; each gets its own brainstorm when it becomes relevant. diff --git a/docs/plans/animation-system-audit.md b/docs/plans/animation-system-audit.md new file mode 100644 index 0000000..5d6cc54 --- /dev/null +++ b/docs/plans/animation-system-audit.md @@ -0,0 +1,557 @@ +# Animation System Audit + +Phase A audit for `feature/animation-system-complete`. + +Date: 2026-04-28. + +## Summary + +The animation core is much stronger than the feature surface around it. +`AnimationSequencer` already handles cyclic state changes, transition links, +negative-speed retail remaps, mid-cycle speed changes, frame hooks, and +PosFrame root-motion accumulation. `GameWindow.OnLiveMotionUpdated` already +routes `InterpretedMotionState.Commands[]` through `PlayAction`, which means +server-broadcast NPC/monster/emote/action overlays are likely to animate when +the server emits motion commands. + +The remaining gap is mostly orchestration: + +- local combat/spell/item-use commands build wire packets but do not yet drive + the local visible action immediately; +- several combat/spell/emote packet surfaces need conformance fixes before + animation triggers can be trusted: combat mode enum values, split + melee/missile attack builders, `CombatCommenceAttack`, `AttackDone`, + damage/death notification parsers, `MagicSchool` enum order, and outbound + emote/soul-emote builders; +- combat/spell/item-use game events populate state/chat but do not yet map to + animation overlays for attacker/defender/caster; +- style changes are handled as simple `SetCycle(style, motion)` swaps, not the + full ACE `MotionTable.GetObjectSequence` multi-link style transition chain; +- held posture/emote commands need a small command resolver and tests around + one-shot-vs-persistent routing; +- death needs explicit `Sanctuary` action -> `Dead/Fallen` persistence rather + than relying on chat/health side effects. + +## Evidence Sources + +Named retail decomp: + +- `CMotionTable::is_allowed` at `0x005226C0` +- `CMotionTable::get_link` at `0x00522710` +- `CSequence::update_internal` at `0x005255D0` +- `CMotionInterp::adjust_motion` at `0x00528010` +- `CMotionInterp::charge_jump` at `0x005281C0` +- `CMotionInterp::get_jump_v_z` at `0x00527AA0` +- `CMotionInterp::jump` at `0x00528780` +- `CMotionInterp::apply_current_movement` at `0x00528870` +- `CMotionInterp::HitGround` at `0x00528AC0` +- `CMotionInterp::LeaveGround` at `0x00528B00` +- `CMotionInterp::DoMotion` at `0x00528D20` +- `CMotionInterp::DoInterpretedMotion` at `0x00528360` +- `ClientCombatSystem::HandleCommenceAttackEvent` at `0x0056AD20` +- `ClientCombatSystem::SetCombatMode` at `0x0056BE30` +- `ClientCombatSystem::StartAttackRequest` at `0x0056C040` +- `ClientCombatSystem::EndAttackRequest` at `0x0056C0E0` +- `ClientCombatSystem::StartPowerBarBuild` at `0x0056ADB0` +- `ClientCombatSystem::GetPowerBarLevel` at `0x0056ADE0` +- `ClientCombatSystem::ExecuteAttack` at `0x0056BB70` +- `ClientCombatSystem::HandleDefenderNotificationEvent` at `0x0056C920` +- `ClientCombatSystem::HandleEvasionDefenderNotificationEvent` at `0x0056C620` +- `ClientCombatSystem::HandlePlayerDeathEvent` at `0x0056C320` +- `ClientCombatSystem::HandleAttackerNotificationEvent` at `0x0056B420` +- `ClientCombatSystem::HandleAttackDoneEvent` at `0x0056C500` +- `CM_Combat::Event_ChangeCombatMode` at `0x006A9A70` +- `CM_Combat::Event_TargetedMeleeAttack` at `0x006A9C10` +- `CM_Combat::Event_TargetedMissileAttack` at `0x006A9D60` +- `AttackHook::Execute` at `0x00526B70` +- `gmSpellcastingUI::Cast` at `0x004C6050` +- `ClientMagicSystem::CastSpell` at `0x00568040` +- `ClientMagicSystem::FreeHandsAndCastSpell` at `0x00566EF0` +- `ClientMagicSystem::GetAppropriateSpellFormula` at `0x00567D50` +- `CM_Magic::Event_CastUntargetedSpell` at `0x006A3150` +- `CM_Magic::Event_CastTargetedSpell` at `0x006A3040` +- `ItemHolder::UseObject` at `0x00588A80` +- `CM_Inventory::Event_UseEvent` at `0x006AC3B0` +- `CM_Inventory::Event_UseWithTargetEvent` at `0x006AC480` +- `CM_Item::DispatchUI_UseDone` at `0x006A8510` +- `CommandInterpreter::PlayerIsDead` at `0x006B3D70` +- `SmartBox::HandlePlayScriptID` at `0x00452020` +- `CM_Physics::DispatchSB_PlayScriptID` at `0x006ACC40` +- `CM_Physics::DispatchSB_PlayScriptType` at `0x006AC6E0` +- `ClientCommunicationSystem::DoEmote` at `0x00578AD0` +- `ClientCommunicationSystem::Pose` at `0x00580480` +- `ClientCommunicationSystem::Handle_Communication__HearEmote` at + `0x0057CBE0` +- `ClientCommunicationSystem::Handle_Communication__HearSoulEmote` at + `0x0057D020` +- `ChatPoseTable::InqChatPoseCommand` at `0x00570AD0` +- `ChatEmoteData::Pack` at `0x004FCE80` + +Cross-reference material: + +- `docs/research/deepdives/r03-motion-animation.md` +- `C:\Users\erikn\source\repos\acdream\references\ACE\Source\ACE.Server\Physics\Animation\MotionTable.cs` +- `C:\Users\erikn\source\repos\acdream\references\ACE\Source\ACE.Server\Physics\Animation\MotionInterp.cs` +- `C:\Users\erikn\source\repos\acdream\references\ACE\Source\ACE.Entity\Enum\MotionCommand.cs` +- `C:\Users\erikn\source\repos\acdream\references\holtburger\apps\holtburger-cli\src\pages\game\combat.rs` +- `C:\Users\erikn\source\repos\acdream\references\holtburger\crates\holtburger-core\src\client\messages.rs` +- `C:\Users\erikn\source\repos\acdream\references\holtburger\crates\holtburger-protocol\src\messages\movement\types.rs` + +The clean worktree intentionally does not contain `references/`; it was read +read-only from the original checkout path above. + +## Current Code Surface + +Core animation: + +- `src/AcDream.Core/Physics/AnimationSequencer.cs` + - `SetCycle(style, motion, speedMod, skipTransitionLink)` handles cyclic + state changes and transition links. + - `PlayAction(motionCommand, speedMod)` handles Action, Modifier, and + ChatEmote one-shots through Links/Modifiers lookup. + - `Advance(dt)` emits pending hooks and accumulates PosFrame deltas. + - Missing: full style-transition chain, durable modifier list, action queue + accounting, and a public command-resolution facade that callers can test + without `GameWindow`. +- `src/AcDream.Core/Physics/MotionInterpreter.cs` + - Handles locomotion, jump, leave-ground/hit-ground, and basic contact + guards. + - Missing: full retail `MotionState`, action list, modifier list, hold-key + run application, combat-state guards, and `move_to_interpreted_state`. +- `src/AcDream.Core/Physics/MotionCommandResolver.cs` + - Reconstructs full 32-bit commands from 16-bit wire values. + +App integration: + +- `src/AcDream.App/Input/PlayerMovementController.cs` + - Local walk/run/strafe/turn/jump driver. It does not own combat/spell/item + action animation. +- `src/AcDream.App/Rendering/GameWindow.cs` + - `OnLiveMotionUpdated` is the main inbound motion/action router. + - `OnLiveVectorUpdated` seeds airborne jump arcs and Falling cycles. + - `OnLivePositionUpdated` snaps positions and lands airborne remotes. + - `TickAnimations` advances sequencers and drains hooks. + - `UpdatePlayerAnimation` drives the local movement cycle. + - Missing: typed animation coordinator for combat/spell/use/death/emote + events; too much command mapping still lives inline. + +Wire/state: + +- `src/AcDream.Core.Net/Messages/AttackTargetRequest.cs`: outbound attack + request exists, but currently combines melee and missile into one layout; + retail/ACE/holtburger use distinct `0x0008` melee and `0x000A` missile + payloads. +- `src/AcDream.Core.Net/Messages/CastSpellRequest.cs`: outbound spell request + exists. +- `src/AcDream.Core.Net/Messages/CharacterActions.cs`: combat mode request + exists, but the combat-mode enum must be corrected to retail values + `NonCombat=1`, `Melee=2`, `Missile=4`, `Magic=8`. +- `src/AcDream.Core.Net/Messages/InteractRequests.cs`: use/use-with-target + request exists. +- `src/AcDream.Core.Net/GameEventWiring.cs`: combat, spell, item, chat events + route into state classes. +- Missing: public `WorldSession.SendAttack/SendCast/SendUse/ChangeCombatMode` + wrappers and animation-side subscriptions. + +## Retail Command Catalogue To Use + +From ACE `MotionCommand.cs` + `r03-motion-animation.md`: + +- Locomotion substates: Ready `0x41000003`, WalkForward `0x45000005`, + WalkBackward `0x45000006`, RunForward `0x44000007`, TurnRight + `0x6500000D`, TurnLeft `0x6500000E`, SideStepRight `0x6500000F`, + SideStepLeft `0x65000010`, Falling `0x40000015`. +- Held/posture substates: Crouch `0x41000012`, Sitting `0x41000013`, + Sleeping `0x41000014`, Dead `0x40000011`, Fallen `0x40000008`. +- Item/use substates: Reload `0x40000016`, Unload `0x40000017`, Pickup + `0x40000018`, StoreInBackpack `0x40000019`, Eat `0x4000001A`, Drink + `0x4000001B`, Reading `0x4000001C`. +- Spell substates/actions: CastSpell `0x400000D3`, MagicBlast + `0x4000002B`, MagicSelfHead `0x4000002C`, MagicSelfHeart `0x4000002D`, + MagicBonus..MagicPenalty `0x4000002E..0x40000034`, MagicTransfer + `0x40000035`, MagicEnchantItem `0x40000037`, MagicPortal `0x40000038`, + MagicPray `0x40000039`, MagicPowerUp01..10 `0x1000006F..0x10000078`, + MagicPowerUp01Purple..10Purple `0x1000012B..0x10000134`. +- Combat actions: Sanctuary `0x10000057`, ThrustMed/Low/High + `0x10000058..0x1000005A`, SlashHigh/Med/Low `0x1000005B..0x1000005D`, + BackhandHigh/Med/Low `0x1000005E..0x10000060`, Shoot `0x10000061`, + AttackHigh/Med/Low1..6 `0x10000062..0x1000006A` and + `0x10000186..0x1000018E`, MissileAttack1..3 `0x100000D0..0x100000D2`, + SpecialAttack1..3 `0x100000CD..0x100000CF`, dual-wield/offhand ranges + `0x10000173..0x1000019A`. +- ChatEmote actions: Wave `0x13000087`, BowDeep `0x1300007D`, Laugh + `0x13000080`, Point `0x13000084`, Salute `0x1300008A`, Kneel + `0x13000092`, HaveASeat `0x13000152`, DrudgeDance `0x13000151`, plus + the full `0x1200/0x1300` ranges in `r03`. +- Persistent emote states: `0x430000EA..0x430000FD`, SnowAngelState + `0x43000118`, CurtseyState `0x4300011A`, AFKState `0x4300011B`, + MeditateState `0x4300011C`, SitState `0x4300013A`, + SitCrossleggedState `0x4300013B`, SitBackState `0x4300013C`, + PossumState `0x43000142`, HaveASeatState `0x43000145`. ACE's enum is a + useful alias catalog but has a shifted range for some late chat-emote states; + named-retail values win when hard-coding constants. + +## Category Audit + +### 1. Own Player Movement + +Status: mostly working. + +Evidence: retail `CMotionInterp` jump/grounding symbols listed above; ACE +`MotionInterp.cs` for `adjust_motion`, `apply_current_movement`, `HitGround`, +and `LeaveGround`. + +acdream locations: `PlayerMovementController`, `MotionInterpreter`, +`UpdatePlayerAnimation`, `OnLiveVectorUpdated`, `OnLivePositionUpdated`. + +Gaps: + +- held postures exist as retail commands but are not driven by a general + posture/action API; +- `MotionInterpreter` does not yet own full `MotionState`, so non-locomotion + commands cannot be uniformly tested there; +- mounted/swimming need dat/retail verification before any implementation. + +Tests to add: + +- posture state `SetCycle` tests for Crouch/Sitting/Sleeping; +- `motion_allows_jump` conformance for item/spell/aim/posture ranges; +- local action does not stomp Falling while airborne. + +### 2. Other Players' Movement + +Status: partially working after the K-fix series. + +Evidence: `UpdateMotion` handling in `OnLiveMotionUpdated`; retail +`CMotionInterp::DoInterpretedMotion` and `apply_current_movement`; ACE +`MotionInterp.apply_current_movement`. + +acdream locations: `RemoteMotion`, `OnLiveMotionUpdated`, +`OnLiveVectorUpdated`, `OnLivePositionUpdated`, `TickAnimations`. + +Gaps: + +- remote action overlays only happen when the server includes + `InterpretedMotionState.Commands[]`; combat/spell game events do not yet + synthesize overlays when the wire omits motion commands; +- no test fixture exercises `OnLiveMotionUpdated` command-list routing outside + `GameWindow`; +- root-motion deltas are accumulated but not applied to remote body transforms. + +Tests to add: + +- command-list `Wave` -> `PlayAction` routing through a new coordinator; +- airborne remote ignores mid-arc locomotion cycle swaps but still updates + interpreted movement; +- landing swaps Falling back to current interpreted command. + +### 3. NPC Movement + +Status: likely works for UpdateMotion-driven locomotion and simple gestures; +not verified. + +Evidence: retail MotionTable/InterpretedMotionState path; ACE +`MotionTable.GetObjectSequence` and `MotionInterp.move_to_interpreted_state`. + +acdream locations: `CreateObject.ParseServerMotionState`, +`OnLiveMotionUpdated`, `TickAnimations`. + +Gaps: + +- no NPC-specific live test checklist; +- no retained action/modifier list, so repeated scripted gestures are + fire-and-forget overlays only; +- no head-look/threat-pose state beyond whatever arrives as motion commands. + +Tests to add: + +- synthetic NPC `UpdateMotion` with `Commands=[Wave, Ready]` plays one-shot + then returns to Ready; +- style-default fallback for creature motion tables. + +### 4. Monster Movement + +Status: locomotion probably works when `MotionTableId` and UpdateMotion are +present; special attacks are unknown. + +Evidence: ACE MotionTable supports monster actions such as HeadThrow, +FistSlam, BreatheFlame, SpinAttack, Bite, SpecialAttack1..3. + +acdream locations: same as NPC movement; `AnimationHookRouter` for VFX/audio +side effects. + +Gaps: + +- attack action overlays for monsters depend on server motion command lists; +- no mapping from combat events to visible monster attack/hit reactions; +- no exotic creature spot-checks. + +Tests to add: + +- `PlayAction(BreatheFlame)` resolves from Links/Modifiers when synthetic data + provides it; +- Attack hooks fire exactly once for a synthetic monster action. + +### 5. Combat Actions + +Status: wire codecs and combat state exist; visual action orchestration is +missing for local and event-driven paths. + +Evidence: + +- retail `ClientCombatSystem::StartPowerBarBuild`, + `ClientCombatSystem::GetPowerBarLevel`, `ClientCombatSystem::ExecuteAttack`, + `HandleCommenceAttackEvent`, `HandleAttackerNotificationEvent`, + `HandleAttackDoneEvent`; +- ACE `MotionTable.GetAttackFrames` scans Attack hooks and is the canonical + hit-frame source; +- holtburger combat UI tracks `AttackCommenced`, `AttackDone`, victim, + attacker, defender, evasion, and killed feedback as runtime state. + +acdream locations: + +- `AttackTargetRequest` exists but no `WorldSession.SendAttack` wrapper was + found; +- `CombatState` emits `DamageTaken`, `DamageDealtAccepted`, evasion, + `AttackDone`, and `KillLanded`; +- `GameEventWiring` registers combat event parsers; +- `AnimationSequencer.PlayAction` can play the swing once the command is known. + +Gaps: + +- combat-mode enum values are currently non-retail for missile/magic; +- melee/missile attack request builders need to be split to retail layouts: + `0x0008 targetGuid, attackHeight, power` and + `0x000A targetGuid, attackHeight, accuracy`; +- `CombatCommenceAttack (0x01B8)` is enumerated but not parsed/wired; +- `AttackDone (0x01A7)` and attacker/defender/death notification parsers need + ACE/holtburger fixtures before downstream animation can trust them; +- `CombatState` has no `CurrentMode`, no attack sequence active flag, no + selected target, and no power-bar state; +- no local predictive swing on attack request; +- hit reactions (Twitch/Stagger/Tipped/FallDown) are not mapped from defender + notifications; +- style changes for draw/sheath do not run the full style-transition chain. + +Tests to add: + +- parse/wire `CombatCommenceAttack`; +- `CombatAnimationCoordinator` maps height/power/style to attack command; +- defender hit quadrant maps to a stable flinch command; +- `AttackHook` dispatch is one-shot. + +### 6. Spell Casting + +Status: outbound cast packets and spellbook/enchantment state exist; visible +cast-stage animation is missing. + +Evidence: + +- retail `ClientMagicSystem::CastSpell` and `FreeHandsAndCastSpell`; +- `gmSpellcastingUI::Cast` calls `ClientMagicSystem::CastSpell`; +- outbound cast actions are `0x0048` untargeted (`spellId`) and `0x004A` + targeted (`targetGuid`, `spellId`); +- retail/ACE school order is `War=1`, `Life=2`, `Item=3`, `Creature=4`, + `Void=5`; +- MotionCommand spell catalogue above; +- `GameEventWiring` wires spellbook/enchantment updates but not casting + animation. + +acdream locations: + +- `CastSpellRequest` targeted/untargeted builders; +- `Spellbook`, `SpellTable`, `GameEventWiring` spell handlers; +- `AnimationHookRouter` already routes hooks to audio/VFX sinks. + +Gaps: + +- no cast coordinator reading server `UpdateMotion`, spellcasting chat, + `PlayScript.Fizzle`, `UseDone`, and errors into one local cast timeline; +- no fizzle/interruption animation mapping from `PlayScript.Fizzle = 0x51` + (ACE sends speed `0.5`) and `WeenieError`; +- no recoil/release state; +- no local immediate cast animation on request. +- `MagicSchool` enum currently needs conformance against the retail/ACE order. + +Tests to add: + +- spell school/effect classifier maps to MagicBlast/MagicSelf/MagicPortal; +- fizzle error maps to a one-shot action or recovery state once retail + command is confirmed; +- cast request triggers local action overlay without waiting for enchantment. + +### 7. Emotes + +Status: inbound text parsers and chat display exist; motion command-list +emotes likely animate if server emits them. Slash-command-to-emote wire and +text-event-to-animation are missing. + +Evidence: + +- retail `ClientCommunicationSystem::DoEmote`, `HelpEmote`, + `DoEmoteList`, `InitializeEmoteInputActionHash`; +- retail `ClientCommunicationSystem::Pose` looks up a token in + `ChatPoseTable`, issues the motion command locally, then sends SoulEmote; +- `ChatEmoteData::Pack`; +- ACE MotionCommand ChatEmote range. + +acdream locations: + +- `EmoteText` and `SoulEmote` top-level parsers; +- `ChatLog.OnEmote` / `OnSoulEmote`; +- `GameWindow.OnLiveMotionUpdated` command-list `PlayAction` route. + +Gaps: + +- no outbound `Communication_Emote (0x01DF)` or + `Communication_SoulEmote (0x01E1)` GameAction builder found; +- `MoveToState` currently writes zero command-list entries, so the client + cannot yet send pose/emote commands in the retail motion-state path; +- `ChatInputParser` has no `/em`, `/emote`, `/me`, `/sit`, `/kneel`, + `/sleep`, or `/lie` parsing; +- `EmoteText`/`SoulEmote` text events do not carry an emote id, so they + should not be used as the primary animation source unless retail proves a + deterministic text -> command mapping; +- held postures need `SetCycle`, not `PlayAction`. + +Tests to add: + +- `MotionCommandResolver` reconstructs representative ChatEmotes; +- command-list Wave routes to `PlayAction`; +- persistent Sit/Meditate routes to `SetCycle`. + +### 8. Death Animations + +Status: death chat and killer notifications exist; pose transition is missing. + +Evidence: + +- retail `CommandInterpreter::PlayerIsDead` checks forward command + `0x40000011`; +- MotionCommand `Sanctuary = 0x10000057` is an action and must not be used as + the persistent death state; +- MotionCommand `Dead = 0x40000011` and `Fallen = 0x40000008` are persistent + states; +- `PlayerKilled` top-level message and `KillerNotification (0x01AD)` are + parsed/wired. + +acdream locations: + +- `PlayerKilled`, `ChatLog.OnPlayerKilled`; +- `CombatState.OnKillerNotification`; +- `MotionCommand.Dead` currently incorrectly comments `0x10000057` in + `MotionInterpreter`; this should be split into `Sanctuary` action and + `Dead` substate before death work. + +Gaps: + +- no explicit death animation coordinator; +- no hit-direction-aware fall; +- no dead-pose persistence or respawn reset. + +Tests to add: + +- death event plays Sanctuary then persists Dead/Fallen; +- movement is blocked while Dead/Fallen; +- respawn/reset returns to Ready. + +### 9. Item-Use Animations + +Status: outbound use builders exist; local visible use animations are missing. + +Evidence: + +- retail `ItemHolder::UseObject`; +- MotionCommand item/use states: Pickup, StoreInBackpack, Eat, Drink, + Reading, HouseRecall, LifestoneRecall. + +acdream locations: + +- `InteractRequests.BuildUse` / `BuildUseWithTarget`; +- `ItemRepository`, appraise/use-done event enum. + +Gaps: + +- no `WorldSession.SendUse` wrapper found; +- `UseDone (0x01C7)` is enumerated but not parsed/wired; +- `0xF754 PlayScriptId` is wired, but target anchoring and speed handling need + audit; `0xF755 PlayScriptType` is not wired; +- no item-class-to-motion mapping. + +Tests to add: + +- potion use maps to Drink; +- food maps to Eat; +- scroll/book maps to Reading; +- recall spell/item maps to recall command once retail source is confirmed. + +### 10. Mounting / Dismounting + +Status: not implemented; likely not relevant to retail AC character movement. + +Evidence: `r03` lists `Graze` as a monster-only/mount-like stance, but no +player mount feature has been verified in retail references in this audit. + +Action: defer until a server/content feature requires it. Do not invent +mounting behavior. + +### 11. Floating-Point / Polish + +Status: partially implemented. + +Evidence: + +- `AnimationSequencer.MultiplyCyclicFramerate` exists and is tested; +- `LocalAnimationSpeed` exists in `MovementResult`; +- PosFrame deltas are accumulated in `AnimationSequencer`. + +Gaps: + +- root-motion deltas are not composed into entity/body transforms; +- remote animation speed scaling is tied to ForwardSpeed/SidestepSpeed/TurnSpeed + only when UpdateMotion carries them; +- style-transition and modifier physics combination are incomplete. + +Tests to add: + +- same-motion/different-speed rescale remains green; +- root-motion delta is consumed by an integration coordinator; +- modifiers combine velocity/omega instead of replacing base cycle physics. + +## Implementation Order + +1. Extract an `AnimationCommandRouter` in Core/App-adjacent code that owns + `SetCycle` vs `PlayAction` routing for full 32-bit commands. Move the + command-list logic out of `GameWindow.OnLiveMotionUpdated` into tests. +2. Add missing MotionCommand constants and fix the `Dead`/`Sanctuary` + distinction. +3. Fix combat wire conformance first: combat-mode enum, split attack builders, + `CombatCommenceAttack`, `AttackDone`, damage/evasion/death notification + parsers, and fixtures from ACE/holtburger. +4. Wire `CombatState.CurrentMode` and `WorldSession.SendAttack/ChangeCombatMode`; + trigger local and remote swing overlays through the router. +5. Add spell-cast event/state wiring: `WorldSession.SendCast`, school enum + conformance, `UpdateMotion` cast actions, spellcasting chat, + `PlayScript.Fizzle`, `UseDone`, and errors. +6. Add outbound emote/soul-emote builders, MoveToState command-list emission, + chat parser aliases, and posture routing. +7. Add item-use wrappers, `UseDone`, script target anchoring, and + item-class-to-motion mapping. +8. Add death coordinator and respawn reset. +9. Port full ACE style-transition/modifier/action queue semantics into a + `MotionTableWalker` or equivalent, replacing `SetCycle` special cases only + after the category tests cover current behavior. +10. Apply/consume root motion where retail expects it; leave purely decorative + PosFrames un-applied when decomp/ACE proves they should not move the body. + +## Visual Sign-Off Points + +The agent can build, test, and live-launch autonomously, but these require +user visual confirmation before claiming complete: + +- local attack swing + defender flinch; +- local spell windup -> release/fizzle; +- local `/wave` and persistent sit/lie/kneel/sleep; +- local death pose and respawn recovery; +- potion drink/eat/read animations; +- remote observer view for all of the above.