feat(anim): motion-action-queue infrastructure + retail jump-is-physics-only note
Adds AnimationSequencer.PlayAction as the proper path for Action and
Modifier-class motions (the MotionTable.Modifiers dict, distinct from
Cycles). Action nodes are inserted before the looping cyclic tail so
they drain once and the cycle resumes naturally — leveraging the
sequencer's existing "non-looping head drains, cyclic tail wraps"
queue semantics.
What this does:
- New AnimationSequencer.PlayAction(motionCommand, speedMod=1f):
- Resolves (style<<16) | (motion&0xFFFFFF) from MotionTable.Modifiers
- Falls back to (motion&0xFFFFFF) plain key
- Silent no-op when not found (some motion tables lack these)
- Inserts AnimNodes before _firstCyclic; re-points the cursor when on
the cyclic tail so the action plays immediately
- New MotionCommand.Jump (0x2500003B) + MotionCommand.FallDown (0x10000050)
constants.
- GameWindow.UpdatePlayerAnimation fires PlayAction(Jump) on
result.JumpExtent.HasValue and PlayAction(FallDown) on JustLanded.
Key research finding: retail does NOT animate jumps.
- ACE Player.HandleActionJump explicitly clears PendingMotions and sets
IsAnimating=false during a jump (Player.cs:914-915).
- Empirical verification: the player humanoid's MotionTable only has 8
Modifier entries — all TurnRight/SideStepRight stance variants. No
Jump (0x2500003B) or FallDown (0x10000050) entries.
- Jump is a physics-only action: the character keeps whatever cycle
was active (walk/run/idle) while the physics body arcs through the
air. There is no "raise arms to jump" pose in retail.
PlayAction is still called on jump/land as a safety hatch for creature
Setups that DO carry leap animations in their Modifiers dict (drudge
jumps, monster pounces, etc.). For player humanoids it's a no-op. The
infrastructure is also ready for future emote/combat actions that
legitimately use the Modifiers dict.
470 tests pass, build clean.
This commit is contained in:
parent
3308cddda7
commit
08ea2c0af8
3 changed files with 128 additions and 0 deletions
|
|
@ -3024,11 +3024,46 @@ public sealed class GameWindow : IDisposable
|
|||
/// to match the current motion command. Only re-resolves when the command
|
||||
/// actually changes (forward → run, idle → walk, etc.) to avoid re-building
|
||||
/// the animation entry every frame.
|
||||
///
|
||||
/// <para>
|
||||
/// Action motions (Jump, FallDown, emotes, attacks) are routed through
|
||||
/// <see cref="AcDream.Core.Physics.AnimationSequencer.PlayAction"/> — they
|
||||
/// live in the motion table's Modifiers dict, not the Cycles dict, and
|
||||
/// are inserted into the queue on top of the current cycle instead of
|
||||
/// replacing it.
|
||||
/// </para>
|
||||
/// </summary>
|
||||
private void UpdatePlayerAnimation(AcDream.App.Input.MovementResult result)
|
||||
{
|
||||
if (_dats is null) return;
|
||||
|
||||
// ── Action-motion events (jump / land) ─────────────────────────────
|
||||
//
|
||||
// Retail does NOT animate jumps — confirmed via ACE's HandleActionJump
|
||||
// (Player.cs:914-915) which explicitly clears PendingMotions and
|
||||
// sets IsAnimating=false during the jump. The character keeps
|
||||
// whatever cycle it was on and the physics body arcs through the air.
|
||||
// Humanoid Setup MotionTables have NO entry for Jump (0x2500003B)
|
||||
// or FallDown (0x10000050) in the Modifiers dict — verified empirically
|
||||
// (only 8 TurnRight stance-variants + SideStepRight).
|
||||
//
|
||||
// We still call PlayAction here as a no-op safety hatch: if a future
|
||||
// Setup / creature DOES carry a jump/fall modifier in its MotionTable
|
||||
// (e.g. a leaping-monster) the sequencer will pick it up for free.
|
||||
// For player humanoids, the lookup silently misses and nothing changes.
|
||||
if (result.JumpExtent.HasValue || result.JustLanded)
|
||||
{
|
||||
if (_entitiesByServerGuid.TryGetValue(_playerServerGuid, out var actionPe)
|
||||
&& _animatedEntities.TryGetValue(actionPe.Id, out var actionAe)
|
||||
&& actionAe.Sequencer is not null)
|
||||
{
|
||||
if (result.JumpExtent.HasValue)
|
||||
actionAe.Sequencer.PlayAction(AcDream.Core.Physics.MotionCommand.Jump);
|
||||
if (result.JustLanded)
|
||||
actionAe.Sequencer.PlayAction(AcDream.Core.Physics.MotionCommand.FallDown);
|
||||
}
|
||||
}
|
||||
|
||||
// Determine the animation command: forward takes priority, then sidestep,
|
||||
// then turn, then idle (Ready 0x41000003).
|
||||
//
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue