From 0868849b3d6517490896a97628105d02119a6544 Mon Sep 17 00:00:00 2001 From: Erik Date: Mon, 13 Apr 2026 00:49:47 +0200 Subject: [PATCH] fix(anim): left-side motion fallback to right-side animation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit AC MotionTables typically define cycles for TurnRight and SideStepRight but not TurnLeft/SideStepLeft — the left variants reuse the right-side animation played in reverse. When the left-side command doesn't resolve to a cycle, fall back to the right-side equivalent so the player isn't stuck in idle pose. Fallback map: TurnLeft (0x000E) → TurnRight (0x000D) SideStepLeft (0x0010) → SideStepRight (0x000F) WalkBackward (0x0006) → WalkForward (0x0005) Co-Authored-By: Claude Opus 4.6 (1M context) --- src/AcDream.App/Rendering/GameWindow.cs | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/src/AcDream.App/Rendering/GameWindow.cs b/src/AcDream.App/Rendering/GameWindow.cs index 6186e33..e3f2dcf 100644 --- a/src/AcDream.App/Rendering/GameWindow.cs +++ b/src/AcDream.App/Rendering/GameWindow.cs @@ -2054,6 +2054,28 @@ public sealed class GameWindow : IDisposable stanceOverride: NonCombatStance, commandOverride: cmdOverride); + // AC reuses right-side animations for left-side motions (played in + // reverse). If the left-side command has no cycle, fall back to the + // right-side equivalent so the player isn't stuck in idle. + if (cycle is null || cycle.Framerate == 0f || cycle.HighFrame <= cycle.LowFrame) + { + ushort fallback = cmdOverride switch + { + 0x000E => 0x000D, // TurnLeft → TurnRight + 0x0010 => 0x000F, // SideStepLeft → SideStepRight + 0x0006 => 0x0005, // WalkBackward → WalkForward + _ => (ushort)0, + }; + if (fallback != 0) + { + cycle = AcDream.Core.Meshing.MotionResolver.GetIdleCycle( + ae.Setup, _dats, + motionTableIdOverride: _playerMotionTableId, + stanceOverride: NonCombatStance, + commandOverride: fallback); + } + } + if (cycle is null || cycle.Framerate == 0f || cycle.HighFrame <= cycle.LowFrame) return; // If the entity has a sequencer, use SetCycle for transition-link-aware