blacktrigram 0.7.68 → 0.7.70

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (219) hide show
  1. package/lib/App2.js.map +1 -1
  2. package/lib/assets/index.css +987 -987
  3. package/lib/audio/AudioAssetLoader.js.map +1 -1
  4. package/lib/audio/AudioAssetRegistry.js +2 -2
  5. package/lib/audio/AudioAssetRegistry.js.map +1 -1
  6. package/lib/audio/AudioCache.js.map +1 -1
  7. package/lib/audio/AudioManager.js +1 -1
  8. package/lib/audio/AudioManager.js.map +1 -1
  9. package/lib/audio/AudioMonitor.js.map +1 -1
  10. package/lib/audio/AudioPool.js.map +1 -1
  11. package/lib/audio/AudioProvider.js.map +1 -1
  12. package/lib/audio/AudioUtils.js.map +1 -1
  13. package/lib/audio/BoneImpactAudioMap.js.map +1 -1
  14. package/lib/components/screens/combat/CombatScreen3D.js +3 -3
  15. package/lib/components/screens/combat/CombatScreen3D.js.map +1 -1
  16. package/lib/components/screens/combat/components/controls/CombatControlsPanel.js.map +1 -1
  17. package/lib/components/screens/combat/components/controls/PauseMenu.js.map +1 -1
  18. package/lib/components/screens/combat/components/effects/BloodDecals3D.js.map +1 -1
  19. package/lib/components/screens/combat/components/effects/BloodParticles3D.js.map +1 -1
  20. package/lib/components/screens/combat/components/effects/BloodViscosity3D.js.map +1 -1
  21. package/lib/components/screens/combat/components/effects/CombatParticleEffects3D.js.map +1 -1
  22. package/lib/components/screens/combat/components/effects/InternalDamage3D.js.map +1 -1
  23. package/lib/components/screens/combat/components/effects/ParticleAudio3D.js.map +1 -1
  24. package/lib/components/screens/combat/components/hud/FPSMonitor.js.map +1 -1
  25. package/lib/components/screens/combat/components/indicators/BalanceIndicator.js.map +1 -1
  26. package/lib/components/screens/combat/hooks/useAICombat.js.map +1 -1
  27. package/lib/components/screens/combat/hooks/useCombatActions.js.map +1 -1
  28. package/lib/components/screens/combat/hooks/useCombatAttackMovement.js.map +1 -1
  29. package/lib/components/screens/controls/ControlsScreen3D.js.map +1 -1
  30. package/lib/components/screens/controls/components/ControlCategoryTabsOverlayHtml.js.map +1 -1
  31. package/lib/components/screens/endscreen/components/MatchStatisticsDisplayOverlayHtml.js.map +1 -1
  32. package/lib/components/screens/endscreen/components/PerformanceBreakdownOverlayHtml.js.map +1 -1
  33. package/lib/components/screens/endscreen/components/VictoryAnimation3D.js.map +1 -1
  34. package/lib/components/screens/intro/IntroScreen3D.js +1 -1
  35. package/lib/components/screens/intro/IntroScreen3D.js.map +1 -1
  36. package/lib/components/screens/intro/components/AbilityListOverlayHtml.js.map +1 -1
  37. package/lib/components/screens/intro/components/ArchetypeCardGridOverlayHtml.js.map +1 -1
  38. package/lib/components/screens/intro/components/ArchetypeDisplayOverlayHtml.js.map +1 -1
  39. package/lib/components/screens/intro/components/EnhancedArchetypeDisplayOverlayHtml.js.map +1 -1
  40. package/lib/components/screens/intro/components/MenuButtonsOverlayHtml.js.map +1 -1
  41. package/lib/components/screens/intro/components/MenuSectionOverlayHtml.js.map +1 -1
  42. package/lib/components/screens/intro/components/StatBarOverlayHtml.js.map +1 -1
  43. package/lib/components/screens/philosophy/PhilosophyScreen3D.js.map +1 -1
  44. package/lib/components/screens/training/TrainingScreen3D.js +1 -1
  45. package/lib/components/screens/training/TrainingScreen3D.js.map +1 -1
  46. package/lib/components/screens/training/components/AnatomyOverlay3D.js.map +1 -1
  47. package/lib/components/screens/training/components/FootworkDrillsOverlayHtml.js.map +1 -1
  48. package/lib/components/screens/training/components/HitFeedbackEffect3D.js.map +1 -1
  49. package/lib/components/screens/training/components/TrainingButtonsOverlayHtml.js.map +1 -1
  50. package/lib/components/screens/training/components/TrainingDummy3D.js.map +1 -1
  51. package/lib/components/screens/training/components/TrainingModeSelectorOverlayHtml.js.map +1 -1
  52. package/lib/components/screens/training/components/TrainingStatsOverlayHtml.js.map +1 -1
  53. package/lib/components/screens/training/components/VitalPointMarker3D.js.map +1 -1
  54. package/lib/components/screens/training/hooks/useAttackMovement.js.map +1 -1
  55. package/lib/components/screens/training/hooks/useTrainingActions.js.map +1 -1
  56. package/lib/components/shared/mobile/ActionButtons.js.map +1 -1
  57. package/lib/components/shared/mobile/GestureRecognizerPure.js.map +1 -1
  58. package/lib/components/shared/mobile/HapticController.js.map +1 -1
  59. package/lib/components/shared/mobile/StanceWheelPure.js.map +1 -1
  60. package/lib/components/shared/mobile/VirtualDPad.js.map +1 -1
  61. package/lib/components/shared/three/anatomy/BoneAttachedMuscles.js.map +1 -1
  62. package/lib/components/shared/three/anatomy/BoneClothing.js.map +1 -1
  63. package/lib/components/shared/three/anatomy/BoneRenderer.js.map +1 -1
  64. package/lib/components/shared/three/anatomy/Face3D.js.map +1 -1
  65. package/lib/components/shared/three/effects/ActionFeedback.js.map +1 -1
  66. package/lib/components/shared/three/effects/DamageNumbers.js.map +1 -1
  67. package/lib/components/shared/three/effects/HitEffects3D.js.map +1 -1
  68. package/lib/components/shared/three/effects/StanceTransitionEffect.js.map +1 -1
  69. package/lib/components/shared/three/effects/VitalPointMarkers3D.js.map +1 -1
  70. package/lib/components/shared/three/indicators/ElementalColorSystem.js.map +1 -1
  71. package/lib/components/shared/three/indicators/GuardIndicator.js.map +1 -1
  72. package/lib/components/shared/three/scene/CombatArena3D.js.map +1 -1
  73. package/lib/components/shared/three/ui/BodyPartHealthDisplay.js.map +1 -1
  74. package/lib/components/shared/three/ui/CombatReadinessBar.js.map +1 -1
  75. package/lib/components/shared/three/ui/HealthBar.js.map +1 -1
  76. package/lib/components/shared/three/ui/PlayerHUD.js.map +1 -1
  77. package/lib/components/shared/three/ui/SpeedIndicatorHUD.js.map +1 -1
  78. package/lib/components/shared/three/ui/StaminaBar.js.map +1 -1
  79. package/lib/components/shared/three/ui/TechniqueCard.js.map +1 -1
  80. package/lib/components/shared/three/ui/VitalPointOverlayControlsHtml.js.map +1 -1
  81. package/lib/components/shared/ui/SplashScreen.js +2 -2
  82. package/lib/components/shared/ui/VitalPointOverlayControlsPure.js.map +1 -1
  83. package/lib/components/ui/combat/BalanceIndicatorOverlayHtml.js.map +1 -1
  84. package/lib/data/techniques.js.map +1 -1
  85. package/lib/hooks/useCombatTimer.js.map +1 -1
  86. package/lib/hooks/useKeyboardControls.js.map +1 -1
  87. package/lib/hooks/useSkeletalAnimation.js +2 -2
  88. package/lib/hooks/useSkeletalAnimation.js.map +1 -1
  89. package/lib/hooks/useTechniqueSelection.js.map +1 -1
  90. package/lib/hooks/useWebGLContextLossHandler.js.map +1 -1
  91. package/lib/index.js +2 -2
  92. package/lib/systems/CombatSystem.js.map +1 -1
  93. package/lib/systems/EffectCalculator.js.map +1 -1
  94. package/lib/systems/ResponsiveScaling.js.map +1 -1
  95. package/lib/systems/TrigramSystem.js.map +1 -1
  96. package/lib/systems/ai/AIPersonality.js.map +1 -1
  97. package/lib/systems/ai/ArchetypeEnforcer.js.map +1 -1
  98. package/lib/systems/ai/DecisionTree.js.map +1 -1
  99. package/lib/systems/animation/builders/KeyframeConfig.js.map +1 -1
  100. package/lib/systems/animation/builders/KeyframeInterpolation.js.map +1 -1
  101. package/lib/systems/animation/builders/MartialArtsAnimationBuilder.js.map +1 -1
  102. package/lib/systems/animation/builders/SkeletonRig.js +1 -1
  103. package/lib/systems/animation/builders/SkeletonRig.js.map +1 -1
  104. package/lib/systems/animation/catalogs/AttackAnimations.js +1 -1
  105. package/lib/systems/animation/catalogs/AttackAnimations.js.map +1 -1
  106. package/lib/systems/animation/catalogs/BasicAnimations.js +1 -1
  107. package/lib/systems/animation/catalogs/BasicAnimations.js.map +1 -1
  108. package/lib/systems/animation/catalogs/ComboAnimations.js +1 -1
  109. package/lib/systems/animation/catalogs/ComboAnimations.js.map +1 -1
  110. package/lib/systems/animation/catalogs/DarkOpsAnimations.js +1 -1
  111. package/lib/systems/animation/catalogs/DarkOpsAnimations.js.map +1 -1
  112. package/lib/systems/animation/catalogs/DefensiveAnimations.js +2 -11
  113. package/lib/systems/animation/catalogs/DefensiveAnimations.js.map +1 -1
  114. package/lib/systems/animation/catalogs/ElbowKneeAnimations.js +1 -1
  115. package/lib/systems/animation/catalogs/ElbowKneeAnimations.js.map +1 -1
  116. package/lib/systems/animation/catalogs/FootworkSkeletalAnimations.js +1 -1
  117. package/lib/systems/animation/catalogs/FootworkSkeletalAnimations.js.map +1 -1
  118. package/lib/systems/animation/catalogs/GamRedirectionAnimations.js.map +1 -1
  119. package/lib/systems/animation/catalogs/GamStanceAnimations.js.map +1 -1
  120. package/lib/systems/animation/catalogs/GamTechniqueAnimations.js.map +1 -1
  121. package/lib/systems/animation/catalogs/GanStanceAnimations.js +1 -1
  122. package/lib/systems/animation/catalogs/GanStanceAnimations.js.map +1 -1
  123. package/lib/systems/animation/catalogs/GanTechniqueAnimations.js +1 -1
  124. package/lib/systems/animation/catalogs/GanTechniqueAnimations.js.map +1 -1
  125. package/lib/systems/animation/catalogs/GeonStanceAnimations.js +1 -1
  126. package/lib/systems/animation/catalogs/GeonStanceAnimations.js.map +1 -1
  127. package/lib/systems/animation/catalogs/GonTechniqueAnimations.js +1 -1
  128. package/lib/systems/animation/catalogs/GonTechniqueAnimations.js.map +1 -1
  129. package/lib/systems/animation/catalogs/GrapplingAnimations.js +1 -1
  130. package/lib/systems/animation/catalogs/GrapplingAnimations.js.map +1 -1
  131. package/lib/systems/animation/catalogs/JinStanceAnimations.js +1 -1
  132. package/lib/systems/animation/catalogs/JinStanceAnimations.js.map +1 -1
  133. package/lib/systems/animation/catalogs/JinTechniqueAnimations.js +1 -1
  134. package/lib/systems/animation/catalogs/JinTechniqueAnimations.js.map +1 -1
  135. package/lib/systems/animation/catalogs/KickAnimations.js +1 -1
  136. package/lib/systems/animation/catalogs/KickAnimations.js.map +1 -1
  137. package/lib/systems/animation/catalogs/LiStanceAnimations.js +1 -1
  138. package/lib/systems/animation/catalogs/LiStanceAnimations.js.map +1 -1
  139. package/lib/systems/animation/catalogs/LiTechniqueAnimations.js.map +1 -1
  140. package/lib/systems/animation/catalogs/MovementAnimations.js +1 -1
  141. package/lib/systems/animation/catalogs/MovementAnimations.js.map +1 -1
  142. package/lib/systems/animation/catalogs/PunchAnimations.js +1 -1
  143. package/lib/systems/animation/catalogs/PunchAnimations.js.map +1 -1
  144. package/lib/systems/animation/catalogs/SonStanceAnimations.js +1 -1
  145. package/lib/systems/animation/catalogs/SonStanceAnimations.js.map +1 -1
  146. package/lib/systems/animation/catalogs/SonTechniqueAnimations.js +1 -1
  147. package/lib/systems/animation/catalogs/SonTechniqueAnimations.js.map +1 -1
  148. package/lib/systems/animation/catalogs/SpecializedPunchAnimations.js +1 -1
  149. package/lib/systems/animation/catalogs/SpecializedPunchAnimations.js.map +1 -1
  150. package/lib/systems/animation/catalogs/StanceAnimations.js +1 -1
  151. package/lib/systems/animation/catalogs/StanceAnimations.js.map +1 -1
  152. package/lib/systems/animation/catalogs/StanceAttackAnimations.js +215 -272
  153. package/lib/systems/animation/catalogs/StanceAttackAnimations.js.map +1 -1
  154. package/lib/systems/animation/catalogs/StanceIdleAnimations.js +2 -11
  155. package/lib/systems/animation/catalogs/StanceIdleAnimations.js.map +1 -1
  156. package/lib/systems/animation/catalogs/StanceLocomotionAnimations.js +2 -2
  157. package/lib/systems/animation/catalogs/StanceLocomotionAnimations.js.map +1 -1
  158. package/lib/systems/animation/catalogs/StepSkeletalAnimations.js +1 -1
  159. package/lib/systems/animation/catalogs/StepSkeletalAnimations.js.map +1 -1
  160. package/lib/systems/animation/catalogs/TaeJointLockAnimations.js.map +1 -1
  161. package/lib/systems/animation/catalogs/TaeStanceAnimations.js +1 -1
  162. package/lib/systems/animation/catalogs/TaeStanceAnimations.js.map +1 -1
  163. package/lib/systems/animation/core/AnimationOptimizations.js +1 -1
  164. package/lib/systems/animation/core/AnimationOptimizations.js.map +1 -1
  165. package/lib/systems/animation/core/AnimationPriority.js.map +1 -1
  166. package/lib/systems/animation/core/AnimationRegistry.js +11 -209
  167. package/lib/systems/animation/core/AnimationRegistry.js.map +1 -1
  168. package/lib/systems/animation/core/AnimationStateMachine.js +2 -2
  169. package/lib/systems/animation/core/AnimationStateMachine.js.map +1 -1
  170. package/lib/systems/animation/core/TechniqueAnimationMapper.js +1 -1
  171. package/lib/systems/animation/core/TechniqueAnimationMapper.js.map +1 -1
  172. package/lib/systems/animation/core/TechniqueAnimationMapping.js +1 -1
  173. package/lib/systems/animation/core/TechniqueAnimationMapping.js.map +1 -1
  174. package/lib/systems/animation/systems/MuscleActivation.js.map +1 -1
  175. package/lib/systems/bodypart/BodyPartDamageIntegration.js.map +1 -1
  176. package/lib/systems/bodypart/BodyPartHealthSystem.js.map +1 -1
  177. package/lib/systems/bodypart/BodyPartPositionMapping.js.map +1 -1
  178. package/lib/systems/bodypart/InjuryTracker.js.map +1 -1
  179. package/lib/systems/breathing/BreathingDisruptionSystem.js.map +1 -1
  180. package/lib/systems/combat/BalanceSystem.js.map +1 -1
  181. package/lib/systems/combat/BreakingStatusEffects.js.map +1 -1
  182. package/lib/systems/combat/CombatStateSystem.js.map +1 -1
  183. package/lib/systems/combat/GrappleSystem.js.map +1 -1
  184. package/lib/systems/combat/painConsciousnessUtils.js.map +1 -1
  185. package/lib/systems/combat/typeGuards.js.map +1 -1
  186. package/lib/systems/index.js +2 -2
  187. package/lib/systems/movement/integration.js.map +1 -1
  188. package/lib/systems/physics/AttackMovementPhysics.js.map +1 -1
  189. package/lib/systems/physics/CollisionDetection.js +1 -1
  190. package/lib/systems/physics/CollisionDetection.js.map +1 -1
  191. package/lib/systems/physics/KnockbackPhysics.js.map +1 -1
  192. package/lib/systems/physics/MovementPhysics.js.map +1 -1
  193. package/lib/systems/physics/PhysicalReachCalculator.js.map +1 -1
  194. package/lib/systems/physics/SpeedModifierSystem.js.map +1 -1
  195. package/lib/systems/trigram/KoreanTechniques.js.map +1 -1
  196. package/lib/systems/trigram/StanceManager.js.map +1 -1
  197. package/lib/systems/trigram/TransitionCalculator.js.map +1 -1
  198. package/lib/systems/trigram/TrigramCalculator.js.map +1 -1
  199. package/lib/systems/trigram/techniques/TechniqueConfig.js.map +1 -1
  200. package/lib/systems/trigram/techniques/index.js.map +1 -1
  201. package/lib/systems/trigram/types.js.map +1 -1
  202. package/lib/systems/vitalpoint/DamageCalculator.js.map +1 -1
  203. package/lib/systems/vitalpoint/KoreanAnatomy.js.map +1 -1
  204. package/lib/systems/vitalpoint/KoreanVitalPoints.js.map +1 -1
  205. package/lib/systems/vitalpoint/MeridianVitalPointMapping.js.map +1 -1
  206. package/lib/types/constants/designSystem.js.map +1 -1
  207. package/lib/utils/colorHelpers.js.map +1 -1
  208. package/lib/utils/colorUtils.js.map +1 -1
  209. package/lib/utils/controlMapping.js.map +1 -1
  210. package/lib/utils/deviceDetection.js.map +1 -1
  211. package/lib/utils/effectUtils.js.map +1 -1
  212. package/lib/utils/fabricTextures.js.map +1 -1
  213. package/lib/utils/htmlOverlayHelpers.js.map +1 -1
  214. package/lib/utils/performance/PerformanceMonitor.js.map +1 -1
  215. package/lib/utils/performance/PerformanceOverlay3D.js.map +1 -1
  216. package/lib/utils/safeAreaUtils.js.map +1 -1
  217. package/lib/utils/stanceHelpers.js.map +1 -1
  218. package/lib/utils/visualEffects.js.map +1 -1
  219. package/package.json +11 -11
@@ -1 +1 @@
1
- {"version":3,"file":"AnimationStateMachine.js","names":[],"sources":["../../../../src/systems/animation/core/AnimationStateMachine.ts"],"sourcesContent":["/**\n * Player Animation State Machine for Black Trigram\n *\n * Manages player character animations with frame-accurate timing at 60fps.\n * Supports animation priorities, transitions, and event callbacks.\n *\n * Based on game-design.md specifications:\n * - Attack: 12 frames (200ms at 60fps)\n * - Block: 4 frames (67ms at 60fps)\n * - Walk: 6 frames\n * - Stance change: 600ms\n *\n * @module systems/animation/AnimationStateMachine\n * @category Animation\n * @korean 애니메이션상태머신\n */\n\nimport { TrigramStance } from \"@/types/common\";\nimport type { AnimationKeyframe } from \"@/types/skeletal\";\nimport {\n createMotionPredictionState,\n predictFutureKeyframe,\n updateMotionPrediction,\n type EasingName,\n type MotionPredictionState,\n} from \"../builders/KeyframeInterpolation\";\nimport {\n AnimationQueue,\n canInterrupt,\n type AnimationRequest,\n type ConflictResolutionStrategy,\n} from \"./AnimationPriority\";\nimport {\n getStanceTransition,\n isTransitionAllowed,\n type StanceTransition,\n} from \"./AnimationTransitions\";\nimport type {\n AnimationConfig,\n AnimationEvents,\n AnimationMachineState,\n AnimationPriority,\n AnimationUpdateResult,\n FallType,\n MutableAnimationConfig,\n} from \"./types\";\nimport { AnimationState, FALL_TO_GROUND_MAP, STEP_PRIORITY } from \"./types\";\n\n/**\n * Default animation configurations based on game-design.md\n *\n * Frame timings:\n * - Attack: 12 frames = 200ms at 60fps\n * - Block: 4 frames = 67ms at 60fps\n * - Walk: 6 frames = 100ms at 60fps\n * - Hit: 4 frames = 67ms at 60fps\n * - Stance change: 36 frames = 600ms at 60fps\n * - Stance guards: 4-6 frames = breathing animation at 60fps\n * - Tactical steps: 18 frames = 300ms at 60fps, 30cm distance\n *\n * Defensive animations (방어 애니메이션):\n * - Block Success (막기): 8 frames = 133ms - absorb impact, maintain guard\n * - Parry Deflect (받아넘기기): 10 frames = 167ms - redirect attack, counter window\n * - Guard Break (방어붕괴): 15 frames = 250ms - arms forced wide, vulnerable\n * - Guard Recovery (방어복구): 12 frames = 200ms - restore guard position\n *\n * @korean 기본애니메이션설정\n */\nexport const DEFAULT_ANIMATION_CONFIGS: Map<AnimationState, AnimationConfig> =\n new Map<AnimationState, AnimationConfig>([\n [\n AnimationState.IDLE,\n {\n state: AnimationState.IDLE,\n frames: 4,\n fps: 60,\n loop: true,\n interruptible: true,\n priority: 0 as AnimationPriority,\n duration: 4 / 60,\n },\n ],\n [\n AnimationState.WALK,\n {\n state: AnimationState.WALK,\n frames: 6,\n fps: 60,\n loop: true,\n interruptible: true,\n priority: 1 as AnimationPriority,\n duration: 6 / 60,\n },\n ],\n [\n AnimationState.RUN,\n {\n state: AnimationState.RUN,\n frames: 8,\n fps: 60,\n loop: true,\n interruptible: true,\n priority: 2 as AnimationPriority,\n duration: 8 / 60,\n },\n ],\n [\n AnimationState.STANCE_CHANGE,\n {\n state: AnimationState.STANCE_CHANGE,\n frames: 36, // 600ms at 60fps\n fps: 60,\n loop: false,\n interruptible: false,\n priority: 3 as AnimationPriority,\n duration: 0.6,\n easing: \"smooth-transition\", // Smooth S-curve for stance changes\n },\n ],\n [\n AnimationState.STANCE_SIDE_SWITCH,\n {\n state: AnimationState.STANCE_SIDE_SWITCH,\n frames: 24, // 400ms at 60fps for left↔right switch\n fps: 60,\n loop: false,\n interruptible: false,\n priority: 3 as AnimationPriority,\n duration: 0.4,\n },\n ],\n [\n AnimationState.DEFEND,\n {\n state: AnimationState.DEFEND,\n frames: 4, // 67ms at 60fps\n fps: 60,\n loop: false,\n interruptible: true,\n priority: 4 as AnimationPriority,\n duration: 4 / 60,\n },\n ],\n // Defensive animations (방어 애니메이션) - Enhanced guard break system\n [\n AnimationState.DEFEND_BLOCK_SUCCESS,\n {\n state: AnimationState.DEFEND_BLOCK_SUCCESS,\n frames: 8, // 133ms at 60fps - absorb impact, maintain guard\n fps: 60,\n loop: false,\n interruptible: false, // Must complete block animation\n priority: 6 as AnimationPriority, // Higher than defend, same as hit\n duration: 0.133,\n easing: \"controlled-slow\", // Controlled deceleration for impact absorption\n },\n ],\n [\n AnimationState.DEFEND_PARRY,\n {\n state: AnimationState.DEFEND_PARRY,\n frames: 10, // 167ms at 60fps - redirect attack, open counter opportunity\n fps: 60,\n loop: false,\n interruptible: false, // Must complete parry animation\n priority: 7 as AnimationPriority, // Higher than block, creates counter window\n duration: 0.167,\n counterWindow: 0.2, // 200ms counter-attack opportunity after parry\n },\n ],\n [\n AnimationState.DEFEND_GUARD_BREAK,\n {\n state: AnimationState.DEFEND_GUARD_BREAK,\n frames: 15, // 250ms at 60fps - arms forced wide, vulnerable state\n fps: 60,\n loop: false,\n interruptible: false, // Cannot interrupt guard break\n priority: 8 as AnimationPriority, // Highest priority (same as fall)\n duration: 0.25,\n vulnerabilityDuration: 0.5, // 500ms vulnerable state after guard break\n },\n ],\n [\n AnimationState.DEFEND_RECOVERY,\n {\n state: AnimationState.DEFEND_RECOVERY,\n frames: 12, // 200ms at 60fps - restore guard position\n fps: 60,\n loop: false,\n interruptible: true, // Can be interrupted by attacks\n priority: 2 as AnimationPriority, // Same as run, lower than defend\n duration: 0.2,\n easing: \"natural-motion\", // Physics-based recovery\n },\n ],\n [\n AnimationState.ATTACK,\n {\n state: AnimationState.ATTACK,\n frames: 12, // 200ms at 60fps\n fps: 60,\n loop: false,\n interruptible: true,\n priority: STEP_PRIORITY,\n duration: 12 / 60,\n easing: \"explosive-power\", // Explosive acceleration for attacks\n },\n ],\n [\n AnimationState.HIT,\n {\n state: AnimationState.HIT,\n frames: 4, // 67ms at 60fps\n fps: 60,\n loop: false,\n interruptible: false,\n priority: 6 as AnimationPriority,\n duration: 4 / 60,\n },\n ],\n [\n AnimationState.KO,\n {\n state: AnimationState.KO,\n frames: 30, // 500ms at 60fps\n fps: 60,\n loop: false,\n interruptible: false,\n priority: 7 as AnimationPriority,\n duration: 0.5,\n },\n ],\n // Fall animations (낙법 애니메이션) - Priority 8 (highest)\n [\n AnimationState.FALL_FORWARD,\n {\n state: AnimationState.FALL_FORWARD,\n frames: 24, // 400ms at 60fps - stumble forward, knee collapse, hands brace, face-down\n fps: 60,\n loop: false,\n interruptible: false,\n priority: 8 as AnimationPriority,\n duration: 0.4,\n },\n ],\n [\n AnimationState.FALL_BACKWARD,\n {\n state: AnimationState.FALL_BACKWARD,\n frames: 30, // 500ms at 60fps - backward stumble, sit, back impact, supine\n fps: 60,\n loop: false,\n interruptible: false,\n priority: 8 as AnimationPriority,\n duration: 0.5,\n },\n ],\n [\n AnimationState.FALL_SIDE_LEFT,\n {\n state: AnimationState.FALL_SIDE_LEFT,\n frames: 27, // 450ms at 60fps - rotation, shoulder roll, side sprawl\n fps: 60,\n loop: false,\n interruptible: false,\n priority: 8 as AnimationPriority,\n duration: 0.45,\n },\n ],\n [\n AnimationState.FALL_SIDE_RIGHT,\n {\n state: AnimationState.FALL_SIDE_RIGHT,\n frames: 27, // 450ms at 60fps - rotation, shoulder roll, side sprawl\n fps: 60,\n loop: false,\n interruptible: false,\n priority: 8 as AnimationPriority,\n duration: 0.45,\n },\n ],\n // Ground state animations (지면 자세) - Breathing loops\n [\n AnimationState.GROUND_PRONE,\n {\n state: AnimationState.GROUND_PRONE,\n frames: 4, // Breathing loop on ground (face down)\n fps: 60,\n loop: true,\n interruptible: true,\n priority: 0 as AnimationPriority,\n duration: 4 / 60,\n },\n ],\n [\n AnimationState.GROUND_SUPINE,\n {\n state: AnimationState.GROUND_SUPINE,\n frames: 4, // Breathing loop on ground (face up)\n fps: 60,\n loop: true,\n interruptible: true,\n priority: 0 as AnimationPriority,\n duration: 4 / 60,\n },\n ],\n [\n AnimationState.GROUND_SIDE_LEFT,\n {\n state: AnimationState.GROUND_SIDE_LEFT,\n frames: 4, // Breathing loop on ground (left side)\n fps: 60,\n loop: true,\n interruptible: true,\n priority: 0 as AnimationPriority,\n duration: 4 / 60,\n },\n ],\n [\n AnimationState.GROUND_SIDE_RIGHT,\n {\n state: AnimationState.GROUND_SIDE_RIGHT,\n frames: 4, // Breathing loop on ground (right side)\n fps: 60,\n loop: true,\n interruptible: true,\n priority: 0 as AnimationPriority,\n duration: 4 / 60,\n },\n ],\n // Recovery animations (기상 애니메이션) - Priority 9 (higher than falls)\n [\n AnimationState.RECOVERY_PRONE_STANDUP,\n {\n state: AnimationState.RECOVERY_PRONE_STANDUP,\n frames: 30, // 500ms at 60fps - push up from prone, rise to standing\n fps: 60,\n loop: false,\n interruptible: false, // Last 6 frames (100ms) are interruptible\n priority: 9 as AnimationPriority,\n duration: 0.5,\n },\n ],\n [\n AnimationState.RECOVERY_SUPINE_STANDUP,\n {\n state: AnimationState.RECOVERY_SUPINE_STANDUP,\n frames: 36, // 600ms at 60fps - sit up, roll forward, stand\n fps: 60,\n loop: false,\n interruptible: false, // Last 6 frames (100ms) are interruptible\n priority: 9 as AnimationPriority,\n duration: 0.6,\n },\n ],\n [\n AnimationState.RECOVERY_ROLL,\n {\n state: AnimationState.RECOVERY_ROLL,\n frames: 24, // 400ms at 60fps - roll to side, spring to feet (quick recovery)\n fps: 60,\n loop: false,\n interruptible: false, // Last 6 frames (100ms) are interruptible\n priority: 9 as AnimationPriority,\n duration: 0.4,\n },\n ],\n [\n AnimationState.RECOVERY_DEFENSIVE,\n {\n state: AnimationState.RECOVERY_DEFENSIVE,\n frames: 42, // 700ms at 60fps - slow rise with guard up (vulnerable but defended)\n fps: 60,\n loop: false,\n interruptible: false, // Last 6 frames (100ms) are interruptible\n priority: 9 as AnimationPriority,\n duration: 0.7,\n },\n ],\n // 180-degree turn animations (180도 회전 애니메이션)\n [\n AnimationState.TURN_LEFT,\n {\n state: AnimationState.TURN_LEFT,\n frames: 12, // 200ms at 60fps for 180° turn\n fps: 60,\n loop: false,\n interruptible: false,\n priority: STEP_PRIORITY, // Same priority as attacks/steps - committed action\n duration: 12 / 60,\n },\n ],\n [\n AnimationState.TURN_RIGHT,\n {\n state: AnimationState.TURN_RIGHT,\n frames: 12, // 200ms at 60fps for 180° turn\n fps: 60,\n loop: false,\n interruptible: false,\n priority: STEP_PRIORITY, // Same priority as attacks/steps - committed action\n duration: 12 / 60,\n },\n ],\n // Stance-specific guard animations (팔괘 방어 자세)\n [\n AnimationState.STANCE_GUARD_GEON,\n {\n state: AnimationState.STANCE_GUARD_GEON,\n frames: 6, // Breathing animation\n fps: 60,\n loop: true,\n interruptible: true,\n priority: 0 as AnimationPriority,\n duration: 6 / 60,\n },\n ],\n [\n AnimationState.STANCE_GUARD_TAE,\n {\n state: AnimationState.STANCE_GUARD_TAE,\n frames: 6, // Breathing animation\n fps: 60,\n loop: true,\n interruptible: true,\n priority: 0 as AnimationPriority,\n duration: 6 / 60,\n },\n ],\n [\n AnimationState.STANCE_GUARD_LI,\n {\n state: AnimationState.STANCE_GUARD_LI,\n frames: 4, // Controlled breathing\n fps: 60,\n loop: true,\n interruptible: true,\n priority: 0 as AnimationPriority,\n duration: 4 / 60,\n },\n ],\n [\n AnimationState.STANCE_GUARD_JIN,\n {\n state: AnimationState.STANCE_GUARD_JIN,\n frames: 5, // Deep breathing\n fps: 60,\n loop: true,\n interruptible: true,\n priority: 0 as AnimationPriority,\n duration: 5 / 60,\n },\n ],\n [\n AnimationState.STANCE_GUARD_SON,\n {\n state: AnimationState.STANCE_GUARD_SON,\n frames: 6, // Rhythmic breathing\n fps: 60,\n loop: true,\n interruptible: true,\n priority: 0 as AnimationPriority,\n duration: 6 / 60,\n },\n ],\n [\n AnimationState.STANCE_GUARD_GAM,\n {\n state: AnimationState.STANCE_GUARD_GAM,\n frames: 6, // Flowing breathing\n fps: 60,\n loop: true,\n interruptible: true,\n priority: 0 as AnimationPriority,\n duration: 6 / 60,\n },\n ],\n [\n AnimationState.STANCE_GUARD_GAN,\n {\n state: AnimationState.STANCE_GUARD_GAN,\n frames: 4, // Steady breathing\n fps: 60,\n loop: true,\n interruptible: true,\n priority: 0 as AnimationPriority,\n duration: 4 / 60,\n },\n ],\n [\n AnimationState.STANCE_GUARD_GON,\n {\n state: AnimationState.STANCE_GUARD_GON,\n frames: 5, // Deep diaphragm breathing\n fps: 60,\n loop: true,\n interruptible: true,\n priority: 0 as AnimationPriority,\n duration: 5 / 60,\n },\n ],\n // Tactical step animations (전술적 발걸음)\n // 18 frames = 300ms at 60fps, 30cm distance per step\n [\n AnimationState.STEP_FORWARD,\n {\n state: AnimationState.STEP_FORWARD,\n frames: 18,\n fps: 60,\n loop: false,\n interruptible: false, // Non-interruptible for commitment\n priority: STEP_PRIORITY,\n duration: 0.3,\n },\n ],\n [\n AnimationState.STEP_BACK,\n {\n state: AnimationState.STEP_BACK,\n frames: 18,\n fps: 60,\n loop: false,\n interruptible: false,\n priority: STEP_PRIORITY,\n duration: 0.3,\n },\n ],\n [\n AnimationState.STEP_LEFT,\n {\n state: AnimationState.STEP_LEFT,\n frames: 18,\n fps: 60,\n loop: false,\n interruptible: false,\n priority: STEP_PRIORITY,\n duration: 0.3,\n },\n ],\n [\n AnimationState.STEP_RIGHT,\n {\n state: AnimationState.STEP_RIGHT,\n frames: 18,\n fps: 60,\n loop: false,\n interruptible: false,\n priority: STEP_PRIORITY,\n duration: 0.3,\n },\n ],\n [\n AnimationState.STEP_FORWARD_LEFT,\n {\n state: AnimationState.STEP_FORWARD_LEFT,\n frames: 18,\n fps: 60,\n loop: false,\n interruptible: false,\n priority: STEP_PRIORITY,\n duration: 0.3,\n },\n ],\n [\n AnimationState.STEP_FORWARD_RIGHT,\n {\n state: AnimationState.STEP_FORWARD_RIGHT,\n frames: 18,\n fps: 60,\n loop: false,\n interruptible: false,\n priority: STEP_PRIORITY,\n duration: 0.3,\n },\n ],\n [\n AnimationState.STEP_BACK_LEFT,\n {\n state: AnimationState.STEP_BACK_LEFT,\n frames: 18,\n fps: 60,\n loop: false,\n interruptible: false,\n priority: STEP_PRIORITY,\n duration: 0.3,\n },\n ],\n [\n AnimationState.STEP_BACK_RIGHT,\n {\n state: AnimationState.STEP_BACK_RIGHT,\n frames: 18,\n fps: 60,\n loop: false,\n interruptible: false,\n priority: STEP_PRIORITY,\n duration: 0.3,\n },\n ],\n // Footwork patterns (보법) - Korean martial arts specialized footwork\n // Circular step (원형보) - Lateral movement maintaining guard facing\n [\n AnimationState.FOOTWORK_CIRCULAR_LEFT,\n {\n state: AnimationState.FOOTWORK_CIRCULAR_LEFT,\n frames: 18, // 300ms at 60fps\n fps: 60,\n loop: false,\n interruptible: false, // Committed footwork\n priority: STEP_PRIORITY, // Same as tactical steps\n duration: 0.3,\n },\n ],\n [\n AnimationState.FOOTWORK_CIRCULAR_RIGHT,\n {\n state: AnimationState.FOOTWORK_CIRCULAR_RIGHT,\n frames: 18, // 300ms at 60fps\n fps: 60,\n loop: false,\n interruptible: false,\n priority: STEP_PRIORITY,\n duration: 0.3,\n },\n ],\n // Pivot step (축족회전) - Rotation on planted foot\n [\n AnimationState.FOOTWORK_PIVOT_LEFT,\n {\n state: AnimationState.FOOTWORK_PIVOT_LEFT,\n frames: 15, // 250ms at 60fps\n fps: 60,\n loop: false,\n interruptible: false,\n priority: STEP_PRIORITY,\n duration: 0.25,\n },\n ],\n [\n AnimationState.FOOTWORK_PIVOT_RIGHT,\n {\n state: AnimationState.FOOTWORK_PIVOT_RIGHT,\n frames: 15, // 250ms at 60fps\n fps: 60,\n loop: false,\n interruptible: false,\n priority: STEP_PRIORITY,\n duration: 0.25,\n },\n ],\n // Slide step (미끄럼보) - Both feet move together\n [\n AnimationState.FOOTWORK_SLIDE_FORWARD,\n {\n state: AnimationState.FOOTWORK_SLIDE_FORWARD,\n frames: 12, // 200ms at 60fps\n fps: 60,\n loop: false,\n interruptible: true, // Can be interrupted\n priority: 4 as AnimationPriority, // Same as defend\n duration: 0.2,\n },\n ],\n [\n AnimationState.FOOTWORK_SLIDE_BACK,\n {\n state: AnimationState.FOOTWORK_SLIDE_BACK,\n frames: 12, // 200ms at 60fps\n fps: 60,\n loop: false,\n interruptible: true,\n priority: 4 as AnimationPriority,\n duration: 0.2,\n },\n ],\n [\n AnimationState.FOOTWORK_SLIDE_LEFT,\n {\n state: AnimationState.FOOTWORK_SLIDE_LEFT,\n frames: 12, // 200ms at 60fps\n fps: 60,\n loop: false,\n interruptible: true,\n priority: 4 as AnimationPriority,\n duration: 0.2,\n },\n ],\n [\n AnimationState.FOOTWORK_SLIDE_RIGHT,\n {\n state: AnimationState.FOOTWORK_SLIDE_RIGHT,\n frames: 12, // 200ms at 60fps\n fps: 60,\n loop: false,\n interruptible: true,\n priority: 4 as AnimationPriority,\n duration: 0.2,\n },\n ],\n // Shuffle step (섞음보) - Quick micro-adjustment\n [\n AnimationState.FOOTWORK_SHUFFLE,\n {\n state: AnimationState.FOOTWORK_SHUFFLE,\n frames: 6, // 100ms at 60fps\n fps: 60,\n loop: false,\n interruptible: true,\n priority: 3 as AnimationPriority, // Same as stance_change\n duration: 0.1,\n },\n ],\n ]);\n\n/**\n * Player Animation State Machine\n *\n * Manages animation state, transitions, and timing with frame-accurate updates.\n * Integrates priority system, automatic animation queueing, and event callbacks.\n *\n * **Animation Queue**: Enabled by default with max size 3 and timestamp-based\n * conflict resolution. Automatically queues animations that cannot execute\n * immediately and processes them when the current animation completes.\n *\n * @example\n * ```typescript\n * const machine = new PlayerAnimationStateMachine(DEFAULT_ANIMATION_CONFIGS, {\n * onAnimationStart: (state) => console.log(`Started ${state}`),\n * onAnimationComplete: (state) => console.log(`Completed ${state}`),\n * onFrame: (frame, state) => {\n * if (state === \"attack\" && frame === 6) {\n * // Execute attack at midpoint (frame 6 of 12)\n * executeAttackLogic();\n * }\n * }\n * });\n *\n * // Animation queue is enabled by default\n * // Use transitionToQueued() for automatic queueing\n * machine.transitionToQueued(AnimationState.ATTACK); // Queues if can't execute\n *\n * // Or use regular transitionTo() (no queueing)\n * machine.transitionTo(AnimationState.ATTACK); // Fails if can't execute\n *\n * // In game loop (useFrame)\n * useFrame((state, delta) => {\n * const result = machine.update(delta);\n * updatePlayerVisuals(result.state, result.frame);\n * });\n * ```\n *\n * @korean 플레이어애니메이션상태머신\n */\nexport class PlayerAnimationStateMachine {\n /**\n * Static mapping from TrigramStance to guard AnimationState\n * Prevents repeated object allocation in transitionToStanceGuard()\n * @korean 자세방어상태맵\n */\n private static readonly GUARD_STATE_MAP: Record<\n TrigramStance,\n AnimationState\n > = {\n [TrigramStance.GEON]: AnimationState.STANCE_GUARD_GEON,\n [TrigramStance.TAE]: AnimationState.STANCE_GUARD_TAE,\n [TrigramStance.LI]: AnimationState.STANCE_GUARD_LI,\n [TrigramStance.JIN]: AnimationState.STANCE_GUARD_JIN,\n [TrigramStance.SON]: AnimationState.STANCE_GUARD_SON,\n [TrigramStance.GAM]: AnimationState.STANCE_GUARD_GAM,\n [TrigramStance.GAN]: AnimationState.STANCE_GUARD_GAN,\n [TrigramStance.GON]: AnimationState.STANCE_GUARD_GON,\n };\n\n /**\n * Static reverse mapping from guard AnimationState to TrigramStance\n * Prevents repeated object allocation in getCurrentGuardStance()\n * @korean 방어상태자세맵\n */\n private static readonly STANCE_FROM_GUARD_MAP: Record<string, TrigramStance> =\n {\n [AnimationState.STANCE_GUARD_GEON]: TrigramStance.GEON,\n [AnimationState.STANCE_GUARD_TAE]: TrigramStance.TAE,\n [AnimationState.STANCE_GUARD_LI]: TrigramStance.LI,\n [AnimationState.STANCE_GUARD_JIN]: TrigramStance.JIN,\n [AnimationState.STANCE_GUARD_SON]: TrigramStance.SON,\n [AnimationState.STANCE_GUARD_GAM]: TrigramStance.GAM,\n [AnimationState.STANCE_GUARD_GAN]: TrigramStance.GAN,\n [AnimationState.STANCE_GUARD_GON]: TrigramStance.GON,\n };\n\n private currentState: AnimationState = AnimationState.IDLE;\n private frameIndex = 0;\n private timeAccumulator = 0;\n private previousState: AnimationState | null = null;\n private justStarted = false;\n private justCompleted = false;\n\n /**\n * Current stance transition data (null when not in stance_change animation)\n *\n * **Korean**: 현재 자세 전환 데이터\n *\n * Tracks the active stance transition for use during stance_change animation.\n * Provides access to keyframes and blend weights for smooth interpolation.\n *\n * @korean 현재자세전환데이터\n */\n private currentStanceTransition: StanceTransition | null = null;\n\n /**\n * Motion prediction state for latency reduction\n *\n * **Korean**: 동작 예측 상태\n *\n * Tracks animation velocities for motion prediction to reduce perceived latency.\n * Updated each frame with velocity calculations for smooth anticipation.\n *\n * @korean 동작예측상태\n */\n private motionPrediction: MotionPredictionState =\n createMotionPredictionState();\n\n /**\n * Enable motion prediction for latency reduction\n *\n * **Korean**: 동작 예측 활성화\n *\n * When enabled, predicts future animation frames based on current velocity\n * to reduce perceived input latency by 16-33ms (1-2 frames at 60fps).\n *\n * @korean 동작예측활성화\n */\n private enableMotionPrediction: boolean = false;\n\n /**\n * Motion prediction time ahead (seconds)\n *\n * **Korean**: 예측 시간\n *\n * How far ahead to predict motion (default: 1 frame = 16.67ms at 60fps).\n * Typical range: 0.016-0.033 seconds for <50ms total latency.\n *\n * @korean 예측시간\n */\n private predictionTimeAhead: number = 0.01667; // 1 frame at 60fps\n\n /**\n * Previous keyframe for motion prediction velocity calculation\n *\n * **Korean**: 이전 키프레임\n *\n * @korean 이전키프레임\n */\n private previousKeyframe: AnimationKeyframe | null = null;\n\n /**\n * Preferred easing function for smooth transitions\n *\n * **Korean**: 선호 이징 함수\n *\n * Default easing curve for animation blending and transitions.\n * Can be overridden per animation or transition.\n *\n * @korean 선호이징함수\n */\n private preferredEasing: EasingName = \"natural-motion\";\n\n /**\n * Animation queue for pending animations\n *\n * **Korean**: 애니메이션 대기열\n *\n * Stores animation requests that couldn't be executed immediately\n * due to non-interruptible animations or priority conflicts.\n * Processed automatically when current animation completes.\n *\n * Enabled by default with max size 3 and timestamp-based conflict resolution.\n * Can be disabled with disableQueue() or reconfigured with enableQueue().\n *\n * @korean 애니메이션대기열\n */\n private animationQueue: AnimationQueue | null = new AnimationQueue(\n 3,\n \"timestamp\",\n );\n\n /**\n * Conflict resolution strategy for equal-priority animations\n *\n * **Korean**: 충돌 해결 전략\n *\n * Determines how to resolve conflicts when multiple animations\n * have equal priority. Default: timestamp (FIFO).\n *\n * @korean 충돌해결전략\n */\n private conflictStrategy: ConflictResolutionStrategy = \"timestamp\";\n\n /**\n * Create a new animation state machine\n *\n * Clones the provided config map so per-instance mutations\n * (e.g. dynamic attack duration) don't affect shared defaults.\n *\n * @param animations - Map of animation configurations (cloned internally)\n * @param events - Optional event callbacks\n *\n * @korean 생성자\n */\n private readonly animations: Map<AnimationState, MutableAnimationConfig>;\n\n constructor(\n animations: Map<AnimationState, AnimationConfig>,\n private readonly events?: AnimationEvents,\n ) {\n // Clone so we can safely mutate per-instance (e.g. attack duration)\n this.animations = new Map(\n Array.from(animations.entries()).map(([k, v]) => [k, { ...v }]),\n );\n }\n\n /**\n * Update animation state with delta time\n *\n * Call this in useFrame for 60fps updates.\n * Handles frame progression, looping, and completion.\n *\n * @param deltaTime - Time elapsed since last update (in seconds)\n * @returns Animation update result with current state and frame\n *\n * @korean 업데이트\n */\n update(deltaTime: number): AnimationUpdateResult {\n const currentAnim = this.animations.get(this.currentState);\n if (!currentAnim) {\n return {\n state: this.currentState,\n frame: 0,\n progress: 0,\n justCompleted: false,\n justStarted: false,\n };\n }\n\n // Reset just started/completed flags\n const wasJustStarted = this.justStarted;\n this.justStarted = false;\n const previousJustCompleted = this.justCompleted;\n this.justCompleted = false;\n\n // Accumulate time\n this.timeAccumulator += deltaTime;\n const frameDuration = 1 / currentAnim.fps;\n\n // Check if we should advance to next frame\n if (this.timeAccumulator >= frameDuration) {\n const previousFrame = this.frameIndex;\n this.frameIndex++;\n this.timeAccumulator -= frameDuration;\n\n // Emit frame event\n if (this.events?.onFrame && previousFrame !== this.frameIndex) {\n this.events.onFrame(this.frameIndex, this.currentState);\n }\n\n // Handle animation completion\n if (this.frameIndex >= currentAnim.frames) {\n if (currentAnim.loop) {\n // Loop back to start\n this.frameIndex = 0;\n } else {\n // Animation completed\n this.justCompleted = true;\n if (this.events?.onAnimationComplete) {\n this.events.onAnimationComplete(this.currentState);\n }\n\n // Auto-transition logic\n // Fall animations transition to ground states using the mapping\n if (this.currentState.startsWith(\"fall_\")) {\n const fallType = this.currentState.replace(\"fall_\", \"\");\n\n // Validate that fallType is a valid FallType before using in map\n if (\n fallType === \"forward\" ||\n fallType === \"backward\" ||\n fallType === \"side_left\" ||\n fallType === \"side_right\"\n ) {\n const groundState = FALL_TO_GROUND_MAP[fallType as FallType];\n const groundAnimKey = `ground_${groundState}`;\n\n // Validate that the constructed ground animation state actually exists\n if (\n DEFAULT_ANIMATION_CONFIGS.has(groundAnimKey as AnimationState)\n ) {\n const groundAnimState = groundAnimKey as AnimationState;\n\n this.previousState = this.currentState;\n this.currentState = groundAnimState;\n this.frameIndex = 0;\n this.timeAccumulator = 0;\n this.justStarted = true;\n\n if (this.events?.onAnimationStart) {\n this.events.onAnimationStart(groundAnimState);\n }\n } else {\n // Fallback: if mapping is invalid, safely transition to idle\n // instead of entering an undefined animation state.\n console.warn(\n \"[AnimationStateMachine] Invalid ground animation mapping for fall type:\",\n fallType,\n \"->\",\n groundAnimKey,\n );\n this.previousState = this.currentState;\n this.currentState = AnimationState.IDLE;\n this.frameIndex = 0;\n this.timeAccumulator = 0;\n this.justStarted = true;\n\n if (this.events?.onAnimationStart) {\n this.events.onAnimationStart(AnimationState.IDLE);\n }\n }\n } else {\n // Invalid fall type - fallback to idle\n console.warn(\n \"[AnimationStateMachine] Invalid fall animation state:\",\n this.currentState,\n );\n this.previousState = this.currentState;\n this.currentState = AnimationState.IDLE;\n this.frameIndex = 0;\n this.timeAccumulator = 0;\n this.justStarted = true;\n\n if (this.events?.onAnimationStart) {\n this.events.onAnimationStart(AnimationState.IDLE);\n }\n }\n }\n // Recovery animations transition to idle when complete\n else if (this.currentState.startsWith(\"recovery_\")) {\n this.previousState = this.currentState;\n this.currentState = AnimationState.IDLE;\n this.frameIndex = 0;\n this.timeAccumulator = 0;\n this.justStarted = true;\n\n if (this.events?.onAnimationStart) {\n this.events.onAnimationStart(AnimationState.IDLE);\n }\n }\n // Non-fall, non-recovery, non-looping animations transition to idle\n else if (\n this.currentState !== AnimationState.IDLE &&\n this.currentState !== AnimationState.KO &&\n !this.currentState.startsWith(\"ground_\")\n ) {\n // Clear stance transition data if completing stance_change\n if (this.currentState === AnimationState.STANCE_CHANGE) {\n this.clearStanceTransition();\n }\n\n // Transition to idle first\n this.previousState = this.currentState;\n this.currentState = AnimationState.IDLE;\n this.frameIndex = 0;\n this.timeAccumulator = 0;\n this.justStarted = true;\n\n if (this.events?.onAnimationStart) {\n this.events.onAnimationStart(AnimationState.IDLE);\n }\n\n // Then try to process next queued animation (from idle state)\n this.processNextQueuedAnimation();\n } else {\n // Stay on last frame (for ko and ground states)\n this.frameIndex = currentAnim.frames - 1;\n }\n }\n }\n }\n\n const progress =\n currentAnim.frames > 0 ? this.frameIndex / currentAnim.frames : 0;\n\n return {\n state: this.currentState,\n frame: this.frameIndex,\n progress,\n justCompleted: previousJustCompleted,\n justStarted: wasJustStarted,\n };\n }\n\n /**\n * Attempt to transition to a new animation state\n *\n * Checks transition rules and priority system before transitioning.\n *\n * @param newState - Target animation state\n * @returns Whether transition was successful\n *\n * @example\n * ```typescript\n * // Successful transitions\n * machine.transitionTo(\"walk\"); // idle -> walk\n * machine.transitionTo(\"attack\"); // walk -> attack\n *\n * // Failed transition (invalid or lower priority)\n * machine.transitionTo(\"walk\"); // attack -> walk (blocked, must complete first)\n * ```\n *\n * @korean 상태전환\n */\n transitionTo(newState: AnimationState): boolean {\n // Don't transition to same state\n if (this.currentState === newState) {\n return false;\n }\n\n // Check if transition is allowed by rules\n if (!isTransitionAllowed(this.currentState, newState)) {\n return false;\n }\n\n const currentAnim = this.animations.get(this.currentState);\n const newAnim = this.animations.get(newState);\n\n if (!newAnim) {\n return false;\n }\n\n // Check priority system\n if (\n currentAnim &&\n !canInterrupt(this.currentState, newState, currentAnim.interruptible)\n ) {\n return false;\n }\n\n // Emit interrupt event if current animation wasn't completed\n if (this.frameIndex < (currentAnim?.frames ?? 0) - 1) {\n if (this.events?.onAnimationInterrupted) {\n this.events.onAnimationInterrupted(this.currentState, newState);\n }\n }\n\n // Clear stance transition data if interrupting stance_change\n if (this.currentState === AnimationState.STANCE_CHANGE) {\n this.clearStanceTransition();\n }\n\n // Execute transition\n this.previousState = this.currentState;\n this.currentState = newState;\n this.frameIndex = 0;\n this.timeAccumulator = 0;\n this.justStarted = true;\n this.justCompleted = false;\n\n // Emit start event\n if (this.events?.onAnimationStart) {\n this.events.onAnimationStart(newState);\n }\n\n return true;\n }\n\n /**\n * Transition to ATTACK state with a technique-specific duration.\n *\n * The default ATTACK config is 200ms (12 frames), but real techniques\n * range from 350ms to 1200ms. This method overrides the ATTACK frame\n * count to match the actual skeletal animation duration so the state\n * machine stays in ATTACK for the full technique.\n *\n * @param durationSeconds - The skeletal animation duration in seconds\n * @returns Whether transition was successful\n *\n * @example\n * ```typescript\n * // Jab animation is 0.55s (TECHNIQUE_TIMING.FAST)\n * machine.transitionToAttack(0.55);\n * ```\n *\n * @korean 공격전환 (기술별 지속시간)\n */\n transitionToAttack(durationSeconds: number): boolean {\n const attackConfig = this.animations.get(AnimationState.ATTACK);\n if (attackConfig) {\n const fps = attackConfig.fps || 60;\n attackConfig.frames = Math.max(1, Math.round(durationSeconds * fps));\n attackConfig.duration = durationSeconds;\n }\n return this.transitionTo(AnimationState.ATTACK);\n }\n\n /**\n * Get current animation state\n *\n * @returns Current animation state\n * @korean 현재상태가져오기\n */\n getCurrentState(): AnimationState {\n return this.currentState;\n }\n\n /**\n * Get current frame index\n *\n * @returns Current frame index (0 to frames-1)\n * @korean 현재프레임가져오기\n */\n getCurrentFrame(): number {\n return this.frameIndex;\n }\n\n /**\n * Get previous animation state\n *\n * @returns Previous animation state or null\n * @korean 이전상태가져오기\n */\n getPreviousState(): AnimationState | null {\n return this.previousState;\n }\n\n /**\n * Get current animation configuration\n *\n * @returns Current animation config or undefined\n * @korean 현재애니메이션설정가져오기\n */\n getCurrentAnimation(): AnimationConfig | undefined {\n return this.animations.get(this.currentState);\n }\n\n /**\n * Reset animation state machine to idle\n *\n * @korean 초기화\n */\n reset(): void {\n this.currentState = AnimationState.IDLE;\n this.frameIndex = 0;\n this.timeAccumulator = 0;\n this.previousState = null;\n this.justStarted = false;\n this.justCompleted = false;\n }\n\n /**\n * Get full internal state (for debugging/testing)\n *\n * @returns Current state machine state\n * @korean 상태가져오기\n */\n getState(): AnimationMachineState {\n return {\n currentState: this.currentState,\n frameIndex: this.frameIndex,\n timeAccumulator: this.timeAccumulator,\n isPlaying: true,\n previousState: this.previousState,\n };\n }\n\n /**\n * Transition to stance-specific guard animation\n *\n * Convenience method to transition to a stance guard based on trigram stance.\n * Automatically maps trigram stance to corresponding guard animation state.\n *\n * @param stance - Trigram stance identifier\n * @returns Whether transition was successful\n *\n * @example\n * ```typescript\n * // When player changes to Fire stance\n * machine.transitionToStanceGuard(TrigramStance.LI);\n * // Internally transitions to \"stance_guard_li\" animation state\n * ```\n *\n * @korean 자세방어전환\n */\n transitionToStanceGuard(stance: TrigramStance): boolean {\n const guardAnimationState =\n PlayerAnimationStateMachine.GUARD_STATE_MAP[stance];\n\n // Verify the guard animation exists in our configs\n if (!guardAnimationState || !this.animations.has(guardAnimationState)) {\n console.warn(`No guard animation configured for stance: ${stance}`);\n return false;\n }\n\n return this.transitionTo(guardAnimationState);\n }\n\n /**\n * Check if current animation is a stance guard\n *\n * @returns True if currently in a stance guard animation\n * @korean 자세방어상태확인\n */\n isInStanceGuard(): boolean {\n return this.currentState.startsWith(\"stance_guard_\");\n }\n\n /**\n * Get current guard stance if in a guard animation\n *\n * @returns Trigram stance or null if not in guard\n * @korean 현재방어자세가져오기\n */\n getCurrentGuardStance(): TrigramStance | null {\n if (!this.isInStanceGuard()) {\n return null;\n }\n\n const stance =\n PlayerAnimationStateMachine.STANCE_FROM_GUARD_MAP[this.currentState];\n\n // Validate that we got a valid stance\n if (!stance) {\n console.warn(`Invalid guard state detected: ${this.currentState}`);\n return null;\n }\n\n return stance;\n }\n\n /**\n * Transition to stance_change animation with specific stance transition data\n *\n * **Korean**: 자세 전환 애니메이션 시작\n *\n * Initiates a stance change animation with the specific transition data\n * from the 64-transition matrix. This provides stance-specific keyframes\n * and blend weights for smooth interpolation.\n *\n * @param fromStance - Source trigram stance\n * @param toStance - Target trigram stance\n * @returns Whether transition was successful\n *\n * @example\n * ```typescript\n * // Start transition from Heaven to Lake stance\n * const success = machine.transitionToStanceChange(\n * TrigramStance.GEON,\n * TrigramStance.TAE\n * );\n *\n * if (success) {\n * // During update loop, use getStanceTransitionBlend() to interpolate\n * const blend = machine.getStanceTransitionBlend();\n * if (blend) {\n * // Apply blend weights to stance poses\n * applyStanceBlend(blend);\n * }\n * }\n * ```\n *\n * @korean 자세전환애니메이션시작\n */\n transitionToStanceChange(\n fromStance: TrigramStance,\n toStance: TrigramStance,\n ): boolean {\n // Get the specific transition data from the 64-transition matrix\n const transitionData = getStanceTransition(fromStance, toStance);\n\n if (!transitionData) {\n console.warn(\n `[AnimationStateMachine] No transition data found for ${fromStance} -> ${toStance}`,\n );\n return false;\n }\n\n // Store current transition data in case we need to restore it\n const previousTransitionData = this.currentStanceTransition;\n\n // Temporarily set the new transition data\n this.currentStanceTransition = transitionData;\n\n // Initiate the stance_change animation\n const success = this.transitionTo(AnimationState.STANCE_CHANGE);\n\n // If transition failed, restore previous transition data\n if (!success) {\n this.currentStanceTransition = previousTransitionData;\n }\n\n return success;\n }\n\n /**\n * Get current stance transition data\n *\n * **Korean**: 현재 자세 전환 데이터 가져오기\n *\n * Returns the active stance transition data during stance_change animation.\n * Null if not currently in a stance transition.\n *\n * @returns Current stance transition or null\n *\n * @korean 현재자세전환데이터가져오기\n */\n getCurrentStanceTransition(): StanceTransition | null {\n return this.currentStanceTransition;\n }\n\n /**\n * Get interpolated blend weights for current stance transition frame\n *\n * **Korean**: 현재 프레임 블렌드 가중치\n *\n * Returns the interpolated blend data for the current frame during\n * stance_change animation. Uses the keyframe data from the transition\n * matrix to provide smooth stance interpolation.\n *\n * @returns Blend data with stance and weight, or null if not in transition\n *\n * @example\n * ```typescript\n * // In rendering loop during stance transition\n * const blend = machine.getStanceTransitionBlend();\n * if (blend) {\n * console.log(`Frame ${blend.frame}: ${blend.stance} at ${blend.blend}x weight`);\n * // Apply blended pose: blend.blend * targetPose + (1 - blend.blend) * sourcePose\n * }\n * ```\n *\n * @korean 현재프레임블렌드가중치\n */\n getStanceTransitionBlend(): {\n frame: number;\n stance: TrigramStance | \"neutral\";\n blend: number;\n } | null {\n // Only valid during stance_change animation\n if (\n this.currentState !== AnimationState.STANCE_CHANGE ||\n !this.currentStanceTransition\n ) {\n return null;\n }\n\n const keyframes = this.currentStanceTransition.keyframes;\n const currentFrame = this.frameIndex;\n\n // Find the two keyframes to interpolate between\n let prevKeyframe = keyframes[0];\n let nextKeyframe = keyframes[keyframes.length - 1];\n\n for (let i = 0; i < keyframes.length - 1; i++) {\n if (\n keyframes[i].frame <= currentFrame &&\n keyframes[i + 1].frame > currentFrame\n ) {\n prevKeyframe = keyframes[i];\n nextKeyframe = keyframes[i + 1];\n break;\n }\n }\n\n // If we're exactly on a keyframe, return it directly\n const exactKeyframe = keyframes.find((kf) => kf.frame === currentFrame);\n if (exactKeyframe) {\n return {\n frame: currentFrame,\n stance: exactKeyframe.stance,\n blend: exactKeyframe.blend,\n };\n }\n\n // Linear interpolation between keyframes\n const frameRange = nextKeyframe.frame - prevKeyframe.frame;\n const frameProgress =\n frameRange > 0 ? (currentFrame - prevKeyframe.frame) / frameRange : 0;\n\n const interpolatedBlend =\n prevKeyframe.blend +\n (nextKeyframe.blend - prevKeyframe.blend) * frameProgress;\n\n // Use the next keyframe's stance as we're transitioning towards it\n return {\n frame: currentFrame,\n stance: nextKeyframe.stance,\n blend: interpolatedBlend,\n };\n }\n\n /**\n * Check if currently in a stance transition animation\n *\n * **Korean**: 자세 전환 중 확인\n *\n * @returns True if currently executing a stance_change animation\n * @korean 자세전환중확인\n */\n isInStanceTransition(): boolean {\n return (\n this.currentState === AnimationState.STANCE_CHANGE &&\n this.currentStanceTransition !== null\n );\n }\n\n /**\n * Clear stance transition data (called automatically when transition completes)\n *\n * **Korean**: 자세 전환 데이터 초기화\n *\n * @internal\n * @korean 자세전환데이터초기화\n */\n private clearStanceTransition(): void {\n this.currentStanceTransition = null;\n }\n\n /**\n * Enable or disable motion prediction\n *\n * **Korean**: 동작 예측 설정\n *\n * Enables motion prediction to reduce perceived input latency by predicting\n * future animation frames based on current velocity (1-2 frames ahead).\n *\n * @param enabled - Whether to enable motion prediction\n * @param predictionTime - Optional: time ahead to predict (default: 16.67ms)\n *\n * @example\n * ```typescript\n * // Enable motion prediction for 1 frame (16.67ms at 60fps)\n * machine.setMotionPrediction(true);\n *\n * // Enable with 2 frames prediction (33.33ms)\n * machine.setMotionPrediction(true, 0.03333);\n * ```\n *\n * @korean 동작예측설정\n */\n setMotionPrediction(enabled: boolean, predictionTime?: number): void {\n this.enableMotionPrediction = enabled;\n if (predictionTime !== undefined) {\n // Clamp to 50ms maximum for <50ms total latency\n this.predictionTimeAhead = Math.min(predictionTime, 0.05);\n }\n }\n\n /**\n * Get motion prediction state\n *\n * **Korean**: 동작 예측 상태 가져오기\n *\n * @returns Current motion prediction state\n * @korean 동작예측상태가져오기\n */\n getMotionPredictionState(): MotionPredictionState {\n return this.motionPrediction;\n }\n\n /**\n * Check if motion prediction is enabled\n *\n * **Korean**: 동작 예측 활성화 확인\n *\n * @returns True if motion prediction is enabled\n * @korean 동작예측활성화확인\n */\n isMotionPredictionEnabled(): boolean {\n return this.enableMotionPrediction;\n }\n\n /**\n * Set preferred easing function for transitions\n *\n * **Korean**: 선호 이징 함수 설정\n *\n * Sets the default easing curve for animation transitions.\n * Can use presets like \"natural-motion\", \"smooth-transition\", etc.\n *\n * @param easingName - Easing function name\n *\n * @example\n * ```typescript\n * // Use natural motion for Korean martial arts\n * machine.setPreferredEasing(\"natural-motion\");\n *\n * // Use explosive power for strike animations\n * machine.setPreferredEasing(\"explosive-power\");\n * ```\n *\n * @korean 선호이징함수설정\n */\n setPreferredEasing(easingName: EasingName): void {\n this.preferredEasing = easingName;\n }\n\n /**\n * Get preferred easing function\n *\n * **Korean**: 선호 이징 함수 가져오기\n *\n * @returns Current preferred easing name\n * @korean 선호이징함수가져오기\n */\n getPreferredEasing(): EasingName {\n return this.preferredEasing;\n }\n\n /**\n * Update motion prediction with skeletal keyframe data\n *\n * **Korean**: 동작 예측 업데이트\n *\n * This should be called from the skeletal animation layer when applying\n * interpolated keyframes to the rig. It updates velocity tracking for\n * motion prediction to reduce perceived latency.\n *\n * Integration point: Call this from your skeletal animation system after\n * computing the current interpolated keyframe (e.g., from getInterpolatedKeyframe).\n *\n * @param currentKeyframe - Current skeletal animation keyframe with bone positions/rotations\n * @param deltaTime - Time elapsed since last update\n *\n * @example\n * ```typescript\n * // In your skeletal animation update loop:\n * const currentKeyframe = getInterpolatedKeyframe(animation, time);\n *\n * // Update motion prediction (for next frame)\n * if (machine.isMotionPredictionEnabled()) {\n * machine.updateMotionPredictionState(currentKeyframe, deltaTime);\n * }\n *\n * // Apply keyframe to rig\n * applyKeyframeToRig(rig, currentKeyframe);\n * ```\n *\n * @korean 동작예측업데이트\n */\n updateMotionPredictionState(\n currentKeyframe: AnimationKeyframe,\n deltaTime: number,\n ): void {\n if (!this.enableMotionPrediction) {\n return;\n }\n\n // Update velocity tracking if we have a previous keyframe\n if (this.previousKeyframe) {\n this.motionPrediction = updateMotionPrediction(\n this.motionPrediction,\n this.previousKeyframe,\n currentKeyframe,\n deltaTime,\n );\n }\n\n // Store current keyframe for next update\n this.previousKeyframe = currentKeyframe;\n }\n\n /**\n * Get predicted future keyframe for latency reduction\n *\n * **Korean**: 예측된 미래 키프레임 가져오기\n *\n * Returns a keyframe predicted ahead by predictionTimeAhead (default: 1 frame).\n * This reduces perceived input latency by showing where the animation will be\n * in the near future rather than where it currently is.\n *\n * Integration point: Use this instead of the current keyframe when applying\n * to the rig if motion prediction is enabled.\n *\n * @param currentKeyframe - Current skeletal animation keyframe\n * @returns Predicted future keyframe, or current if prediction disabled\n *\n * @example\n * ```typescript\n * // In your skeletal animation update loop:\n * let keyframeToApply = currentKeyframe;\n *\n * if (machine.isMotionPredictionEnabled()) {\n * keyframeToApply = machine.getPredictedKeyframe(currentKeyframe);\n * }\n *\n * applyKeyframeToRig(rig, keyframeToApply);\n * ```\n *\n * @korean 예측키프레임가져오기\n */\n getPredictedKeyframe(currentKeyframe: AnimationKeyframe): AnimationKeyframe {\n if (!this.enableMotionPrediction || !this.previousKeyframe) {\n return currentKeyframe;\n }\n\n return predictFutureKeyframe(\n currentKeyframe,\n this.motionPrediction,\n this.predictionTimeAhead,\n );\n }\n\n // ===== Animation Queue Methods (애니메이션 대기열) =====\n\n /**\n * Enable or reconfigure animation queue system\n *\n * **Korean**: 애니메이션 대기열 활성화/재설정\n *\n * The queue is enabled by default. Use this method to reconfigure the\n * queue size or conflict resolution strategy.\n *\n * @param maxSize - Maximum queue size (default: 3)\n * @param conflictStrategy - Conflict resolution strategy (default: \"timestamp\")\n *\n * @example\n * ```typescript\n * // Queue is enabled by default, but you can reconfigure it\n * machine.enableQueue(5, \"requested\");\n * ```\n *\n * @korean 대기열활성화\n */\n enableQueue(\n maxSize: number = 3,\n conflictStrategy: ConflictResolutionStrategy = \"timestamp\",\n ): void {\n this.animationQueue = new AnimationQueue(maxSize, conflictStrategy);\n this.conflictStrategy = conflictStrategy;\n }\n\n /**\n * Disable animation queue system\n *\n * **Korean**: 애니메이션 대기열 비활성화\n *\n * Disables the queue and clears any pending animations.\n *\n * @korean 대기열비활성화\n */\n disableQueue(): void {\n this.animationQueue = null;\n }\n\n /**\n * Check if queue is enabled\n *\n * **Korean**: 대기열 활성화 여부\n *\n * @returns True if queue is enabled\n * @korean 대기열활성화여부\n */\n isQueueEnabled(): boolean {\n return this.animationQueue !== null;\n }\n\n /**\n * Attempt to transition to a new animation state with queue support\n *\n * **Korean**: 대기열 지원 상태 전환\n *\n * Enhanced version of transitionTo() that automatically queues animations\n * when they cannot be executed immediately. Since the queue is enabled by\n * default, this is the recommended method for animation transitions.\n *\n * The queued animation will be automatically processed when the current\n * animation completes, following priority and conflict resolution rules.\n *\n * @param newState - Target animation state\n * @returns Whether transition was successful, queued, or failed\n *\n * @example\n * ```typescript\n * // Queue is enabled by default\n * const result = machine.transitionToQueued(AnimationState.ATTACK);\n * // Returns \"success\" if transitioned immediately\n * // Returns \"queued\" if couldn't interrupt but was queued\n * // Returns \"failed\" if queue is full or disabled\n * ```\n *\n * @korean 대기열상태전환\n */\n transitionToQueued(\n newState: AnimationState,\n ): \"success\" | \"queued\" | \"failed\" {\n // Try normal transition first\n const timestamp = performance.now();\n const priority = this.animations.get(newState)?.priority ?? 0;\n\n const success = this.transitionTo(newState);\n if (success) {\n return \"success\";\n }\n\n // If transition failed and queue is enabled, try to enqueue\n if (this.animationQueue) {\n const request: AnimationRequest = {\n state: newState,\n timestamp,\n priority,\n };\n\n const enqueued = this.animationQueue.enqueue(request);\n return enqueued ? \"queued\" : \"failed\";\n }\n\n return \"failed\";\n }\n\n /**\n * Process next queued animation if available\n *\n * **Korean**: 다음 대기열 애니메이션 처리\n *\n * Should be called automatically when an animation completes.\n * Dequeues and executes the highest priority pending animation.\n *\n * @returns Whether a queued animation was executed\n *\n * @internal\n * @korean 다음대기열처리\n */\n private processNextQueuedAnimation(): boolean {\n if (!this.animationQueue || this.animationQueue.isEmpty()) {\n return false;\n }\n\n const nextRequest = this.animationQueue.dequeue();\n if (!nextRequest) {\n return false;\n }\n\n // Try to execute the queued animation\n const success = this.transitionTo(nextRequest.state);\n\n if (!success) {\n // Log failure so queued animations do not disappear silently\n // Korean: 대기열 애니메이션 전이가 실패했음을 로그로 남깁니다.\n // This helps diagnose cases where transition rules, missing states,\n // or priority conflicts prevent a queued animation from playing.\n console.warn(\n \"[AnimationStateMachine] Failed to transition to queued animation state\",\n {\n requestedState: nextRequest.state,\n currentState: this.currentState,\n },\n );\n }\n\n return success;\n }\n\n /**\n * Get current animation queue state\n *\n * **Korean**: 현재 대기열 상태\n *\n * Returns information about the current queue state for debugging\n * or UI display.\n *\n * @returns Queue state information\n *\n * @korean 현재대기열상태\n */\n getQueueState(): {\n enabled: boolean;\n size: number;\n maxSize: number;\n pending: readonly AnimationRequest[];\n } {\n if (!this.animationQueue) {\n return {\n enabled: false,\n size: 0,\n maxSize: 0,\n pending: [],\n };\n }\n\n return {\n enabled: true,\n size: this.animationQueue.size(),\n maxSize: this.animationQueue.getMaxSize(),\n pending: this.animationQueue.getAll(),\n };\n }\n\n /**\n * Clear all pending queued animations\n *\n * **Korean**: 모든 대기열 초기화\n *\n * Removes all pending animations from the queue.\n *\n * @korean 모든대기열초기화\n */\n clearQueue(): void {\n this.animationQueue?.clear();\n }\n\n /**\n * Set conflict resolution strategy\n *\n * **Korean**: 충돌 해결 전략 설정\n *\n * Changes the strategy used to resolve equal-priority conflicts.\n *\n * @param strategy - Conflict resolution strategy\n *\n * @korean 충돌해결전략설정\n */\n setConflictStrategy(strategy: ConflictResolutionStrategy): void {\n this.conflictStrategy = strategy;\n\n // Keep the animation queue's strategy in sync with the state machine\n if (this.animationQueue) {\n this.animationQueue.setConflictStrategy(strategy);\n }\n }\n\n /**\n * Get current conflict resolution strategy\n *\n * **Korean**: 충돌 해결 전략 가져오기\n *\n * @returns Current conflict resolution strategy\n * @korean 충돌해결전략가져오기\n */\n getConflictStrategy(): ConflictResolutionStrategy {\n return this.conflictStrategy;\n }\n\n /**\n * Dispose of the animation state machine\n *\n * **Korean**: 애니메이션 상태 머신 해제\n *\n * Clears all internal state, queues, and references to prevent memory leaks.\n * Should be called when the state machine is no longer needed (e.g., component unmount).\n *\n * @korean 애니메이션상태머신해제\n */\n dispose(): void {\n // Clear animation queue\n this.animationQueue?.clear();\n this.animationQueue = null;\n\n // Clear stance transition data\n this.currentStanceTransition = null;\n\n // Clear motion prediction state\n this.motionPrediction = createMotionPredictionState();\n this.previousKeyframe = null;\n\n // Reset state to initial values\n this.currentState = AnimationState.IDLE;\n this.previousState = AnimationState.IDLE;\n this.frameIndex = 0;\n this.timeAccumulator = 0;\n this.justStarted = false;\n this.justCompleted = false;\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAoEA,IAAa,4BACX,IAAI,IAAqC;CACvC,CACE,eAAe,MACf;EACE,OAAO,eAAe;EACtB,QAAQ;EACR,KAAK;EACL,MAAM;EACN,eAAe;EACf,UAAU;EACV,UAAU,IAAI;CAChB,CACF;CACA,CACE,eAAe,MACf;EACE,OAAO,eAAe;EACtB,QAAQ;EACR,KAAK;EACL,MAAM;EACN,eAAe;EACf,UAAU;EACV,UAAU,IAAI;CAChB,CACF;CACA,CACE,eAAe,KACf;EACE,OAAO,eAAe;EACtB,QAAQ;EACR,KAAK;EACL,MAAM;EACN,eAAe;EACf,UAAU;EACV,UAAU,IAAI;CAChB,CACF;CACA,CACE,eAAe,eACf;EACE,OAAO,eAAe;EACtB,QAAQ;EACR,KAAK;EACL,MAAM;EACN,eAAe;EACf,UAAU;EACV,UAAU;EACV,QAAQ;CACV,CACF;CACA,CACE,eAAe,oBACf;EACE,OAAO,eAAe;EACtB,QAAQ;EACR,KAAK;EACL,MAAM;EACN,eAAe;EACf,UAAU;EACV,UAAU;CACZ,CACF;CACA,CACE,eAAe,QACf;EACE,OAAO,eAAe;EACtB,QAAQ;EACR,KAAK;EACL,MAAM;EACN,eAAe;EACf,UAAU;EACV,UAAU,IAAI;CAChB,CACF;CAEA,CACE,eAAe,sBACf;EACE,OAAO,eAAe;EACtB,QAAQ;EACR,KAAK;EACL,MAAM;EACN,eAAe;EACf,UAAU;EACV,UAAU;EACV,QAAQ;CACV,CACF;CACA,CACE,eAAe,cACf;EACE,OAAO,eAAe;EACtB,QAAQ;EACR,KAAK;EACL,MAAM;EACN,eAAe;EACf,UAAU;EACV,UAAU;EACV,eAAe;CACjB,CACF;CACA,CACE,eAAe,oBACf;EACE,OAAO,eAAe;EACtB,QAAQ;EACR,KAAK;EACL,MAAM;EACN,eAAe;EACf,UAAU;EACV,UAAU;EACV,uBAAuB;CACzB,CACF;CACA,CACE,eAAe,iBACf;EACE,OAAO,eAAe;EACtB,QAAQ;EACR,KAAK;EACL,MAAM;EACN,eAAe;EACf,UAAU;EACV,UAAU;EACV,QAAQ;CACV,CACF;CACA,CACE,eAAe,QACf;EACE,OAAO,eAAe;EACtB,QAAQ;EACR,KAAK;EACL,MAAM;EACN,eAAe;EACf,UAAA;EACA,UAAU,KAAK;EACf,QAAQ;CACV,CACF;CACA,CACE,eAAe,KACf;EACE,OAAO,eAAe;EACtB,QAAQ;EACR,KAAK;EACL,MAAM;EACN,eAAe;EACf,UAAU;EACV,UAAU,IAAI;CAChB,CACF;CACA,CACE,eAAe,IACf;EACE,OAAO,eAAe;EACtB,QAAQ;EACR,KAAK;EACL,MAAM;EACN,eAAe;EACf,UAAU;EACV,UAAU;CACZ,CACF;CAEA,CACE,eAAe,cACf;EACE,OAAO,eAAe;EACtB,QAAQ;EACR,KAAK;EACL,MAAM;EACN,eAAe;EACf,UAAU;EACV,UAAU;CACZ,CACF;CACA,CACE,eAAe,eACf;EACE,OAAO,eAAe;EACtB,QAAQ;EACR,KAAK;EACL,MAAM;EACN,eAAe;EACf,UAAU;EACV,UAAU;CACZ,CACF;CACA,CACE,eAAe,gBACf;EACE,OAAO,eAAe;EACtB,QAAQ;EACR,KAAK;EACL,MAAM;EACN,eAAe;EACf,UAAU;EACV,UAAU;CACZ,CACF;CACA,CACE,eAAe,iBACf;EACE,OAAO,eAAe;EACtB,QAAQ;EACR,KAAK;EACL,MAAM;EACN,eAAe;EACf,UAAU;EACV,UAAU;CACZ,CACF;CAEA,CACE,eAAe,cACf;EACE,OAAO,eAAe;EACtB,QAAQ;EACR,KAAK;EACL,MAAM;EACN,eAAe;EACf,UAAU;EACV,UAAU,IAAI;CAChB,CACF;CACA,CACE,eAAe,eACf;EACE,OAAO,eAAe;EACtB,QAAQ;EACR,KAAK;EACL,MAAM;EACN,eAAe;EACf,UAAU;EACV,UAAU,IAAI;CAChB,CACF;CACA,CACE,eAAe,kBACf;EACE,OAAO,eAAe;EACtB,QAAQ;EACR,KAAK;EACL,MAAM;EACN,eAAe;EACf,UAAU;EACV,UAAU,IAAI;CAChB,CACF;CACA,CACE,eAAe,mBACf;EACE,OAAO,eAAe;EACtB,QAAQ;EACR,KAAK;EACL,MAAM;EACN,eAAe;EACf,UAAU;EACV,UAAU,IAAI;CAChB,CACF;CAEA,CACE,eAAe,wBACf;EACE,OAAO,eAAe;EACtB,QAAQ;EACR,KAAK;EACL,MAAM;EACN,eAAe;EACf,UAAU;EACV,UAAU;CACZ,CACF;CACA,CACE,eAAe,yBACf;EACE,OAAO,eAAe;EACtB,QAAQ;EACR,KAAK;EACL,MAAM;EACN,eAAe;EACf,UAAU;EACV,UAAU;CACZ,CACF;CACA,CACE,eAAe,eACf;EACE,OAAO,eAAe;EACtB,QAAQ;EACR,KAAK;EACL,MAAM;EACN,eAAe;EACf,UAAU;EACV,UAAU;CACZ,CACF;CACA,CACE,eAAe,oBACf;EACE,OAAO,eAAe;EACtB,QAAQ;EACR,KAAK;EACL,MAAM;EACN,eAAe;EACf,UAAU;EACV,UAAU;CACZ,CACF;CAEA,CACE,eAAe,WACf;EACE,OAAO,eAAe;EACtB,QAAQ;EACR,KAAK;EACL,MAAM;EACN,eAAe;EACf,UAAA;EACA,UAAU,KAAK;CACjB,CACF;CACA,CACE,eAAe,YACf;EACE,OAAO,eAAe;EACtB,QAAQ;EACR,KAAK;EACL,MAAM;EACN,eAAe;EACf,UAAA;EACA,UAAU,KAAK;CACjB,CACF;CAEA,CACE,eAAe,mBACf;EACE,OAAO,eAAe;EACtB,QAAQ;EACR,KAAK;EACL,MAAM;EACN,eAAe;EACf,UAAU;EACV,UAAU,IAAI;CAChB,CACF;CACA,CACE,eAAe,kBACf;EACE,OAAO,eAAe;EACtB,QAAQ;EACR,KAAK;EACL,MAAM;EACN,eAAe;EACf,UAAU;EACV,UAAU,IAAI;CAChB,CACF;CACA,CACE,eAAe,iBACf;EACE,OAAO,eAAe;EACtB,QAAQ;EACR,KAAK;EACL,MAAM;EACN,eAAe;EACf,UAAU;EACV,UAAU,IAAI;CAChB,CACF;CACA,CACE,eAAe,kBACf;EACE,OAAO,eAAe;EACtB,QAAQ;EACR,KAAK;EACL,MAAM;EACN,eAAe;EACf,UAAU;EACV,UAAU,IAAI;CAChB,CACF;CACA,CACE,eAAe,kBACf;EACE,OAAO,eAAe;EACtB,QAAQ;EACR,KAAK;EACL,MAAM;EACN,eAAe;EACf,UAAU;EACV,UAAU,IAAI;CAChB,CACF;CACA,CACE,eAAe,kBACf;EACE,OAAO,eAAe;EACtB,QAAQ;EACR,KAAK;EACL,MAAM;EACN,eAAe;EACf,UAAU;EACV,UAAU,IAAI;CAChB,CACF;CACA,CACE,eAAe,kBACf;EACE,OAAO,eAAe;EACtB,QAAQ;EACR,KAAK;EACL,MAAM;EACN,eAAe;EACf,UAAU;EACV,UAAU,IAAI;CAChB,CACF;CACA,CACE,eAAe,kBACf;EACE,OAAO,eAAe;EACtB,QAAQ;EACR,KAAK;EACL,MAAM;EACN,eAAe;EACf,UAAU;EACV,UAAU,IAAI;CAChB,CACF;CAGA,CACE,eAAe,cACf;EACE,OAAO,eAAe;EACtB,QAAQ;EACR,KAAK;EACL,MAAM;EACN,eAAe;EACf,UAAA;EACA,UAAU;CACZ,CACF;CACA,CACE,eAAe,WACf;EACE,OAAO,eAAe;EACtB,QAAQ;EACR,KAAK;EACL,MAAM;EACN,eAAe;EACf,UAAA;EACA,UAAU;CACZ,CACF;CACA,CACE,eAAe,WACf;EACE,OAAO,eAAe;EACtB,QAAQ;EACR,KAAK;EACL,MAAM;EACN,eAAe;EACf,UAAA;EACA,UAAU;CACZ,CACF;CACA,CACE,eAAe,YACf;EACE,OAAO,eAAe;EACtB,QAAQ;EACR,KAAK;EACL,MAAM;EACN,eAAe;EACf,UAAA;EACA,UAAU;CACZ,CACF;CACA,CACE,eAAe,mBACf;EACE,OAAO,eAAe;EACtB,QAAQ;EACR,KAAK;EACL,MAAM;EACN,eAAe;EACf,UAAA;EACA,UAAU;CACZ,CACF;CACA,CACE,eAAe,oBACf;EACE,OAAO,eAAe;EACtB,QAAQ;EACR,KAAK;EACL,MAAM;EACN,eAAe;EACf,UAAA;EACA,UAAU;CACZ,CACF;CACA,CACE,eAAe,gBACf;EACE,OAAO,eAAe;EACtB,QAAQ;EACR,KAAK;EACL,MAAM;EACN,eAAe;EACf,UAAA;EACA,UAAU;CACZ,CACF;CACA,CACE,eAAe,iBACf;EACE,OAAO,eAAe;EACtB,QAAQ;EACR,KAAK;EACL,MAAM;EACN,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"}
1
+ {"version":3,"file":"AnimationStateMachine.js","names":[],"sources":["../../../../src/systems/animation/core/AnimationStateMachine.ts"],"sourcesContent":["/**\n * Player Animation State Machine for Black Trigram\n *\n * Manages player character animations with frame-accurate timing at 60fps.\n * Supports animation priorities, transitions, and event callbacks.\n *\n * Based on game-design.md specifications:\n * - Attack: 12 frames (200ms at 60fps)\n * - Block: 4 frames (67ms at 60fps)\n * - Walk: 6 frames\n * - Stance change: 600ms\n *\n * @module systems/animation/AnimationStateMachine\n * @category Animation\n * @korean 애니메이션상태머신\n */\n\nimport { TrigramStance } from \"@/types/common\";\nimport type { AnimationKeyframe } from \"@/types/skeletal\";\nimport {\n createMotionPredictionState,\n predictFutureKeyframe,\n updateMotionPrediction,\n type EasingName,\n type MotionPredictionState,\n} from \"../builders/KeyframeInterpolation\";\nimport {\n AnimationQueue,\n canInterrupt,\n type AnimationRequest,\n type ConflictResolutionStrategy,\n} from \"./AnimationPriority\";\nimport {\n getStanceTransition,\n isTransitionAllowed,\n type StanceTransition,\n} from \"./AnimationTransitions\";\nimport type {\n AnimationConfig,\n AnimationEvents,\n AnimationMachineState,\n AnimationPriority,\n AnimationUpdateResult,\n FallType,\n MutableAnimationConfig,\n} from \"./types\";\nimport { AnimationState, FALL_TO_GROUND_MAP, STEP_PRIORITY } from \"./types\";\n\n/**\n * Default animation configurations based on game-design.md\n *\n * Frame timings:\n * - Attack: 12 frames = 200ms at 60fps\n * - Block: 4 frames = 67ms at 60fps\n * - Walk: 6 frames = 100ms at 60fps\n * - Hit: 4 frames = 67ms at 60fps\n * - Stance change: 36 frames = 600ms at 60fps\n * - Stance guards: 4-6 frames = breathing animation at 60fps\n * - Tactical steps: 18 frames = 300ms at 60fps, 30cm distance\n *\n * Defensive animations (방어 애니메이션):\n * - Block Success (막기): 8 frames = 133ms - absorb impact, maintain guard\n * - Parry Deflect (받아넘기기): 10 frames = 167ms - redirect attack, counter window\n * - Guard Break (방어붕괴): 15 frames = 250ms - arms forced wide, vulnerable\n * - Guard Recovery (방어복구): 12 frames = 200ms - restore guard position\n *\n * @korean 기본애니메이션설정\n */\nexport const DEFAULT_ANIMATION_CONFIGS: Map<AnimationState, AnimationConfig> =\n new Map<AnimationState, AnimationConfig>([\n [\n AnimationState.IDLE,\n {\n state: AnimationState.IDLE,\n frames: 4,\n fps: 60,\n loop: true,\n interruptible: true,\n priority: 0 as AnimationPriority,\n duration: 4 / 60,\n },\n ],\n [\n AnimationState.WALK,\n {\n state: AnimationState.WALK,\n frames: 6,\n fps: 60,\n loop: true,\n interruptible: true,\n priority: 1 as AnimationPriority,\n duration: 6 / 60,\n },\n ],\n [\n AnimationState.RUN,\n {\n state: AnimationState.RUN,\n frames: 8,\n fps: 60,\n loop: true,\n interruptible: true,\n priority: 2 as AnimationPriority,\n duration: 8 / 60,\n },\n ],\n [\n AnimationState.STANCE_CHANGE,\n {\n state: AnimationState.STANCE_CHANGE,\n frames: 36, // 600ms at 60fps\n fps: 60,\n loop: false,\n interruptible: false,\n priority: 3 as AnimationPriority,\n duration: 0.6,\n easing: \"smooth-transition\", // Smooth S-curve for stance changes\n },\n ],\n [\n AnimationState.STANCE_SIDE_SWITCH,\n {\n state: AnimationState.STANCE_SIDE_SWITCH,\n frames: 24, // 400ms at 60fps for left↔right switch\n fps: 60,\n loop: false,\n interruptible: false,\n priority: 3 as AnimationPriority,\n duration: 0.4,\n },\n ],\n [\n AnimationState.DEFEND,\n {\n state: AnimationState.DEFEND,\n frames: 4, // 67ms at 60fps\n fps: 60,\n loop: false,\n interruptible: true,\n priority: 4 as AnimationPriority,\n duration: 4 / 60,\n },\n ],\n // Defensive animations (방어 애니메이션) - Enhanced guard break system\n [\n AnimationState.DEFEND_BLOCK_SUCCESS,\n {\n state: AnimationState.DEFEND_BLOCK_SUCCESS,\n frames: 8, // 133ms at 60fps - absorb impact, maintain guard\n fps: 60,\n loop: false,\n interruptible: false, // Must complete block animation\n priority: 6 as AnimationPriority, // Higher than defend, same as hit\n duration: 0.133,\n easing: \"controlled-slow\", // Controlled deceleration for impact absorption\n },\n ],\n [\n AnimationState.DEFEND_PARRY,\n {\n state: AnimationState.DEFEND_PARRY,\n frames: 10, // 167ms at 60fps - redirect attack, open counter opportunity\n fps: 60,\n loop: false,\n interruptible: false, // Must complete parry animation\n priority: 7 as AnimationPriority, // Higher than block, creates counter window\n duration: 0.167,\n counterWindow: 0.2, // 200ms counter-attack opportunity after parry\n },\n ],\n [\n AnimationState.DEFEND_GUARD_BREAK,\n {\n state: AnimationState.DEFEND_GUARD_BREAK,\n frames: 15, // 250ms at 60fps - arms forced wide, vulnerable state\n fps: 60,\n loop: false,\n interruptible: false, // Cannot interrupt guard break\n priority: 8 as AnimationPriority, // Highest priority (same as fall)\n duration: 0.25,\n vulnerabilityDuration: 0.5, // 500ms vulnerable state after guard break\n },\n ],\n [\n AnimationState.DEFEND_RECOVERY,\n {\n state: AnimationState.DEFEND_RECOVERY,\n frames: 12, // 200ms at 60fps - restore guard position\n fps: 60,\n loop: false,\n interruptible: true, // Can be interrupted by attacks\n priority: 2 as AnimationPriority, // Same as run, lower than defend\n duration: 0.2,\n easing: \"natural-motion\", // Physics-based recovery\n },\n ],\n [\n AnimationState.ATTACK,\n {\n state: AnimationState.ATTACK,\n frames: 12, // 200ms at 60fps\n fps: 60,\n loop: false,\n interruptible: true,\n priority: STEP_PRIORITY,\n duration: 12 / 60,\n easing: \"explosive-power\", // Explosive acceleration for attacks\n },\n ],\n [\n AnimationState.HIT,\n {\n state: AnimationState.HIT,\n frames: 4, // 67ms at 60fps\n fps: 60,\n loop: false,\n interruptible: false,\n priority: 6 as AnimationPriority,\n duration: 4 / 60,\n },\n ],\n [\n AnimationState.KO,\n {\n state: AnimationState.KO,\n frames: 30, // 500ms at 60fps\n fps: 60,\n loop: false,\n interruptible: false,\n priority: 7 as AnimationPriority,\n duration: 0.5,\n },\n ],\n // Fall animations (낙법 애니메이션) - Priority 8 (highest)\n [\n AnimationState.FALL_FORWARD,\n {\n state: AnimationState.FALL_FORWARD,\n frames: 24, // 400ms at 60fps - stumble forward, knee collapse, hands brace, face-down\n fps: 60,\n loop: false,\n interruptible: false,\n priority: 8 as AnimationPriority,\n duration: 0.4,\n },\n ],\n [\n AnimationState.FALL_BACKWARD,\n {\n state: AnimationState.FALL_BACKWARD,\n frames: 30, // 500ms at 60fps - backward stumble, sit, back impact, supine\n fps: 60,\n loop: false,\n interruptible: false,\n priority: 8 as AnimationPriority,\n duration: 0.5,\n },\n ],\n [\n AnimationState.FALL_SIDE_LEFT,\n {\n state: AnimationState.FALL_SIDE_LEFT,\n frames: 27, // 450ms at 60fps - rotation, shoulder roll, side sprawl\n fps: 60,\n loop: false,\n interruptible: false,\n priority: 8 as AnimationPriority,\n duration: 0.45,\n },\n ],\n [\n AnimationState.FALL_SIDE_RIGHT,\n {\n state: AnimationState.FALL_SIDE_RIGHT,\n frames: 27, // 450ms at 60fps - rotation, shoulder roll, side sprawl\n fps: 60,\n loop: false,\n interruptible: false,\n priority: 8 as AnimationPriority,\n duration: 0.45,\n },\n ],\n // Ground state animations (지면 자세) - Breathing loops\n [\n AnimationState.GROUND_PRONE,\n {\n state: AnimationState.GROUND_PRONE,\n frames: 4, // Breathing loop on ground (face down)\n fps: 60,\n loop: true,\n interruptible: true,\n priority: 0 as AnimationPriority,\n duration: 4 / 60,\n },\n ],\n [\n AnimationState.GROUND_SUPINE,\n {\n state: AnimationState.GROUND_SUPINE,\n frames: 4, // Breathing loop on ground (face up)\n fps: 60,\n loop: true,\n interruptible: true,\n priority: 0 as AnimationPriority,\n duration: 4 / 60,\n },\n ],\n [\n AnimationState.GROUND_SIDE_LEFT,\n {\n state: AnimationState.GROUND_SIDE_LEFT,\n frames: 4, // Breathing loop on ground (left side)\n fps: 60,\n loop: true,\n interruptible: true,\n priority: 0 as AnimationPriority,\n duration: 4 / 60,\n },\n ],\n [\n AnimationState.GROUND_SIDE_RIGHT,\n {\n state: AnimationState.GROUND_SIDE_RIGHT,\n frames: 4, // Breathing loop on ground (right side)\n fps: 60,\n loop: true,\n interruptible: true,\n priority: 0 as AnimationPriority,\n duration: 4 / 60,\n },\n ],\n // Recovery animations (기상 애니메이션) - Priority 9 (higher than falls)\n [\n AnimationState.RECOVERY_PRONE_STANDUP,\n {\n state: AnimationState.RECOVERY_PRONE_STANDUP,\n frames: 30, // 500ms at 60fps - push up from prone, rise to standing\n fps: 60,\n loop: false,\n interruptible: false, // Last 6 frames (100ms) are interruptible\n priority: 9 as AnimationPriority,\n duration: 0.5,\n },\n ],\n [\n AnimationState.RECOVERY_SUPINE_STANDUP,\n {\n state: AnimationState.RECOVERY_SUPINE_STANDUP,\n frames: 36, // 600ms at 60fps - sit up, roll forward, stand\n fps: 60,\n loop: false,\n interruptible: false, // Last 6 frames (100ms) are interruptible\n priority: 9 as AnimationPriority,\n duration: 0.6,\n },\n ],\n [\n AnimationState.RECOVERY_ROLL,\n {\n state: AnimationState.RECOVERY_ROLL,\n frames: 24, // 400ms at 60fps - roll to side, spring to feet (quick recovery)\n fps: 60,\n loop: false,\n interruptible: false, // Last 6 frames (100ms) are interruptible\n priority: 9 as AnimationPriority,\n duration: 0.4,\n },\n ],\n [\n AnimationState.RECOVERY_DEFENSIVE,\n {\n state: AnimationState.RECOVERY_DEFENSIVE,\n frames: 42, // 700ms at 60fps - slow rise with guard up (vulnerable but defended)\n fps: 60,\n loop: false,\n interruptible: false, // Last 6 frames (100ms) are interruptible\n priority: 9 as AnimationPriority,\n duration: 0.7,\n },\n ],\n // 180-degree turn animations (180도 회전 애니메이션)\n [\n AnimationState.TURN_LEFT,\n {\n state: AnimationState.TURN_LEFT,\n frames: 12, // 200ms at 60fps for 180° turn\n fps: 60,\n loop: false,\n interruptible: false,\n priority: STEP_PRIORITY, // Same priority as attacks/steps - committed action\n duration: 12 / 60,\n },\n ],\n [\n AnimationState.TURN_RIGHT,\n {\n state: AnimationState.TURN_RIGHT,\n frames: 12, // 200ms at 60fps for 180° turn\n fps: 60,\n loop: false,\n interruptible: false,\n priority: STEP_PRIORITY, // Same priority as attacks/steps - committed action\n duration: 12 / 60,\n },\n ],\n // Stance-specific guard animations (팔괘 방어 자세)\n [\n AnimationState.STANCE_GUARD_GEON,\n {\n state: AnimationState.STANCE_GUARD_GEON,\n frames: 6, // Breathing animation\n fps: 60,\n loop: true,\n interruptible: true,\n priority: 0 as AnimationPriority,\n duration: 6 / 60,\n },\n ],\n [\n AnimationState.STANCE_GUARD_TAE,\n {\n state: AnimationState.STANCE_GUARD_TAE,\n frames: 6, // Breathing animation\n fps: 60,\n loop: true,\n interruptible: true,\n priority: 0 as AnimationPriority,\n duration: 6 / 60,\n },\n ],\n [\n AnimationState.STANCE_GUARD_LI,\n {\n state: AnimationState.STANCE_GUARD_LI,\n frames: 4, // Controlled breathing\n fps: 60,\n loop: true,\n interruptible: true,\n priority: 0 as AnimationPriority,\n duration: 4 / 60,\n },\n ],\n [\n AnimationState.STANCE_GUARD_JIN,\n {\n state: AnimationState.STANCE_GUARD_JIN,\n frames: 5, // Deep breathing\n fps: 60,\n loop: true,\n interruptible: true,\n priority: 0 as AnimationPriority,\n duration: 5 / 60,\n },\n ],\n [\n AnimationState.STANCE_GUARD_SON,\n {\n state: AnimationState.STANCE_GUARD_SON,\n frames: 6, // Rhythmic breathing\n fps: 60,\n loop: true,\n interruptible: true,\n priority: 0 as AnimationPriority,\n duration: 6 / 60,\n },\n ],\n [\n AnimationState.STANCE_GUARD_GAM,\n {\n state: AnimationState.STANCE_GUARD_GAM,\n frames: 6, // Flowing breathing\n fps: 60,\n loop: true,\n interruptible: true,\n priority: 0 as AnimationPriority,\n duration: 6 / 60,\n },\n ],\n [\n AnimationState.STANCE_GUARD_GAN,\n {\n state: AnimationState.STANCE_GUARD_GAN,\n frames: 4, // Steady breathing\n fps: 60,\n loop: true,\n interruptible: true,\n priority: 0 as AnimationPriority,\n duration: 4 / 60,\n },\n ],\n [\n AnimationState.STANCE_GUARD_GON,\n {\n state: AnimationState.STANCE_GUARD_GON,\n frames: 5, // Deep diaphragm breathing\n fps: 60,\n loop: true,\n interruptible: true,\n priority: 0 as AnimationPriority,\n duration: 5 / 60,\n },\n ],\n // Tactical step animations (전술적 발걸음)\n // 18 frames = 300ms at 60fps, 30cm distance per step\n [\n AnimationState.STEP_FORWARD,\n {\n state: AnimationState.STEP_FORWARD,\n frames: 18,\n fps: 60,\n loop: false,\n interruptible: false, // Non-interruptible for commitment\n priority: STEP_PRIORITY,\n duration: 0.3,\n },\n ],\n [\n AnimationState.STEP_BACK,\n {\n state: AnimationState.STEP_BACK,\n frames: 18,\n fps: 60,\n loop: false,\n interruptible: false,\n priority: STEP_PRIORITY,\n duration: 0.3,\n },\n ],\n [\n AnimationState.STEP_LEFT,\n {\n state: AnimationState.STEP_LEFT,\n frames: 18,\n fps: 60,\n loop: false,\n interruptible: false,\n priority: STEP_PRIORITY,\n duration: 0.3,\n },\n ],\n [\n AnimationState.STEP_RIGHT,\n {\n state: AnimationState.STEP_RIGHT,\n frames: 18,\n fps: 60,\n loop: false,\n interruptible: false,\n priority: STEP_PRIORITY,\n duration: 0.3,\n },\n ],\n [\n AnimationState.STEP_FORWARD_LEFT,\n {\n state: AnimationState.STEP_FORWARD_LEFT,\n frames: 18,\n fps: 60,\n loop: false,\n interruptible: false,\n priority: STEP_PRIORITY,\n duration: 0.3,\n },\n ],\n [\n AnimationState.STEP_FORWARD_RIGHT,\n {\n state: AnimationState.STEP_FORWARD_RIGHT,\n frames: 18,\n fps: 60,\n loop: false,\n interruptible: false,\n priority: STEP_PRIORITY,\n duration: 0.3,\n },\n ],\n [\n AnimationState.STEP_BACK_LEFT,\n {\n state: AnimationState.STEP_BACK_LEFT,\n frames: 18,\n fps: 60,\n loop: false,\n interruptible: false,\n priority: STEP_PRIORITY,\n duration: 0.3,\n },\n ],\n [\n AnimationState.STEP_BACK_RIGHT,\n {\n state: AnimationState.STEP_BACK_RIGHT,\n frames: 18,\n fps: 60,\n loop: false,\n interruptible: false,\n priority: STEP_PRIORITY,\n duration: 0.3,\n },\n ],\n // Footwork patterns (보법) - Korean martial arts specialized footwork\n // Circular step (원형보) - Lateral movement maintaining guard facing\n [\n AnimationState.FOOTWORK_CIRCULAR_LEFT,\n {\n state: AnimationState.FOOTWORK_CIRCULAR_LEFT,\n frames: 18, // 300ms at 60fps\n fps: 60,\n loop: false,\n interruptible: false, // Committed footwork\n priority: STEP_PRIORITY, // Same as tactical steps\n duration: 0.3,\n },\n ],\n [\n AnimationState.FOOTWORK_CIRCULAR_RIGHT,\n {\n state: AnimationState.FOOTWORK_CIRCULAR_RIGHT,\n frames: 18, // 300ms at 60fps\n fps: 60,\n loop: false,\n interruptible: false,\n priority: STEP_PRIORITY,\n duration: 0.3,\n },\n ],\n // Pivot step (축족회전) - Rotation on planted foot\n [\n AnimationState.FOOTWORK_PIVOT_LEFT,\n {\n state: AnimationState.FOOTWORK_PIVOT_LEFT,\n frames: 15, // 250ms at 60fps\n fps: 60,\n loop: false,\n interruptible: false,\n priority: STEP_PRIORITY,\n duration: 0.25,\n },\n ],\n [\n AnimationState.FOOTWORK_PIVOT_RIGHT,\n {\n state: AnimationState.FOOTWORK_PIVOT_RIGHT,\n frames: 15, // 250ms at 60fps\n fps: 60,\n loop: false,\n interruptible: false,\n priority: STEP_PRIORITY,\n duration: 0.25,\n },\n ],\n // Slide step (미끄럼보) - Both feet move together\n [\n AnimationState.FOOTWORK_SLIDE_FORWARD,\n {\n state: AnimationState.FOOTWORK_SLIDE_FORWARD,\n frames: 12, // 200ms at 60fps\n fps: 60,\n loop: false,\n interruptible: true, // Can be interrupted\n priority: 4 as AnimationPriority, // Same as defend\n duration: 0.2,\n },\n ],\n [\n AnimationState.FOOTWORK_SLIDE_BACK,\n {\n state: AnimationState.FOOTWORK_SLIDE_BACK,\n frames: 12, // 200ms at 60fps\n fps: 60,\n loop: false,\n interruptible: true,\n priority: 4 as AnimationPriority,\n duration: 0.2,\n },\n ],\n [\n AnimationState.FOOTWORK_SLIDE_LEFT,\n {\n state: AnimationState.FOOTWORK_SLIDE_LEFT,\n frames: 12, // 200ms at 60fps\n fps: 60,\n loop: false,\n interruptible: true,\n priority: 4 as AnimationPriority,\n duration: 0.2,\n },\n ],\n [\n AnimationState.FOOTWORK_SLIDE_RIGHT,\n {\n state: AnimationState.FOOTWORK_SLIDE_RIGHT,\n frames: 12, // 200ms at 60fps\n fps: 60,\n loop: false,\n interruptible: true,\n priority: 4 as AnimationPriority,\n duration: 0.2,\n },\n ],\n // Shuffle step (섞음보) - Quick micro-adjustment\n [\n AnimationState.FOOTWORK_SHUFFLE,\n {\n state: AnimationState.FOOTWORK_SHUFFLE,\n frames: 6, // 100ms at 60fps\n fps: 60,\n loop: false,\n interruptible: true,\n priority: 3 as AnimationPriority, // Same as stance_change\n duration: 0.1,\n },\n ],\n ]);\n\n/**\n * Player Animation State Machine\n *\n * Manages animation state, transitions, and timing with frame-accurate updates.\n * Integrates priority system, automatic animation queueing, and event callbacks.\n *\n * **Animation Queue**: Enabled by default with max size 3 and timestamp-based\n * conflict resolution. Automatically queues animations that cannot execute\n * immediately and processes them when the current animation completes.\n *\n * @example\n * ```typescript\n * const machine = new PlayerAnimationStateMachine(DEFAULT_ANIMATION_CONFIGS, {\n * onAnimationStart: (state) => console.log(`Started ${state}`),\n * onAnimationComplete: (state) => console.log(`Completed ${state}`),\n * onFrame: (frame, state) => {\n * if (state === \"attack\" && frame === 6) {\n * // Execute attack at midpoint (frame 6 of 12)\n * executeAttackLogic();\n * }\n * }\n * });\n *\n * // Animation queue is enabled by default\n * // Use transitionToQueued() for automatic queueing\n * machine.transitionToQueued(AnimationState.ATTACK); // Queues if can't execute\n *\n * // Or use regular transitionTo() (no queueing)\n * machine.transitionTo(AnimationState.ATTACK); // Fails if can't execute\n *\n * // In game loop (useFrame)\n * useFrame((state, delta) => {\n * const result = machine.update(delta);\n * updatePlayerVisuals(result.state, result.frame);\n * });\n * ```\n *\n * @korean 플레이어애니메이션상태머신\n */\nexport class PlayerAnimationStateMachine {\n /**\n * Static mapping from TrigramStance to guard AnimationState\n * Prevents repeated object allocation in transitionToStanceGuard()\n * @korean 자세방어상태맵\n */\n private static readonly GUARD_STATE_MAP: Record<\n TrigramStance,\n AnimationState\n > = {\n [TrigramStance.GEON]: AnimationState.STANCE_GUARD_GEON,\n [TrigramStance.TAE]: AnimationState.STANCE_GUARD_TAE,\n [TrigramStance.LI]: AnimationState.STANCE_GUARD_LI,\n [TrigramStance.JIN]: AnimationState.STANCE_GUARD_JIN,\n [TrigramStance.SON]: AnimationState.STANCE_GUARD_SON,\n [TrigramStance.GAM]: AnimationState.STANCE_GUARD_GAM,\n [TrigramStance.GAN]: AnimationState.STANCE_GUARD_GAN,\n [TrigramStance.GON]: AnimationState.STANCE_GUARD_GON,\n };\n\n /**\n * Static reverse mapping from guard AnimationState to TrigramStance\n * Prevents repeated object allocation in getCurrentGuardStance()\n * @korean 방어상태자세맵\n */\n private static readonly STANCE_FROM_GUARD_MAP: Record<string, TrigramStance> =\n {\n [AnimationState.STANCE_GUARD_GEON]: TrigramStance.GEON,\n [AnimationState.STANCE_GUARD_TAE]: TrigramStance.TAE,\n [AnimationState.STANCE_GUARD_LI]: TrigramStance.LI,\n [AnimationState.STANCE_GUARD_JIN]: TrigramStance.JIN,\n [AnimationState.STANCE_GUARD_SON]: TrigramStance.SON,\n [AnimationState.STANCE_GUARD_GAM]: TrigramStance.GAM,\n [AnimationState.STANCE_GUARD_GAN]: TrigramStance.GAN,\n [AnimationState.STANCE_GUARD_GON]: TrigramStance.GON,\n };\n\n private currentState: AnimationState = AnimationState.IDLE;\n private frameIndex = 0;\n private timeAccumulator = 0;\n private previousState: AnimationState | null = null;\n private justStarted = false;\n private justCompleted = false;\n\n /**\n * Current stance transition data (null when not in stance_change animation)\n *\n * **Korean**: 현재 자세 전환 데이터\n *\n * Tracks the active stance transition for use during stance_change animation.\n * Provides access to keyframes and blend weights for smooth interpolation.\n *\n * @korean 현재자세전환데이터\n */\n private currentStanceTransition: StanceTransition | null = null;\n\n /**\n * Motion prediction state for latency reduction\n *\n * **Korean**: 동작 예측 상태\n *\n * Tracks animation velocities for motion prediction to reduce perceived latency.\n * Updated each frame with velocity calculations for smooth anticipation.\n *\n * @korean 동작예측상태\n */\n private motionPrediction: MotionPredictionState =\n createMotionPredictionState();\n\n /**\n * Enable motion prediction for latency reduction\n *\n * **Korean**: 동작 예측 활성화\n *\n * When enabled, predicts future animation frames based on current velocity\n * to reduce perceived input latency by 16-33ms (1-2 frames at 60fps).\n *\n * @korean 동작예측활성화\n */\n private enableMotionPrediction: boolean = false;\n\n /**\n * Motion prediction time ahead (seconds)\n *\n * **Korean**: 예측 시간\n *\n * How far ahead to predict motion (default: 1 frame = 16.67ms at 60fps).\n * Typical range: 0.016-0.033 seconds for <50ms total latency.\n *\n * @korean 예측시간\n */\n private predictionTimeAhead: number = 0.01667; // 1 frame at 60fps\n\n /**\n * Previous keyframe for motion prediction velocity calculation\n *\n * **Korean**: 이전 키프레임\n *\n * @korean 이전키프레임\n */\n private previousKeyframe: AnimationKeyframe | null = null;\n\n /**\n * Preferred easing function for smooth transitions\n *\n * **Korean**: 선호 이징 함수\n *\n * Default easing curve for animation blending and transitions.\n * Can be overridden per animation or transition.\n *\n * @korean 선호이징함수\n */\n private preferredEasing: EasingName = \"natural-motion\";\n\n /**\n * Animation queue for pending animations\n *\n * **Korean**: 애니메이션 대기열\n *\n * Stores animation requests that couldn't be executed immediately\n * due to non-interruptible animations or priority conflicts.\n * Processed automatically when current animation completes.\n *\n * Enabled by default with max size 3 and timestamp-based conflict resolution.\n * Can be disabled with disableQueue() or reconfigured with enableQueue().\n *\n * @korean 애니메이션대기열\n */\n private animationQueue: AnimationQueue | null = new AnimationQueue(\n 3,\n \"timestamp\",\n );\n\n /**\n * Conflict resolution strategy for equal-priority animations\n *\n * **Korean**: 충돌 해결 전략\n *\n * Determines how to resolve conflicts when multiple animations\n * have equal priority. Default: timestamp (FIFO).\n *\n * @korean 충돌해결전략\n */\n private conflictStrategy: ConflictResolutionStrategy = \"timestamp\";\n\n /**\n * Create a new animation state machine\n *\n * Clones the provided config map so per-instance mutations\n * (e.g. dynamic attack duration) don't affect shared defaults.\n *\n * @param animations - Map of animation configurations (cloned internally)\n * @param events - Optional event callbacks\n *\n * @korean 생성자\n */\n private readonly animations: Map<AnimationState, MutableAnimationConfig>;\n\n constructor(\n animations: Map<AnimationState, AnimationConfig>,\n private readonly events?: AnimationEvents,\n ) {\n // Clone so we can safely mutate per-instance (e.g. attack duration)\n this.animations = new Map(\n Array.from(animations.entries()).map(([k, v]) => [k, { ...v }]),\n );\n }\n\n /**\n * Update animation state with delta time\n *\n * Call this in useFrame for 60fps updates.\n * Handles frame progression, looping, and completion.\n *\n * @param deltaTime - Time elapsed since last update (in seconds)\n * @returns Animation update result with current state and frame\n *\n * @korean 업데이트\n */\n update(deltaTime: number): AnimationUpdateResult {\n const currentAnim = this.animations.get(this.currentState);\n if (!currentAnim) {\n return {\n state: this.currentState,\n frame: 0,\n progress: 0,\n justCompleted: false,\n justStarted: false,\n };\n }\n\n // Reset just started/completed flags\n const wasJustStarted = this.justStarted;\n this.justStarted = false;\n const previousJustCompleted = this.justCompleted;\n this.justCompleted = false;\n\n // Accumulate time\n this.timeAccumulator += deltaTime;\n const frameDuration = 1 / currentAnim.fps;\n\n // Check if we should advance to next frame\n if (this.timeAccumulator >= frameDuration) {\n const previousFrame = this.frameIndex;\n this.frameIndex++;\n this.timeAccumulator -= frameDuration;\n\n // Emit frame event\n if (this.events?.onFrame && previousFrame !== this.frameIndex) {\n this.events.onFrame(this.frameIndex, this.currentState);\n }\n\n // Handle animation completion\n if (this.frameIndex >= currentAnim.frames) {\n if (currentAnim.loop) {\n // Loop back to start\n this.frameIndex = 0;\n } else {\n // Animation completed\n this.justCompleted = true;\n if (this.events?.onAnimationComplete) {\n this.events.onAnimationComplete(this.currentState);\n }\n\n // Auto-transition logic\n // Fall animations transition to ground states using the mapping\n if (this.currentState.startsWith(\"fall_\")) {\n const fallType = this.currentState.replace(\"fall_\", \"\");\n\n // Validate that fallType is a valid FallType before using in map\n if (\n fallType === \"forward\" ||\n fallType === \"backward\" ||\n fallType === \"side_left\" ||\n fallType === \"side_right\"\n ) {\n const groundState = FALL_TO_GROUND_MAP[fallType as FallType];\n const groundAnimKey = `ground_${groundState}`;\n\n // Validate that the constructed ground animation state actually exists\n if (\n DEFAULT_ANIMATION_CONFIGS.has(groundAnimKey as AnimationState)\n ) {\n const groundAnimState = groundAnimKey as AnimationState;\n\n this.previousState = this.currentState;\n this.currentState = groundAnimState;\n this.frameIndex = 0;\n this.timeAccumulator = 0;\n this.justStarted = true;\n\n if (this.events?.onAnimationStart) {\n this.events.onAnimationStart(groundAnimState);\n }\n } else {\n // Fallback: if mapping is invalid, safely transition to idle\n // instead of entering an undefined animation state.\n console.warn(\n \"[AnimationStateMachine] Invalid ground animation mapping for fall type:\",\n fallType,\n \"->\",\n groundAnimKey,\n );\n this.previousState = this.currentState;\n this.currentState = AnimationState.IDLE;\n this.frameIndex = 0;\n this.timeAccumulator = 0;\n this.justStarted = true;\n\n if (this.events?.onAnimationStart) {\n this.events.onAnimationStart(AnimationState.IDLE);\n }\n }\n } else {\n // Invalid fall type - fallback to idle\n console.warn(\n \"[AnimationStateMachine] Invalid fall animation state:\",\n this.currentState,\n );\n this.previousState = this.currentState;\n this.currentState = AnimationState.IDLE;\n this.frameIndex = 0;\n this.timeAccumulator = 0;\n this.justStarted = true;\n\n if (this.events?.onAnimationStart) {\n this.events.onAnimationStart(AnimationState.IDLE);\n }\n }\n }\n // Recovery animations transition to idle when complete\n else if (this.currentState.startsWith(\"recovery_\")) {\n this.previousState = this.currentState;\n this.currentState = AnimationState.IDLE;\n this.frameIndex = 0;\n this.timeAccumulator = 0;\n this.justStarted = true;\n\n if (this.events?.onAnimationStart) {\n this.events.onAnimationStart(AnimationState.IDLE);\n }\n }\n // Non-fall, non-recovery, non-looping animations transition to idle\n else if (\n this.currentState !== AnimationState.IDLE &&\n this.currentState !== AnimationState.KO &&\n !this.currentState.startsWith(\"ground_\")\n ) {\n // Clear stance transition data if completing stance_change\n if (this.currentState === AnimationState.STANCE_CHANGE) {\n this.clearStanceTransition();\n }\n\n // Transition to idle first\n this.previousState = this.currentState;\n this.currentState = AnimationState.IDLE;\n this.frameIndex = 0;\n this.timeAccumulator = 0;\n this.justStarted = true;\n\n if (this.events?.onAnimationStart) {\n this.events.onAnimationStart(AnimationState.IDLE);\n }\n\n // Then try to process next queued animation (from idle state)\n this.processNextQueuedAnimation();\n } else {\n // Stay on last frame (for ko and ground states)\n this.frameIndex = currentAnim.frames - 1;\n }\n }\n }\n }\n\n const progress =\n currentAnim.frames > 0 ? this.frameIndex / currentAnim.frames : 0;\n\n return {\n state: this.currentState,\n frame: this.frameIndex,\n progress,\n justCompleted: previousJustCompleted,\n justStarted: wasJustStarted,\n };\n }\n\n /**\n * Attempt to transition to a new animation state\n *\n * Checks transition rules and priority system before transitioning.\n *\n * @param newState - Target animation state\n * @returns Whether transition was successful\n *\n * @example\n * ```typescript\n * // Successful transitions\n * machine.transitionTo(\"walk\"); // idle -> walk\n * machine.transitionTo(\"attack\"); // walk -> attack\n *\n * // Failed transition (invalid or lower priority)\n * machine.transitionTo(\"walk\"); // attack -> walk (blocked, must complete first)\n * ```\n *\n * @korean 상태전환\n */\n transitionTo(newState: AnimationState): boolean {\n // Don't transition to same state\n if (this.currentState === newState) {\n return false;\n }\n\n // Check if transition is allowed by rules\n if (!isTransitionAllowed(this.currentState, newState)) {\n return false;\n }\n\n const currentAnim = this.animations.get(this.currentState);\n const newAnim = this.animations.get(newState);\n\n if (!newAnim) {\n return false;\n }\n\n // Check priority system\n if (\n currentAnim &&\n !canInterrupt(this.currentState, newState, currentAnim.interruptible)\n ) {\n return false;\n }\n\n // Emit interrupt event if current animation wasn't completed\n if (this.frameIndex < (currentAnim?.frames ?? 0) - 1) {\n if (this.events?.onAnimationInterrupted) {\n this.events.onAnimationInterrupted(this.currentState, newState);\n }\n }\n\n // Clear stance transition data if interrupting stance_change\n if (this.currentState === AnimationState.STANCE_CHANGE) {\n this.clearStanceTransition();\n }\n\n // Execute transition\n this.previousState = this.currentState;\n this.currentState = newState;\n this.frameIndex = 0;\n this.timeAccumulator = 0;\n this.justStarted = true;\n this.justCompleted = false;\n\n // Emit start event\n if (this.events?.onAnimationStart) {\n this.events.onAnimationStart(newState);\n }\n\n return true;\n }\n\n /**\n * Transition to ATTACK state with a technique-specific duration.\n *\n * The default ATTACK config is 200ms (12 frames), but real techniques\n * range from 350ms to 1200ms. This method overrides the ATTACK frame\n * count to match the actual skeletal animation duration so the state\n * machine stays in ATTACK for the full technique.\n *\n * @param durationSeconds - The skeletal animation duration in seconds\n * @returns Whether transition was successful\n *\n * @example\n * ```typescript\n * // Jab animation is 0.55s (TECHNIQUE_TIMING.FAST)\n * machine.transitionToAttack(0.55);\n * ```\n *\n * @korean 공격전환 (기술별 지속시간)\n */\n transitionToAttack(durationSeconds: number): boolean {\n const attackConfig = this.animations.get(AnimationState.ATTACK);\n if (attackConfig) {\n const fps = attackConfig.fps || 60;\n attackConfig.frames = Math.max(1, Math.round(durationSeconds * fps));\n attackConfig.duration = durationSeconds;\n }\n return this.transitionTo(AnimationState.ATTACK);\n }\n\n /**\n * Get current animation state\n *\n * @returns Current animation state\n * @korean 현재상태가져오기\n */\n getCurrentState(): AnimationState {\n return this.currentState;\n }\n\n /**\n * Get current frame index\n *\n * @returns Current frame index (0 to frames-1)\n * @korean 현재프레임가져오기\n */\n getCurrentFrame(): number {\n return this.frameIndex;\n }\n\n /**\n * Get previous animation state\n *\n * @returns Previous animation state or null\n * @korean 이전상태가져오기\n */\n getPreviousState(): AnimationState | null {\n return this.previousState;\n }\n\n /**\n * Get current animation configuration\n *\n * @returns Current animation config or undefined\n * @korean 현재애니메이션설정가져오기\n */\n getCurrentAnimation(): AnimationConfig | undefined {\n return this.animations.get(this.currentState);\n }\n\n /**\n * Reset animation state machine to idle\n *\n * @korean 초기화\n */\n reset(): void {\n this.currentState = AnimationState.IDLE;\n this.frameIndex = 0;\n this.timeAccumulator = 0;\n this.previousState = null;\n this.justStarted = false;\n this.justCompleted = false;\n }\n\n /**\n * Get full internal state (for debugging/testing)\n *\n * @returns Current state machine state\n * @korean 상태가져오기\n */\n getState(): AnimationMachineState {\n return {\n currentState: this.currentState,\n frameIndex: this.frameIndex,\n timeAccumulator: this.timeAccumulator,\n isPlaying: true,\n previousState: this.previousState,\n };\n }\n\n /**\n * Transition to stance-specific guard animation\n *\n * Convenience method to transition to a stance guard based on trigram stance.\n * Automatically maps trigram stance to corresponding guard animation state.\n *\n * @param stance - Trigram stance identifier\n * @returns Whether transition was successful\n *\n * @example\n * ```typescript\n * // When player changes to Fire stance\n * machine.transitionToStanceGuard(TrigramStance.LI);\n * // Internally transitions to \"stance_guard_li\" animation state\n * ```\n *\n * @korean 자세방어전환\n */\n transitionToStanceGuard(stance: TrigramStance): boolean {\n const guardAnimationState =\n PlayerAnimationStateMachine.GUARD_STATE_MAP[stance];\n\n // Verify the guard animation exists in our configs\n if (!guardAnimationState || !this.animations.has(guardAnimationState)) {\n console.warn(`No guard animation configured for stance: ${stance}`);\n return false;\n }\n\n return this.transitionTo(guardAnimationState);\n }\n\n /**\n * Check if current animation is a stance guard\n *\n * @returns True if currently in a stance guard animation\n * @korean 자세방어상태확인\n */\n isInStanceGuard(): boolean {\n return this.currentState.startsWith(\"stance_guard_\");\n }\n\n /**\n * Get current guard stance if in a guard animation\n *\n * @returns Trigram stance or null if not in guard\n * @korean 현재방어자세가져오기\n */\n getCurrentGuardStance(): TrigramStance | null {\n if (!this.isInStanceGuard()) {\n return null;\n }\n\n const stance =\n PlayerAnimationStateMachine.STANCE_FROM_GUARD_MAP[this.currentState];\n\n // Validate that we got a valid stance\n if (!stance) {\n console.warn(`Invalid guard state detected: ${this.currentState}`);\n return null;\n }\n\n return stance;\n }\n\n /**\n * Transition to stance_change animation with specific stance transition data\n *\n * **Korean**: 자세 전환 애니메이션 시작\n *\n * Initiates a stance change animation with the specific transition data\n * from the 64-transition matrix. This provides stance-specific keyframes\n * and blend weights for smooth interpolation.\n *\n * @param fromStance - Source trigram stance\n * @param toStance - Target trigram stance\n * @returns Whether transition was successful\n *\n * @example\n * ```typescript\n * // Start transition from Heaven to Lake stance\n * const success = machine.transitionToStanceChange(\n * TrigramStance.GEON,\n * TrigramStance.TAE\n * );\n *\n * if (success) {\n * // During update loop, use getStanceTransitionBlend() to interpolate\n * const blend = machine.getStanceTransitionBlend();\n * if (blend) {\n * // Apply blend weights to stance poses\n * applyStanceBlend(blend);\n * }\n * }\n * ```\n *\n * @korean 자세전환애니메이션시작\n */\n transitionToStanceChange(\n fromStance: TrigramStance,\n toStance: TrigramStance,\n ): boolean {\n // Get the specific transition data from the 64-transition matrix\n const transitionData = getStanceTransition(fromStance, toStance);\n\n if (!transitionData) {\n console.warn(\n `[AnimationStateMachine] No transition data found for ${fromStance} -> ${toStance}`,\n );\n return false;\n }\n\n // Store current transition data in case we need to restore it\n const previousTransitionData = this.currentStanceTransition;\n\n // Temporarily set the new transition data\n this.currentStanceTransition = transitionData;\n\n // Initiate the stance_change animation\n const success = this.transitionTo(AnimationState.STANCE_CHANGE);\n\n // If transition failed, restore previous transition data\n if (!success) {\n this.currentStanceTransition = previousTransitionData;\n }\n\n return success;\n }\n\n /**\n * Get current stance transition data\n *\n * **Korean**: 현재 자세 전환 데이터 가져오기\n *\n * Returns the active stance transition data during stance_change animation.\n * Null if not currently in a stance transition.\n *\n * @returns Current stance transition or null\n *\n * @korean 현재자세전환데이터가져오기\n */\n getCurrentStanceTransition(): StanceTransition | null {\n return this.currentStanceTransition;\n }\n\n /**\n * Get interpolated blend weights for current stance transition frame\n *\n * **Korean**: 현재 프레임 블렌드 가중치\n *\n * Returns the interpolated blend data for the current frame during\n * stance_change animation. Uses the keyframe data from the transition\n * matrix to provide smooth stance interpolation.\n *\n * @returns Blend data with stance and weight, or null if not in transition\n *\n * @example\n * ```typescript\n * // In rendering loop during stance transition\n * const blend = machine.getStanceTransitionBlend();\n * if (blend) {\n * console.log(`Frame ${blend.frame}: ${blend.stance} at ${blend.blend}x weight`);\n * // Apply blended pose: blend.blend * targetPose + (1 - blend.blend) * sourcePose\n * }\n * ```\n *\n * @korean 현재프레임블렌드가중치\n */\n getStanceTransitionBlend(): {\n frame: number;\n stance: TrigramStance | \"neutral\";\n blend: number;\n } | null {\n // Only valid during stance_change animation\n if (\n this.currentState !== AnimationState.STANCE_CHANGE ||\n !this.currentStanceTransition\n ) {\n return null;\n }\n\n const keyframes = this.currentStanceTransition.keyframes;\n const currentFrame = this.frameIndex;\n\n // Find the two keyframes to interpolate between\n let prevKeyframe = keyframes[0];\n let nextKeyframe = keyframes[keyframes.length - 1];\n\n for (let i = 0; i < keyframes.length - 1; i++) {\n if (\n keyframes[i].frame <= currentFrame &&\n keyframes[i + 1].frame > currentFrame\n ) {\n prevKeyframe = keyframes[i];\n nextKeyframe = keyframes[i + 1];\n break;\n }\n }\n\n // If we're exactly on a keyframe, return it directly\n const exactKeyframe = keyframes.find((kf) => kf.frame === currentFrame);\n if (exactKeyframe) {\n return {\n frame: currentFrame,\n stance: exactKeyframe.stance,\n blend: exactKeyframe.blend,\n };\n }\n\n // Linear interpolation between keyframes\n const frameRange = nextKeyframe.frame - prevKeyframe.frame;\n const frameProgress =\n frameRange > 0 ? (currentFrame - prevKeyframe.frame) / frameRange : 0;\n\n const interpolatedBlend =\n prevKeyframe.blend +\n (nextKeyframe.blend - prevKeyframe.blend) * frameProgress;\n\n // Use the next keyframe's stance as we're transitioning towards it\n return {\n frame: currentFrame,\n stance: nextKeyframe.stance,\n blend: interpolatedBlend,\n };\n }\n\n /**\n * Check if currently in a stance transition animation\n *\n * **Korean**: 자세 전환 중 확인\n *\n * @returns True if currently executing a stance_change animation\n * @korean 자세전환중확인\n */\n isInStanceTransition(): boolean {\n return (\n this.currentState === AnimationState.STANCE_CHANGE &&\n this.currentStanceTransition !== null\n );\n }\n\n /**\n * Clear stance transition data (called automatically when transition completes)\n *\n * **Korean**: 자세 전환 데이터 초기화\n *\n * @internal\n * @korean 자세전환데이터초기화\n */\n private clearStanceTransition(): void {\n this.currentStanceTransition = null;\n }\n\n /**\n * Enable or disable motion prediction\n *\n * **Korean**: 동작 예측 설정\n *\n * Enables motion prediction to reduce perceived input latency by predicting\n * future animation frames based on current velocity (1-2 frames ahead).\n *\n * @param enabled - Whether to enable motion prediction\n * @param predictionTime - Optional: time ahead to predict (default: 16.67ms)\n *\n * @example\n * ```typescript\n * // Enable motion prediction for 1 frame (16.67ms at 60fps)\n * machine.setMotionPrediction(true);\n *\n * // Enable with 2 frames prediction (33.33ms)\n * machine.setMotionPrediction(true, 0.03333);\n * ```\n *\n * @korean 동작예측설정\n */\n setMotionPrediction(enabled: boolean, predictionTime?: number): void {\n this.enableMotionPrediction = enabled;\n if (predictionTime !== undefined) {\n // Clamp to 50ms maximum for <50ms total latency\n this.predictionTimeAhead = Math.min(predictionTime, 0.05);\n }\n }\n\n /**\n * Get motion prediction state\n *\n * **Korean**: 동작 예측 상태 가져오기\n *\n * @returns Current motion prediction state\n * @korean 동작예측상태가져오기\n */\n getMotionPredictionState(): MotionPredictionState {\n return this.motionPrediction;\n }\n\n /**\n * Check if motion prediction is enabled\n *\n * **Korean**: 동작 예측 활성화 확인\n *\n * @returns True if motion prediction is enabled\n * @korean 동작예측활성화확인\n */\n isMotionPredictionEnabled(): boolean {\n return this.enableMotionPrediction;\n }\n\n /**\n * Set preferred easing function for transitions\n *\n * **Korean**: 선호 이징 함수 설정\n *\n * Sets the default easing curve for animation transitions.\n * Can use presets like \"natural-motion\", \"smooth-transition\", etc.\n *\n * @param easingName - Easing function name\n *\n * @example\n * ```typescript\n * // Use natural motion for Korean martial arts\n * machine.setPreferredEasing(\"natural-motion\");\n *\n * // Use explosive power for strike animations\n * machine.setPreferredEasing(\"explosive-power\");\n * ```\n *\n * @korean 선호이징함수설정\n */\n setPreferredEasing(easingName: EasingName): void {\n this.preferredEasing = easingName;\n }\n\n /**\n * Get preferred easing function\n *\n * **Korean**: 선호 이징 함수 가져오기\n *\n * @returns Current preferred easing name\n * @korean 선호이징함수가져오기\n */\n getPreferredEasing(): EasingName {\n return this.preferredEasing;\n }\n\n /**\n * Update motion prediction with skeletal keyframe data\n *\n * **Korean**: 동작 예측 업데이트\n *\n * This should be called from the skeletal animation layer when applying\n * interpolated keyframes to the rig. It updates velocity tracking for\n * motion prediction to reduce perceived latency.\n *\n * Integration point: Call this from your skeletal animation system after\n * computing the current interpolated keyframe (e.g., from getInterpolatedKeyframe).\n *\n * @param currentKeyframe - Current skeletal animation keyframe with bone positions/rotations\n * @param deltaTime - Time elapsed since last update\n *\n * @example\n * ```typescript\n * // In your skeletal animation update loop:\n * const currentKeyframe = getInterpolatedKeyframe(animation, time);\n *\n * // Update motion prediction (for next frame)\n * if (machine.isMotionPredictionEnabled()) {\n * machine.updateMotionPredictionState(currentKeyframe, deltaTime);\n * }\n *\n * // Apply keyframe to rig\n * applyKeyframeToRig(rig, currentKeyframe);\n * ```\n *\n * @korean 동작예측업데이트\n */\n updateMotionPredictionState(\n currentKeyframe: AnimationKeyframe,\n deltaTime: number,\n ): void {\n if (!this.enableMotionPrediction) {\n return;\n }\n\n // Update velocity tracking if we have a previous keyframe\n if (this.previousKeyframe) {\n this.motionPrediction = updateMotionPrediction(\n this.motionPrediction,\n this.previousKeyframe,\n currentKeyframe,\n deltaTime,\n );\n }\n\n // Store current keyframe for next update\n this.previousKeyframe = currentKeyframe;\n }\n\n /**\n * Get predicted future keyframe for latency reduction\n *\n * **Korean**: 예측된 미래 키프레임 가져오기\n *\n * Returns a keyframe predicted ahead by predictionTimeAhead (default: 1 frame).\n * This reduces perceived input latency by showing where the animation will be\n * in the near future rather than where it currently is.\n *\n * Integration point: Use this instead of the current keyframe when applying\n * to the rig if motion prediction is enabled.\n *\n * @param currentKeyframe - Current skeletal animation keyframe\n * @returns Predicted future keyframe, or current if prediction disabled\n *\n * @example\n * ```typescript\n * // In your skeletal animation update loop:\n * let keyframeToApply = currentKeyframe;\n *\n * if (machine.isMotionPredictionEnabled()) {\n * keyframeToApply = machine.getPredictedKeyframe(currentKeyframe);\n * }\n *\n * applyKeyframeToRig(rig, keyframeToApply);\n * ```\n *\n * @korean 예측키프레임가져오기\n */\n getPredictedKeyframe(currentKeyframe: AnimationKeyframe): AnimationKeyframe {\n if (!this.enableMotionPrediction || !this.previousKeyframe) {\n return currentKeyframe;\n }\n\n return predictFutureKeyframe(\n currentKeyframe,\n this.motionPrediction,\n this.predictionTimeAhead,\n );\n }\n\n // ===== Animation Queue Methods (애니메이션 대기열) =====\n\n /**\n * Enable or reconfigure animation queue system\n *\n * **Korean**: 애니메이션 대기열 활성화/재설정\n *\n * The queue is enabled by default. Use this method to reconfigure the\n * queue size or conflict resolution strategy.\n *\n * @param maxSize - Maximum queue size (default: 3)\n * @param conflictStrategy - Conflict resolution strategy (default: \"timestamp\")\n *\n * @example\n * ```typescript\n * // Queue is enabled by default, but you can reconfigure it\n * machine.enableQueue(5, \"requested\");\n * ```\n *\n * @korean 대기열활성화\n */\n enableQueue(\n maxSize: number = 3,\n conflictStrategy: ConflictResolutionStrategy = \"timestamp\",\n ): void {\n this.animationQueue = new AnimationQueue(maxSize, conflictStrategy);\n this.conflictStrategy = conflictStrategy;\n }\n\n /**\n * Disable animation queue system\n *\n * **Korean**: 애니메이션 대기열 비활성화\n *\n * Disables the queue and clears any pending animations.\n *\n * @korean 대기열비활성화\n */\n disableQueue(): void {\n this.animationQueue = null;\n }\n\n /**\n * Check if queue is enabled\n *\n * **Korean**: 대기열 활성화 여부\n *\n * @returns True if queue is enabled\n * @korean 대기열활성화여부\n */\n isQueueEnabled(): boolean {\n return this.animationQueue !== null;\n }\n\n /**\n * Attempt to transition to a new animation state with queue support\n *\n * **Korean**: 대기열 지원 상태 전환\n *\n * Enhanced version of transitionTo() that automatically queues animations\n * when they cannot be executed immediately. Since the queue is enabled by\n * default, this is the recommended method for animation transitions.\n *\n * The queued animation will be automatically processed when the current\n * animation completes, following priority and conflict resolution rules.\n *\n * @param newState - Target animation state\n * @returns Whether transition was successful, queued, or failed\n *\n * @example\n * ```typescript\n * // Queue is enabled by default\n * const result = machine.transitionToQueued(AnimationState.ATTACK);\n * // Returns \"success\" if transitioned immediately\n * // Returns \"queued\" if couldn't interrupt but was queued\n * // Returns \"failed\" if queue is full or disabled\n * ```\n *\n * @korean 대기열상태전환\n */\n transitionToQueued(\n newState: AnimationState,\n ): \"success\" | \"queued\" | \"failed\" {\n // Try normal transition first\n const timestamp = performance.now();\n const priority = this.animations.get(newState)?.priority ?? 0;\n\n const success = this.transitionTo(newState);\n if (success) {\n return \"success\";\n }\n\n // If transition failed and queue is enabled, try to enqueue\n if (this.animationQueue) {\n const request: AnimationRequest = {\n state: newState,\n timestamp,\n priority,\n };\n\n const enqueued = this.animationQueue.enqueue(request);\n return enqueued ? \"queued\" : \"failed\";\n }\n\n return \"failed\";\n }\n\n /**\n * Process next queued animation if available\n *\n * **Korean**: 다음 대기열 애니메이션 처리\n *\n * Should be called automatically when an animation completes.\n * Dequeues and executes the highest priority pending animation.\n *\n * @returns Whether a queued animation was executed\n *\n * @internal\n * @korean 다음대기열처리\n */\n private processNextQueuedAnimation(): boolean {\n if (!this.animationQueue || this.animationQueue.isEmpty()) {\n return false;\n }\n\n const nextRequest = this.animationQueue.dequeue();\n if (!nextRequest) {\n return false;\n }\n\n // Try to execute the queued animation\n const success = this.transitionTo(nextRequest.state);\n\n if (!success) {\n // Log failure so queued animations do not disappear silently\n // Korean: 대기열 애니메이션 전이가 실패했음을 로그로 남깁니다.\n // This helps diagnose cases where transition rules, missing states,\n // or priority conflicts prevent a queued animation from playing.\n console.warn(\n \"[AnimationStateMachine] Failed to transition to queued animation state\",\n {\n requestedState: nextRequest.state,\n currentState: this.currentState,\n },\n );\n }\n\n return success;\n }\n\n /**\n * Get current animation queue state\n *\n * **Korean**: 현재 대기열 상태\n *\n * Returns information about the current queue state for debugging\n * or UI display.\n *\n * @returns Queue state information\n *\n * @korean 현재대기열상태\n */\n getQueueState(): {\n enabled: boolean;\n size: number;\n maxSize: number;\n pending: readonly AnimationRequest[];\n } {\n if (!this.animationQueue) {\n return {\n enabled: false,\n size: 0,\n maxSize: 0,\n pending: [],\n };\n }\n\n return {\n enabled: true,\n size: this.animationQueue.size(),\n maxSize: this.animationQueue.getMaxSize(),\n pending: this.animationQueue.getAll(),\n };\n }\n\n /**\n * Clear all pending queued animations\n *\n * **Korean**: 모든 대기열 초기화\n *\n * Removes all pending animations from the queue.\n *\n * @korean 모든대기열초기화\n */\n clearQueue(): void {\n this.animationQueue?.clear();\n }\n\n /**\n * Set conflict resolution strategy\n *\n * **Korean**: 충돌 해결 전략 설정\n *\n * Changes the strategy used to resolve equal-priority conflicts.\n *\n * @param strategy - Conflict resolution strategy\n *\n * @korean 충돌해결전략설정\n */\n setConflictStrategy(strategy: ConflictResolutionStrategy): void {\n this.conflictStrategy = strategy;\n\n // Keep the animation queue's strategy in sync with the state machine\n if (this.animationQueue) {\n this.animationQueue.setConflictStrategy(strategy);\n }\n }\n\n /**\n * Get current conflict resolution strategy\n *\n * **Korean**: 충돌 해결 전략 가져오기\n *\n * @returns Current conflict resolution strategy\n * @korean 충돌해결전략가져오기\n */\n getConflictStrategy(): ConflictResolutionStrategy {\n return this.conflictStrategy;\n }\n\n /**\n * Dispose of the animation state machine\n *\n * **Korean**: 애니메이션 상태 머신 해제\n *\n * Clears all internal state, queues, and references to prevent memory leaks.\n * Should be called when the state machine is no longer needed (e.g., component unmount).\n *\n * @korean 애니메이션상태머신해제\n */\n dispose(): void {\n // Clear animation queue\n this.animationQueue?.clear();\n this.animationQueue = null;\n\n // Clear stance transition data\n this.currentStanceTransition = null;\n\n // Clear motion prediction state\n this.motionPrediction = createMotionPredictionState();\n this.previousKeyframe = null;\n\n // Reset state to initial values\n this.currentState = AnimationState.IDLE;\n this.previousState = AnimationState.IDLE;\n this.frameIndex = 0;\n this.timeAccumulator = 0;\n this.justStarted = false;\n this.justCompleted = false;\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAoEA,IAAa,4CACX,IAAI,IAAqC;CACvC,CACE,eAAe,MACf;EACE,OAAO,eAAe;EACtB,QAAQ;EACR,KAAK;EACL,MAAM;EACN,eAAe;EACf,UAAU;EACV,UAAU,IAAI;CAChB,CACF;CACA,CACE,eAAe,MACf;EACE,OAAO,eAAe;EACtB,QAAQ;EACR,KAAK;EACL,MAAM;EACN,eAAe;EACf,UAAU;EACV,UAAU,IAAI;CAChB,CACF;CACA,CACE,eAAe,KACf;EACE,OAAO,eAAe;EACtB,QAAQ;EACR,KAAK;EACL,MAAM;EACN,eAAe;EACf,UAAU;EACV,UAAU,IAAI;CAChB,CACF;CACA,CACE,eAAe,eACf;EACE,OAAO,eAAe;EACtB,QAAQ;EACR,KAAK;EACL,MAAM;EACN,eAAe;EACf,UAAU;EACV,UAAU;EACV,QAAQ;CACV,CACF;CACA,CACE,eAAe,oBACf;EACE,OAAO,eAAe;EACtB,QAAQ;EACR,KAAK;EACL,MAAM;EACN,eAAe;EACf,UAAU;EACV,UAAU;CACZ,CACF;CACA,CACE,eAAe,QACf;EACE,OAAO,eAAe;EACtB,QAAQ;EACR,KAAK;EACL,MAAM;EACN,eAAe;EACf,UAAU;EACV,UAAU,IAAI;CAChB,CACF;CAEA,CACE,eAAe,sBACf;EACE,OAAO,eAAe;EACtB,QAAQ;EACR,KAAK;EACL,MAAM;EACN,eAAe;EACf,UAAU;EACV,UAAU;EACV,QAAQ;CACV,CACF;CACA,CACE,eAAe,cACf;EACE,OAAO,eAAe;EACtB,QAAQ;EACR,KAAK;EACL,MAAM;EACN,eAAe;EACf,UAAU;EACV,UAAU;EACV,eAAe;CACjB,CACF;CACA,CACE,eAAe,oBACf;EACE,OAAO,eAAe;EACtB,QAAQ;EACR,KAAK;EACL,MAAM;EACN,eAAe;EACf,UAAU;EACV,UAAU;EACV,uBAAuB;CACzB,CACF;CACA,CACE,eAAe,iBACf;EACE,OAAO,eAAe;EACtB,QAAQ;EACR,KAAK;EACL,MAAM;EACN,eAAe;EACf,UAAU;EACV,UAAU;EACV,QAAQ;CACV,CACF;CACA,CACE,eAAe,QACf;EACE,OAAO,eAAe;EACtB,QAAQ;EACR,KAAK;EACL,MAAM;EACN,eAAe;EACf,UAAA;EACA,UAAU,KAAK;EACf,QAAQ;CACV,CACF;CACA,CACE,eAAe,KACf;EACE,OAAO,eAAe;EACtB,QAAQ;EACR,KAAK;EACL,MAAM;EACN,eAAe;EACf,UAAU;EACV,UAAU,IAAI;CAChB,CACF;CACA,CACE,eAAe,IACf;EACE,OAAO,eAAe;EACtB,QAAQ;EACR,KAAK;EACL,MAAM;EACN,eAAe;EACf,UAAU;EACV,UAAU;CACZ,CACF;CAEA,CACE,eAAe,cACf;EACE,OAAO,eAAe;EACtB,QAAQ;EACR,KAAK;EACL,MAAM;EACN,eAAe;EACf,UAAU;EACV,UAAU;CACZ,CACF;CACA,CACE,eAAe,eACf;EACE,OAAO,eAAe;EACtB,QAAQ;EACR,KAAK;EACL,MAAM;EACN,eAAe;EACf,UAAU;EACV,UAAU;CACZ,CACF;CACA,CACE,eAAe,gBACf;EACE,OAAO,eAAe;EACtB,QAAQ;EACR,KAAK;EACL,MAAM;EACN,eAAe;EACf,UAAU;EACV,UAAU;CACZ,CACF;CACA,CACE,eAAe,iBACf;EACE,OAAO,eAAe;EACtB,QAAQ;EACR,KAAK;EACL,MAAM;EACN,eAAe;EACf,UAAU;EACV,UAAU;CACZ,CACF;CAEA,CACE,eAAe,cACf;EACE,OAAO,eAAe;EACtB,QAAQ;EACR,KAAK;EACL,MAAM;EACN,eAAe;EACf,UAAU;EACV,UAAU,IAAI;CAChB,CACF;CACA,CACE,eAAe,eACf;EACE,OAAO,eAAe;EACtB,QAAQ;EACR,KAAK;EACL,MAAM;EACN,eAAe;EACf,UAAU;EACV,UAAU,IAAI;CAChB,CACF;CACA,CACE,eAAe,kBACf;EACE,OAAO,eAAe;EACtB,QAAQ;EACR,KAAK;EACL,MAAM;EACN,eAAe;EACf,UAAU;EACV,UAAU,IAAI;CAChB,CACF;CACA,CACE,eAAe,mBACf;EACE,OAAO,eAAe;EACtB,QAAQ;EACR,KAAK;EACL,MAAM;EACN,eAAe;EACf,UAAU;EACV,UAAU,IAAI;CAChB,CACF;CAEA,CACE,eAAe,wBACf;EACE,OAAO,eAAe;EACtB,QAAQ;EACR,KAAK;EACL,MAAM;EACN,eAAe;EACf,UAAU;EACV,UAAU;CACZ,CACF;CACA,CACE,eAAe,yBACf;EACE,OAAO,eAAe;EACtB,QAAQ;EACR,KAAK;EACL,MAAM;EACN,eAAe;EACf,UAAU;EACV,UAAU;CACZ,CACF;CACA,CACE,eAAe,eACf;EACE,OAAO,eAAe;EACtB,QAAQ;EACR,KAAK;EACL,MAAM;EACN,eAAe;EACf,UAAU;EACV,UAAU;CACZ,CACF;CACA,CACE,eAAe,oBACf;EACE,OAAO,eAAe;EACtB,QAAQ;EACR,KAAK;EACL,MAAM;EACN,eAAe;EACf,UAAU;EACV,UAAU;CACZ,CACF;CAEA,CACE,eAAe,WACf;EACE,OAAO,eAAe;EACtB,QAAQ;EACR,KAAK;EACL,MAAM;EACN,eAAe;EACf,UAAA;EACA,UAAU,KAAK;CACjB,CACF;CACA,CACE,eAAe,YACf;EACE,OAAO,eAAe;EACtB,QAAQ;EACR,KAAK;EACL,MAAM;EACN,eAAe;EACf,UAAA;EACA,UAAU,KAAK;CACjB,CACF;CAEA,CACE,eAAe,mBACf;EACE,OAAO,eAAe;EACtB,QAAQ;EACR,KAAK;EACL,MAAM;EACN,eAAe;EACf,UAAU;EACV,UAAU,IAAI;CAChB,CACF;CACA,CACE,eAAe,kBACf;EACE,OAAO,eAAe;EACtB,QAAQ;EACR,KAAK;EACL,MAAM;EACN,eAAe;EACf,UAAU;EACV,UAAU,IAAI;CAChB,CACF;CACA,CACE,eAAe,iBACf;EACE,OAAO,eAAe;EACtB,QAAQ;EACR,KAAK;EACL,MAAM;EACN,eAAe;EACf,UAAU;EACV,UAAU,IAAI;CAChB,CACF;CACA,CACE,eAAe,kBACf;EACE,OAAO,eAAe;EACtB,QAAQ;EACR,KAAK;EACL,MAAM;EACN,eAAe;EACf,UAAU;EACV,UAAU,IAAI;CAChB,CACF;CACA,CACE,eAAe,kBACf;EACE,OAAO,eAAe;EACtB,QAAQ;EACR,KAAK;EACL,MAAM;EACN,eAAe;EACf,UAAU;EACV,UAAU,IAAI;CAChB,CACF;CACA,CACE,eAAe,kBACf;EACE,OAAO,eAAe;EACtB,QAAQ;EACR,KAAK;EACL,MAAM;EACN,eAAe;EACf,UAAU;EACV,UAAU,IAAI;CAChB,CACF;CACA,CACE,eAAe,kBACf;EACE,OAAO,eAAe;EACtB,QAAQ;EACR,KAAK;EACL,MAAM;EACN,eAAe;EACf,UAAU;EACV,UAAU,IAAI;CAChB,CACF;CACA,CACE,eAAe,kBACf;EACE,OAAO,eAAe;EACtB,QAAQ;EACR,KAAK;EACL,MAAM;EACN,eAAe;EACf,UAAU;EACV,UAAU,IAAI;CAChB,CACF;CAGA,CACE,eAAe,cACf;EACE,OAAO,eAAe;EACtB,QAAQ;EACR,KAAK;EACL,MAAM;EACN,eAAe;EACf,UAAA;EACA,UAAU;CACZ,CACF;CACA,CACE,eAAe,WACf;EACE,OAAO,eAAe;EACtB,QAAQ;EACR,KAAK;EACL,MAAM;EACN,eAAe;EACf,UAAA;EACA,UAAU;CACZ,CACF;CACA,CACE,eAAe,WACf;EACE,OAAO,eAAe;EACtB,QAAQ;EACR,KAAK;EACL,MAAM;EACN,eAAe;EACf,UAAA;EACA,UAAU;CACZ,CACF;CACA,CACE,eAAe,YACf;EACE,OAAO,eAAe;EACtB,QAAQ;EACR,KAAK;EACL,MAAM;EACN,eAAe;EACf,UAAA;EACA,UAAU;CACZ,CACF;CACA,CACE,eAAe,mBACf;EACE,OAAO,eAAe;EACtB,QAAQ;EACR,KAAK;EACL,MAAM;EACN,eAAe;EACf,UAAA;EACA,UAAU;CACZ,CACF;CACA,CACE,eAAe,oBACf;EACE,OAAO,eAAe;EACtB,QAAQ;EACR,KAAK;EACL,MAAM;EACN,eAAe;EACf,UAAA;EACA,UAAU;CACZ,CACF;CACA,CACE,eAAe,gBACf;EACE,OAAO,eAAe;EACtB,QAAQ;EACR,KAAK;EACL,MAAM;EACN,eAAe;EACf,UAAA;EACA,UAAU;CACZ,CACF;CACA,CACE,eAAe,iBACf;EACE,OAAO,eAAe;EACtB,QAAQ;EACR,KAAK;EACL,MAAM;EACN,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"}
@@ -377,7 +377,7 @@ var TechniqueAnimationMapper = class {
377
377
  * @private
378
378
  */
379
379
  initializeFallbacks() {
380
- return new Map([
380
+ return /* @__PURE__ */ new Map([
381
381
  ["strike", AnimationState.ATTACK],
382
382
  ["joint", AnimationState.ATTACK],
383
383
  ["throw", AnimationState.ATTACK],
@@ -1 +1 @@
1
- {"version":3,"file":"TechniqueAnimationMapper.js","names":[],"sources":["../../../../src/systems/animation/core/TechniqueAnimationMapper.ts"],"sourcesContent":["/**\n * Technique Animation Mapper\n *\n * Maps 70 Korean techniques to appropriate attack animation types.\n * Implements the technique-to-animation link system for authentic martial arts animations.\n *\n * @module systems/animation/TechniqueAnimationMapper\n * @category Animation System\n * @korean 기술애니메이션매퍼\n */\n\nimport { AttackAnimationType } from \"@/types/skeletal\";\nimport { ATTACK_ANIMATIONS } from \"../catalogs/AttackAnimations\";\n\n/**\n * Maps AttackAnimationType to existing skeletal animation names\n *\n * Links the new enum-based system to existing animation implementation.\n * New animations will be added to AttackAnimations.ts as needed.\n *\n * @korean 애니메이션타입맵\n */\nconst ANIMATION_TYPE_TO_NAME_MAP: Record<AttackAnimationType, string> = {\n // Punch category (주먹 타격) - use existing animations\n [AttackAnimationType.PUNCH_HIGH]: \"jab\", // High punch to head\n [AttackAnimationType.PUNCH_MID]: \"cross\", // Mid-level cross punch\n [AttackAnimationType.PUNCH_LOW]: \"jab\", // Low punch to body (reuse jab for now)\n\n // Kick category (발차기) - use dedicated animations\n [AttackAnimationType.KICK_FRONT]: \"front_kick\", // Front kick (앞차기)\n [AttackAnimationType.KICK_SIDE]: \"side_kick\", // Side kick (옆차기) - NEW dedicated animation\n [AttackAnimationType.KICK_ROUNDHOUSE]: \"roundhouse_kick\", // Roundhouse kick (돌려차기)\n\n // Elbow category (팔꿈치 타격) - use dedicated elbow animations\n [AttackAnimationType.ELBOW_STRIKE]: \"elbow_strike\",\n [AttackAnimationType.ELBOW_UPPERCUT]: \"elbow_uppercut\", // NEW dedicated uppercut animation\n\n // Knee category (무릎 타격) - use dedicated knee animation\n [AttackAnimationType.KNEE_STRIKE]: \"knee_strike\",\n [AttackAnimationType.KNEE_CLINCH]: \"knee_strike\", // Reuse knee strike in clinch position\n\n // Pressure point category (급소 타격) - use precise jab motion\n [AttackAnimationType.PRESSURE_POINT]: \"jab\",\n [AttackAnimationType.PRESSURE_POINT_RAPID]: \"jab\",\n};\n\n/**\n * Get skeletal animation name for an attack animation type\n *\n * @param animationType - Attack animation type enum\n * @returns Name of skeletal animation from ATTACK_ANIMATIONS map\n *\n * @korean 애니메이션타입에서이름가져오기\n */\nexport function getAnimationNameForType(\n animationType: AttackAnimationType,\n): string {\n return ANIMATION_TYPE_TO_NAME_MAP[animationType];\n}\n\n/**\n * Check if an animation type has a defined animation\n *\n * @param animationType - Attack animation type enum\n * @returns True if animation exists in ATTACK_ANIMATIONS map\n *\n * @korean 애니메이션존재확인\n */\nexport function hasAnimationForType(\n animationType: AttackAnimationType,\n): boolean {\n const animationName = ANIMATION_TYPE_TO_NAME_MAP[animationType];\n return ATTACK_ANIMATIONS.has(animationName);\n}\n\n/**\n * Determines appropriate animation type from technique characteristics\n *\n * Uses technique name, damage type, and keywords to automatically\n * select the best-matching attack animation type.\n *\n * @param techniqueName - English or Korean technique name\n * @param techniqueId - Technique ID\n * @param damageType - Type of damage dealt\n * @returns Best-matching attack animation type\n *\n * @korean 기술에서애니메이션타입결정\n */\nexport function determineAnimationTypeForTechnique(\n techniqueName: string,\n techniqueId: string,\n damageType?: string,\n): AttackAnimationType {\n const searchText = `${techniqueName} ${techniqueId}`.toLowerCase();\n\n // Pressure point / precise strikes (급소)\n if (\n searchText.includes(\"pressure\") ||\n searchText.includes(\"precise\") ||\n searchText.includes(\"nerve\") ||\n searchText.includes(\"vital\") ||\n searchText.includes(\"급소\") ||\n searchText.includes(\"신경\") ||\n damageType === \"nerve\" ||\n damageType === \"pressure\"\n ) {\n // Check for rapid/multi-hit variants\n if (\n searchText.includes(\"rapid\") ||\n searchText.includes(\"multiple\") ||\n searchText.includes(\"연속\")\n ) {\n return AttackAnimationType.PRESSURE_POINT_RAPID;\n }\n return AttackAnimationType.PRESSURE_POINT;\n }\n\n // Kicks (차기)\n if (\n searchText.includes(\"kick\") ||\n searchText.includes(\"차기\") ||\n searchText.includes(\"발\")\n ) {\n if (\n searchText.includes(\"roundhouse\") ||\n searchText.includes(\"round\") ||\n searchText.includes(\"돌려\") ||\n searchText.includes(\"회전\")\n ) {\n return AttackAnimationType.KICK_ROUNDHOUSE;\n }\n if (\n searchText.includes(\"side\") ||\n searchText.includes(\"옆\") ||\n searchText.includes(\"측면\")\n ) {\n return AttackAnimationType.KICK_SIDE;\n }\n // Default to front kick\n return AttackAnimationType.KICK_FRONT;\n }\n\n // Elbow strikes (팔꿈치)\n if (\n searchText.includes(\"elbow\") ||\n searchText.includes(\"팔꿈치\") ||\n searchText.includes(\"팔굽\")\n ) {\n if (\n searchText.includes(\"uppercut\") ||\n searchText.includes(\"올려\") ||\n searchText.includes(\"치켜\")\n ) {\n return AttackAnimationType.ELBOW_UPPERCUT;\n }\n return AttackAnimationType.ELBOW_STRIKE;\n }\n\n // Knee strikes (무릎)\n if (\n searchText.includes(\"knee\") ||\n searchText.includes(\"무릎\") ||\n searchText.includes(\"슬\")\n ) {\n if (\n searchText.includes(\"clinch\") ||\n searchText.includes(\"grab\") ||\n searchText.includes(\"잡고\")\n ) {\n return AttackAnimationType.KNEE_CLINCH;\n }\n return AttackAnimationType.KNEE_STRIKE;\n }\n\n // Punches (주먹) - check target height\n if (\n searchText.includes(\"punch\") ||\n searchText.includes(\"strike\") ||\n searchText.includes(\"주먹\") ||\n searchText.includes(\"권\") ||\n searchText.includes(\"격\")\n ) {\n // High attacks (head level)\n if (\n searchText.includes(\"head\") ||\n searchText.includes(\"high\") ||\n searchText.includes(\"temple\") ||\n searchText.includes(\"jaw\") ||\n searchText.includes(\"머리\") ||\n searchText.includes(\"관자\") ||\n searchText.includes(\"턱\")\n ) {\n return AttackAnimationType.PUNCH_HIGH;\n }\n // Low attacks (body level)\n if (\n searchText.includes(\"low\") ||\n searchText.includes(\"body\") ||\n searchText.includes(\"ribs\") ||\n searchText.includes(\"아래\") ||\n searchText.includes(\"복부\") ||\n searchText.includes(\"늑골\")\n ) {\n return AttackAnimationType.PUNCH_LOW;\n }\n // Default to mid-level punch\n return AttackAnimationType.PUNCH_MID;\n }\n\n // Default: mid-level punch for any unmatched technique\n return AttackAnimationType.PUNCH_MID;\n}\n\n/**\n * Calculates animation speed modifier based on technique power level\n *\n * Implements the rule:\n * - Light techniques (damage <20): 1.2x speed\n * - Normal techniques (damage 20-35): 1.0x speed\n * - Heavy techniques (damage >35): 0.8x speed\n *\n * @param damage - Base damage of the technique\n * @returns Speed modifier (0.8 - 1.2)\n *\n * @korean 기술위력에서속도배율계산\n */\nexport function calculateSpeedModifierForDamage(damage: number): number {\n if (damage < 20) {\n return 1.2; // Light, fast techniques\n } else if (damage > 35) {\n return 0.8; // Heavy, powerful techniques\n }\n return 1.0; // Normal speed\n}\n\n/**\n * Get animation duration adjusted by speed modifier\n *\n * @param baseAnimationName - Name of base animation\n * @param speedModifier - Speed multiplier (0.8 - 1.2)\n * @returns Adjusted duration in milliseconds\n *\n * @korean 조정된애니메이션지속시간\n */\nexport function getAdjustedAnimationDuration(\n baseAnimationName: string,\n speedModifier: number,\n): number {\n const animation = ATTACK_ANIMATIONS.get(baseAnimationName);\n if (!animation) {\n console.warn(\"Animation not found:\", baseAnimationName);\n return 200; // Default 200ms for missing animations\n }\n\n // Convert seconds to milliseconds and apply speed modifier\n const baseDurationMs = animation.duration * 1000;\n return Math.round(baseDurationMs / speedModifier);\n}\n\n/**\n * Comprehensive Technique Animation Mapping System\n *\n * **Korean**: 포괄적 기술 애니메이션 매핑 시스템\n *\n * Maps all 1024 technique configuration combinations\n * (8 stances × 4 technique types × 8 body parts × 4 intensities)\n * to appropriate animations with O(1) lookup performance.\n *\n * ## Architecture\n *\n * - **Primary Map**: Exact stance-type-part-intensity combinations\n * - **Fallback System**: 3-tier graceful degradation\n * 1. Exact match\n * 2. Intensity-agnostic match (same stance/type/part, medium intensity)\n * 3. Technique type generic (fallback to base technique animation)\n *\n * ## Korean Martial Arts Integration\n *\n * Each trigram stance influences animation selection:\n * - **☰ 건 (Geon)**: Direct, forceful animations\n * - **☱ 태 (Tae)**: Fluid, flowing animations\n * - **☲ 리 (Li)**: Precise, rapid animations\n * - **☳ 진 (Jin)**: Explosive, shocking animations\n * - **☴ 손 (Son)**: Continuous, pressuring animations\n * - **☵ 감 (Gam)**: Adaptive, countering animations\n * - **☶ 간 (Gan)**: Defensive, immovable animations\n * - **☷ 곤 (Gon)**: Grounding, takedown animations\n *\n * @module systems/animation/TechniqueAnimationMapper\n * @category Animation System\n * @korean 기술애니메이션매퍼\n */\n\nimport { TrigramStance } from \"@/types\";\nimport { BodyPart } from \"../../bodypart/types\";\nimport {\n AnimationPriority,\n AnimationState,\n MappingValidationResult,\n TechniqueAnimation,\n TechniqueAnimationKey,\n TechniqueIntensity,\n TechniqueTypeCategory,\n} from \"./types\";\n\n/**\n * Body part Korean terminology for animation names\n *\n * **Korean**: 신체 부위 한글 용어\n */\nconst BODY_PART_KOREAN: Record<string, string> = {\n [BodyPart.HEAD]: \"두부\",\n [BodyPart.NECK]: \"경부\",\n [BodyPart.TORSO_UPPER]: \"상체\",\n [BodyPart.TORSO_LOWER]: \"하체\",\n [BodyPart.ARM_LEFT]: \"좌팔\",\n [BodyPart.ARM_RIGHT]: \"우팔\",\n [BodyPart.LEG_LEFT]: \"좌각\",\n [BodyPart.LEG_RIGHT]: \"우각\",\n};\n\n/**\n * Body part English terminology for animation names\n */\nconst BODY_PART_ENGLISH: Record<string, string> = {\n [BodyPart.HEAD]: \"Head\",\n [BodyPart.NECK]: \"Neck\",\n [BodyPart.TORSO_UPPER]: \"Upper Torso\",\n [BodyPart.TORSO_LOWER]: \"Lower Torso\",\n [BodyPart.ARM_LEFT]: \"Left Arm\",\n [BodyPart.ARM_RIGHT]: \"Right Arm\",\n [BodyPart.LEG_LEFT]: \"Left Leg\",\n [BodyPart.LEG_RIGHT]: \"Right Leg\",\n};\n\n/**\n * Technique type Korean terminology\n *\n * **Korean**: 기술 유형 한글 용어\n */\nconst TECHNIQUE_TYPE_KOREAN: Record<TechniqueTypeCategory, string> = {\n strike: \"타격\",\n joint: \"관절\",\n throw: \"던지기\",\n pressure_point: \"급소\",\n};\n\n/**\n * Technique type English terminology\n */\nconst TECHNIQUE_TYPE_ENGLISH: Record<TechniqueTypeCategory, string> = {\n strike: \"Strike\",\n joint: \"Joint Lock\",\n throw: \"Throw\",\n pressure_point: \"Pressure Point\",\n};\n\n/**\n * Stance Korean names\n */\nconst STANCE_KOREAN: Record<string, string> = {\n [TrigramStance.GEON]: \"건괘\",\n [TrigramStance.TAE]: \"태괘\",\n [TrigramStance.LI]: \"리괘\",\n [TrigramStance.JIN]: \"진괘\",\n [TrigramStance.SON]: \"손괘\",\n [TrigramStance.GAM]: \"감괘\",\n [TrigramStance.GAN]: \"간괘\",\n [TrigramStance.GON]: \"곤괘\",\n};\n\n/**\n * Stance English names\n */\nconst STANCE_ENGLISH: Record<string, string> = {\n [TrigramStance.GEON]: \"Heaven\",\n [TrigramStance.TAE]: \"Lake\",\n [TrigramStance.LI]: \"Fire\",\n [TrigramStance.JIN]: \"Thunder\",\n [TrigramStance.SON]: \"Wind\",\n [TrigramStance.GAM]: \"Water\",\n [TrigramStance.GAN]: \"Mountain\",\n [TrigramStance.GON]: \"Earth\",\n};\n\n/**\n * Stance Animation Characteristics Configuration\n *\n * **Korean**: 자세 애니메이션 특성 설정\n *\n * Data-driven configuration for each stance's animation modifiers,\n * eliminating code duplication across stance mapping methods.\n *\n * @internal\n */\ninterface StanceAnimationConfig {\n /** Base duration multiplier (1.0 = normal) */\n readonly durationMultiplier: number;\n /** Impact frame offset from base */\n readonly impactFrameOffset: number;\n /** Recovery frames offset from base */\n readonly recoveryFrameOffset: number;\n /** Technique-specific overrides (optional) */\n readonly techniqueOverrides?: Partial<\n Record<\n TechniqueTypeCategory,\n {\n durationMultiplier?: number;\n impactFrameOffset?: number;\n recoveryFrameOffset?: number;\n }\n >\n >;\n}\n\n/**\n * Stance animation characteristics by trigram\n *\n * **Korean**: 팔괘 자세별 애니메이션 특성\n *\n * Each stance has unique timing characteristics reflecting its philosophy:\n * - ☰ 건 (Geon/Heaven): Direct, powerful - standard timing with technique variations\n * - ☱ 태 (Tae/Lake): Fluid, flowing - faster recovery\n * - ☲ 리 (Li/Fire): Precise, rapid - fastest overall\n * - ☳ 진 (Jin/Thunder): Explosive - fast attack, longer recovery\n * - ☴ 손 (Son/Wind): Continuous - slightly fast, mobile\n * - ☵ 감 (Gam/Water): Adaptive - slightly slower, standard recovery\n * - ☶ 간 (Gan/Mountain): Defensive - slowest, longest recovery\n * - ☷ 곤 (Gon/Earth): Grounding - standard with throw specialization\n */\nconst STANCE_ANIMATION_CONFIG: Record<TrigramStance, StanceAnimationConfig> = {\n [TrigramStance.GEON]: {\n durationMultiplier: 1.0,\n impactFrameOffset: 0,\n recoveryFrameOffset: 0,\n techniqueOverrides: {\n joint: { durationMultiplier: 1.2 },\n throw: {\n durationMultiplier: 1.5,\n impactFrameOffset: 4,\n recoveryFrameOffset: 5,\n },\n pressure_point: {\n durationMultiplier: 0.8,\n impactFrameOffset: -2,\n },\n },\n },\n [TrigramStance.TAE]: {\n durationMultiplier: 1.0,\n impactFrameOffset: 0,\n recoveryFrameOffset: -2,\n techniqueOverrides: {\n joint: { durationMultiplier: 1.3 },\n },\n },\n [TrigramStance.LI]: {\n durationMultiplier: 0.85,\n impactFrameOffset: -1,\n recoveryFrameOffset: -3,\n },\n [TrigramStance.JIN]: {\n durationMultiplier: 0.9,\n impactFrameOffset: 0,\n recoveryFrameOffset: 2,\n },\n [TrigramStance.SON]: {\n durationMultiplier: 0.95,\n impactFrameOffset: 0,\n recoveryFrameOffset: -1,\n },\n [TrigramStance.GAM]: {\n durationMultiplier: 1.1,\n impactFrameOffset: 1,\n recoveryFrameOffset: 0,\n },\n [TrigramStance.GAN]: {\n durationMultiplier: 1.2,\n impactFrameOffset: 2,\n recoveryFrameOffset: 3,\n },\n [TrigramStance.GON]: {\n durationMultiplier: 1.0,\n impactFrameOffset: 0,\n recoveryFrameOffset: 0,\n techniqueOverrides: {\n throw: {\n durationMultiplier: 1.6,\n impactFrameOffset: 3,\n recoveryFrameOffset: 4,\n },\n },\n },\n};\n\n/**\n * Comprehensive Technique Animation Mapper Class\n *\n * **Korean**: 기술 애니메이션 매퍼 클래스\n *\n * Provides O(1) lookup for all 1024 technique-stance combinations\n * with intelligent fallback system and build-time validation.\n *\n * @class\n */\nexport class TechniqueAnimationMapper {\n /** Primary animation mapping table */\n private readonly animationMap: Map<string, TechniqueAnimation>;\n\n /** Fallback animations by technique type */\n private readonly fallbackMap: Map<TechniqueTypeCategory, AnimationState>;\n\n /** Cache for generated combinations (validation) */\n private allCombinationsCache?: readonly TechniqueAnimationKey[];\n\n constructor() {\n this.animationMap = new Map();\n this.fallbackMap = this.initializeFallbacks();\n this.initializeCompleteMapping();\n }\n\n /**\n * Get animation for specific technique\n *\n * **Korean**: 특정 기술의 애니메이션 가져오기\n *\n * Uses 3-tier lookup strategy:\n * 1. Exact match (stance + type + part + intensity)\n * 2. Intensity-agnostic (stance + type + part + medium intensity)\n * 3. Technique type fallback (generic animation for technique type)\n *\n * @param key - Technique animation key\n * @returns Technique animation configuration\n *\n * @korean 애니메이션가져오기\n */\n public getAnimation(key: TechniqueAnimationKey): TechniqueAnimation {\n // Try exact match first\n const lookupKey = this.createLookupKey(key);\n const exactMatch = this.animationMap.get(lookupKey);\n if (exactMatch) {\n return exactMatch;\n }\n\n // Fallback: Try without intensity (use medium)\n const genericKey = this.createLookupKey({\n ...key,\n intensity: \"medium\",\n });\n const genericMatch = this.animationMap.get(genericKey);\n if (genericMatch) {\n return this.adjustForIntensity(genericMatch, key.intensity);\n }\n\n // Final fallback: Use technique type generic animation\n return this.getFallbackAnimation(key);\n }\n\n /**\n * Create composite lookup key from technique animation key\n *\n * **Korean**: 복합 조회 키 생성\n *\n * Format: \"stance-type-part-intensity\"\n * Example: \"geon-strike-head-heavy\"\n *\n * @param key - Technique animation key\n * @returns Composite lookup string\n *\n * @private\n */\n private createLookupKey(key: TechniqueAnimationKey): string {\n return `${key.stance}-${key.techniqueType}-${key.bodyPart}-${key.intensity}`;\n }\n\n /**\n * Initialize fallback animations by technique type\n *\n * **Korean**: 기술 유형별 대체 애니메이션 초기화\n *\n * @private\n */\n private initializeFallbacks(): Map<TechniqueTypeCategory, AnimationState> {\n return new Map([\n [\"strike\", AnimationState.ATTACK],\n [\"joint\", AnimationState.ATTACK],\n [\"throw\", AnimationState.ATTACK],\n [\"pressure_point\", AnimationState.ATTACK],\n ]);\n }\n\n /**\n * Initialize complete mapping table\n *\n * **Korean**: 전체 매핑 테이블 초기화\n *\n * Maps all 1024 combinations organized by:\n * - 8 stances\n * - 4 technique types\n * - 8 body parts\n * - 4 intensity levels\n *\n * Uses data-driven STANCE_ANIMATION_CONFIG to eliminate code duplication.\n *\n * @private\n */\n private initializeCompleteMapping(): void {\n const bodyParts = Object.values(BodyPart);\n const intensities: TechniqueIntensity[] = [\n \"light\",\n \"medium\",\n \"heavy\",\n \"critical\",\n ];\n const techniqueTypes: TechniqueTypeCategory[] = [\n \"strike\",\n \"joint\",\n \"throw\",\n \"pressure_point\",\n ];\n\n // Map all 8 trigram stances using configuration\n Object.values(TrigramStance).forEach((stance) => {\n const config = STANCE_ANIMATION_CONFIG[stance];\n\n bodyParts.forEach((bodyPart) => {\n intensities.forEach((intensity) => {\n techniqueTypes.forEach((techniqueType) => {\n // Get base values with technique-specific overrides\n const override = config.techniqueOverrides?.[techniqueType];\n const durationMult =\n override?.durationMultiplier ?? config.durationMultiplier;\n const impactOffset =\n override?.impactFrameOffset ?? config.impactFrameOffset;\n const recoveryOffset =\n override?.recoveryFrameOffset ?? config.recoveryFrameOffset;\n\n this.mapTechnique(\n { stance, techniqueType, bodyPart, intensity },\n {\n animationState: AnimationState.ATTACK,\n duration:\n this.getDurationForIntensity(intensity) * durationMult,\n impactFrame:\n this.getImpactFrameForIntensity(intensity) + impactOffset,\n recoveryFrames:\n this.getRecoveryFramesForIntensity(intensity) +\n recoveryOffset,\n priority: AnimationPriority.ATTACK,\n koreanName: this.generateKoreanName(\n stance,\n techniqueType,\n bodyPart,\n intensity,\n ),\n englishName: this.generateEnglishName(\n stance,\n techniqueType,\n bodyPart,\n intensity,\n ),\n },\n );\n });\n });\n });\n });\n }\n /**\n * Add single technique mapping to the map with automatic rotation calculation\n *\n * **Korean**: 단일 기술 매핑 추가\n *\n * @private\n */\n private mapTechnique(\n key: TechniqueAnimationKey,\n animation: TechniqueAnimation,\n ): void {\n // Calculate rotation properties if not provided\n const torsoRotation =\n animation.torsoRotation ??\n this.getTorsoRotationForStanceTechnique(key.stance, key.techniqueType);\n const hipEngagement =\n animation.hipEngagement ??\n this.getHipEngagementForStanceTechnique(\n key.stance,\n key.techniqueType,\n key.intensity,\n );\n const powerModifier =\n animation.powerModifier ??\n this.calculatePowerModifier(hipEngagement, key.techniqueType);\n\n // Create complete animation with rotation properties\n const completeAnimation: TechniqueAnimation = {\n ...animation,\n torsoRotation,\n hipEngagement,\n powerModifier,\n };\n\n const lookupKey = this.createLookupKey(key);\n this.animationMap.set(lookupKey, completeAnimation);\n }\n\n /**\n * Get duration based on intensity level\n *\n * **Korean**: 강도 레벨에 따른 지속시간 가져오기\n *\n * @private\n */\n private getDurationForIntensity(intensity: TechniqueIntensity): number {\n const baseDuration = 0.6; // 600ms base\n switch (intensity) {\n case \"light\":\n return baseDuration * 0.7; // 420ms\n case \"medium\":\n return baseDuration; // 600ms\n case \"heavy\":\n return baseDuration * 1.3; // 780ms\n case \"critical\":\n return baseDuration * 1.6; // 960ms\n }\n }\n\n /**\n * Get impact frame based on intensity level\n *\n * **Korean**: 강도 레벨에 따른 충격 프레임 가져오기\n *\n * @private\n */\n private getImpactFrameForIntensity(intensity: TechniqueIntensity): number {\n switch (intensity) {\n case \"light\":\n return 8; // Frame 8 at 60fps\n case \"medium\":\n return 10; // Frame 10\n case \"heavy\":\n return 14; // Frame 14\n case \"critical\":\n return 18; // Frame 18\n }\n }\n\n /**\n * Get recovery frames based on intensity level\n *\n * **Korean**: 강도 레벨에 따른 회복 프레임 가져오기\n *\n * @private\n */\n private getRecoveryFramesForIntensity(intensity: TechniqueIntensity): number {\n switch (intensity) {\n case \"light\":\n return 8; // 8 frames recovery\n case \"medium\":\n return 12; // 12 frames recovery\n case \"heavy\":\n return 18; // 18 frames recovery\n case \"critical\":\n return 24; // 24 frames recovery\n }\n }\n\n /**\n * Calculate torso rotation for stance-technique combination\n *\n * **Korean**: 자세-기술 조합의 허리 회전 계산\n *\n * Returns rotation in radians (-π/2 to π/2)\n *\n * @private\n */\n private getTorsoRotationForStanceTechnique(\n stance: string,\n techniqueType: TechniqueTypeCategory,\n ): number {\n // Stance-specific base rotations (in radians)\n const stanceRotations: Record<string, number> = {\n [TrigramStance.GEON]: Math.PI / 6, // 30° - Direct, moderate rotation\n [TrigramStance.TAE]: Math.PI / 18, // 10° - Minimal rotation, fluid\n [TrigramStance.LI]: Math.PI / 9, // 20° - Quick snap rotation\n [TrigramStance.JIN]: Math.PI / 4, // 45° - Wide explosive rotation\n [TrigramStance.SON]: Math.PI / 12, // 15° - Continuous adaptive flow\n [TrigramStance.GAM]: Math.PI / 8, // 22.5° - Circular wave motion\n [TrigramStance.GAN]: Math.PI / 36, // 5° - Stable, minimal rotation\n [TrigramStance.GON]: Math.PI / 3, // 60° - Deep grounded rotation\n };\n\n const baseRotation = stanceRotations[stance] ?? 0;\n\n // Technique type modifiers\n const techniqueMultipliers: Record<TechniqueTypeCategory, number> = {\n strike: 1.2, // 20% more rotation for strikes\n joint: 0.7, // 30% less for joint locks\n throw: 1.5, // 50% more for throws\n pressure_point: 0.8, // 20% less for precision strikes\n };\n\n const multiplier = techniqueMultipliers[techniqueType] ?? 1.0;\n return Math.min(Math.PI / 2, baseRotation * multiplier); // Clamp to ±90°\n }\n\n /**\n * Calculate hip engagement for stance-technique combination\n *\n * **Korean**: 자세-기술 조합의 골반 참여도 계산\n *\n * Returns engagement factor (0-1)\n *\n * @private\n */\n private getHipEngagementForStanceTechnique(\n stance: string,\n techniqueType: TechniqueTypeCategory,\n intensity: TechniqueIntensity,\n ): number {\n // Stance-specific base hip engagement\n const stanceEngagement: Record<string, number> = {\n [TrigramStance.GEON]: 0.9, // High engagement - direct force\n [TrigramStance.TAE]: 0.5, // Moderate - fluid movement\n [TrigramStance.LI]: 0.7, // Good - rapid techniques\n [TrigramStance.JIN]: 1.0, // Maximum - explosive power\n [TrigramStance.SON]: 0.6, // Moderate - continuous pressure\n [TrigramStance.GAM]: 0.7, // Good - adaptive flow\n [TrigramStance.GAN]: 0.3, // Low - stable defensive\n [TrigramStance.GON]: 0.95, // Very high - grounding techniques\n };\n\n const baseEngagement = stanceEngagement[stance] ?? 0.6;\n\n // Technique type modifiers\n const techniqueMultipliers: Record<TechniqueTypeCategory, number> = {\n strike: 1.0, // Full engagement for strikes\n joint: 0.6, // Lower for joint manipulation\n throw: 1.1, // Highest for throws\n pressure_point: 0.7, // Moderate for precision\n };\n\n // Intensity modifiers\n const intensityMultipliers: Record<TechniqueIntensity, number> = {\n light: 0.7,\n medium: 0.9,\n heavy: 1.0,\n critical: 1.1,\n };\n\n const engagement =\n baseEngagement *\n techniqueMultipliers[techniqueType] *\n intensityMultipliers[intensity];\n\n return Math.min(1.0, Math.max(0.0, engagement)); // Clamp to 0-1\n }\n\n /**\n * Calculate power modifier from hip engagement\n *\n * **Korean**: 골반 참여도로부터 파워 배율 계산\n *\n * Based on PR #1132 calculateHipRotationPowerModifier system\n *\n * @private\n */\n private calculatePowerModifier(\n hipEngagement: number,\n techniqueType: TechniqueTypeCategory,\n ): number {\n // Base power curve: 1.0 + (engagement * maxBonus)\n const maxBonusByType: Record<TechniqueTypeCategory, number> = {\n strike: 0.3, // 30% max bonus for strikes\n joint: 0.1, // 10% for joint locks\n throw: 0.2, // 20% for throws\n pressure_point: 0.25, // 25% for pressure points\n };\n\n const maxBonus = maxBonusByType[techniqueType] ?? 0.2;\n return 1.0 + hipEngagement * maxBonus;\n }\n\n /**\n * Generate Korean technique name\n *\n * **Korean**: 한글 기술 이름 생성\n *\n * Format: \"스탠스 신체부위 강도 기술유형\"\n * Example: \"건괘 두부 강 타격\"\n *\n * @private\n */\n private generateKoreanName(\n stance: string,\n techniqueType: TechniqueTypeCategory,\n bodyPart: string,\n intensity: TechniqueIntensity,\n ): string {\n const stanceName = STANCE_KOREAN[stance] ?? stance;\n const bodyPartName = BODY_PART_KOREAN[bodyPart] ?? bodyPart;\n const techniqueName = TECHNIQUE_TYPE_KOREAN[techniqueType] ?? techniqueType;\n const intensityName = this.getIntensityKorean(intensity);\n\n return `${stanceName} ${bodyPartName} ${intensityName} ${techniqueName}`;\n }\n\n /**\n * Generate English technique name\n *\n * Format: \"Stance BodyPart Intensity TechniqueType\"\n * Example: \"Heaven Head Heavy Strike\"\n *\n * @private\n */\n private generateEnglishName(\n stance: string,\n techniqueType: TechniqueTypeCategory,\n bodyPart: string,\n intensity: TechniqueIntensity,\n ): string {\n const stanceName = STANCE_ENGLISH[stance] ?? stance;\n const bodyPartName = BODY_PART_ENGLISH[bodyPart] ?? bodyPart;\n const techniqueName =\n TECHNIQUE_TYPE_ENGLISH[techniqueType] ?? techniqueType;\n const intensityName =\n intensity.charAt(0).toUpperCase() + intensity.slice(1);\n\n return `${stanceName} ${bodyPartName} ${intensityName} ${techniqueName}`;\n }\n\n /**\n * Get Korean intensity name\n *\n * @private\n */\n private getIntensityKorean(intensity: TechniqueIntensity): string {\n switch (intensity) {\n case \"light\":\n return \"경\";\n case \"medium\":\n return \"중\";\n case \"heavy\":\n return \"강\";\n case \"critical\":\n return \"극\";\n }\n }\n\n /**\n * Adjust animation for different intensity\n *\n * **Korean**: 강도에 따른 애니메이션 조정\n *\n * @private\n */\n private adjustForIntensity(\n animation: TechniqueAnimation,\n intensity: TechniqueIntensity,\n ): TechniqueAnimation {\n const durationMultiplier =\n this.getDurationMultiplierForIntensity(intensity);\n\n return {\n ...animation,\n duration: animation.duration * durationMultiplier,\n impactFrame: Math.round(animation.impactFrame * durationMultiplier),\n recoveryFrames: Math.round(animation.recoveryFrames * durationMultiplier),\n };\n }\n\n /**\n * Get duration multiplier for intensity\n *\n * @private\n */\n private getDurationMultiplierForIntensity(\n intensity: TechniqueIntensity,\n ): number {\n switch (intensity) {\n case \"light\":\n return 0.7;\n case \"medium\":\n return 1.0;\n case \"heavy\":\n return 1.3;\n case \"critical\":\n return 1.6;\n }\n }\n\n /**\n * Get fallback animation when no exact match\n *\n * **Korean**: 일치하는 항목이 없을 때 대체 애니메이션 가져오기\n *\n * @private\n */\n private getFallbackAnimation(key: TechniqueAnimationKey): TechniqueAnimation {\n const fallbackState =\n this.fallbackMap.get(key.techniqueType) ?? AnimationState.ATTACK;\n\n return {\n animationState: fallbackState,\n duration: this.getDurationForIntensity(key.intensity),\n impactFrame: this.getImpactFrameForIntensity(key.intensity),\n recoveryFrames: this.getRecoveryFramesForIntensity(key.intensity),\n priority: AnimationPriority.ATTACK,\n koreanName: \"일반 기술\",\n englishName: \"Generic Technique\",\n };\n }\n\n /**\n * Generate all possible technique combinations\n *\n * **Korean**: 모든 가능한 기술 조합 생성\n *\n * @private\n */\n private generateAllCombinations(): readonly TechniqueAnimationKey[] {\n if (this.allCombinationsCache) {\n return this.allCombinationsCache;\n }\n\n const stances = Object.values(TrigramStance);\n const techniqueTypes: TechniqueTypeCategory[] = [\n \"strike\",\n \"joint\",\n \"throw\",\n \"pressure_point\",\n ];\n const bodyParts = Object.values(BodyPart);\n const intensities: TechniqueIntensity[] = [\n \"light\",\n \"medium\",\n \"heavy\",\n \"critical\",\n ];\n\n const combinations: TechniqueAnimationKey[] = [];\n\n stances.forEach((stance) => {\n techniqueTypes.forEach((techniqueType) => {\n bodyParts.forEach((bodyPart) => {\n intensities.forEach((intensity) => {\n combinations.push({\n stance,\n techniqueType,\n bodyPart,\n intensity,\n });\n });\n });\n });\n });\n\n this.allCombinationsCache = combinations;\n return combinations;\n }\n\n /**\n * Validate mapping completeness at build time\n *\n * **Korean**: 빌드 시간에 매핑 완전성 검증\n *\n * Reports coverage percentage and lists missing mappings.\n * Expected: 8 stances × 4 technique types × 8 body parts × 4 intensities = 1024 combinations\n *\n * @returns Validation result with coverage and missing mappings\n *\n * @korean 완전성검증\n */\n public validateCompleteness(): MappingValidationResult {\n const allCombinations = this.generateAllCombinations();\n const missing: TechniqueAnimationKey[] = [];\n\n allCombinations.forEach((combo) => {\n const key = this.createLookupKey(combo);\n if (!this.animationMap.has(key)) {\n missing.push(combo);\n }\n });\n\n const mapped = allCombinations.length - missing.length;\n const coverage = (mapped / allCombinations.length) * 100;\n\n return {\n coverage,\n total: allCombinations.length,\n mapped,\n missing,\n };\n }\n\n /**\n * Get total number of mapped combinations\n *\n * **Korean**: 매핑된 조합의 총 개수 가져오기\n *\n * @returns Number of mapped combinations\n *\n */\n public getMappedCount(): number {\n return this.animationMap.size;\n }\n}\n\n/**\n * Singleton instance of TechniqueAnimationMapper\n *\n * **Korean**: 기술 애니메이션 매퍼 싱글톤 인스턴스\n *\n * Use this instance throughout the application for consistent\n * technique animation mapping.\n *\n * @korean 싱글톤인스턴스\n */\nexport const techniqueAnimationMapper = new TechniqueAnimationMapper();\n"],"mappings":"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"}
1
+ {"version":3,"file":"TechniqueAnimationMapper.js","names":[],"sources":["../../../../src/systems/animation/core/TechniqueAnimationMapper.ts"],"sourcesContent":["/**\n * Technique Animation Mapper\n *\n * Maps 70 Korean techniques to appropriate attack animation types.\n * Implements the technique-to-animation link system for authentic martial arts animations.\n *\n * @module systems/animation/TechniqueAnimationMapper\n * @category Animation System\n * @korean 기술애니메이션매퍼\n */\n\nimport { AttackAnimationType } from \"@/types/skeletal\";\nimport { ATTACK_ANIMATIONS } from \"../catalogs/AttackAnimations\";\n\n/**\n * Maps AttackAnimationType to existing skeletal animation names\n *\n * Links the new enum-based system to existing animation implementation.\n * New animations will be added to AttackAnimations.ts as needed.\n *\n * @korean 애니메이션타입맵\n */\nconst ANIMATION_TYPE_TO_NAME_MAP: Record<AttackAnimationType, string> = {\n // Punch category (주먹 타격) - use existing animations\n [AttackAnimationType.PUNCH_HIGH]: \"jab\", // High punch to head\n [AttackAnimationType.PUNCH_MID]: \"cross\", // Mid-level cross punch\n [AttackAnimationType.PUNCH_LOW]: \"jab\", // Low punch to body (reuse jab for now)\n\n // Kick category (발차기) - use dedicated animations\n [AttackAnimationType.KICK_FRONT]: \"front_kick\", // Front kick (앞차기)\n [AttackAnimationType.KICK_SIDE]: \"side_kick\", // Side kick (옆차기) - NEW dedicated animation\n [AttackAnimationType.KICK_ROUNDHOUSE]: \"roundhouse_kick\", // Roundhouse kick (돌려차기)\n\n // Elbow category (팔꿈치 타격) - use dedicated elbow animations\n [AttackAnimationType.ELBOW_STRIKE]: \"elbow_strike\",\n [AttackAnimationType.ELBOW_UPPERCUT]: \"elbow_uppercut\", // NEW dedicated uppercut animation\n\n // Knee category (무릎 타격) - use dedicated knee animation\n [AttackAnimationType.KNEE_STRIKE]: \"knee_strike\",\n [AttackAnimationType.KNEE_CLINCH]: \"knee_strike\", // Reuse knee strike in clinch position\n\n // Pressure point category (급소 타격) - use precise jab motion\n [AttackAnimationType.PRESSURE_POINT]: \"jab\",\n [AttackAnimationType.PRESSURE_POINT_RAPID]: \"jab\",\n};\n\n/**\n * Get skeletal animation name for an attack animation type\n *\n * @param animationType - Attack animation type enum\n * @returns Name of skeletal animation from ATTACK_ANIMATIONS map\n *\n * @korean 애니메이션타입에서이름가져오기\n */\nexport function getAnimationNameForType(\n animationType: AttackAnimationType,\n): string {\n return ANIMATION_TYPE_TO_NAME_MAP[animationType];\n}\n\n/**\n * Check if an animation type has a defined animation\n *\n * @param animationType - Attack animation type enum\n * @returns True if animation exists in ATTACK_ANIMATIONS map\n *\n * @korean 애니메이션존재확인\n */\nexport function hasAnimationForType(\n animationType: AttackAnimationType,\n): boolean {\n const animationName = ANIMATION_TYPE_TO_NAME_MAP[animationType];\n return ATTACK_ANIMATIONS.has(animationName);\n}\n\n/**\n * Determines appropriate animation type from technique characteristics\n *\n * Uses technique name, damage type, and keywords to automatically\n * select the best-matching attack animation type.\n *\n * @param techniqueName - English or Korean technique name\n * @param techniqueId - Technique ID\n * @param damageType - Type of damage dealt\n * @returns Best-matching attack animation type\n *\n * @korean 기술에서애니메이션타입결정\n */\nexport function determineAnimationTypeForTechnique(\n techniqueName: string,\n techniqueId: string,\n damageType?: string,\n): AttackAnimationType {\n const searchText = `${techniqueName} ${techniqueId}`.toLowerCase();\n\n // Pressure point / precise strikes (급소)\n if (\n searchText.includes(\"pressure\") ||\n searchText.includes(\"precise\") ||\n searchText.includes(\"nerve\") ||\n searchText.includes(\"vital\") ||\n searchText.includes(\"급소\") ||\n searchText.includes(\"신경\") ||\n damageType === \"nerve\" ||\n damageType === \"pressure\"\n ) {\n // Check for rapid/multi-hit variants\n if (\n searchText.includes(\"rapid\") ||\n searchText.includes(\"multiple\") ||\n searchText.includes(\"연속\")\n ) {\n return AttackAnimationType.PRESSURE_POINT_RAPID;\n }\n return AttackAnimationType.PRESSURE_POINT;\n }\n\n // Kicks (차기)\n if (\n searchText.includes(\"kick\") ||\n searchText.includes(\"차기\") ||\n searchText.includes(\"발\")\n ) {\n if (\n searchText.includes(\"roundhouse\") ||\n searchText.includes(\"round\") ||\n searchText.includes(\"돌려\") ||\n searchText.includes(\"회전\")\n ) {\n return AttackAnimationType.KICK_ROUNDHOUSE;\n }\n if (\n searchText.includes(\"side\") ||\n searchText.includes(\"옆\") ||\n searchText.includes(\"측면\")\n ) {\n return AttackAnimationType.KICK_SIDE;\n }\n // Default to front kick\n return AttackAnimationType.KICK_FRONT;\n }\n\n // Elbow strikes (팔꿈치)\n if (\n searchText.includes(\"elbow\") ||\n searchText.includes(\"팔꿈치\") ||\n searchText.includes(\"팔굽\")\n ) {\n if (\n searchText.includes(\"uppercut\") ||\n searchText.includes(\"올려\") ||\n searchText.includes(\"치켜\")\n ) {\n return AttackAnimationType.ELBOW_UPPERCUT;\n }\n return AttackAnimationType.ELBOW_STRIKE;\n }\n\n // Knee strikes (무릎)\n if (\n searchText.includes(\"knee\") ||\n searchText.includes(\"무릎\") ||\n searchText.includes(\"슬\")\n ) {\n if (\n searchText.includes(\"clinch\") ||\n searchText.includes(\"grab\") ||\n searchText.includes(\"잡고\")\n ) {\n return AttackAnimationType.KNEE_CLINCH;\n }\n return AttackAnimationType.KNEE_STRIKE;\n }\n\n // Punches (주먹) - check target height\n if (\n searchText.includes(\"punch\") ||\n searchText.includes(\"strike\") ||\n searchText.includes(\"주먹\") ||\n searchText.includes(\"권\") ||\n searchText.includes(\"격\")\n ) {\n // High attacks (head level)\n if (\n searchText.includes(\"head\") ||\n searchText.includes(\"high\") ||\n searchText.includes(\"temple\") ||\n searchText.includes(\"jaw\") ||\n searchText.includes(\"머리\") ||\n searchText.includes(\"관자\") ||\n searchText.includes(\"턱\")\n ) {\n return AttackAnimationType.PUNCH_HIGH;\n }\n // Low attacks (body level)\n if (\n searchText.includes(\"low\") ||\n searchText.includes(\"body\") ||\n searchText.includes(\"ribs\") ||\n searchText.includes(\"아래\") ||\n searchText.includes(\"복부\") ||\n searchText.includes(\"늑골\")\n ) {\n return AttackAnimationType.PUNCH_LOW;\n }\n // Default to mid-level punch\n return AttackAnimationType.PUNCH_MID;\n }\n\n // Default: mid-level punch for any unmatched technique\n return AttackAnimationType.PUNCH_MID;\n}\n\n/**\n * Calculates animation speed modifier based on technique power level\n *\n * Implements the rule:\n * - Light techniques (damage <20): 1.2x speed\n * - Normal techniques (damage 20-35): 1.0x speed\n * - Heavy techniques (damage >35): 0.8x speed\n *\n * @param damage - Base damage of the technique\n * @returns Speed modifier (0.8 - 1.2)\n *\n * @korean 기술위력에서속도배율계산\n */\nexport function calculateSpeedModifierForDamage(damage: number): number {\n if (damage < 20) {\n return 1.2; // Light, fast techniques\n } else if (damage > 35) {\n return 0.8; // Heavy, powerful techniques\n }\n return 1.0; // Normal speed\n}\n\n/**\n * Get animation duration adjusted by speed modifier\n *\n * @param baseAnimationName - Name of base animation\n * @param speedModifier - Speed multiplier (0.8 - 1.2)\n * @returns Adjusted duration in milliseconds\n *\n * @korean 조정된애니메이션지속시간\n */\nexport function getAdjustedAnimationDuration(\n baseAnimationName: string,\n speedModifier: number,\n): number {\n const animation = ATTACK_ANIMATIONS.get(baseAnimationName);\n if (!animation) {\n console.warn(\"Animation not found:\", baseAnimationName);\n return 200; // Default 200ms for missing animations\n }\n\n // Convert seconds to milliseconds and apply speed modifier\n const baseDurationMs = animation.duration * 1000;\n return Math.round(baseDurationMs / speedModifier);\n}\n\n/**\n * Comprehensive Technique Animation Mapping System\n *\n * **Korean**: 포괄적 기술 애니메이션 매핑 시스템\n *\n * Maps all 1024 technique configuration combinations\n * (8 stances × 4 technique types × 8 body parts × 4 intensities)\n * to appropriate animations with O(1) lookup performance.\n *\n * ## Architecture\n *\n * - **Primary Map**: Exact stance-type-part-intensity combinations\n * - **Fallback System**: 3-tier graceful degradation\n * 1. Exact match\n * 2. Intensity-agnostic match (same stance/type/part, medium intensity)\n * 3. Technique type generic (fallback to base technique animation)\n *\n * ## Korean Martial Arts Integration\n *\n * Each trigram stance influences animation selection:\n * - **☰ 건 (Geon)**: Direct, forceful animations\n * - **☱ 태 (Tae)**: Fluid, flowing animations\n * - **☲ 리 (Li)**: Precise, rapid animations\n * - **☳ 진 (Jin)**: Explosive, shocking animations\n * - **☴ 손 (Son)**: Continuous, pressuring animations\n * - **☵ 감 (Gam)**: Adaptive, countering animations\n * - **☶ 간 (Gan)**: Defensive, immovable animations\n * - **☷ 곤 (Gon)**: Grounding, takedown animations\n *\n * @module systems/animation/TechniqueAnimationMapper\n * @category Animation System\n * @korean 기술애니메이션매퍼\n */\n\nimport { TrigramStance } from \"@/types\";\nimport { BodyPart } from \"../../bodypart/types\";\nimport {\n AnimationPriority,\n AnimationState,\n MappingValidationResult,\n TechniqueAnimation,\n TechniqueAnimationKey,\n TechniqueIntensity,\n TechniqueTypeCategory,\n} from \"./types\";\n\n/**\n * Body part Korean terminology for animation names\n *\n * **Korean**: 신체 부위 한글 용어\n */\nconst BODY_PART_KOREAN: Record<string, string> = {\n [BodyPart.HEAD]: \"두부\",\n [BodyPart.NECK]: \"경부\",\n [BodyPart.TORSO_UPPER]: \"상체\",\n [BodyPart.TORSO_LOWER]: \"하체\",\n [BodyPart.ARM_LEFT]: \"좌팔\",\n [BodyPart.ARM_RIGHT]: \"우팔\",\n [BodyPart.LEG_LEFT]: \"좌각\",\n [BodyPart.LEG_RIGHT]: \"우각\",\n};\n\n/**\n * Body part English terminology for animation names\n */\nconst BODY_PART_ENGLISH: Record<string, string> = {\n [BodyPart.HEAD]: \"Head\",\n [BodyPart.NECK]: \"Neck\",\n [BodyPart.TORSO_UPPER]: \"Upper Torso\",\n [BodyPart.TORSO_LOWER]: \"Lower Torso\",\n [BodyPart.ARM_LEFT]: \"Left Arm\",\n [BodyPart.ARM_RIGHT]: \"Right Arm\",\n [BodyPart.LEG_LEFT]: \"Left Leg\",\n [BodyPart.LEG_RIGHT]: \"Right Leg\",\n};\n\n/**\n * Technique type Korean terminology\n *\n * **Korean**: 기술 유형 한글 용어\n */\nconst TECHNIQUE_TYPE_KOREAN: Record<TechniqueTypeCategory, string> = {\n strike: \"타격\",\n joint: \"관절\",\n throw: \"던지기\",\n pressure_point: \"급소\",\n};\n\n/**\n * Technique type English terminology\n */\nconst TECHNIQUE_TYPE_ENGLISH: Record<TechniqueTypeCategory, string> = {\n strike: \"Strike\",\n joint: \"Joint Lock\",\n throw: \"Throw\",\n pressure_point: \"Pressure Point\",\n};\n\n/**\n * Stance Korean names\n */\nconst STANCE_KOREAN: Record<string, string> = {\n [TrigramStance.GEON]: \"건괘\",\n [TrigramStance.TAE]: \"태괘\",\n [TrigramStance.LI]: \"리괘\",\n [TrigramStance.JIN]: \"진괘\",\n [TrigramStance.SON]: \"손괘\",\n [TrigramStance.GAM]: \"감괘\",\n [TrigramStance.GAN]: \"간괘\",\n [TrigramStance.GON]: \"곤괘\",\n};\n\n/**\n * Stance English names\n */\nconst STANCE_ENGLISH: Record<string, string> = {\n [TrigramStance.GEON]: \"Heaven\",\n [TrigramStance.TAE]: \"Lake\",\n [TrigramStance.LI]: \"Fire\",\n [TrigramStance.JIN]: \"Thunder\",\n [TrigramStance.SON]: \"Wind\",\n [TrigramStance.GAM]: \"Water\",\n [TrigramStance.GAN]: \"Mountain\",\n [TrigramStance.GON]: \"Earth\",\n};\n\n/**\n * Stance Animation Characteristics Configuration\n *\n * **Korean**: 자세 애니메이션 특성 설정\n *\n * Data-driven configuration for each stance's animation modifiers,\n * eliminating code duplication across stance mapping methods.\n *\n * @internal\n */\ninterface StanceAnimationConfig {\n /** Base duration multiplier (1.0 = normal) */\n readonly durationMultiplier: number;\n /** Impact frame offset from base */\n readonly impactFrameOffset: number;\n /** Recovery frames offset from base */\n readonly recoveryFrameOffset: number;\n /** Technique-specific overrides (optional) */\n readonly techniqueOverrides?: Partial<\n Record<\n TechniqueTypeCategory,\n {\n durationMultiplier?: number;\n impactFrameOffset?: number;\n recoveryFrameOffset?: number;\n }\n >\n >;\n}\n\n/**\n * Stance animation characteristics by trigram\n *\n * **Korean**: 팔괘 자세별 애니메이션 특성\n *\n * Each stance has unique timing characteristics reflecting its philosophy:\n * - ☰ 건 (Geon/Heaven): Direct, powerful - standard timing with technique variations\n * - ☱ 태 (Tae/Lake): Fluid, flowing - faster recovery\n * - ☲ 리 (Li/Fire): Precise, rapid - fastest overall\n * - ☳ 진 (Jin/Thunder): Explosive - fast attack, longer recovery\n * - ☴ 손 (Son/Wind): Continuous - slightly fast, mobile\n * - ☵ 감 (Gam/Water): Adaptive - slightly slower, standard recovery\n * - ☶ 간 (Gan/Mountain): Defensive - slowest, longest recovery\n * - ☷ 곤 (Gon/Earth): Grounding - standard with throw specialization\n */\nconst STANCE_ANIMATION_CONFIG: Record<TrigramStance, StanceAnimationConfig> = {\n [TrigramStance.GEON]: {\n durationMultiplier: 1.0,\n impactFrameOffset: 0,\n recoveryFrameOffset: 0,\n techniqueOverrides: {\n joint: { durationMultiplier: 1.2 },\n throw: {\n durationMultiplier: 1.5,\n impactFrameOffset: 4,\n recoveryFrameOffset: 5,\n },\n pressure_point: {\n durationMultiplier: 0.8,\n impactFrameOffset: -2,\n },\n },\n },\n [TrigramStance.TAE]: {\n durationMultiplier: 1.0,\n impactFrameOffset: 0,\n recoveryFrameOffset: -2,\n techniqueOverrides: {\n joint: { durationMultiplier: 1.3 },\n },\n },\n [TrigramStance.LI]: {\n durationMultiplier: 0.85,\n impactFrameOffset: -1,\n recoveryFrameOffset: -3,\n },\n [TrigramStance.JIN]: {\n durationMultiplier: 0.9,\n impactFrameOffset: 0,\n recoveryFrameOffset: 2,\n },\n [TrigramStance.SON]: {\n durationMultiplier: 0.95,\n impactFrameOffset: 0,\n recoveryFrameOffset: -1,\n },\n [TrigramStance.GAM]: {\n durationMultiplier: 1.1,\n impactFrameOffset: 1,\n recoveryFrameOffset: 0,\n },\n [TrigramStance.GAN]: {\n durationMultiplier: 1.2,\n impactFrameOffset: 2,\n recoveryFrameOffset: 3,\n },\n [TrigramStance.GON]: {\n durationMultiplier: 1.0,\n impactFrameOffset: 0,\n recoveryFrameOffset: 0,\n techniqueOverrides: {\n throw: {\n durationMultiplier: 1.6,\n impactFrameOffset: 3,\n recoveryFrameOffset: 4,\n },\n },\n },\n};\n\n/**\n * Comprehensive Technique Animation Mapper Class\n *\n * **Korean**: 기술 애니메이션 매퍼 클래스\n *\n * Provides O(1) lookup for all 1024 technique-stance combinations\n * with intelligent fallback system and build-time validation.\n *\n * @class\n */\nexport class TechniqueAnimationMapper {\n /** Primary animation mapping table */\n private readonly animationMap: Map<string, TechniqueAnimation>;\n\n /** Fallback animations by technique type */\n private readonly fallbackMap: Map<TechniqueTypeCategory, AnimationState>;\n\n /** Cache for generated combinations (validation) */\n private allCombinationsCache?: readonly TechniqueAnimationKey[];\n\n constructor() {\n this.animationMap = new Map();\n this.fallbackMap = this.initializeFallbacks();\n this.initializeCompleteMapping();\n }\n\n /**\n * Get animation for specific technique\n *\n * **Korean**: 특정 기술의 애니메이션 가져오기\n *\n * Uses 3-tier lookup strategy:\n * 1. Exact match (stance + type + part + intensity)\n * 2. Intensity-agnostic (stance + type + part + medium intensity)\n * 3. Technique type fallback (generic animation for technique type)\n *\n * @param key - Technique animation key\n * @returns Technique animation configuration\n *\n * @korean 애니메이션가져오기\n */\n public getAnimation(key: TechniqueAnimationKey): TechniqueAnimation {\n // Try exact match first\n const lookupKey = this.createLookupKey(key);\n const exactMatch = this.animationMap.get(lookupKey);\n if (exactMatch) {\n return exactMatch;\n }\n\n // Fallback: Try without intensity (use medium)\n const genericKey = this.createLookupKey({\n ...key,\n intensity: \"medium\",\n });\n const genericMatch = this.animationMap.get(genericKey);\n if (genericMatch) {\n return this.adjustForIntensity(genericMatch, key.intensity);\n }\n\n // Final fallback: Use technique type generic animation\n return this.getFallbackAnimation(key);\n }\n\n /**\n * Create composite lookup key from technique animation key\n *\n * **Korean**: 복합 조회 키 생성\n *\n * Format: \"stance-type-part-intensity\"\n * Example: \"geon-strike-head-heavy\"\n *\n * @param key - Technique animation key\n * @returns Composite lookup string\n *\n * @private\n */\n private createLookupKey(key: TechniqueAnimationKey): string {\n return `${key.stance}-${key.techniqueType}-${key.bodyPart}-${key.intensity}`;\n }\n\n /**\n * Initialize fallback animations by technique type\n *\n * **Korean**: 기술 유형별 대체 애니메이션 초기화\n *\n * @private\n */\n private initializeFallbacks(): Map<TechniqueTypeCategory, AnimationState> {\n return new Map([\n [\"strike\", AnimationState.ATTACK],\n [\"joint\", AnimationState.ATTACK],\n [\"throw\", AnimationState.ATTACK],\n [\"pressure_point\", AnimationState.ATTACK],\n ]);\n }\n\n /**\n * Initialize complete mapping table\n *\n * **Korean**: 전체 매핑 테이블 초기화\n *\n * Maps all 1024 combinations organized by:\n * - 8 stances\n * - 4 technique types\n * - 8 body parts\n * - 4 intensity levels\n *\n * Uses data-driven STANCE_ANIMATION_CONFIG to eliminate code duplication.\n *\n * @private\n */\n private initializeCompleteMapping(): void {\n const bodyParts = Object.values(BodyPart);\n const intensities: TechniqueIntensity[] = [\n \"light\",\n \"medium\",\n \"heavy\",\n \"critical\",\n ];\n const techniqueTypes: TechniqueTypeCategory[] = [\n \"strike\",\n \"joint\",\n \"throw\",\n \"pressure_point\",\n ];\n\n // Map all 8 trigram stances using configuration\n Object.values(TrigramStance).forEach((stance) => {\n const config = STANCE_ANIMATION_CONFIG[stance];\n\n bodyParts.forEach((bodyPart) => {\n intensities.forEach((intensity) => {\n techniqueTypes.forEach((techniqueType) => {\n // Get base values with technique-specific overrides\n const override = config.techniqueOverrides?.[techniqueType];\n const durationMult =\n override?.durationMultiplier ?? config.durationMultiplier;\n const impactOffset =\n override?.impactFrameOffset ?? config.impactFrameOffset;\n const recoveryOffset =\n override?.recoveryFrameOffset ?? config.recoveryFrameOffset;\n\n this.mapTechnique(\n { stance, techniqueType, bodyPart, intensity },\n {\n animationState: AnimationState.ATTACK,\n duration:\n this.getDurationForIntensity(intensity) * durationMult,\n impactFrame:\n this.getImpactFrameForIntensity(intensity) + impactOffset,\n recoveryFrames:\n this.getRecoveryFramesForIntensity(intensity) +\n recoveryOffset,\n priority: AnimationPriority.ATTACK,\n koreanName: this.generateKoreanName(\n stance,\n techniqueType,\n bodyPart,\n intensity,\n ),\n englishName: this.generateEnglishName(\n stance,\n techniqueType,\n bodyPart,\n intensity,\n ),\n },\n );\n });\n });\n });\n });\n }\n /**\n * Add single technique mapping to the map with automatic rotation calculation\n *\n * **Korean**: 단일 기술 매핑 추가\n *\n * @private\n */\n private mapTechnique(\n key: TechniqueAnimationKey,\n animation: TechniqueAnimation,\n ): void {\n // Calculate rotation properties if not provided\n const torsoRotation =\n animation.torsoRotation ??\n this.getTorsoRotationForStanceTechnique(key.stance, key.techniqueType);\n const hipEngagement =\n animation.hipEngagement ??\n this.getHipEngagementForStanceTechnique(\n key.stance,\n key.techniqueType,\n key.intensity,\n );\n const powerModifier =\n animation.powerModifier ??\n this.calculatePowerModifier(hipEngagement, key.techniqueType);\n\n // Create complete animation with rotation properties\n const completeAnimation: TechniqueAnimation = {\n ...animation,\n torsoRotation,\n hipEngagement,\n powerModifier,\n };\n\n const lookupKey = this.createLookupKey(key);\n this.animationMap.set(lookupKey, completeAnimation);\n }\n\n /**\n * Get duration based on intensity level\n *\n * **Korean**: 강도 레벨에 따른 지속시간 가져오기\n *\n * @private\n */\n private getDurationForIntensity(intensity: TechniqueIntensity): number {\n const baseDuration = 0.6; // 600ms base\n switch (intensity) {\n case \"light\":\n return baseDuration * 0.7; // 420ms\n case \"medium\":\n return baseDuration; // 600ms\n case \"heavy\":\n return baseDuration * 1.3; // 780ms\n case \"critical\":\n return baseDuration * 1.6; // 960ms\n }\n }\n\n /**\n * Get impact frame based on intensity level\n *\n * **Korean**: 강도 레벨에 따른 충격 프레임 가져오기\n *\n * @private\n */\n private getImpactFrameForIntensity(intensity: TechniqueIntensity): number {\n switch (intensity) {\n case \"light\":\n return 8; // Frame 8 at 60fps\n case \"medium\":\n return 10; // Frame 10\n case \"heavy\":\n return 14; // Frame 14\n case \"critical\":\n return 18; // Frame 18\n }\n }\n\n /**\n * Get recovery frames based on intensity level\n *\n * **Korean**: 강도 레벨에 따른 회복 프레임 가져오기\n *\n * @private\n */\n private getRecoveryFramesForIntensity(intensity: TechniqueIntensity): number {\n switch (intensity) {\n case \"light\":\n return 8; // 8 frames recovery\n case \"medium\":\n return 12; // 12 frames recovery\n case \"heavy\":\n return 18; // 18 frames recovery\n case \"critical\":\n return 24; // 24 frames recovery\n }\n }\n\n /**\n * Calculate torso rotation for stance-technique combination\n *\n * **Korean**: 자세-기술 조합의 허리 회전 계산\n *\n * Returns rotation in radians (-π/2 to π/2)\n *\n * @private\n */\n private getTorsoRotationForStanceTechnique(\n stance: string,\n techniqueType: TechniqueTypeCategory,\n ): number {\n // Stance-specific base rotations (in radians)\n const stanceRotations: Record<string, number> = {\n [TrigramStance.GEON]: Math.PI / 6, // 30° - Direct, moderate rotation\n [TrigramStance.TAE]: Math.PI / 18, // 10° - Minimal rotation, fluid\n [TrigramStance.LI]: Math.PI / 9, // 20° - Quick snap rotation\n [TrigramStance.JIN]: Math.PI / 4, // 45° - Wide explosive rotation\n [TrigramStance.SON]: Math.PI / 12, // 15° - Continuous adaptive flow\n [TrigramStance.GAM]: Math.PI / 8, // 22.5° - Circular wave motion\n [TrigramStance.GAN]: Math.PI / 36, // 5° - Stable, minimal rotation\n [TrigramStance.GON]: Math.PI / 3, // 60° - Deep grounded rotation\n };\n\n const baseRotation = stanceRotations[stance] ?? 0;\n\n // Technique type modifiers\n const techniqueMultipliers: Record<TechniqueTypeCategory, number> = {\n strike: 1.2, // 20% more rotation for strikes\n joint: 0.7, // 30% less for joint locks\n throw: 1.5, // 50% more for throws\n pressure_point: 0.8, // 20% less for precision strikes\n };\n\n const multiplier = techniqueMultipliers[techniqueType] ?? 1.0;\n return Math.min(Math.PI / 2, baseRotation * multiplier); // Clamp to ±90°\n }\n\n /**\n * Calculate hip engagement for stance-technique combination\n *\n * **Korean**: 자세-기술 조합의 골반 참여도 계산\n *\n * Returns engagement factor (0-1)\n *\n * @private\n */\n private getHipEngagementForStanceTechnique(\n stance: string,\n techniqueType: TechniqueTypeCategory,\n intensity: TechniqueIntensity,\n ): number {\n // Stance-specific base hip engagement\n const stanceEngagement: Record<string, number> = {\n [TrigramStance.GEON]: 0.9, // High engagement - direct force\n [TrigramStance.TAE]: 0.5, // Moderate - fluid movement\n [TrigramStance.LI]: 0.7, // Good - rapid techniques\n [TrigramStance.JIN]: 1.0, // Maximum - explosive power\n [TrigramStance.SON]: 0.6, // Moderate - continuous pressure\n [TrigramStance.GAM]: 0.7, // Good - adaptive flow\n [TrigramStance.GAN]: 0.3, // Low - stable defensive\n [TrigramStance.GON]: 0.95, // Very high - grounding techniques\n };\n\n const baseEngagement = stanceEngagement[stance] ?? 0.6;\n\n // Technique type modifiers\n const techniqueMultipliers: Record<TechniqueTypeCategory, number> = {\n strike: 1.0, // Full engagement for strikes\n joint: 0.6, // Lower for joint manipulation\n throw: 1.1, // Highest for throws\n pressure_point: 0.7, // Moderate for precision\n };\n\n // Intensity modifiers\n const intensityMultipliers: Record<TechniqueIntensity, number> = {\n light: 0.7,\n medium: 0.9,\n heavy: 1.0,\n critical: 1.1,\n };\n\n const engagement =\n baseEngagement *\n techniqueMultipliers[techniqueType] *\n intensityMultipliers[intensity];\n\n return Math.min(1.0, Math.max(0.0, engagement)); // Clamp to 0-1\n }\n\n /**\n * Calculate power modifier from hip engagement\n *\n * **Korean**: 골반 참여도로부터 파워 배율 계산\n *\n * Based on PR #1132 calculateHipRotationPowerModifier system\n *\n * @private\n */\n private calculatePowerModifier(\n hipEngagement: number,\n techniqueType: TechniqueTypeCategory,\n ): number {\n // Base power curve: 1.0 + (engagement * maxBonus)\n const maxBonusByType: Record<TechniqueTypeCategory, number> = {\n strike: 0.3, // 30% max bonus for strikes\n joint: 0.1, // 10% for joint locks\n throw: 0.2, // 20% for throws\n pressure_point: 0.25, // 25% for pressure points\n };\n\n const maxBonus = maxBonusByType[techniqueType] ?? 0.2;\n return 1.0 + hipEngagement * maxBonus;\n }\n\n /**\n * Generate Korean technique name\n *\n * **Korean**: 한글 기술 이름 생성\n *\n * Format: \"스탠스 신체부위 강도 기술유형\"\n * Example: \"건괘 두부 강 타격\"\n *\n * @private\n */\n private generateKoreanName(\n stance: string,\n techniqueType: TechniqueTypeCategory,\n bodyPart: string,\n intensity: TechniqueIntensity,\n ): string {\n const stanceName = STANCE_KOREAN[stance] ?? stance;\n const bodyPartName = BODY_PART_KOREAN[bodyPart] ?? bodyPart;\n const techniqueName = TECHNIQUE_TYPE_KOREAN[techniqueType] ?? techniqueType;\n const intensityName = this.getIntensityKorean(intensity);\n\n return `${stanceName} ${bodyPartName} ${intensityName} ${techniqueName}`;\n }\n\n /**\n * Generate English technique name\n *\n * Format: \"Stance BodyPart Intensity TechniqueType\"\n * Example: \"Heaven Head Heavy Strike\"\n *\n * @private\n */\n private generateEnglishName(\n stance: string,\n techniqueType: TechniqueTypeCategory,\n bodyPart: string,\n intensity: TechniqueIntensity,\n ): string {\n const stanceName = STANCE_ENGLISH[stance] ?? stance;\n const bodyPartName = BODY_PART_ENGLISH[bodyPart] ?? bodyPart;\n const techniqueName =\n TECHNIQUE_TYPE_ENGLISH[techniqueType] ?? techniqueType;\n const intensityName =\n intensity.charAt(0).toUpperCase() + intensity.slice(1);\n\n return `${stanceName} ${bodyPartName} ${intensityName} ${techniqueName}`;\n }\n\n /**\n * Get Korean intensity name\n *\n * @private\n */\n private getIntensityKorean(intensity: TechniqueIntensity): string {\n switch (intensity) {\n case \"light\":\n return \"경\";\n case \"medium\":\n return \"중\";\n case \"heavy\":\n return \"강\";\n case \"critical\":\n return \"극\";\n }\n }\n\n /**\n * Adjust animation for different intensity\n *\n * **Korean**: 강도에 따른 애니메이션 조정\n *\n * @private\n */\n private adjustForIntensity(\n animation: TechniqueAnimation,\n intensity: TechniqueIntensity,\n ): TechniqueAnimation {\n const durationMultiplier =\n this.getDurationMultiplierForIntensity(intensity);\n\n return {\n ...animation,\n duration: animation.duration * durationMultiplier,\n impactFrame: Math.round(animation.impactFrame * durationMultiplier),\n recoveryFrames: Math.round(animation.recoveryFrames * durationMultiplier),\n };\n }\n\n /**\n * Get duration multiplier for intensity\n *\n * @private\n */\n private getDurationMultiplierForIntensity(\n intensity: TechniqueIntensity,\n ): number {\n switch (intensity) {\n case \"light\":\n return 0.7;\n case \"medium\":\n return 1.0;\n case \"heavy\":\n return 1.3;\n case \"critical\":\n return 1.6;\n }\n }\n\n /**\n * Get fallback animation when no exact match\n *\n * **Korean**: 일치하는 항목이 없을 때 대체 애니메이션 가져오기\n *\n * @private\n */\n private getFallbackAnimation(key: TechniqueAnimationKey): TechniqueAnimation {\n const fallbackState =\n this.fallbackMap.get(key.techniqueType) ?? AnimationState.ATTACK;\n\n return {\n animationState: fallbackState,\n duration: this.getDurationForIntensity(key.intensity),\n impactFrame: this.getImpactFrameForIntensity(key.intensity),\n recoveryFrames: this.getRecoveryFramesForIntensity(key.intensity),\n priority: AnimationPriority.ATTACK,\n koreanName: \"일반 기술\",\n englishName: \"Generic Technique\",\n };\n }\n\n /**\n * Generate all possible technique combinations\n *\n * **Korean**: 모든 가능한 기술 조합 생성\n *\n * @private\n */\n private generateAllCombinations(): readonly TechniqueAnimationKey[] {\n if (this.allCombinationsCache) {\n return this.allCombinationsCache;\n }\n\n const stances = Object.values(TrigramStance);\n const techniqueTypes: TechniqueTypeCategory[] = [\n \"strike\",\n \"joint\",\n \"throw\",\n \"pressure_point\",\n ];\n const bodyParts = Object.values(BodyPart);\n const intensities: TechniqueIntensity[] = [\n \"light\",\n \"medium\",\n \"heavy\",\n \"critical\",\n ];\n\n const combinations: TechniqueAnimationKey[] = [];\n\n stances.forEach((stance) => {\n techniqueTypes.forEach((techniqueType) => {\n bodyParts.forEach((bodyPart) => {\n intensities.forEach((intensity) => {\n combinations.push({\n stance,\n techniqueType,\n bodyPart,\n intensity,\n });\n });\n });\n });\n });\n\n this.allCombinationsCache = combinations;\n return combinations;\n }\n\n /**\n * Validate mapping completeness at build time\n *\n * **Korean**: 빌드 시간에 매핑 완전성 검증\n *\n * Reports coverage percentage and lists missing mappings.\n * Expected: 8 stances × 4 technique types × 8 body parts × 4 intensities = 1024 combinations\n *\n * @returns Validation result with coverage and missing mappings\n *\n * @korean 완전성검증\n */\n public validateCompleteness(): MappingValidationResult {\n const allCombinations = this.generateAllCombinations();\n const missing: TechniqueAnimationKey[] = [];\n\n allCombinations.forEach((combo) => {\n const key = this.createLookupKey(combo);\n if (!this.animationMap.has(key)) {\n missing.push(combo);\n }\n });\n\n const mapped = allCombinations.length - missing.length;\n const coverage = (mapped / allCombinations.length) * 100;\n\n return {\n coverage,\n total: allCombinations.length,\n mapped,\n missing,\n };\n }\n\n /**\n * Get total number of mapped combinations\n *\n * **Korean**: 매핑된 조합의 총 개수 가져오기\n *\n * @returns Number of mapped combinations\n *\n */\n public getMappedCount(): number {\n return this.animationMap.size;\n }\n}\n\n/**\n * Singleton instance of TechniqueAnimationMapper\n *\n * **Korean**: 기술 애니메이션 매퍼 싱글톤 인스턴스\n *\n * Use this instance throughout the application for consistent\n * technique animation mapping.\n *\n * @korean 싱글톤인스턴스\n */\nexport const techniqueAnimationMapper = new TechniqueAnimationMapper();\n"],"mappings":"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"}
@@ -747,7 +747,7 @@ var DEFAULT_CONFIGS = {
747
747
  *
748
748
  * @korean 기술애니메이션매핑
749
749
  */
750
- var TECHNIQUE_ANIMATIONS = new Map([
750
+ var TECHNIQUE_ANIMATIONS = /* @__PURE__ */ new Map([
751
751
  ["geon_heaven_strike", {
752
752
  type: AnimationType.HEAVEN_STRIKE,
753
753
  speed: 1