diff --git a/src/AcDream.App/Rendering/GameWindow.cs b/src/AcDream.App/Rendering/GameWindow.cs
index b304d37..26e3ab6 100644
--- a/src/AcDream.App/Rendering/GameWindow.cs
+++ b/src/AcDream.App/Rendering/GameWindow.cs
@@ -2427,12 +2427,14 @@ public sealed class GameWindow : IDisposable
// K-fix10 (2026-04-26): force the Falling animation cycle on
// the remote so the legs match the arc. Mirrors the local
// player's UpdatePlayerAnimation path which sets
- // animCommand = Falling whenever !IsOnGround. Without this,
- // the remote's existing locomotion cycle (RunForward,
- // Ready, etc.) keeps playing through the jump — body goes
- // up but legs stay running. Style is the sequencer's
- // current style (NonCombat 0x8000003D for humanoids); cycle
- // pace = 1.0 (Falling animation has its own baked rate).
+ // animCommand = Falling whenever !IsOnGround.
+ //
+ // K-fix18 (2026-04-26): pass skipTransitionLink:true so the
+ // RunForward→Falling transition frames don't play first.
+ // Without that flag the remote stood still for ~100 ms at
+ // the start of the jump while the link drained, then
+ // folded into Falling. Skipping the link makes the pose
+ // engage immediately on jump start.
if (_entitiesByServerGuid.TryGetValue(update.Guid, out var ent)
&& _animatedEntities.TryGetValue(ent.Id, out var ae)
&& ae.Sequencer is not null)
@@ -2440,7 +2442,8 @@ public sealed class GameWindow : IDisposable
uint style = ae.Sequencer.CurrentStyle != 0
? ae.Sequencer.CurrentStyle
: 0x8000003Du; // NonCombat default
- ae.Sequencer.SetCycle(style, AcDream.Core.Physics.MotionCommand.Falling, 1.0f);
+ ae.Sequencer.SetCycle(style, AcDream.Core.Physics.MotionCommand.Falling, 1.0f,
+ skipTransitionLink: true);
}
}
@@ -5067,7 +5070,16 @@ public sealed class GameWindow : IDisposable
{
animScale = s;
}
- ae.Sequencer.SetCycle(fullStyle, animCommand, animSpeed * animScale);
+ // K-fix18 (2026-04-26): when transitioning into Falling
+ // (jump start), skip the link so the legs engage Falling
+ // immediately. Without this the local player visibly
+ // stood still for ~100 ms at the start of every jump
+ // while the RunForward→Falling transition link drained.
+ // For everything else (Walk → Run, Run → Ready, etc.) we
+ // keep the link so transitions stay smooth.
+ bool skipLink = animCommand == AcDream.Core.Physics.MotionCommand.Falling;
+ ae.Sequencer.SetCycle(fullStyle, animCommand, animSpeed * animScale,
+ skipTransitionLink: skipLink);
}
// Legacy path: update the manual slerp fields (for entities without sequencer)
diff --git a/src/AcDream.Core/Physics/AnimationSequencer.cs b/src/AcDream.Core/Physics/AnimationSequencer.cs
index 9655ed1..9afe076 100644
--- a/src/AcDream.Core/Physics/AnimationSequencer.cs
+++ b/src/AcDream.Core/Physics/AnimationSequencer.cs
@@ -322,7 +322,15 @@ public sealed class AnimationSequencer
/// MotionCommand style / stance (e.g. NonCombat 0x003D0000).
/// Target motion command (e.g. WalkForward 0x45000005).
/// Speed multiplier applied to framerates (1.0 = normal).
- public void SetCycle(uint style, uint motion, float speedMod = 1f)
+ /// K-fix18 (2026-04-26): when true, do
+ /// NOT enqueue the transition-link frames between the previous and
+ /// new cycle. Used when the caller wants the new cycle to engage
+ /// instantly — e.g. swapping to Falling on a jump start, where the
+ /// RunForward→Falling link is a short "stop running" pose that
+ /// makes the jump look delayed (legs stand still for ~100 ms while
+ /// the link drains, then fold into Falling). Defaults to false to
+ /// preserve normal smooth transitions for everything else.
+ public void SetCycle(uint style, uint motion, float speedMod = 1f, bool skipTransitionLink = false)
{
// ── adjust_motion: remap left→right / backward→forward variants ───
// ACE MotionInterp.cs:394-428. The MotionTable never stores TurnLeft,
@@ -372,9 +380,12 @@ public sealed class AnimationSequencer
// CurrentSpeedMod defaults to 1.0 (positive) on a fresh sequencer,
// so a Ready → WalkBackward transition correctly enters GetLink's
// negative-speed (reversed-key) branch.
- MotionData? linkData = CurrentMotion != 0
- ? GetLink(style, CurrentMotion, CurrentSpeedMod, adjustedMotion, adjustedSpeed)
- : null;
+ // K-fix18: when the caller asked to skip the transition link
+ // (instant-engage cases like Falling on jump start), force
+ // linkData to null so only the cycle gets enqueued.
+ MotionData? linkData = (skipTransitionLink || CurrentMotion == 0)
+ ? null
+ : GetLink(style, CurrentMotion, CurrentSpeedMod, adjustedMotion, adjustedSpeed);
// Resolve target cycle using the ADJUSTED motion (TurnRight not TurnLeft).
int cycleKey = (int)(((style & 0xFFFFu) << 16) | (adjustedMotion & 0xFFFFFFu));
@@ -384,6 +395,19 @@ public sealed class AnimationSequencer
// been played yet (ACE behaviour: non-cyclic anims drain naturally).
ClearCyclicTail();
+ // K-fix18: when the caller asked for instant-engage, ALSO drain
+ // any in-flight non-cyclic transition frames from the previous
+ // cycle. Without this, the old RunForward → ??? link would
+ // continue draining for ~100 ms before the new Falling cycle
+ // starts, defeating the "skip the link" intent.
+ if (skipTransitionLink)
+ {
+ _queue.Clear();
+ _currNode = null;
+ _firstCyclic = null;
+ _framePosition = 0.0;
+ }
+
// Clear sequence-wide physics before the rebuild. Retail's
// GetObjectSequence calls sequence.clear_physics() before each
// add_motion chain (MotionTable.cs L100-L101, L152-L153).