Merge feature/animation-system-complete — Phase L.1c animation MVP
21 commits porting retail's MoveToManager-equivalent client-side behavior for server-controlled creature locomotion and combat engagement. Shipped as MVP after live visual verification across multiple iteration rounds with the user. Highlights: -186a584— initial Phase L.1c port: extracts Origin / target guid / MovementParameters block from MoveTo packets (movementType 6/7), adds RemoteMoveToDriver per-tick body-orientation steering with ±20° aux-turn-equivalent snap tolerance. -d247aef— corrected arrival predicate semantics + 1.5 s stale-destination timeout for entities leaving the streaming view. -f794832— root-caused "creature won't stop to attack" via two research subagents converging on retail CMotionInterp::move_to_interpreted_state's unconditional forward_command bulk-copy. Lifted ServerMoveToActive flag clearing + InterpretedState bulk-copy out of substate-only branch so Action-class swing UMs (mt=0 ForwardCommand=AttackHigh1) clear stale MoveTo state and zero forward velocity. -ff6d3d0— RemoteMoveToDriver.ClampApproachVelocity caps horizontal velocity at the final-approach tick so body lands EXACTLY at DistanceToObject instead of overshooting through the player. -37de771— bulk-copy ForwardCommand for MoveTo packets too (closed the regression where MoveTo creatures stayed at default ForwardCommand=Ready in InterpretedState and only translated via UpdatePosition snaps). -34d7f4d+e71ed73— AnimationSequencer.HasCycle query + fallback chain (requested → WalkForward → Ready → no-op) at BOTH the OnLiveMotionUpdated path AND the spawn handler. Prevents ClearCyclicTail from wiping the body's cyclic tail when ACE CreateObject carries CurrentMotionState.ForwardCommand pointing to an Action-class motion (e.g. AttackHigh1 from a mid-swing creature) which has no cyclic-table entry — was the "torso on the ground" symptom for monsters seen in combat by a fresh observer. Cross-references: docs/research/named-retail/acclient_2013_pseudo_c.txt (MoveToManager 0x00529680 + 0x0052a240 + 0x00529d80, CMotionInterp::move_to_interpreted_state 0x00528xxx, MovementParameters::UnPackNet 0x0052ac50), references/ACE/Source/ ACE.Server/Physics/Animation/MoveToManager.cs (port aid), references/holtburger/ (cross-check on snapshot-only client behavior), docs/research/2026-04-28-remote-moveto-pseudocode.md (the Phase L.1c pseudocode doc). Tests: 1404 → 1422 (parser type-7 path retention, type-6 target guid retention, driver arrival semantics, retail-faithful chase/flee branches, approach-velocity clamp scenarios, HasCycle present/missing, AttackHigh1 wire layout). Pending follow-ups (filed for future): target-guid live resolution for type 6 packets (residual chase lag), StickToObject sticky-target guid trailing field, full MoveToManager state machine port (CheckProgressMade stall detector, Sticky/StickTo, use_final_heading, pending_actions queue). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
commit
b93dfe95d8
44 changed files with 4580 additions and 301 deletions
|
|
@ -306,6 +306,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.
|
||||
|
|
|
|||
557
docs/plans/animation-system-audit.md
Normal file
557
docs/plans/animation-system-audit.md
Normal file
|
|
@ -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.
|
||||
Loading…
Add table
Add a link
Reference in a new issue