blacktrigram 0.7.39 → 0.7.40

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 (354) hide show
  1. package/lib/App2.js.map +1 -1
  2. package/lib/audio/AudioAssetLoader.js.map +1 -1
  3. package/lib/audio/AudioAssetRegistry.js.map +1 -1
  4. package/lib/audio/AudioCache.js.map +1 -1
  5. package/lib/audio/AudioManager.js.map +1 -1
  6. package/lib/audio/AudioMonitor.js.map +1 -1
  7. package/lib/audio/AudioPool.js.map +1 -1
  8. package/lib/audio/AudioProvider.js.map +1 -1
  9. package/lib/audio/AudioUtils.js.map +1 -1
  10. package/lib/audio/BoneImpactAudioMap.js.map +1 -1
  11. package/lib/audio/VariantSelector.js.map +1 -1
  12. package/lib/audio/types.js.map +1 -1
  13. package/lib/components/screens/combat/CombatScreen3D.js.map +1 -1
  14. package/lib/components/screens/combat/components/controls/CombatButtons.js.map +1 -1
  15. package/lib/components/screens/combat/components/controls/CombatControlsPanel.js.map +1 -1
  16. package/lib/components/screens/combat/components/controls/ControlsGuide.js.map +1 -1
  17. package/lib/components/screens/combat/components/controls/KeyboardHints.js.map +1 -1
  18. package/lib/components/screens/combat/components/controls/PauseMenu.js.map +1 -1
  19. package/lib/components/screens/combat/components/controls/PauseMenuButton.js.map +1 -1
  20. package/lib/components/screens/combat/components/controls/QuickSettings.js.map +1 -1
  21. package/lib/components/screens/combat/components/effects/BloodDecals3D.js.map +1 -1
  22. package/lib/components/screens/combat/components/effects/BloodLossOverlayHtml.js.map +1 -1
  23. package/lib/components/screens/combat/components/effects/BloodParticles3D.js.map +1 -1
  24. package/lib/components/screens/combat/components/effects/BloodViscosity3D.js.map +1 -1
  25. package/lib/components/screens/combat/components/effects/CombatParticleEffects3D.js.map +1 -1
  26. package/lib/components/screens/combat/components/effects/ConsciousnessBlur.js.map +1 -1
  27. package/lib/components/screens/combat/components/effects/InternalDamage3D.js.map +1 -1
  28. package/lib/components/screens/combat/components/effects/PainVignette.js.map +1 -1
  29. package/lib/components/screens/combat/components/effects/ParticleAudio3D.js.map +1 -1
  30. package/lib/components/screens/combat/components/effects/TraumaOverlay3D.js.map +1 -1
  31. package/lib/components/screens/combat/components/feedback/MatchCountdown.js.map +1 -1
  32. package/lib/components/screens/combat/components/feedback/RoundAnnouncementOverlayHtml.js.map +1 -1
  33. package/lib/components/screens/combat/components/feedback/RoundDisplayStatus.js.map +1 -1
  34. package/lib/components/screens/combat/components/feedback/RoundStartAnnouncementOverlayHtml.js.map +1 -1
  35. package/lib/components/screens/combat/components/hud/CombatBottomHUD.js.map +1 -1
  36. package/lib/components/screens/combat/components/hud/CombatLeftHUD.js.map +1 -1
  37. package/lib/components/screens/combat/components/hud/CombatPortraitStatusStrip.js.map +1 -1
  38. package/lib/components/screens/combat/components/hud/CombatRightHUD.js.map +1 -1
  39. package/lib/components/screens/combat/components/hud/CombatTopHUD.js.map +1 -1
  40. package/lib/components/screens/combat/components/hud/DifficultyIndicator.js.map +1 -1
  41. package/lib/components/screens/combat/components/hud/FPSMonitor.js.map +1 -1
  42. package/lib/components/screens/combat/components/hud/MobileControlsWrapper.js.map +1 -1
  43. package/lib/components/screens/combat/components/hud/PlayerStateOverlayHtml.js.map +1 -1
  44. package/lib/components/screens/combat/components/indicators/BalanceIndicator.js.map +1 -1
  45. package/lib/components/screens/combat/components/indicators/InputBufferDisplay.js.map +1 -1
  46. package/lib/components/screens/combat/components/indicators/StaminaWarning.js.map +1 -1
  47. package/lib/components/screens/combat/components/indicators/TechniqueNameDisplay.js.map +1 -1
  48. package/lib/components/screens/combat/helpers/AnimationUpdater.js.map +1 -1
  49. package/lib/components/screens/combat/helpers/combatHelpers.js.map +1 -1
  50. package/lib/components/screens/combat/hooks/useAICombat.js.map +1 -1
  51. package/lib/components/screens/combat/hooks/useCombatActions.js.map +1 -1
  52. package/lib/components/screens/combat/hooks/useCombatAttackMovement.js.map +1 -1
  53. package/lib/components/screens/combat/hooks/useCombatAudio.js.map +1 -1
  54. package/lib/components/screens/combat/hooks/useCombatLayout.js.map +1 -1
  55. package/lib/components/screens/combat/hooks/useCombatState.js.map +1 -1
  56. package/lib/components/screens/controls/ControlsScreen3D.js.map +1 -1
  57. package/lib/components/screens/controls/components/ControlBindingsOverlayHtml.js.map +1 -1
  58. package/lib/components/screens/controls/components/ControlCategoryTabsOverlayHtml.js.map +1 -1
  59. package/lib/components/screens/controls/components/GamepadVisualization3D.js.map +1 -1
  60. package/lib/components/screens/controls/components/InteractiveControlDemoOverlayHtml.js.map +1 -1
  61. package/lib/components/screens/controls/components/Key3D.js.map +1 -1
  62. package/lib/components/screens/controls/components/VisualKeyboard3D.js.map +1 -1
  63. package/lib/components/screens/controls/constants/ControlsConstants.js.map +1 -1
  64. package/lib/components/screens/controls/hooks/useControlsState.js.map +1 -1
  65. package/lib/components/screens/endscreen/EndScreen3D.js.map +1 -1
  66. package/lib/components/screens/endscreen/components/DefeatAnimation3D.js.map +1 -1
  67. package/lib/components/screens/endscreen/components/MatchStatisticsDisplayOverlayHtml.js.map +1 -1
  68. package/lib/components/screens/endscreen/components/NavigationButtonsOverlayHtml.js.map +1 -1
  69. package/lib/components/screens/endscreen/components/PerformanceBreakdownOverlayHtml.js.map +1 -1
  70. package/lib/components/screens/endscreen/components/PerformanceRatingOverlayHtml.js.map +1 -1
  71. package/lib/components/screens/endscreen/components/VictoryAnimation3D.js.map +1 -1
  72. package/lib/components/screens/endscreen/components/WinnerDisplayOverlayHtml.js.map +1 -1
  73. package/lib/components/screens/intro/IntroScreen3D.js +1 -1
  74. package/lib/components/screens/intro/IntroScreen3D.js.map +1 -1
  75. package/lib/components/screens/intro/components/AbilityListOverlayHtml.js.map +1 -1
  76. package/lib/components/screens/intro/components/ArchetypeCardGridOverlayHtml.js.map +1 -1
  77. package/lib/components/screens/intro/components/ArchetypeCardOverlayHtml.js.map +1 -1
  78. package/lib/components/screens/intro/components/ArchetypeDisplayOverlayHtml.js.map +1 -1
  79. package/lib/components/screens/intro/components/EnhancedArchetypeDisplayOverlayHtml.js.map +1 -1
  80. package/lib/components/screens/intro/components/MenuButtonsOverlayHtml.js.map +1 -1
  81. package/lib/components/screens/intro/components/MenuSectionOverlayHtml.js.map +1 -1
  82. package/lib/components/screens/intro/components/StatBarOverlayHtml.js.map +1 -1
  83. package/lib/components/screens/philosophy/PhilosophyScreen3D.js.map +1 -1
  84. package/lib/components/screens/training/TrainingScreen3D.js.map +1 -1
  85. package/lib/components/screens/training/components/AnatomyControlsOverlayHtml.js.map +1 -1
  86. package/lib/components/screens/training/components/AnatomyOverlay3D.js.map +1 -1
  87. package/lib/components/screens/training/components/FootPlacementMarkers3D.js.map +1 -1
  88. package/lib/components/screens/training/components/FootworkDrillsOverlayHtml.js.map +1 -1
  89. package/lib/components/screens/training/components/HitFeedbackEffect3D.js.map +1 -1
  90. package/lib/components/screens/training/components/TrainingButtonsOverlayHtml.js.map +1 -1
  91. package/lib/components/screens/training/components/TrainingControlsOverlayHtml.js.map +1 -1
  92. package/lib/components/screens/training/components/TrainingDummy3D.js.map +1 -1
  93. package/lib/components/screens/training/components/TrainingFeedbackOverlayHtml.js.map +1 -1
  94. package/lib/components/screens/training/components/TrainingModeSelectorOverlayHtml.js.map +1 -1
  95. package/lib/components/screens/training/components/TrainingStatsOverlayHtml.js.map +1 -1
  96. package/lib/components/screens/training/components/VitalPointMarker3D.js.map +1 -1
  97. package/lib/components/screens/training/components/VitalPointTrainingOverlayHtml.js.map +1 -1
  98. package/lib/components/screens/training/components/hud/TrainingBottomHUD.js.map +1 -1
  99. package/lib/components/screens/training/components/hud/TrainingLeftHUD.js.map +1 -1
  100. package/lib/components/screens/training/components/hud/TrainingRightHUD.js.map +1 -1
  101. package/lib/components/screens/training/components/hud/TrainingTopHUD.js.map +1 -1
  102. package/lib/components/screens/training/hooks/useAttackMovement.js.map +1 -1
  103. package/lib/components/screens/training/hooks/useTrainingActions.js.map +1 -1
  104. package/lib/components/screens/training/hooks/useTrainingLayout.js.map +1 -1
  105. package/lib/components/screens/training/hooks/useTrainingState.js.map +1 -1
  106. package/lib/components/shared/base/BaseButton.js.map +1 -1
  107. package/lib/components/shared/base/BaseButtonOverlayHtml.js.map +1 -1
  108. package/lib/components/shared/base/BasePanel.js.map +1 -1
  109. package/lib/components/shared/base/BaseText.js.map +1 -1
  110. package/lib/components/shared/base/useKoreanTheme.js.map +1 -1
  111. package/lib/components/shared/debug/PerformanceDebugOverlayHtml.js.map +1 -1
  112. package/lib/components/shared/mobile/ActionButtons.js.map +1 -1
  113. package/lib/components/shared/mobile/GestureRecognizerPure.js.map +1 -1
  114. package/lib/components/shared/mobile/HapticController.js.map +1 -1
  115. package/lib/components/shared/mobile/MobileControlsPure.js.map +1 -1
  116. package/lib/components/shared/mobile/StanceWheelPure.js.map +1 -1
  117. package/lib/components/shared/mobile/TouchOptimizer.js.map +1 -1
  118. package/lib/components/shared/mobile/VirtualDPad.js.map +1 -1
  119. package/lib/components/shared/three/anatomy/BodySurface.js.map +1 -1
  120. package/lib/components/shared/three/anatomy/BoneAttachedMuscles.js.map +1 -1
  121. package/lib/components/shared/three/anatomy/BoneClothing.js.map +1 -1
  122. package/lib/components/shared/three/anatomy/BoneRenderer.js.map +1 -1
  123. package/lib/components/shared/three/anatomy/Face3D.js.map +1 -1
  124. package/lib/components/shared/three/anatomy/Foot3D.js.map +1 -1
  125. package/lib/components/shared/three/anatomy/Hand3D.js.map +1 -1
  126. package/lib/components/shared/three/effects/ActionFeedback.js.map +1 -1
  127. package/lib/components/shared/three/effects/DamageNumbers.js.map +1 -1
  128. package/lib/components/shared/three/effects/HitEffects3D.js.map +1 -1
  129. package/lib/components/shared/three/effects/PlayerStateIndicators.js.map +1 -1
  130. package/lib/components/shared/three/effects/StanceSymbol3D.js.map +1 -1
  131. package/lib/components/shared/three/effects/StanceTransitionEffect.js.map +1 -1
  132. package/lib/components/shared/three/effects/VitalPointMarkers3D.js.map +1 -1
  133. package/lib/components/shared/three/indicators/ElementalColorSystem.js.map +1 -1
  134. package/lib/components/shared/three/indicators/GuardIndicator.js.map +1 -1
  135. package/lib/components/shared/three/indicators/HapticFeedback.js.map +1 -1
  136. package/lib/components/shared/three/indicators/StanceChangeIndicator.js.map +1 -1
  137. package/lib/components/shared/three/models/Player3DWithTransitions.js.map +1 -1
  138. package/lib/components/shared/three/models/SkeletalPlayer3D.js.map +1 -1
  139. package/lib/components/shared/three/optimization/AdaptiveQuality.js.map +1 -1
  140. package/lib/components/shared/three/scene/AtmosphericParticles3D.js.map +1 -1
  141. package/lib/components/shared/three/scene/BackgroundScene3D.js.map +1 -1
  142. package/lib/components/shared/three/scene/CombatArena3D.js.map +1 -1
  143. package/lib/components/shared/three/scene/KoreanSignage3D.js.map +1 -1
  144. package/lib/components/shared/three/ui/ArchetypeCard.js.map +1 -1
  145. package/lib/components/shared/three/ui/BodyPartHealthDisplay.js.map +1 -1
  146. package/lib/components/shared/three/ui/BreathingIndicator2.js.map +1 -1
  147. package/lib/components/shared/three/ui/CombatReadinessBar.js.map +1 -1
  148. package/lib/components/shared/three/ui/ComboCounter.js.map +1 -1
  149. package/lib/components/shared/three/ui/HealthBar.js.map +1 -1
  150. package/lib/components/shared/three/ui/KoreanButton.js.map +1 -1
  151. package/lib/components/shared/three/ui/KoreanPanel.js.map +1 -1
  152. package/lib/components/shared/three/ui/KoreanText.js.map +1 -1
  153. package/lib/components/shared/three/ui/MenuList.js.map +1 -1
  154. package/lib/components/shared/three/ui/PlayerHUD.js.map +1 -1
  155. package/lib/components/shared/three/ui/ProgressBar.js.map +1 -1
  156. package/lib/components/shared/three/ui/SpeedIndicatorHUD.js.map +1 -1
  157. package/lib/components/shared/three/ui/StaminaBar.js.map +1 -1
  158. package/lib/components/shared/three/ui/TechniqueBar.js.map +1 -1
  159. package/lib/components/shared/three/ui/TechniqueCard.js.map +1 -1
  160. package/lib/components/shared/three/ui/VitalPointOverlayControlsHtml.js.map +1 -1
  161. package/lib/components/shared/ui/BackButton.js.map +1 -1
  162. package/lib/components/shared/ui/BaseHUDContainer.js.map +1 -1
  163. package/lib/components/shared/ui/CombatTimer.js.map +1 -1
  164. package/lib/components/shared/ui/ErrorModal.js.map +1 -1
  165. package/lib/components/shared/ui/LoadingState.js.map +1 -1
  166. package/lib/components/shared/ui/SplashScreen.js +2 -2
  167. package/lib/components/shared/ui/SplashScreen.js.map +1 -1
  168. package/lib/components/shared/ui/VitalPointOverlayControlsPure.js.map +1 -1
  169. package/lib/components/shared/ui/VolumeControl.js.map +1 -1
  170. package/lib/components/shared/ui/shared/ConfirmDialog.js.map +1 -1
  171. package/lib/components/ui/combat/BalanceIndicatorOverlayHtml.js.map +1 -1
  172. package/lib/constants/bodyDimensions.js.map +1 -1
  173. package/lib/constants/bodyRenderingConstants.js.map +1 -1
  174. package/lib/data/archetypeClothing.js.map +1 -1
  175. package/lib/data/archetypePhysicalAttributes.js.map +1 -1
  176. package/lib/data/techniqueMappings.js.map +1 -1
  177. package/lib/data/techniques.js.map +1 -1
  178. package/lib/hooks/useActionFeedback.js.map +1 -1
  179. package/lib/hooks/useBalanceAnimations.js.map +1 -1
  180. package/lib/hooks/useCombatTimer.js.map +1 -1
  181. package/lib/hooks/useDebounce.js.map +1 -1
  182. package/lib/hooks/useHUDLayout.js.map +1 -1
  183. package/lib/hooks/useHandPoseTransitions.js.map +1 -1
  184. package/lib/hooks/useKeyboardControls.js.map +1 -1
  185. package/lib/hooks/useMatchCountdown.js.map +1 -1
  186. package/lib/hooks/useMuscleActivation.js.map +1 -1
  187. package/lib/hooks/usePauseMenu.js.map +1 -1
  188. package/lib/hooks/usePlayerAnimation.js.map +1 -1
  189. package/lib/hooks/useResponsiveLayout.js.map +1 -1
  190. package/lib/hooks/useRoundTransition.js.map +1 -1
  191. package/lib/hooks/useSkeletalAnimation.js.map +1 -1
  192. package/lib/hooks/useTechniqueSelection.js.map +1 -1
  193. package/lib/hooks/useThrottle.js.map +1 -1
  194. package/lib/hooks/useTouchControls.js.map +1 -1
  195. package/lib/hooks/useWebGLContextLossHandler.js.map +1 -1
  196. package/lib/hooks/useWindowSize.js.map +1 -1
  197. package/lib/systems/CombatSystem.js.map +1 -1
  198. package/lib/systems/EffectCalculator.js.map +1 -1
  199. package/lib/systems/LayoutSystem.js.map +1 -1
  200. package/lib/systems/PlayerEffectManager.js.map +1 -1
  201. package/lib/systems/ResponsiveScaling.js.map +1 -1
  202. package/lib/systems/TrigramSystem.js.map +1 -1
  203. package/lib/systems/VitalPointSystem.js.map +1 -1
  204. package/lib/systems/ai/AIPersonality.js.map +1 -1
  205. package/lib/systems/ai/AdaptiveDifficulty.js.map +1 -1
  206. package/lib/systems/ai/ArchetypeEnforcer.js.map +1 -1
  207. package/lib/systems/ai/ComboSystem.js.map +1 -1
  208. package/lib/systems/ai/DecisionTree.js.map +1 -1
  209. package/lib/systems/ai/TrainingAI.js.map +1 -1
  210. package/lib/systems/ai/types.js.map +1 -1
  211. package/lib/systems/animation/builders/AnimationBuilder.js.map +1 -1
  212. package/lib/systems/animation/builders/HandPoseApplicator.js.map +1 -1
  213. package/lib/systems/animation/builders/HandPoses.js.map +1 -1
  214. package/lib/systems/animation/builders/KeyframeConfig.js.map +1 -1
  215. package/lib/systems/animation/builders/KeyframeInterpolation.js +3 -90
  216. package/lib/systems/animation/builders/KeyframeInterpolation.js.map +1 -1
  217. package/lib/systems/animation/builders/KickPhaseApplicator.js.map +1 -1
  218. package/lib/systems/animation/builders/KoreanGuardPositions.js.map +1 -1
  219. package/lib/systems/animation/builders/MartialArtsAnimationBuilder.js.map +1 -1
  220. package/lib/systems/animation/builders/MartialArtsConstants.js.map +1 -1
  221. package/lib/systems/animation/builders/MartialPoseApplicator.js.map +1 -1
  222. package/lib/systems/animation/builders/PunchPhaseApplicator.js.map +1 -1
  223. package/lib/systems/animation/builders/SkeletonRig.js.map +1 -1
  224. package/lib/systems/animation/builders/TrigramGuardApplicator.js.map +1 -1
  225. package/lib/systems/animation/catalogs/DefensiveAnimations.js.map +1 -1
  226. package/lib/systems/animation/catalogs/FootworkSkeletalAnimations.js.map +1 -1
  227. package/lib/systems/animation/catalogs/RecoveryAnimations.js.map +1 -1
  228. package/lib/systems/animation/catalogs/StanceAnimations.js.map +1 -1
  229. package/lib/systems/animation/catalogs/StanceAttackAnimations.js.map +1 -1
  230. package/lib/systems/animation/catalogs/StanceGuardPoses.js.map +1 -1
  231. package/lib/systems/animation/catalogs/StanceIdleAnimations.js.map +1 -1
  232. package/lib/systems/animation/catalogs/StanceLocomotionAnimations.js.map +1 -1
  233. package/lib/systems/animation/catalogs/StepSkeletalAnimations.js.map +1 -1
  234. package/lib/systems/animation/core/AnimationHitTiming.js.map +1 -1
  235. package/lib/systems/animation/core/AnimationOptimizations.js.map +1 -1
  236. package/lib/systems/animation/core/AnimationPriority.js.map +1 -1
  237. package/lib/systems/animation/core/AnimationRegistry.js.map +1 -1
  238. package/lib/systems/animation/core/AnimationStateMachine.js.map +1 -1
  239. package/lib/systems/animation/core/AnimationTransitions.js.map +1 -1
  240. package/lib/systems/animation/core/LateralityTransform.js.map +1 -1
  241. package/lib/systems/animation/core/RecoveryPhaseEnhancer.js.map +1 -1
  242. package/lib/systems/animation/core/TechniqueAnimationMapper.js.map +1 -1
  243. package/lib/systems/animation/core/TechniqueAnimationMapping.js.map +1 -1
  244. package/lib/systems/animation/core/types.js.map +1 -1
  245. package/lib/systems/animation/systems/AdvancedJointMovements.js.map +1 -1
  246. package/lib/systems/animation/systems/BodyFacingSystem.js.map +1 -1
  247. package/lib/systems/animation/systems/FacialExpressions.js.map +1 -1
  248. package/lib/systems/animation/systems/FallAnimations.js.map +1 -1
  249. package/lib/systems/animation/systems/MuscleActivation.js.map +1 -1
  250. package/lib/systems/bodypart/BodyPartDamageIntegration.js.map +1 -1
  251. package/lib/systems/bodypart/BodyPartHealthSystem.js.map +1 -1
  252. package/lib/systems/bodypart/BodyPartPositionMapping.js.map +1 -1
  253. package/lib/systems/bodypart/CombatInjuryIntegration.js.map +1 -1
  254. package/lib/systems/bodypart/InjuryIntegration.js.map +1 -1
  255. package/lib/systems/bodypart/InjuryTracker.js.map +1 -1
  256. package/lib/systems/bodypart/MovementPenaltySystem.js.map +1 -1
  257. package/lib/systems/bodypart/PlayerInjuryTrackingManager.js.map +1 -1
  258. package/lib/systems/bodypart/types.js.map +1 -1
  259. package/lib/systems/breathing/BreathingDisruptionSystem.js.map +1 -1
  260. package/lib/systems/breathing/feedback.js.map +1 -1
  261. package/lib/systems/breathing/integration.js.map +1 -1
  262. package/lib/systems/combat/BalanceSystem.js.map +1 -1
  263. package/lib/systems/combat/BreakingStatusEffects.js.map +1 -1
  264. package/lib/systems/combat/CombatStateSystem.js.map +1 -1
  265. package/lib/systems/combat/ConsciousnessSystem.js.map +1 -1
  266. package/lib/systems/combat/FallIntegration.js.map +1 -1
  267. package/lib/systems/combat/GrappleSystem.js.map +1 -1
  268. package/lib/systems/combat/LimbExposureSystem.js.map +1 -1
  269. package/lib/systems/combat/PainResponseSystem.js.map +1 -1
  270. package/lib/systems/combat/TrainingCombatSystem.js.map +1 -1
  271. package/lib/systems/combat/painConsciousnessUtils.js.map +1 -1
  272. package/lib/systems/combat/typeGuards.js.map +1 -1
  273. package/lib/systems/effects.js.map +1 -1
  274. package/lib/systems/game.js.map +1 -1
  275. package/lib/systems/movement/InjuryMovementModifier.js.map +1 -1
  276. package/lib/systems/movement/helpers/AccelerationUpdater.js.map +1 -1
  277. package/lib/systems/movement/helpers/accelerationUtils.js.map +1 -1
  278. package/lib/systems/movement/integration.js.map +1 -1
  279. package/lib/systems/physics/AttackMovementPhysics.js.map +1 -1
  280. package/lib/systems/physics/CollisionDetection.js.map +1 -1
  281. package/lib/systems/physics/CoordinateMapper.js.map +1 -1
  282. package/lib/systems/physics/KnockbackPhysics.js.map +1 -1
  283. package/lib/systems/physics/MovementPhysics.js.map +1 -1
  284. package/lib/systems/physics/PhysicalReachCalculator.js.map +1 -1
  285. package/lib/systems/physics/SpeedModifierSystem.js.map +1 -1
  286. package/lib/systems/trigram/KoreanCulture.js.map +1 -1
  287. package/lib/systems/trigram/KoreanTechniques.js.map +1 -1
  288. package/lib/systems/trigram/StanceManager.js.map +1 -1
  289. package/lib/systems/trigram/TransitionCalculator.js.map +1 -1
  290. package/lib/systems/trigram/TrigramCalculator.js.map +1 -1
  291. package/lib/systems/trigram/techniques/DarkOpsTechniques.js.map +1 -1
  292. package/lib/systems/trigram/techniques/GamTechniques.js.map +1 -1
  293. package/lib/systems/trigram/techniques/GanTechniques.js.map +1 -1
  294. package/lib/systems/trigram/techniques/GonTechniques.js.map +1 -1
  295. package/lib/systems/trigram/techniques/SonTechniques.js.map +1 -1
  296. package/lib/systems/trigram/techniques/TechniqueConfig.js.map +1 -1
  297. package/lib/systems/trigram/techniques/index.js.map +1 -1
  298. package/lib/systems/trigram/types/GonTechniqueExtensions.js.map +1 -1
  299. package/lib/systems/trigram/types.js.map +1 -1
  300. package/lib/systems/types.js.map +1 -1
  301. package/lib/systems/vitalpoint/DamageCalculator.js.map +1 -1
  302. package/lib/systems/vitalpoint/HitDetection.js.map +1 -1
  303. package/lib/systems/vitalpoint/KoreanAnatomy.js.map +1 -1
  304. package/lib/systems/vitalpoint/KoreanVitalPoints.js.map +1 -1
  305. package/lib/systems/vitalpoint/MeridianVitalPointMapping.js.map +1 -1
  306. package/lib/types/AccessibilityTypes.js.map +1 -1
  307. package/lib/types/PhysicsTypes.js.map +1 -1
  308. package/lib/types/common.js.map +1 -1
  309. package/lib/types/constants/colors.js.map +1 -1
  310. package/lib/types/constants/designSystem.js.map +1 -1
  311. package/lib/types/constants/layout.js.map +1 -1
  312. package/lib/types/constants/performance.js.map +1 -1
  313. package/lib/types/constants/typography.js.map +1 -1
  314. package/lib/types/facial.js.map +1 -1
  315. package/lib/types/hand-animation.js.map +1 -1
  316. package/lib/types/injury.js.map +1 -1
  317. package/lib/types/physics.js.map +1 -1
  318. package/lib/types/skeletal.js.map +1 -1
  319. package/lib/types/techniqueId.js.map +1 -1
  320. package/lib/utils/accessibility.js.map +1 -1
  321. package/lib/utils/arenaWorldDimensions.js.map +1 -1
  322. package/lib/utils/assetConfig.js.map +1 -1
  323. package/lib/utils/characterScaling.js.map +1 -1
  324. package/lib/utils/colorHelpers.js.map +1 -1
  325. package/lib/utils/colorUtils.js.map +1 -1
  326. package/lib/utils/combatReadiness.js.map +1 -1
  327. package/lib/utils/controlMapping.js.map +1 -1
  328. package/lib/utils/deviceDetection.js.map +1 -1
  329. package/lib/utils/effectUtils.js.map +1 -1
  330. package/lib/utils/fabricTextures.js.map +1 -1
  331. package/lib/utils/hapticFeedback.js.map +1 -1
  332. package/lib/utils/haptics.js.map +1 -1
  333. package/lib/utils/htmlOverlayHelpers.js.map +1 -1
  334. package/lib/utils/inputSystem.js.map +1 -1
  335. package/lib/utils/koreanThemeHelpers.js.map +1 -1
  336. package/lib/utils/math.js.map +1 -1
  337. package/lib/utils/mobileLayoutHelpers.js.map +1 -1
  338. package/lib/utils/mobileUIUtils.js.map +1 -1
  339. package/lib/utils/performance/PerformanceMonitor.js.map +1 -1
  340. package/lib/utils/performance/PerformanceOverlay3D.js.map +1 -1
  341. package/lib/utils/performance/usePerformanceMonitor.js.map +1 -1
  342. package/lib/utils/performanceOptimization.js.map +1 -1
  343. package/lib/utils/player3DHelpers.js.map +1 -1
  344. package/lib/utils/playerUtils.js.map +1 -1
  345. package/lib/utils/responsiveLayout.js.map +1 -1
  346. package/lib/utils/responsiveLayoutHelpers.js.map +1 -1
  347. package/lib/utils/responsiveOrientationConstants.js.map +1 -1
  348. package/lib/utils/safeAreaUtils.js.map +1 -1
  349. package/lib/utils/sharedPhysicsConfig.js.map +1 -1
  350. package/lib/utils/skeletonScaling.js.map +1 -1
  351. package/lib/utils/stanceHelpers.js.map +1 -1
  352. package/lib/utils/threeObjectPool.js.map +1 -1
  353. package/lib/utils/visualEffects.js.map +1 -1
  354. package/package.json +5 -5
@@ -1 +1 @@
1
- {"version":3,"file":"LateralityTransform.js","names":[],"sources":["../../../../src/systems/animation/core/LateralityTransform.ts"],"sourcesContent":["/**\n * Laterality Transform System for Animation Pipeline\n *\n * **Korean**: 측면성 변환 시스템\n *\n * Extends the animation system to support StanceLaterality (왼발서기/오른발서기),\n * enabling authentic Korean martial arts stance mirroring across all animations.\n * Creates 16 distinct stance configurations (8 trigrams × 2 sides).\n *\n * Key Features:\n * - Transform skeletal animations for left/right laterality\n * - Mirror bone rotations (swap left ↔ right, negate Y/Z axes)\n * - Preserve animation timing and type information\n * - Performance optimized (<1ms transformation time)\n *\n * Korean Terminology:\n * - **왼발서기 (Oenbal Seogi)**: Left foot forward, left guard high\n * - **오른발서기 (Oreun Bal Seogi)**: Right foot forward, right guard high\n * - **측면성 (Cheugmyeonseong)**: Laterality/sidedness in martial arts\n *\n * @module systems/animation/LateralityTransform\n * @category Animation\n * @korean 측면성변환\n */\n\nimport type { AnimationKeyframe, SkeletalAnimation } from \"@/types/skeletal\";\nimport * as THREE from \"three\";\nimport type { StanceLaterality } from \"../../trigram/types\";\n\n/**\n * Apply laterality transformation to a skeletal animation.\n *\n * **Korean**: 애니메이션 측면성 적용\n *\n * Transforms a skeletal animation to match the specified laterality (left/right stance).\n * Right laterality returns the original animation; left laterality creates a mirrored version\n * with left/right bones swapped and lateral rotations negated.\n *\n * Transformation Rules:\n * - Swap left ↔ right bone names (e.g., \"shoulder_L\" ↔ \"shoulder_R\")\n * - Negate Y rotation (lateral twist around vertical axis)\n * - Negate Z rotation (roll around forward-back axis)\n * - Preserve X rotation (forward/back bend)\n * - Maintain timing, duration, and easing information\n *\n * Performance: <1ms for typical animation (verified in tests)\n *\n * @param animation - Source skeletal animation to transform\n * @param laterality - Target laterality: \"left\" or \"right\"\n * @returns Transformed animation with mirrored bone rotations for left laterality\n *\n * @example\n * ```typescript\n * // Get right-handed punch animation\n * const rightPunch = GEON_BONE_BREAKING_STRIKE_1;\n *\n * // Create left-handed version\n * const leftPunch = applyLaterality(rightPunch, \"left\");\n * // leftPunch has left hand as striking hand, mirrored rotations\n *\n * // Right laterality returns original (no transformation cost)\n * const sameAnimation = applyLaterality(rightPunch, \"right\");\n * // sameAnimation === rightPunch (same reference)\n * ```\n *\n * @public\n * @category Animation Transform\n * @korean 애니메이션측면성적용\n */\nexport function applyLaterality(\n animation: SkeletalAnimation,\n laterality: StanceLaterality,\n): SkeletalAnimation {\n // Right laterality returns original animation (no transformation)\n if (laterality === \"right\") {\n return animation;\n }\n\n // Left laterality: mirror all keyframes\n const mirroredKeyframes = animation.keyframes.map((keyframe) =>\n mirrorAnimationKeyframe(keyframe),\n );\n\n // Return transformed animation with updated name\n return {\n ...animation,\n name: `${animation.name}_left`,\n koreanName: `${animation.koreanName} (왼발)`,\n keyframes: mirroredKeyframes,\n };\n}\n\n/**\n * Mirror a single animation keyframe for left laterality.\n *\n * **Korean**: 키프레임 좌우 대칭\n *\n * Creates a mirrored version of an animation keyframe by:\n * 1. Swapping left and right bone names\n * 2. Negating Y and Z rotations (lateral twist and roll)\n * 3. Preserving X rotation (forward/back bend)\n * 4. Maintaining timing and easing information\n *\n * Bone Name Swapping:\n * - \"_L\" suffix → \"_R\" suffix\n * - \"_R\" suffix → \"_L\" suffix\n * - \"left_\" prefix → \"right_\" prefix\n * - \"right_\" prefix → \"left_\" prefix\n *\n * @param keyframe - Original animation keyframe\n * @returns Mirrored keyframe with swapped bones and negated rotations\n *\n * @internal\n * @korean 키프레임대칭\n */\nfunction mirrorAnimationKeyframe(\n keyframe: AnimationKeyframe,\n): AnimationKeyframe {\n // Mirror bone rotations\n const mirroredRotations = new Map<string, THREE.Euler>();\n\n keyframe.boneRotations.forEach((rotation, boneName) => {\n const mirroredBoneName = mirrorBoneName(boneName);\n const mirroredRotation = mirrorEuler(rotation);\n mirroredRotations.set(mirroredBoneName, mirroredRotation);\n });\n\n // Mirror bone positions (if present)\n const mirroredPositions = new Map<string, THREE.Vector3>();\n\n if (keyframe.bonePositions) {\n keyframe.bonePositions.forEach((position, boneName) => {\n const mirroredBoneName = mirrorBoneName(boneName);\n const mirroredPosition = mirrorPosition(position);\n mirroredPositions.set(mirroredBoneName, mirroredPosition);\n });\n }\n\n return {\n time: keyframe.time,\n boneRotations: mirroredRotations,\n bonePositions: mirroredPositions,\n easing: keyframe.easing,\n };\n}\n\n/**\n * Mirror a bone name by swapping left/right suffixes and prefixes.\n *\n * **Korean**: 뼈 이름 대칭\n *\n * Transforms bone names to their mirrored counterparts:\n * - \"shoulder_L\" → \"shoulder_R\"\n * - \"upper_arm_R\" → \"upper_arm_L\"\n * - \"left_hand\" → \"right_hand\"\n * - \"right_foot\" → \"left_foot\"\n *\n * If bone has no laterality marker (e.g., \"spine\", \"pelvis\"),\n * returns the original name unchanged.\n *\n * @param boneName - Original bone name\n * @returns Mirrored bone name with swapped left/right markers\n *\n * @example\n * ```typescript\n * mirrorBoneName(\"shoulder_L\") // → \"shoulder_R\"\n * mirrorBoneName(\"upper_arm_R\") // → \"upper_arm_L\"\n * mirrorBoneName(\"left_hand\") // → \"right_hand\"\n * mirrorBoneName(\"spine_upper\") // → \"spine_upper\" (no change)\n * ```\n *\n * @internal\n * @korean 뼈이름대칭\n */\nfunction mirrorBoneName(boneName: string): string {\n // Handle _L and _R suffixes (most common pattern)\n if (boneName.endsWith(\"_L\")) {\n return boneName.slice(0, -2) + \"_R\";\n }\n if (boneName.endsWith(\"_R\")) {\n return boneName.slice(0, -2) + \"_L\";\n }\n\n // Handle left_ and right_ prefixes\n if (boneName.startsWith(\"left_\")) {\n return \"right_\" + boneName.slice(5);\n }\n if (boneName.startsWith(\"right_\")) {\n return \"left_\" + boneName.slice(6);\n }\n\n // No laterality marker - return unchanged (e.g., spine, pelvis, neck)\n return boneName;\n}\n\n/**\n * Mirror a bone rotation by negating Y and Z axes.\n *\n * **Korean**: 뼈 회전 대칭\n *\n * Transforms rotation for left/right mirroring:\n * - X rotation: Preserved (forward/back bend is symmetric)\n * - Y rotation: Negated (lateral twist direction reverses)\n * - Z rotation: Negated (roll direction reverses)\n *\n * This creates anatomically correct mirrored poses where:\n * - Right arm punch becomes left arm punch\n * - Left leg kick becomes right leg kick\n * - Torso twist direction reverses appropriately\n *\n * @param euler - Original bone rotation\n * @returns Mirrored rotation with negated Y and Z axes\n *\n * @example\n * ```typescript\n * // Right arm forward punch: shoulder rotates forward and inward\n * const rightPunch = new THREE.Euler(-1.2, 0.5, 0.6);\n * const leftPunch = mirrorEuler(rightPunch);\n * // leftPunch: (-1.2, -0.5, -0.6) - forward bend preserved, twist/roll negated\n * ```\n *\n * @internal\n * @korean 뼈회전대칭\n */\nfunction mirrorEuler(euler: THREE.Euler): THREE.Euler {\n return new THREE.Euler(\n euler.x, // Preserve forward/back bend\n -euler.y, // Negate lateral twist\n -euler.z, // Negate roll\n euler.order, // Preserve rotation order\n );\n}\n\n/**\n * Mirror a bone position by negating the X coordinate.\n *\n * **Korean**: 뼈 위치 대칭\n *\n * Mirrors position across the YZ plane (body centerline).\n *\n * **World-Space Coordinate System (matching AnimationMirror):**\n * Skeletal animations use world-space coordinates consistent with the 28-bone rig:\n * - X coordinate: Left/right (arms extend along ±X)\n * - Y coordinate: Up/down (pelvis at Y=1.1, legs extend -Y)\n * - Z coordinate: Forward/back (character faces +Z)\n *\n * For mirroring:\n * - X: Negated (left ↔ right)\n * - Y: Preserved (up/down is symmetric)\n * - Z: Preserved (forward/back is symmetric)\n *\n * Used for IK targets and special move positioning where\n * absolute bone positions are specified.\n *\n * @param position - Original bone position offset\n * @returns Mirrored position with negated X coordinate\n *\n * @example\n * ```typescript\n * // Right hand extends to the right (+X)\n * const rightHandPos = new THREE.Vector3(0.5, 1.5, 0.3);\n * const leftHandPos = mirrorPosition(rightHandPos);\n * // leftHandPos: (-0.5, 1.5, 0.3) - extends to the left (-X)\n * ```\n *\n * @internal\n * @korean 뼈위치대칭\n */\nfunction mirrorPosition(position: THREE.Vector3): THREE.Vector3 {\n return new THREE.Vector3(\n -position.x, // Negate left/right (X axis = lateral in world-space rig)\n position.y, // Preserve vertical\n position.z, // Preserve forward/back\n );\n}\n\n/**\n * Get the laterality identifier for an animation.\n *\n * **Korean**: 애니메이션 측면성 식별\n *\n * Determines if an animation is configured for left or right laterality\n * based on its name suffix.\n *\n * @param animation - Skeletal animation to inspect\n * @returns \"left\" if animation has \"_left\" suffix, otherwise \"right\"\n *\n * @example\n * ```typescript\n * const rightAnim = { name: \"geon_punch\", ... };\n * const leftAnim = { name: \"geon_punch_left\", ... };\n *\n * getAnimationLaterality(rightAnim); // → \"right\"\n * getAnimationLaterality(leftAnim); // → \"left\"\n * ```\n *\n * @public\n * @category Animation Query\n * @korean 애니메이션측면성식별\n */\nexport function getAnimationLaterality(\n animation: SkeletalAnimation,\n): StanceLaterality {\n return animation.name.endsWith(\"_left\") ? \"left\" : \"right\";\n}\n\n/**\n * Check if two animations are laterality variants of each other.\n *\n * **Korean**: 애니메이션 측면성 변형 확인\n *\n * Determines if two animations represent the same technique but\n * with different laterality (left vs right stance).\n *\n * @param anim1 - First animation\n * @param anim2 - Second animation\n * @returns True if animations are laterality variants of the same base animation\n *\n * @example\n * ```typescript\n * const rightPunch = { name: \"geon_punch\", ... };\n * const leftPunch = { name: \"geon_punch_left\", ... };\n * const otherMove = { name: \"tae_throw\", ... };\n *\n * areLateralityVariants(rightPunch, leftPunch); // → true\n * areLateralityVariants(rightPunch, otherMove); // → false\n * ```\n *\n * @public\n * @category Animation Query\n * @korean 애니메이션측면성변형확인\n */\nexport function areLateralityVariants(\n anim1: SkeletalAnimation,\n anim2: SkeletalAnimation,\n): boolean {\n // Get base names (remove _left suffix if present)\n const baseName1 = anim1.name.replace(/_left$/, \"\");\n const baseName2 = anim2.name.replace(/_left$/, \"\");\n\n // Check if base names match and laterality differs\n return (\n baseName1 === baseName2 &&\n getAnimationLaterality(anim1) !== getAnimationLaterality(anim2)\n );\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAqEA,SAAgB,gBACd,WACA,YACmB;AAEnB,KAAI,eAAe,QACjB,QAAO;CAIT,MAAM,oBAAoB,UAAU,UAAU,KAAK,aACjD,wBAAwB,SAAS,CAClC;AAGD,QAAO;EACL,GAAG;EACH,MAAM,GAAG,UAAU,KAAK;EACxB,YAAY,GAAG,UAAU,WAAW;EACpC,WAAW;EACZ;;;;;;;;;;;;;;;;;;;;;;;;;AA0BH,SAAS,wBACP,UACmB;CAEnB,MAAM,oCAAoB,IAAI,KAA0B;AAExD,UAAS,cAAc,SAAS,UAAU,aAAa;EACrD,MAAM,mBAAmB,eAAe,SAAS;EACjD,MAAM,mBAAmB,YAAY,SAAS;AAC9C,oBAAkB,IAAI,kBAAkB,iBAAiB;GACzD;CAGF,MAAM,oCAAoB,IAAI,KAA4B;AAE1D,KAAI,SAAS,cACX,UAAS,cAAc,SAAS,UAAU,aAAa;EACrD,MAAM,mBAAmB,eAAe,SAAS;EACjD,MAAM,mBAAmB,eAAe,SAAS;AACjD,oBAAkB,IAAI,kBAAkB,iBAAiB;GACzD;AAGJ,QAAO;EACL,MAAM,SAAS;EACf,eAAe;EACf,eAAe;EACf,QAAQ,SAAS;EAClB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA+BH,SAAS,eAAe,UAA0B;AAEhD,KAAI,SAAS,SAAS,KAAK,CACzB,QAAO,SAAS,MAAM,GAAG,GAAG,GAAG;AAEjC,KAAI,SAAS,SAAS,KAAK,CACzB,QAAO,SAAS,MAAM,GAAG,GAAG,GAAG;AAIjC,KAAI,SAAS,WAAW,QAAQ,CAC9B,QAAO,WAAW,SAAS,MAAM,EAAE;AAErC,KAAI,SAAS,WAAW,SAAS,CAC/B,QAAO,UAAU,SAAS,MAAM,EAAE;AAIpC,QAAO;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAgCT,SAAS,YAAY,OAAiC;AACpD,QAAO,IAAI,MAAM,MACf,MAAM,GACN,CAAC,MAAM,GACP,CAAC,MAAM,GACP,MAAM,MACP;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAsCH,SAAS,eAAe,UAAwC;AAC9D,QAAO,IAAI,MAAM,QACf,CAAC,SAAS,GACV,SAAS,GACT,SAAS,EACV"}
1
+ {"version":3,"file":"LateralityTransform.js","names":[],"sources":["../../../../src/systems/animation/core/LateralityTransform.ts"],"sourcesContent":["/**\n * Laterality Transform System for Animation Pipeline\n *\n * **Korean**: 측면성 변환 시스템\n *\n * Extends the animation system to support StanceLaterality (왼발서기/오른발서기),\n * enabling authentic Korean martial arts stance mirroring across all animations.\n * Creates 16 distinct stance configurations (8 trigrams × 2 sides).\n *\n * Key Features:\n * - Transform skeletal animations for left/right laterality\n * - Mirror bone rotations (swap left ↔ right, negate Y/Z axes)\n * - Preserve animation timing and type information\n * - Performance optimized (<1ms transformation time)\n *\n * Korean Terminology:\n * - **왼발서기 (Oenbal Seogi)**: Left foot forward, left guard high\n * - **오른발서기 (Oreun Bal Seogi)**: Right foot forward, right guard high\n * - **측면성 (Cheugmyeonseong)**: Laterality/sidedness in martial arts\n *\n * @module systems/animation/LateralityTransform\n * @category Animation\n * @korean 측면성변환\n */\n\nimport type { AnimationKeyframe, SkeletalAnimation } from \"@/types/skeletal\";\nimport * as THREE from \"three\";\nimport type { StanceLaterality } from \"../../trigram/types\";\n\n/**\n * Apply laterality transformation to a skeletal animation.\n *\n * **Korean**: 애니메이션 측면성 적용\n *\n * Transforms a skeletal animation to match the specified laterality (left/right stance).\n * Right laterality returns the original animation; left laterality creates a mirrored version\n * with left/right bones swapped and lateral rotations negated.\n *\n * Transformation Rules:\n * - Swap left ↔ right bone names (e.g., \"shoulder_L\" ↔ \"shoulder_R\")\n * - Negate Y rotation (lateral twist around vertical axis)\n * - Negate Z rotation (roll around forward-back axis)\n * - Preserve X rotation (forward/back bend)\n * - Maintain timing, duration, and easing information\n *\n * Performance: <1ms for typical animation (verified in tests)\n *\n * @param animation - Source skeletal animation to transform\n * @param laterality - Target laterality: \"left\" or \"right\"\n * @returns Transformed animation with mirrored bone rotations for left laterality\n *\n * @example\n * ```typescript\n * // Get right-handed punch animation\n * const rightPunch = GEON_BONE_BREAKING_STRIKE_1;\n *\n * // Create left-handed version\n * const leftPunch = applyLaterality(rightPunch, \"left\");\n * // leftPunch has left hand as striking hand, mirrored rotations\n *\n * // Right laterality returns original (no transformation cost)\n * const sameAnimation = applyLaterality(rightPunch, \"right\");\n * // sameAnimation === rightPunch (same reference)\n * ```\n *\n * @public\n * @category Animation Transform\n * @korean 애니메이션측면성적용\n */\nexport function applyLaterality(\n animation: SkeletalAnimation,\n laterality: StanceLaterality,\n): SkeletalAnimation {\n // Right laterality returns original animation (no transformation)\n if (laterality === \"right\") {\n return animation;\n }\n\n // Left laterality: mirror all keyframes\n const mirroredKeyframes = animation.keyframes.map((keyframe) =>\n mirrorAnimationKeyframe(keyframe),\n );\n\n // Return transformed animation with updated name\n return {\n ...animation,\n name: `${animation.name}_left`,\n koreanName: `${animation.koreanName} (왼발)`,\n keyframes: mirroredKeyframes,\n };\n}\n\n/**\n * Mirror a single animation keyframe for left laterality.\n *\n * **Korean**: 키프레임 좌우 대칭\n *\n * Creates a mirrored version of an animation keyframe by:\n * 1. Swapping left and right bone names\n * 2. Negating Y and Z rotations (lateral twist and roll)\n * 3. Preserving X rotation (forward/back bend)\n * 4. Maintaining timing and easing information\n *\n * Bone Name Swapping:\n * - \"_L\" suffix → \"_R\" suffix\n * - \"_R\" suffix → \"_L\" suffix\n * - \"left_\" prefix → \"right_\" prefix\n * - \"right_\" prefix → \"left_\" prefix\n *\n * @param keyframe - Original animation keyframe\n * @returns Mirrored keyframe with swapped bones and negated rotations\n *\n * @internal\n * @korean 키프레임대칭\n */\nfunction mirrorAnimationKeyframe(\n keyframe: AnimationKeyframe,\n): AnimationKeyframe {\n // Mirror bone rotations\n const mirroredRotations = new Map<string, THREE.Euler>();\n\n keyframe.boneRotations.forEach((rotation, boneName) => {\n const mirroredBoneName = mirrorBoneName(boneName);\n const mirroredRotation = mirrorEuler(rotation);\n mirroredRotations.set(mirroredBoneName, mirroredRotation);\n });\n\n // Mirror bone positions (if present)\n const mirroredPositions = new Map<string, THREE.Vector3>();\n\n if (keyframe.bonePositions) {\n keyframe.bonePositions.forEach((position, boneName) => {\n const mirroredBoneName = mirrorBoneName(boneName);\n const mirroredPosition = mirrorPosition(position);\n mirroredPositions.set(mirroredBoneName, mirroredPosition);\n });\n }\n\n return {\n time: keyframe.time,\n boneRotations: mirroredRotations,\n bonePositions: mirroredPositions,\n easing: keyframe.easing,\n };\n}\n\n/**\n * Mirror a bone name by swapping left/right suffixes and prefixes.\n *\n * **Korean**: 뼈 이름 대칭\n *\n * Transforms bone names to their mirrored counterparts:\n * - \"shoulder_L\" → \"shoulder_R\"\n * - \"upper_arm_R\" → \"upper_arm_L\"\n * - \"left_hand\" → \"right_hand\"\n * - \"right_foot\" → \"left_foot\"\n *\n * If bone has no laterality marker (e.g., \"spine\", \"pelvis\"),\n * returns the original name unchanged.\n *\n * @param boneName - Original bone name\n * @returns Mirrored bone name with swapped left/right markers\n *\n * @example\n * ```typescript\n * mirrorBoneName(\"shoulder_L\") // → \"shoulder_R\"\n * mirrorBoneName(\"upper_arm_R\") // → \"upper_arm_L\"\n * mirrorBoneName(\"left_hand\") // → \"right_hand\"\n * mirrorBoneName(\"spine_upper\") // → \"spine_upper\" (no change)\n * ```\n *\n * @internal\n * @korean 뼈이름대칭\n */\nfunction mirrorBoneName(boneName: string): string {\n // Handle _L and _R suffixes (most common pattern)\n if (boneName.endsWith(\"_L\")) {\n return boneName.slice(0, -2) + \"_R\";\n }\n if (boneName.endsWith(\"_R\")) {\n return boneName.slice(0, -2) + \"_L\";\n }\n\n // Handle left_ and right_ prefixes\n if (boneName.startsWith(\"left_\")) {\n return \"right_\" + boneName.slice(5);\n }\n if (boneName.startsWith(\"right_\")) {\n return \"left_\" + boneName.slice(6);\n }\n\n // No laterality marker - return unchanged (e.g., spine, pelvis, neck)\n return boneName;\n}\n\n/**\n * Mirror a bone rotation by negating Y and Z axes.\n *\n * **Korean**: 뼈 회전 대칭\n *\n * Transforms rotation for left/right mirroring:\n * - X rotation: Preserved (forward/back bend is symmetric)\n * - Y rotation: Negated (lateral twist direction reverses)\n * - Z rotation: Negated (roll direction reverses)\n *\n * This creates anatomically correct mirrored poses where:\n * - Right arm punch becomes left arm punch\n * - Left leg kick becomes right leg kick\n * - Torso twist direction reverses appropriately\n *\n * @param euler - Original bone rotation\n * @returns Mirrored rotation with negated Y and Z axes\n *\n * @example\n * ```typescript\n * // Right arm forward punch: shoulder rotates forward and inward\n * const rightPunch = new THREE.Euler(-1.2, 0.5, 0.6);\n * const leftPunch = mirrorEuler(rightPunch);\n * // leftPunch: (-1.2, -0.5, -0.6) - forward bend preserved, twist/roll negated\n * ```\n *\n * @internal\n * @korean 뼈회전대칭\n */\nfunction mirrorEuler(euler: THREE.Euler): THREE.Euler {\n return new THREE.Euler(\n euler.x, // Preserve forward/back bend\n -euler.y, // Negate lateral twist\n -euler.z, // Negate roll\n euler.order, // Preserve rotation order\n );\n}\n\n/**\n * Mirror a bone position by negating the X coordinate.\n *\n * **Korean**: 뼈 위치 대칭\n *\n * Mirrors position across the YZ plane (body centerline).\n *\n * **World-Space Coordinate System (matching AnimationMirror):**\n * Skeletal animations use world-space coordinates consistent with the 28-bone rig:\n * - X coordinate: Left/right (arms extend along ±X)\n * - Y coordinate: Up/down (pelvis at Y=1.1, legs extend -Y)\n * - Z coordinate: Forward/back (character faces +Z)\n *\n * For mirroring:\n * - X: Negated (left ↔ right)\n * - Y: Preserved (up/down is symmetric)\n * - Z: Preserved (forward/back is symmetric)\n *\n * Used for IK targets and special move positioning where\n * absolute bone positions are specified.\n *\n * @param position - Original bone position offset\n * @returns Mirrored position with negated X coordinate\n *\n * @example\n * ```typescript\n * // Right hand extends to the right (+X)\n * const rightHandPos = new THREE.Vector3(0.5, 1.5, 0.3);\n * const leftHandPos = mirrorPosition(rightHandPos);\n * // leftHandPos: (-0.5, 1.5, 0.3) - extends to the left (-X)\n * ```\n *\n * @internal\n * @korean 뼈위치대칭\n */\nfunction mirrorPosition(position: THREE.Vector3): THREE.Vector3 {\n return new THREE.Vector3(\n -position.x, // Negate left/right (X axis = lateral in world-space rig)\n position.y, // Preserve vertical\n position.z, // Preserve forward/back\n );\n}\n\n/**\n * Get the laterality identifier for an animation.\n *\n * **Korean**: 애니메이션 측면성 식별\n *\n * Determines if an animation is configured for left or right laterality\n * based on its name suffix.\n *\n * @param animation - Skeletal animation to inspect\n * @returns \"left\" if animation has \"_left\" suffix, otherwise \"right\"\n *\n * @example\n * ```typescript\n * const rightAnim = { name: \"geon_punch\", ... };\n * const leftAnim = { name: \"geon_punch_left\", ... };\n *\n * getAnimationLaterality(rightAnim); // → \"right\"\n * getAnimationLaterality(leftAnim); // → \"left\"\n * ```\n *\n * @public\n * @category Animation Query\n * @korean 애니메이션측면성식별\n */\nexport function getAnimationLaterality(\n animation: SkeletalAnimation,\n): StanceLaterality {\n return animation.name.endsWith(\"_left\") ? \"left\" : \"right\";\n}\n\n/**\n * Check if two animations are laterality variants of each other.\n *\n * **Korean**: 애니메이션 측면성 변형 확인\n *\n * Determines if two animations represent the same technique but\n * with different laterality (left vs right stance).\n *\n * @param anim1 - First animation\n * @param anim2 - Second animation\n * @returns True if animations are laterality variants of the same base animation\n *\n * @example\n * ```typescript\n * const rightPunch = { name: \"geon_punch\", ... };\n * const leftPunch = { name: \"geon_punch_left\", ... };\n * const otherMove = { name: \"tae_throw\", ... };\n *\n * areLateralityVariants(rightPunch, leftPunch); // → true\n * areLateralityVariants(rightPunch, otherMove); // → false\n * ```\n *\n * @public\n * @category Animation Query\n * @korean 애니메이션측면성변형확인\n */\nexport function areLateralityVariants(\n anim1: SkeletalAnimation,\n anim2: SkeletalAnimation,\n): boolean {\n // Get base names (remove _left suffix if present)\n const baseName1 = anim1.name.replace(/_left$/, \"\");\n const baseName2 = anim2.name.replace(/_left$/, \"\");\n\n // Check if base names match and laterality differs\n return (\n baseName1 === baseName2 &&\n getAnimationLaterality(anim1) !== getAnimationLaterality(anim2)\n );\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAqEA,SAAgB,gBACd,WACA,YACmB;CAEnB,IAAI,eAAe,SACjB,OAAO;CAIT,MAAM,oBAAoB,UAAU,UAAU,KAAK,aACjD,wBAAwB,SAAS,CAClC;CAGD,OAAO;EACL,GAAG;EACH,MAAM,GAAG,UAAU,KAAK;EACxB,YAAY,GAAG,UAAU,WAAW;EACpC,WAAW;EACZ;;;;;;;;;;;;;;;;;;;;;;;;;AA0BH,SAAS,wBACP,UACmB;CAEnB,MAAM,oCAAoB,IAAI,KAA0B;CAExD,SAAS,cAAc,SAAS,UAAU,aAAa;EACrD,MAAM,mBAAmB,eAAe,SAAS;EACjD,MAAM,mBAAmB,YAAY,SAAS;EAC9C,kBAAkB,IAAI,kBAAkB,iBAAiB;GACzD;CAGF,MAAM,oCAAoB,IAAI,KAA4B;CAE1D,IAAI,SAAS,eACX,SAAS,cAAc,SAAS,UAAU,aAAa;EACrD,MAAM,mBAAmB,eAAe,SAAS;EACjD,MAAM,mBAAmB,eAAe,SAAS;EACjD,kBAAkB,IAAI,kBAAkB,iBAAiB;GACzD;CAGJ,OAAO;EACL,MAAM,SAAS;EACf,eAAe;EACf,eAAe;EACf,QAAQ,SAAS;EAClB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA+BH,SAAS,eAAe,UAA0B;CAEhD,IAAI,SAAS,SAAS,KAAK,EACzB,OAAO,SAAS,MAAM,GAAG,GAAG,GAAG;CAEjC,IAAI,SAAS,SAAS,KAAK,EACzB,OAAO,SAAS,MAAM,GAAG,GAAG,GAAG;CAIjC,IAAI,SAAS,WAAW,QAAQ,EAC9B,OAAO,WAAW,SAAS,MAAM,EAAE;CAErC,IAAI,SAAS,WAAW,SAAS,EAC/B,OAAO,UAAU,SAAS,MAAM,EAAE;CAIpC,OAAO;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAgCT,SAAS,YAAY,OAAiC;CACpD,OAAO,IAAI,MAAM,MACf,MAAM,GACN,CAAC,MAAM,GACP,CAAC,MAAM,GACP,MAAM,MACP;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAsCH,SAAS,eAAe,UAAwC;CAC9D,OAAO,IAAI,MAAM,QACf,CAAC,SAAS,GACV,SAAS,GACT,SAAS,EACV"}
@@ -1 +1 @@
1
- {"version":3,"file":"RecoveryPhaseEnhancer.js","names":[],"sources":["../../../../src/systems/animation/core/RecoveryPhaseEnhancer.ts"],"sourcesContent":["/**\n * Recovery Phase Enhancement for Black Trigram Animations\n *\n * Implements realistic recovery animations following Korean martial arts principles:\n * - 균형회복 (Gyunhyeong Hoebog) - Balance restoration\n * - 자세복귀 (Jase Bokgwi) - Stance return\n * - 호흡조절 (Hoheup Jojoel) - Breath control during recovery\n * - 근육이완 (Geunryuk Ihwan) - Muscle relaxation after tension\n *\n * @module systems/animation/RecoveryPhaseEnhancer\n * @category Animation System\n * @korean 복귀애니메이션강화\n */\n\nimport * as THREE from \"three\";\nimport type {\n AnimationKeyframe,\n SkeletalAnimation,\n} from \"@/types/skeletal\";\n\n/**\n * Recovery phase configuration options\n *\n * **Korean**: 복귀 설정\n *\n * @public\n * @category Animation\n * @korean 복귀설정\n */\nexport interface RecoveryPhaseConfig {\n /**\n * Duration of recovery phase in seconds (default: 0.2s = 200ms)\n * Recommended range: 0.15 - 0.25 seconds\n * **Korean**: 복귀 시간\n */\n readonly duration?: number;\n\n /**\n * Percentage of return in intermediate keyframe (default: 0.8 = 80%)\n * **Korean**: 중간 복귀 비율\n */\n readonly intermediateReturnPercent?: number;\n\n /**\n * Initial muscle tension level at peak technique (default: 1.0 = 100%)\n * **Korean**: 최대 근육 긴장도\n */\n readonly peakMuscleTension?: number;\n\n /**\n * Muscle tension at intermediate recovery (default: 0.4 = 40%)\n * **Korean**: 중간 근육 긴장도\n */\n readonly intermediateMuscleTension?: number;\n\n /**\n * Final muscle tension in relaxed state (default: 0.1 = 10%)\n * **Korean**: 최종 근육 긴장도\n */\n readonly finalMuscleTension?: number;\n\n /**\n * Whether to use ease-out interpolation (default: true)\n * **Korean**: 부드러운 감속 사용\n */\n readonly useEaseOut?: boolean;\n}\n\n/**\n * Default recovery phase configuration\n * Based on Korean martial arts recovery principles\n *\n * **Korean**: 기본 복귀 설정\n */\nconst DEFAULT_RECOVERY_CONFIG: Required<RecoveryPhaseConfig> = {\n duration: 0.22, // 220ms - optimal for realistic recovery\n intermediateReturnPercent: 0.8, // 80% back to neutral\n peakMuscleTension: 1.0, // 100% tension at technique peak\n intermediateMuscleTension: 0.4, // 40% tension during recovery\n finalMuscleTension: 0.1, // 10% relaxed state\n useEaseOut: true,\n};\n\n/**\n * Add realistic recovery phase to technique animation\n *\n * **Korean**: 기술 애니메이션에 복귀 단계 추가\n *\n * Enhances technique animations with proper recovery phases following\n * Korean martial arts principles of 복귀 (Bokgwi - recovery/return).\n *\n * The recovery phase consists of two keyframes:\n * 1. **Intermediate recovery** (60% of recovery duration):\n * - Returns 80% toward neutral position\n * - Reduces muscle tension to 40%\n * - Uses ease-out for gradual deceleration\n *\n * 2. **Final recovery** (100% of recovery duration):\n * - Fully returns to neutral stance\n * - Relaxes muscle tension to 10%\n * - Completes breath cycle\n *\n * @param animation - Base technique animation\n * @param config - Recovery phase configuration options\n * @returns Enhanced animation with recovery phase\n *\n * @example\n * ```typescript\n * // Add default 220ms recovery to front kick\n * const kickWithRecovery = addRecoveryPhase(FRONT_KICK_ANIMATION);\n *\n * // Add custom 180ms recovery with faster muscle release\n * const quickRecovery = addRecoveryPhase(JAB_ANIMATION, {\n * duration: 0.18,\n * intermediateMuscleTension: 0.3,\n * finalMuscleTension: 0.05,\n * });\n * ```\n *\n * @korean 복귀단계추가\n */\nexport function addRecoveryPhase(\n animation: SkeletalAnimation,\n config: RecoveryPhaseConfig = {}\n): SkeletalAnimation {\n // Merge with defaults\n const finalConfig: Required<RecoveryPhaseConfig> = {\n ...DEFAULT_RECOVERY_CONFIG,\n ...config,\n };\n\n // Validate animation has at least 2 keyframes\n if (animation.keyframes.length < 2) {\n // In production, this would use proper logging system\n // For now, using console.warn is acceptable for development\n // Suppress console output during tests to avoid cluttering test output\n // Note: In test environments, proper assertions should validate this condition\n if (process.env.NODE_ENV !== \"test\") {\n console.warn(\n `Animation \"${animation.name}\" has fewer than 2 keyframes. Recovery phase not added.`\n );\n }\n return animation;\n }\n\n const lastKeyframe = animation.keyframes[animation.keyframes.length - 1];\n const peakKeyframe = animation.keyframes[animation.keyframes.length - 2];\n\n // Calculate recovery timing\n const recoveryStartTime = lastKeyframe.time;\n const intermediateTime = recoveryStartTime + finalConfig.duration * 0.6;\n const finalTime = recoveryStartTime + finalConfig.duration;\n\n // Create intermediate recovery keyframe (80% back to neutral)\n const intermediateFrame: AnimationKeyframe = {\n time: intermediateTime,\n easing: finalConfig.useEaseOut ? \"ease-out\" : \"linear\",\n boneRotations: new Map(),\n bonePositions: new Map(),\n };\n\n // Interpolate bone rotations 80% toward neutral\n peakKeyframe.boneRotations.forEach((rotation, bone) => {\n const intermediate = new THREE.Euler(\n rotation.x * (1 - finalConfig.intermediateReturnPercent),\n rotation.y * (1 - finalConfig.intermediateReturnPercent),\n rotation.z * (1 - finalConfig.intermediateReturnPercent),\n rotation.order\n );\n intermediateFrame.boneRotations.set(bone, intermediate);\n });\n\n // Interpolate bone positions 80% toward rest\n if (peakKeyframe.bonePositions) {\n peakKeyframe.bonePositions.forEach((position, bone) => {\n // Manual interpolation instead of Vector3.lerp() because bonePositions map\n // may contain objects that aren't true THREE.Vector3 instances with prototype methods.\n // This approach ensures compatibility with any object having x, y, z properties.\n const returnPercent = finalConfig.intermediateReturnPercent;\n const intermediate = new THREE.Vector3(\n position.x * (1 - returnPercent),\n position.y * (1 - returnPercent),\n position.z * (1 - returnPercent)\n );\n intermediateFrame.bonePositions.set(bone, intermediate);\n });\n }\n\n // Create final recovery frame (100% neutral)\n const finalFrame: AnimationKeyframe = {\n time: finalTime,\n easing: finalConfig.useEaseOut ? \"ease-out\" : \"linear\",\n boneRotations: new Map(),\n bonePositions: new Map(),\n };\n\n // All bones return to neutral (0 rotation)\n peakKeyframe.boneRotations.forEach((rotation, bone) => {\n finalFrame.boneRotations.set(\n bone,\n new THREE.Euler(0, 0, 0, rotation.order)\n );\n });\n\n // All positions return to rest\n if (peakKeyframe.bonePositions) {\n peakKeyframe.bonePositions.forEach((_, bone) => {\n finalFrame.bonePositions.set(bone, new THREE.Vector3(0, 0, 0));\n });\n }\n\n // Build enhanced animation\n return {\n ...animation,\n duration: finalTime,\n keyframes: [...animation.keyframes, intermediateFrame, finalFrame],\n };\n}\n\n/**\n * Create technique animation with recovery phase\n *\n * **Korean**: 복귀 단계를 포함한 기술 애니메이션 생성\n *\n * Convenience function that wraps animation creation with automatic recovery phase.\n * Use this when creating new technique animations from scratch.\n *\n * @param baseAnimation - Base animation without recovery\n * @param config - Recovery configuration\n * @returns Complete animation with recovery phase\n *\n * @example\n * ```typescript\n * // Create front kick with recovery\n * const frontKick = createTechniqueWithRecovery(\n * baseFrontKickAnimation,\n * { duration: 0.22 }\n * );\n * ```\n *\n * @korean 복귀단계기술생성\n */\nexport function createTechniqueWithRecovery(\n baseAnimation: SkeletalAnimation,\n config: RecoveryPhaseConfig = {}\n): SkeletalAnimation {\n return addRecoveryPhase(baseAnimation, config);\n}\n\n/**\n * Validate recovery phase quality\n *\n * **Korean**: 복귀 단계 품질 검증\n *\n * Checks if an animation has proper recovery phase characteristics:\n * - Has at least 2 recovery keyframes\n * - Recovery duration between 150-250ms\n * - Uses ease-out interpolation\n * - Returns to neutral position\n *\n * @param animation - Animation to validate\n * @returns Validation result with details\n *\n * @korean 복귀품질검증\n */\nexport interface RecoveryValidationResult {\n /** Whether animation has valid recovery phase */\n readonly isValid: boolean;\n /** Validation issues found */\n readonly issues: string[];\n /** Recovery duration in milliseconds */\n readonly recoveryDuration: number;\n /** Number of recovery keyframes */\n readonly recoveryKeyframes: number;\n}\n\n/**\n * Validate recovery phase in animation\n *\n * @param animation - Animation to validate\n * @returns Validation result\n *\n * @korean 복귀검증\n */\nexport function validateRecoveryPhase(\n animation: SkeletalAnimation\n): RecoveryValidationResult {\n const issues: string[] = [];\n\n if (animation.keyframes.length < 3) {\n issues.push(\n \"Animation has fewer than 3 keyframes (needs at least 3 for recovery)\"\n );\n return {\n isValid: false,\n issues,\n recoveryDuration: 0,\n recoveryKeyframes: 0,\n };\n }\n\n // Recovery phase is from the third-to-last keyframe (peak) to last keyframe (final recovery)\n // This is because addRecoveryPhase adds TWO keyframes: intermediate and final\n const lastKeyframe = animation.keyframes[animation.keyframes.length - 1];\n const thirdLastKeyframe = animation.keyframes[animation.keyframes.length - 3];\n\n const recoveryDuration = (lastKeyframe.time - thirdLastKeyframe.time) * 1000; // ms\n\n // Validate duration\n if (recoveryDuration < 150) {\n issues.push(\n `Recovery duration too short: ${recoveryDuration.toFixed(\n 0\n )}ms (minimum: 150ms)`\n );\n }\n if (recoveryDuration > 250) {\n issues.push(\n `Recovery duration too long: ${recoveryDuration.toFixed(\n 0\n )}ms (maximum: 250ms)`\n );\n }\n\n // Validate easing - both ease-out and ease-in are acceptable for recovery\n // ease-out is preferred but ease-in can also work for final deceleration\n const acceptableEasings = [\"ease-out\", \"ease-in\"];\n if (!acceptableEasings.includes(lastKeyframe.easing ?? \"\")) {\n issues.push(\n `Last keyframe should use ease-out or ease-in interpolation, found: ${lastKeyframe.easing}`\n );\n }\n\n // Validate return to neutral\n let hasNonZeroRotation = false;\n lastKeyframe.boneRotations.forEach((rotation) => {\n if (\n Math.abs(rotation.x) > 0.01 ||\n Math.abs(rotation.y) > 0.01 ||\n Math.abs(rotation.z) > 0.01\n ) {\n hasNonZeroRotation = true;\n }\n });\n\n if (hasNonZeroRotation) {\n issues.push(\"Final keyframe should return to neutral position (0, 0, 0)\");\n }\n\n return {\n isValid: issues.length === 0,\n issues,\n recoveryDuration,\n recoveryKeyframes: 2,\n };\n}\n\n/**\n * Calculate muscle tension at specific time in animation\n *\n * **Korean**: 애니메이션 시간별 근육 긴장도 계산\n *\n * Interpolates muscle tension based on animation progress and recovery phase.\n * Tension peaks during technique execution and gradually releases during recovery.\n *\n * @param animation - Animation with recovery phase\n * @param currentTime - Current time in animation (seconds)\n * @param config - Recovery configuration used\n * @returns Muscle tension value (0.0 to 1.0)\n *\n * @korean 근육긴장도계산\n */\nexport function calculateMuscleTension(\n animation: SkeletalAnimation,\n currentTime: number,\n config: RecoveryPhaseConfig = {}\n): number {\n const finalConfig = { ...DEFAULT_RECOVERY_CONFIG, ...config };\n\n if (animation.keyframes.length < 2) {\n return finalConfig.finalMuscleTension;\n }\n\n // Recovery phase starts at the third-to-last keyframe (after peak)\n const lastKeyframe = animation.keyframes[animation.keyframes.length - 1];\n const recoveryStartTime =\n animation.keyframes.length >= 3\n ? animation.keyframes[animation.keyframes.length - 3].time\n : animation.keyframes[animation.keyframes.length - 2].time;\n\n // Before recovery: peak tension\n if (currentTime < recoveryStartTime) {\n return finalConfig.peakMuscleTension;\n }\n\n // During recovery: interpolate from peak to relaxed\n const recoveryProgress =\n (currentTime - recoveryStartTime) / (lastKeyframe.time - recoveryStartTime);\n const clampedProgress = Math.max(0, Math.min(1, recoveryProgress));\n\n // Use ease-out curve for natural muscle release\n const easedProgress = 1 - Math.pow(1 - clampedProgress, 2);\n\n // Interpolate tension\n if (easedProgress < 0.6) {\n // First 60%: peak -> intermediate\n const t = easedProgress / 0.6;\n return (\n finalConfig.peakMuscleTension +\n (finalConfig.intermediateMuscleTension - finalConfig.peakMuscleTension) *\n t\n );\n } else {\n // Last 40%: intermediate -> final\n const t = (easedProgress - 0.6) / 0.4;\n return (\n finalConfig.intermediateMuscleTension +\n (finalConfig.finalMuscleTension - finalConfig.intermediateMuscleTension) *\n t\n );\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;AA0EA,IAAM,0BAAyD;CAC7D,UAAU;CACV,2BAA2B;CAC3B,mBAAmB;CACnB,2BAA2B;CAC3B,oBAAoB;CACpB,YAAY;CACb;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAwCD,SAAgB,iBACd,WACA,SAA8B,EAAE,EACb;CAEnB,MAAM,cAA6C;EACjD,GAAG;EACH,GAAG;EACJ;AAGD,KAAI,UAAU,UAAU,SAAS,GAAG;AAKlC,MAAA,QAAA,IAAA,aAA6B,OAC3B,SAAQ,KACN,cAAc,UAAU,KAAK,yDAC9B;AAEH,SAAO;;CAGT,MAAM,eAAe,UAAU,UAAU,UAAU,UAAU,SAAS;CACtE,MAAM,eAAe,UAAU,UAAU,UAAU,UAAU,SAAS;CAGtE,MAAM,oBAAoB,aAAa;CACvC,MAAM,mBAAmB,oBAAoB,YAAY,WAAW;CACpE,MAAM,YAAY,oBAAoB,YAAY;CAGlD,MAAM,oBAAuC;EAC3C,MAAM;EACN,QAAQ,YAAY,aAAa,aAAa;EAC9C,+BAAe,IAAI,KAAK;EACxB,+BAAe,IAAI,KAAK;EACzB;AAGD,cAAa,cAAc,SAAS,UAAU,SAAS;EACrD,MAAM,eAAe,IAAI,MAAM,MAC7B,SAAS,KAAK,IAAI,YAAY,4BAC9B,SAAS,KAAK,IAAI,YAAY,4BAC9B,SAAS,KAAK,IAAI,YAAY,4BAC9B,SAAS,MACV;AACD,oBAAkB,cAAc,IAAI,MAAM,aAAa;GACvD;AAGF,KAAI,aAAa,cACf,cAAa,cAAc,SAAS,UAAU,SAAS;EAIrD,MAAM,gBAAgB,YAAY;EAClC,MAAM,eAAe,IAAI,MAAM,QAC7B,SAAS,KAAK,IAAI,gBAClB,SAAS,KAAK,IAAI,gBAClB,SAAS,KAAK,IAAI,eACnB;AACD,oBAAkB,cAAc,IAAI,MAAM,aAAa;GACvD;CAIJ,MAAM,aAAgC;EACpC,MAAM;EACN,QAAQ,YAAY,aAAa,aAAa;EAC9C,+BAAe,IAAI,KAAK;EACxB,+BAAe,IAAI,KAAK;EACzB;AAGD,cAAa,cAAc,SAAS,UAAU,SAAS;AACrD,aAAW,cAAc,IACvB,MACA,IAAI,MAAM,MAAM,GAAG,GAAG,GAAG,SAAS,MAAM,CACzC;GACD;AAGF,KAAI,aAAa,cACf,cAAa,cAAc,SAAS,GAAG,SAAS;AAC9C,aAAW,cAAc,IAAI,MAAM,IAAI,MAAM,QAAQ,GAAG,GAAG,EAAE,CAAC;GAC9D;AAIJ,QAAO;EACL,GAAG;EACH,UAAU;EACV,WAAW;GAAC,GAAG,UAAU;GAAW;GAAmB;GAAW;EACnE"}
1
+ {"version":3,"file":"RecoveryPhaseEnhancer.js","names":[],"sources":["../../../../src/systems/animation/core/RecoveryPhaseEnhancer.ts"],"sourcesContent":["/**\n * Recovery Phase Enhancement for Black Trigram Animations\n *\n * Implements realistic recovery animations following Korean martial arts principles:\n * - 균형회복 (Gyunhyeong Hoebog) - Balance restoration\n * - 자세복귀 (Jase Bokgwi) - Stance return\n * - 호흡조절 (Hoheup Jojoel) - Breath control during recovery\n * - 근육이완 (Geunryuk Ihwan) - Muscle relaxation after tension\n *\n * @module systems/animation/RecoveryPhaseEnhancer\n * @category Animation System\n * @korean 복귀애니메이션강화\n */\n\nimport * as THREE from \"three\";\nimport type {\n AnimationKeyframe,\n SkeletalAnimation,\n} from \"@/types/skeletal\";\n\n/**\n * Recovery phase configuration options\n *\n * **Korean**: 복귀 설정\n *\n * @public\n * @category Animation\n * @korean 복귀설정\n */\nexport interface RecoveryPhaseConfig {\n /**\n * Duration of recovery phase in seconds (default: 0.2s = 200ms)\n * Recommended range: 0.15 - 0.25 seconds\n * **Korean**: 복귀 시간\n */\n readonly duration?: number;\n\n /**\n * Percentage of return in intermediate keyframe (default: 0.8 = 80%)\n * **Korean**: 중간 복귀 비율\n */\n readonly intermediateReturnPercent?: number;\n\n /**\n * Initial muscle tension level at peak technique (default: 1.0 = 100%)\n * **Korean**: 최대 근육 긴장도\n */\n readonly peakMuscleTension?: number;\n\n /**\n * Muscle tension at intermediate recovery (default: 0.4 = 40%)\n * **Korean**: 중간 근육 긴장도\n */\n readonly intermediateMuscleTension?: number;\n\n /**\n * Final muscle tension in relaxed state (default: 0.1 = 10%)\n * **Korean**: 최종 근육 긴장도\n */\n readonly finalMuscleTension?: number;\n\n /**\n * Whether to use ease-out interpolation (default: true)\n * **Korean**: 부드러운 감속 사용\n */\n readonly useEaseOut?: boolean;\n}\n\n/**\n * Default recovery phase configuration\n * Based on Korean martial arts recovery principles\n *\n * **Korean**: 기본 복귀 설정\n */\nconst DEFAULT_RECOVERY_CONFIG: Required<RecoveryPhaseConfig> = {\n duration: 0.22, // 220ms - optimal for realistic recovery\n intermediateReturnPercent: 0.8, // 80% back to neutral\n peakMuscleTension: 1.0, // 100% tension at technique peak\n intermediateMuscleTension: 0.4, // 40% tension during recovery\n finalMuscleTension: 0.1, // 10% relaxed state\n useEaseOut: true,\n};\n\n/**\n * Add realistic recovery phase to technique animation\n *\n * **Korean**: 기술 애니메이션에 복귀 단계 추가\n *\n * Enhances technique animations with proper recovery phases following\n * Korean martial arts principles of 복귀 (Bokgwi - recovery/return).\n *\n * The recovery phase consists of two keyframes:\n * 1. **Intermediate recovery** (60% of recovery duration):\n * - Returns 80% toward neutral position\n * - Reduces muscle tension to 40%\n * - Uses ease-out for gradual deceleration\n *\n * 2. **Final recovery** (100% of recovery duration):\n * - Fully returns to neutral stance\n * - Relaxes muscle tension to 10%\n * - Completes breath cycle\n *\n * @param animation - Base technique animation\n * @param config - Recovery phase configuration options\n * @returns Enhanced animation with recovery phase\n *\n * @example\n * ```typescript\n * // Add default 220ms recovery to front kick\n * const kickWithRecovery = addRecoveryPhase(FRONT_KICK_ANIMATION);\n *\n * // Add custom 180ms recovery with faster muscle release\n * const quickRecovery = addRecoveryPhase(JAB_ANIMATION, {\n * duration: 0.18,\n * intermediateMuscleTension: 0.3,\n * finalMuscleTension: 0.05,\n * });\n * ```\n *\n * @korean 복귀단계추가\n */\nexport function addRecoveryPhase(\n animation: SkeletalAnimation,\n config: RecoveryPhaseConfig = {}\n): SkeletalAnimation {\n // Merge with defaults\n const finalConfig: Required<RecoveryPhaseConfig> = {\n ...DEFAULT_RECOVERY_CONFIG,\n ...config,\n };\n\n // Validate animation has at least 2 keyframes\n if (animation.keyframes.length < 2) {\n // In production, this would use proper logging system\n // For now, using console.warn is acceptable for development\n // Suppress console output during tests to avoid cluttering test output\n // Note: In test environments, proper assertions should validate this condition\n if (process.env.NODE_ENV !== \"test\") {\n console.warn(\n `Animation \"${animation.name}\" has fewer than 2 keyframes. Recovery phase not added.`\n );\n }\n return animation;\n }\n\n const lastKeyframe = animation.keyframes[animation.keyframes.length - 1];\n const peakKeyframe = animation.keyframes[animation.keyframes.length - 2];\n\n // Calculate recovery timing\n const recoveryStartTime = lastKeyframe.time;\n const intermediateTime = recoveryStartTime + finalConfig.duration * 0.6;\n const finalTime = recoveryStartTime + finalConfig.duration;\n\n // Create intermediate recovery keyframe (80% back to neutral)\n const intermediateFrame: AnimationKeyframe = {\n time: intermediateTime,\n easing: finalConfig.useEaseOut ? \"ease-out\" : \"linear\",\n boneRotations: new Map(),\n bonePositions: new Map(),\n };\n\n // Interpolate bone rotations 80% toward neutral\n peakKeyframe.boneRotations.forEach((rotation, bone) => {\n const intermediate = new THREE.Euler(\n rotation.x * (1 - finalConfig.intermediateReturnPercent),\n rotation.y * (1 - finalConfig.intermediateReturnPercent),\n rotation.z * (1 - finalConfig.intermediateReturnPercent),\n rotation.order\n );\n intermediateFrame.boneRotations.set(bone, intermediate);\n });\n\n // Interpolate bone positions 80% toward rest\n if (peakKeyframe.bonePositions) {\n peakKeyframe.bonePositions.forEach((position, bone) => {\n // Manual interpolation instead of Vector3.lerp() because bonePositions map\n // may contain objects that aren't true THREE.Vector3 instances with prototype methods.\n // This approach ensures compatibility with any object having x, y, z properties.\n const returnPercent = finalConfig.intermediateReturnPercent;\n const intermediate = new THREE.Vector3(\n position.x * (1 - returnPercent),\n position.y * (1 - returnPercent),\n position.z * (1 - returnPercent)\n );\n intermediateFrame.bonePositions.set(bone, intermediate);\n });\n }\n\n // Create final recovery frame (100% neutral)\n const finalFrame: AnimationKeyframe = {\n time: finalTime,\n easing: finalConfig.useEaseOut ? \"ease-out\" : \"linear\",\n boneRotations: new Map(),\n bonePositions: new Map(),\n };\n\n // All bones return to neutral (0 rotation)\n peakKeyframe.boneRotations.forEach((rotation, bone) => {\n finalFrame.boneRotations.set(\n bone,\n new THREE.Euler(0, 0, 0, rotation.order)\n );\n });\n\n // All positions return to rest\n if (peakKeyframe.bonePositions) {\n peakKeyframe.bonePositions.forEach((_, bone) => {\n finalFrame.bonePositions.set(bone, new THREE.Vector3(0, 0, 0));\n });\n }\n\n // Build enhanced animation\n return {\n ...animation,\n duration: finalTime,\n keyframes: [...animation.keyframes, intermediateFrame, finalFrame],\n };\n}\n\n/**\n * Create technique animation with recovery phase\n *\n * **Korean**: 복귀 단계를 포함한 기술 애니메이션 생성\n *\n * Convenience function that wraps animation creation with automatic recovery phase.\n * Use this when creating new technique animations from scratch.\n *\n * @param baseAnimation - Base animation without recovery\n * @param config - Recovery configuration\n * @returns Complete animation with recovery phase\n *\n * @example\n * ```typescript\n * // Create front kick with recovery\n * const frontKick = createTechniqueWithRecovery(\n * baseFrontKickAnimation,\n * { duration: 0.22 }\n * );\n * ```\n *\n * @korean 복귀단계기술생성\n */\nexport function createTechniqueWithRecovery(\n baseAnimation: SkeletalAnimation,\n config: RecoveryPhaseConfig = {}\n): SkeletalAnimation {\n return addRecoveryPhase(baseAnimation, config);\n}\n\n/**\n * Validate recovery phase quality\n *\n * **Korean**: 복귀 단계 품질 검증\n *\n * Checks if an animation has proper recovery phase characteristics:\n * - Has at least 2 recovery keyframes\n * - Recovery duration between 150-250ms\n * - Uses ease-out interpolation\n * - Returns to neutral position\n *\n * @param animation - Animation to validate\n * @returns Validation result with details\n *\n * @korean 복귀품질검증\n */\nexport interface RecoveryValidationResult {\n /** Whether animation has valid recovery phase */\n readonly isValid: boolean;\n /** Validation issues found */\n readonly issues: string[];\n /** Recovery duration in milliseconds */\n readonly recoveryDuration: number;\n /** Number of recovery keyframes */\n readonly recoveryKeyframes: number;\n}\n\n/**\n * Validate recovery phase in animation\n *\n * @param animation - Animation to validate\n * @returns Validation result\n *\n * @korean 복귀검증\n */\nexport function validateRecoveryPhase(\n animation: SkeletalAnimation\n): RecoveryValidationResult {\n const issues: string[] = [];\n\n if (animation.keyframes.length < 3) {\n issues.push(\n \"Animation has fewer than 3 keyframes (needs at least 3 for recovery)\"\n );\n return {\n isValid: false,\n issues,\n recoveryDuration: 0,\n recoveryKeyframes: 0,\n };\n }\n\n // Recovery phase is from the third-to-last keyframe (peak) to last keyframe (final recovery)\n // This is because addRecoveryPhase adds TWO keyframes: intermediate and final\n const lastKeyframe = animation.keyframes[animation.keyframes.length - 1];\n const thirdLastKeyframe = animation.keyframes[animation.keyframes.length - 3];\n\n const recoveryDuration = (lastKeyframe.time - thirdLastKeyframe.time) * 1000; // ms\n\n // Validate duration\n if (recoveryDuration < 150) {\n issues.push(\n `Recovery duration too short: ${recoveryDuration.toFixed(\n 0\n )}ms (minimum: 150ms)`\n );\n }\n if (recoveryDuration > 250) {\n issues.push(\n `Recovery duration too long: ${recoveryDuration.toFixed(\n 0\n )}ms (maximum: 250ms)`\n );\n }\n\n // Validate easing - both ease-out and ease-in are acceptable for recovery\n // ease-out is preferred but ease-in can also work for final deceleration\n const acceptableEasings = [\"ease-out\", \"ease-in\"];\n if (!acceptableEasings.includes(lastKeyframe.easing ?? \"\")) {\n issues.push(\n `Last keyframe should use ease-out or ease-in interpolation, found: ${lastKeyframe.easing}`\n );\n }\n\n // Validate return to neutral\n let hasNonZeroRotation = false;\n lastKeyframe.boneRotations.forEach((rotation) => {\n if (\n Math.abs(rotation.x) > 0.01 ||\n Math.abs(rotation.y) > 0.01 ||\n Math.abs(rotation.z) > 0.01\n ) {\n hasNonZeroRotation = true;\n }\n });\n\n if (hasNonZeroRotation) {\n issues.push(\"Final keyframe should return to neutral position (0, 0, 0)\");\n }\n\n return {\n isValid: issues.length === 0,\n issues,\n recoveryDuration,\n recoveryKeyframes: 2,\n };\n}\n\n/**\n * Calculate muscle tension at specific time in animation\n *\n * **Korean**: 애니메이션 시간별 근육 긴장도 계산\n *\n * Interpolates muscle tension based on animation progress and recovery phase.\n * Tension peaks during technique execution and gradually releases during recovery.\n *\n * @param animation - Animation with recovery phase\n * @param currentTime - Current time in animation (seconds)\n * @param config - Recovery configuration used\n * @returns Muscle tension value (0.0 to 1.0)\n *\n * @korean 근육긴장도계산\n */\nexport function calculateMuscleTension(\n animation: SkeletalAnimation,\n currentTime: number,\n config: RecoveryPhaseConfig = {}\n): number {\n const finalConfig = { ...DEFAULT_RECOVERY_CONFIG, ...config };\n\n if (animation.keyframes.length < 2) {\n return finalConfig.finalMuscleTension;\n }\n\n // Recovery phase starts at the third-to-last keyframe (after peak)\n const lastKeyframe = animation.keyframes[animation.keyframes.length - 1];\n const recoveryStartTime =\n animation.keyframes.length >= 3\n ? animation.keyframes[animation.keyframes.length - 3].time\n : animation.keyframes[animation.keyframes.length - 2].time;\n\n // Before recovery: peak tension\n if (currentTime < recoveryStartTime) {\n return finalConfig.peakMuscleTension;\n }\n\n // During recovery: interpolate from peak to relaxed\n const recoveryProgress =\n (currentTime - recoveryStartTime) / (lastKeyframe.time - recoveryStartTime);\n const clampedProgress = Math.max(0, Math.min(1, recoveryProgress));\n\n // Use ease-out curve for natural muscle release\n const easedProgress = 1 - Math.pow(1 - clampedProgress, 2);\n\n // Interpolate tension\n if (easedProgress < 0.6) {\n // First 60%: peak -> intermediate\n const t = easedProgress / 0.6;\n return (\n finalConfig.peakMuscleTension +\n (finalConfig.intermediateMuscleTension - finalConfig.peakMuscleTension) *\n t\n );\n } else {\n // Last 40%: intermediate -> final\n const t = (easedProgress - 0.6) / 0.4;\n return (\n finalConfig.intermediateMuscleTension +\n (finalConfig.finalMuscleTension - finalConfig.intermediateMuscleTension) *\n t\n );\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;AA0EA,IAAM,0BAAyD;CAC7D,UAAU;CACV,2BAA2B;CAC3B,mBAAmB;CACnB,2BAA2B;CAC3B,oBAAoB;CACpB,YAAY;CACb;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAwCD,SAAgB,iBACd,WACA,SAA8B,EAAE,EACb;CAEnB,MAAM,cAA6C;EACjD,GAAG;EACH,GAAG;EACJ;CAGD,IAAI,UAAU,UAAU,SAAS,GAAG;EAKlC,IAAA,QAAA,IAAA,aAA6B,QAC3B,QAAQ,KACN,cAAc,UAAU,KAAK,yDAC9B;EAEH,OAAO;;CAGT,MAAM,eAAe,UAAU,UAAU,UAAU,UAAU,SAAS;CACtE,MAAM,eAAe,UAAU,UAAU,UAAU,UAAU,SAAS;CAGtE,MAAM,oBAAoB,aAAa;CACvC,MAAM,mBAAmB,oBAAoB,YAAY,WAAW;CACpE,MAAM,YAAY,oBAAoB,YAAY;CAGlD,MAAM,oBAAuC;EAC3C,MAAM;EACN,QAAQ,YAAY,aAAa,aAAa;EAC9C,+BAAe,IAAI,KAAK;EACxB,+BAAe,IAAI,KAAK;EACzB;CAGD,aAAa,cAAc,SAAS,UAAU,SAAS;EACrD,MAAM,eAAe,IAAI,MAAM,MAC7B,SAAS,KAAK,IAAI,YAAY,4BAC9B,SAAS,KAAK,IAAI,YAAY,4BAC9B,SAAS,KAAK,IAAI,YAAY,4BAC9B,SAAS,MACV;EACD,kBAAkB,cAAc,IAAI,MAAM,aAAa;GACvD;CAGF,IAAI,aAAa,eACf,aAAa,cAAc,SAAS,UAAU,SAAS;EAIrD,MAAM,gBAAgB,YAAY;EAClC,MAAM,eAAe,IAAI,MAAM,QAC7B,SAAS,KAAK,IAAI,gBAClB,SAAS,KAAK,IAAI,gBAClB,SAAS,KAAK,IAAI,eACnB;EACD,kBAAkB,cAAc,IAAI,MAAM,aAAa;GACvD;CAIJ,MAAM,aAAgC;EACpC,MAAM;EACN,QAAQ,YAAY,aAAa,aAAa;EAC9C,+BAAe,IAAI,KAAK;EACxB,+BAAe,IAAI,KAAK;EACzB;CAGD,aAAa,cAAc,SAAS,UAAU,SAAS;EACrD,WAAW,cAAc,IACvB,MACA,IAAI,MAAM,MAAM,GAAG,GAAG,GAAG,SAAS,MAAM,CACzC;GACD;CAGF,IAAI,aAAa,eACf,aAAa,cAAc,SAAS,GAAG,SAAS;EAC9C,WAAW,cAAc,IAAI,MAAM,IAAI,MAAM,QAAQ,GAAG,GAAG,EAAE,CAAC;GAC9D;CAIJ,OAAO;EACL,GAAG;EACH,UAAU;EACV,WAAW;GAAC,GAAG,UAAU;GAAW;GAAmB;GAAW;EACnE"}
@@ -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 * @public\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 * @public\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 * @public\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 * @public\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 * @public\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 * @public\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 * @public\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 * @public\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 * @public\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 * @public\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 * @public\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 * @public\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 * @public\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 * @public\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 * @public\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 * @public\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 * @public\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 * @public\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 * @public\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 * @public\n * @korean 싱글톤인스턴스\n */\nexport const techniqueAnimationMapper = new TechniqueAnimationMapper();\n"],"mappings":"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"}