blacktrigram 0.7.47 → 0.7.49

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 (471) 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.d.ts.map +1 -1
  14. package/lib/components/screens/combat/CombatScreen3D.js +29 -25
  15. package/lib/components/screens/combat/CombatScreen3D.js.map +1 -1
  16. package/lib/components/screens/combat/components/controls/CombatButtons.js.map +1 -1
  17. package/lib/components/screens/combat/components/controls/CombatControlsPanel.js.map +1 -1
  18. package/lib/components/screens/combat/components/controls/ControlsGuide.js.map +1 -1
  19. package/lib/components/screens/combat/components/controls/KeyboardHints.js.map +1 -1
  20. package/lib/components/screens/combat/components/controls/PauseMenu.js.map +1 -1
  21. package/lib/components/screens/combat/components/controls/PauseMenuButton.js.map +1 -1
  22. package/lib/components/screens/combat/components/controls/QuickSettings.js.map +1 -1
  23. package/lib/components/screens/combat/components/effects/BloodDecals3D.js.map +1 -1
  24. package/lib/components/screens/combat/components/effects/BloodLossOverlayHtml.js.map +1 -1
  25. package/lib/components/screens/combat/components/effects/BloodParticles3D.js.map +1 -1
  26. package/lib/components/screens/combat/components/effects/BloodViscosity3D.js.map +1 -1
  27. package/lib/components/screens/combat/components/effects/CombatParticleEffects3D.js.map +1 -1
  28. package/lib/components/screens/combat/components/effects/ConsciousnessBlur.js.map +1 -1
  29. package/lib/components/screens/combat/components/effects/InternalDamage3D.js.map +1 -1
  30. package/lib/components/screens/combat/components/effects/PainVignette.js.map +1 -1
  31. package/lib/components/screens/combat/components/effects/ParticleAudio3D.js.map +1 -1
  32. package/lib/components/screens/combat/components/effects/TraumaOverlay3D.js.map +1 -1
  33. package/lib/components/screens/combat/components/feedback/MatchCountdown.js.map +1 -1
  34. package/lib/components/screens/combat/components/feedback/RoundAnnouncementOverlayHtml.js.map +1 -1
  35. package/lib/components/screens/combat/components/feedback/RoundDisplayStatus.js.map +1 -1
  36. package/lib/components/screens/combat/components/feedback/RoundStartAnnouncementOverlayHtml.js.map +1 -1
  37. package/lib/components/screens/combat/components/hud/CombatBottomHUD.d.ts.map +1 -1
  38. package/lib/components/screens/combat/components/hud/CombatBottomHUD.js +2 -2
  39. package/lib/components/screens/combat/components/hud/CombatBottomHUD.js.map +1 -1
  40. package/lib/components/screens/combat/components/hud/CombatLeftHUD.js.map +1 -1
  41. package/lib/components/screens/combat/components/hud/CombatPortraitStatusStrip.js +1 -1
  42. package/lib/components/screens/combat/components/hud/CombatPortraitStatusStrip.js.map +1 -1
  43. package/lib/components/screens/combat/components/hud/CombatRightHUD.js.map +1 -1
  44. package/lib/components/screens/combat/components/hud/CombatTopHUD.d.ts.map +1 -1
  45. package/lib/components/screens/combat/components/hud/CombatTopHUD.js +2 -1
  46. package/lib/components/screens/combat/components/hud/CombatTopHUD.js.map +1 -1
  47. package/lib/components/screens/combat/components/hud/DifficultyIndicator.js.map +1 -1
  48. package/lib/components/screens/combat/components/hud/FPSMonitor.js.map +1 -1
  49. package/lib/components/screens/combat/components/hud/MobileControlsWrapper.js.map +1 -1
  50. package/lib/components/screens/combat/components/hud/PlayerStateOverlayHtml.js.map +1 -1
  51. package/lib/components/screens/combat/components/indicators/BalanceIndicator.js.map +1 -1
  52. package/lib/components/screens/combat/components/indicators/InputBufferDisplay.js.map +1 -1
  53. package/lib/components/screens/combat/components/indicators/StaminaWarning.js.map +1 -1
  54. package/lib/components/screens/combat/components/indicators/TechniqueNameDisplay.js.map +1 -1
  55. package/lib/components/screens/combat/helpers/AnimationUpdater.d.ts.map +1 -1
  56. package/lib/components/screens/combat/helpers/AnimationUpdater.js +4 -2
  57. package/lib/components/screens/combat/helpers/AnimationUpdater.js.map +1 -1
  58. package/lib/components/screens/combat/helpers/combatHelpers.js.map +1 -1
  59. package/lib/components/screens/combat/hooks/useAICombat.js.map +1 -1
  60. package/lib/components/screens/combat/hooks/useCombatActions.js.map +1 -1
  61. package/lib/components/screens/combat/hooks/useCombatAttackMovement.js.map +1 -1
  62. package/lib/components/screens/combat/hooks/useCombatAudio.js.map +1 -1
  63. package/lib/components/screens/combat/hooks/useCombatLayout.d.ts.map +1 -1
  64. package/lib/components/screens/combat/hooks/useCombatLayout.js +11 -5
  65. package/lib/components/screens/combat/hooks/useCombatLayout.js.map +1 -1
  66. package/lib/components/screens/combat/hooks/useCombatState.js.map +1 -1
  67. package/lib/components/screens/controls/ControlsScreen3D.js +1 -1
  68. package/lib/components/screens/controls/ControlsScreen3D.js.map +1 -1
  69. package/lib/components/screens/controls/components/ControlBindingsOverlayHtml.js.map +1 -1
  70. package/lib/components/screens/controls/components/ControlCategoryTabsOverlayHtml.js.map +1 -1
  71. package/lib/components/screens/controls/components/GamepadVisualization3D.js.map +1 -1
  72. package/lib/components/screens/controls/components/InteractiveControlDemoOverlayHtml.js.map +1 -1
  73. package/lib/components/screens/controls/components/Key3D.js.map +1 -1
  74. package/lib/components/screens/controls/components/VisualKeyboard3D.js.map +1 -1
  75. package/lib/components/screens/controls/constants/ControlsConstants.js.map +1 -1
  76. package/lib/components/screens/controls/hooks/useControlsState.js.map +1 -1
  77. package/lib/components/screens/endscreen/EndScreen3D.js.map +1 -1
  78. package/lib/components/screens/endscreen/components/DefeatAnimation3D.js.map +1 -1
  79. package/lib/components/screens/endscreen/components/MatchStatisticsDisplayOverlayHtml.js.map +1 -1
  80. package/lib/components/screens/endscreen/components/NavigationButtonsOverlayHtml.js.map +1 -1
  81. package/lib/components/screens/endscreen/components/PerformanceBreakdownOverlayHtml.js.map +1 -1
  82. package/lib/components/screens/endscreen/components/PerformanceRatingOverlayHtml.js.map +1 -1
  83. package/lib/components/screens/endscreen/components/VictoryAnimation3D.js.map +1 -1
  84. package/lib/components/screens/endscreen/components/WinnerDisplayOverlayHtml.js.map +1 -1
  85. package/lib/components/screens/intro/IntroScreen3D.js +1 -1
  86. package/lib/components/screens/intro/IntroScreen3D.js.map +1 -1
  87. package/lib/components/screens/intro/components/AbilityListOverlayHtml.js.map +1 -1
  88. package/lib/components/screens/intro/components/ArchetypeCardGridOverlayHtml.js.map +1 -1
  89. package/lib/components/screens/intro/components/ArchetypeCardOverlayHtml.js.map +1 -1
  90. package/lib/components/screens/intro/components/ArchetypeDisplayOverlayHtml.js.map +1 -1
  91. package/lib/components/screens/intro/components/EnhancedArchetypeDisplayOverlayHtml.js.map +1 -1
  92. package/lib/components/screens/intro/components/MenuButtonsOverlayHtml.js.map +1 -1
  93. package/lib/components/screens/intro/components/MenuSectionOverlayHtml.js.map +1 -1
  94. package/lib/components/screens/intro/components/StatBarOverlayHtml.js.map +1 -1
  95. package/lib/components/screens/philosophy/PhilosophyScreen3D.js +1 -1
  96. package/lib/components/screens/philosophy/PhilosophyScreen3D.js.map +1 -1
  97. package/lib/components/screens/training/TrainingScreen3D.d.ts.map +1 -1
  98. package/lib/components/screens/training/TrainingScreen3D.js +3 -11
  99. package/lib/components/screens/training/TrainingScreen3D.js.map +1 -1
  100. package/lib/components/screens/training/components/AnatomyControlsOverlayHtml.js.map +1 -1
  101. package/lib/components/screens/training/components/AnatomyOverlay3D.js.map +1 -1
  102. package/lib/components/screens/training/components/FootPlacementMarkers3D.js.map +1 -1
  103. package/lib/components/screens/training/components/FootworkDrillsOverlayHtml.js.map +1 -1
  104. package/lib/components/screens/training/components/HitFeedbackEffect3D.js.map +1 -1
  105. package/lib/components/screens/training/components/TrainingButtonsOverlayHtml.js.map +1 -1
  106. package/lib/components/screens/training/components/TrainingControlsOverlayHtml.js.map +1 -1
  107. package/lib/components/screens/training/components/TrainingDummy3D.js.map +1 -1
  108. package/lib/components/screens/training/components/TrainingFeedbackOverlayHtml.js.map +1 -1
  109. package/lib/components/screens/training/components/TrainingModeSelectorOverlayHtml.js.map +1 -1
  110. package/lib/components/screens/training/components/TrainingStatsOverlayHtml.js.map +1 -1
  111. package/lib/components/screens/training/components/VitalPointMarker3D.js.map +1 -1
  112. package/lib/components/screens/training/components/VitalPointTrainingOverlayHtml.js.map +1 -1
  113. package/lib/components/screens/training/components/hud/TrainingBottomHUD.d.ts.map +1 -1
  114. package/lib/components/screens/training/components/hud/TrainingBottomHUD.js +2 -2
  115. package/lib/components/screens/training/components/hud/TrainingBottomHUD.js.map +1 -1
  116. package/lib/components/screens/training/components/hud/TrainingLeftHUD.js.map +1 -1
  117. package/lib/components/screens/training/components/hud/TrainingRightHUD.js.map +1 -1
  118. package/lib/components/screens/training/components/hud/TrainingTopHUD.js.map +1 -1
  119. package/lib/components/screens/training/hooks/useAttackMovement.js.map +1 -1
  120. package/lib/components/screens/training/hooks/useTrainingActions.d.ts +1 -0
  121. package/lib/components/screens/training/hooks/useTrainingActions.d.ts.map +1 -1
  122. package/lib/components/screens/training/hooks/useTrainingActions.js +6 -4
  123. package/lib/components/screens/training/hooks/useTrainingActions.js.map +1 -1
  124. package/lib/components/screens/training/hooks/useTrainingLayout.d.ts.map +1 -1
  125. package/lib/components/screens/training/hooks/useTrainingLayout.js +11 -5
  126. package/lib/components/screens/training/hooks/useTrainingLayout.js.map +1 -1
  127. package/lib/components/screens/training/hooks/useTrainingState.js.map +1 -1
  128. package/lib/components/shared/base/BaseButton.js.map +1 -1
  129. package/lib/components/shared/base/BaseButtonOverlayHtml.js.map +1 -1
  130. package/lib/components/shared/base/BasePanel.js.map +1 -1
  131. package/lib/components/shared/base/BaseText.js.map +1 -1
  132. package/lib/components/shared/base/useKoreanTheme.js.map +1 -1
  133. package/lib/components/shared/debug/PerformanceDebugOverlayHtml.js.map +1 -1
  134. package/lib/components/shared/mobile/ActionButtons.js.map +1 -1
  135. package/lib/components/shared/mobile/GestureRecognizerPure.js.map +1 -1
  136. package/lib/components/shared/mobile/HapticController.js.map +1 -1
  137. package/lib/components/shared/mobile/MobileControlsPure.js.map +1 -1
  138. package/lib/components/shared/mobile/StanceWheelPure.js.map +1 -1
  139. package/lib/components/shared/mobile/TouchOptimizer.js.map +1 -1
  140. package/lib/components/shared/mobile/VirtualDPad.js.map +1 -1
  141. package/lib/components/shared/three/anatomy/BodySurface.js.map +1 -1
  142. package/lib/components/shared/three/anatomy/BoneAttachedMuscles.js.map +1 -1
  143. package/lib/components/shared/three/anatomy/BoneClothing.js.map +1 -1
  144. package/lib/components/shared/three/anatomy/BoneRenderer.js.map +1 -1
  145. package/lib/components/shared/three/anatomy/Face3D.js.map +1 -1
  146. package/lib/components/shared/three/anatomy/Foot3D.js.map +1 -1
  147. package/lib/components/shared/three/anatomy/Hand3D.js.map +1 -1
  148. package/lib/components/shared/three/effects/ActionFeedback.js.map +1 -1
  149. package/lib/components/shared/three/effects/DamageNumbers.js.map +1 -1
  150. package/lib/components/shared/three/effects/HitEffects3D.js.map +1 -1
  151. package/lib/components/shared/three/effects/PlayerStateIndicators.js.map +1 -1
  152. package/lib/components/shared/three/effects/StanceSymbol3D.js.map +1 -1
  153. package/lib/components/shared/three/effects/StanceTransitionEffect.js.map +1 -1
  154. package/lib/components/shared/three/effects/VitalPointMarkers3D.js.map +1 -1
  155. package/lib/components/shared/three/indicators/ElementalColorSystem.js.map +1 -1
  156. package/lib/components/shared/three/indicators/GuardIndicator.js.map +1 -1
  157. package/lib/components/shared/three/indicators/HapticFeedback.js.map +1 -1
  158. package/lib/components/shared/three/indicators/StanceChangeIndicator.js.map +1 -1
  159. package/lib/components/shared/three/models/Player3DWithTransitions.js.map +1 -1
  160. package/lib/components/shared/three/models/SkeletalPlayer3D.d.ts.map +1 -1
  161. package/lib/components/shared/three/models/SkeletalPlayer3D.js +7 -5
  162. package/lib/components/shared/three/models/SkeletalPlayer3D.js.map +1 -1
  163. package/lib/components/shared/three/optimization/AdaptiveQuality.js.map +1 -1
  164. package/lib/components/shared/three/scene/AtmosphericParticles3D.js.map +1 -1
  165. package/lib/components/shared/three/scene/BackgroundScene3D.js.map +1 -1
  166. package/lib/components/shared/three/scene/CombatArena3D.js.map +1 -1
  167. package/lib/components/shared/three/scene/KoreanSignage3D.js.map +1 -1
  168. package/lib/components/shared/three/ui/ArchetypeCard.js.map +1 -1
  169. package/lib/components/shared/three/ui/BodyPartHealthDisplay.js.map +1 -1
  170. package/lib/components/shared/three/ui/BreathingIndicator2.js.map +1 -1
  171. package/lib/components/shared/three/ui/CombatReadinessBar.js.map +1 -1
  172. package/lib/components/shared/three/ui/ComboCounter.js.map +1 -1
  173. package/lib/components/shared/three/ui/HealthBar.js.map +1 -1
  174. package/lib/components/shared/three/ui/KoreanButton.js.map +1 -1
  175. package/lib/components/shared/three/ui/KoreanPanel.js.map +1 -1
  176. package/lib/components/shared/three/ui/KoreanText.js.map +1 -1
  177. package/lib/components/shared/three/ui/MenuList.js.map +1 -1
  178. package/lib/components/shared/three/ui/PlayerHUD.js.map +1 -1
  179. package/lib/components/shared/three/ui/ProgressBar.js.map +1 -1
  180. package/lib/components/shared/three/ui/SpeedIndicatorHUD.js.map +1 -1
  181. package/lib/components/shared/three/ui/StaminaBar.js.map +1 -1
  182. package/lib/components/shared/three/ui/TechniqueBar.js.map +1 -1
  183. package/lib/components/shared/three/ui/TechniqueCard.js.map +1 -1
  184. package/lib/components/shared/three/ui/VitalPointOverlayControlsHtml.js.map +1 -1
  185. package/lib/components/shared/ui/BackButton.js.map +1 -1
  186. package/lib/components/shared/ui/BaseHUDContainer.js.map +1 -1
  187. package/lib/components/shared/ui/CombatTimer.js.map +1 -1
  188. package/lib/components/shared/ui/ErrorModal.js.map +1 -1
  189. package/lib/components/shared/ui/LoadingState.js.map +1 -1
  190. package/lib/components/shared/ui/SplashScreen.js +2 -2
  191. package/lib/components/shared/ui/SplashScreen.js.map +1 -1
  192. package/lib/components/shared/ui/VitalPointOverlayControlsPure.js.map +1 -1
  193. package/lib/components/shared/ui/VolumeControl.js.map +1 -1
  194. package/lib/components/shared/ui/shared/ConfirmDialog.js.map +1 -1
  195. package/lib/components/ui/combat/BalanceIndicatorOverlayHtml.js.map +1 -1
  196. package/lib/constants/bodyDimensions.js.map +1 -1
  197. package/lib/constants/bodyRenderingConstants.js.map +1 -1
  198. package/lib/data/archetypeClothing.js.map +1 -1
  199. package/lib/data/archetypePhysicalAttributes.js.map +1 -1
  200. package/lib/data/techniqueMappings.js.map +1 -1
  201. package/lib/data/techniques.js.map +1 -1
  202. package/lib/hooks/useActionFeedback.js.map +1 -1
  203. package/lib/hooks/useBalanceAnimations.js.map +1 -1
  204. package/lib/hooks/useCombatTimer.js.map +1 -1
  205. package/lib/hooks/useDebounce.js.map +1 -1
  206. package/lib/hooks/useHUDLayout.d.ts.map +1 -1
  207. package/lib/hooks/useHUDLayout.js +3 -2
  208. package/lib/hooks/useHUDLayout.js.map +1 -1
  209. package/lib/hooks/useHandPoseTransitions.js.map +1 -1
  210. package/lib/hooks/useKeyboardControls.js.map +1 -1
  211. package/lib/hooks/useMatchCountdown.js.map +1 -1
  212. package/lib/hooks/useMuscleActivation.js.map +1 -1
  213. package/lib/hooks/usePauseMenu.js.map +1 -1
  214. package/lib/hooks/usePlayerAnimation.js.map +1 -1
  215. package/lib/hooks/useResponsiveLayout.js.map +1 -1
  216. package/lib/hooks/useRoundTransition.js.map +1 -1
  217. package/lib/hooks/useSkeletalAnimation.d.ts.map +1 -1
  218. package/lib/hooks/useSkeletalAnimation.js +1 -1
  219. package/lib/hooks/useSkeletalAnimation.js.map +1 -1
  220. package/lib/hooks/useTechniqueSelection.js.map +1 -1
  221. package/lib/hooks/useThrottle.js.map +1 -1
  222. package/lib/hooks/useTouchControls.js.map +1 -1
  223. package/lib/hooks/useWebGLContextLossHandler.js.map +1 -1
  224. package/lib/hooks/useWindowSize.js.map +1 -1
  225. package/lib/systems/CombatSystem.js.map +1 -1
  226. package/lib/systems/EffectCalculator.js.map +1 -1
  227. package/lib/systems/LayoutSystem.js.map +1 -1
  228. package/lib/systems/PlayerEffectManager.js.map +1 -1
  229. package/lib/systems/ResponsiveScaling.js.map +1 -1
  230. package/lib/systems/TrigramSystem.js.map +1 -1
  231. package/lib/systems/VitalPointSystem.js.map +1 -1
  232. package/lib/systems/ai/AIPersonality.js.map +1 -1
  233. package/lib/systems/ai/AdaptiveDifficulty.js +16 -16
  234. package/lib/systems/ai/AdaptiveDifficulty.js.map +1 -1
  235. package/lib/systems/ai/ArchetypeEnforcer.js.map +1 -1
  236. package/lib/systems/ai/ComboSystem.js.map +1 -1
  237. package/lib/systems/ai/DecisionTree.js.map +1 -1
  238. package/lib/systems/ai/TrainingAI.js.map +1 -1
  239. package/lib/systems/ai/types.js.map +1 -1
  240. package/lib/systems/animation/builders/AnimationBuilder.js.map +1 -1
  241. package/lib/systems/animation/builders/HandPoseApplicator.js.map +1 -1
  242. package/lib/systems/animation/builders/HandPoses.js.map +1 -1
  243. package/lib/systems/animation/builders/KeyframeConfig.js.map +1 -1
  244. package/lib/systems/animation/builders/KeyframeInterpolation.js.map +1 -1
  245. package/lib/systems/animation/builders/KickPhaseApplicator.d.ts +6 -0
  246. package/lib/systems/animation/builders/KickPhaseApplicator.d.ts.map +1 -1
  247. package/lib/systems/animation/builders/KickPhaseApplicator.js +16 -9
  248. package/lib/systems/animation/builders/KickPhaseApplicator.js.map +1 -1
  249. package/lib/systems/animation/builders/KoreanGuardPositions.d.ts +4 -4
  250. package/lib/systems/animation/builders/KoreanGuardPositions.js.map +1 -1
  251. package/lib/systems/animation/builders/MartialArtsAnimationBuilder.d.ts +1 -1
  252. package/lib/systems/animation/builders/MartialArtsAnimationBuilder.d.ts.map +1 -1
  253. package/lib/systems/animation/builders/MartialArtsAnimationBuilder.js +5 -5
  254. package/lib/systems/animation/builders/MartialArtsAnimationBuilder.js.map +1 -1
  255. package/lib/systems/animation/builders/MartialArtsConstants.d.ts +112 -71
  256. package/lib/systems/animation/builders/MartialArtsConstants.d.ts.map +1 -1
  257. package/lib/systems/animation/builders/MartialArtsConstants.js +113 -72
  258. package/lib/systems/animation/builders/MartialArtsConstants.js.map +1 -1
  259. package/lib/systems/animation/builders/MartialPoseApplicator.js.map +1 -1
  260. package/lib/systems/animation/builders/PunchPhaseApplicator.js.map +1 -1
  261. package/lib/systems/animation/builders/SkeletonRig.js.map +1 -1
  262. package/lib/systems/animation/builders/TrigramGuardApplicator.js.map +1 -1
  263. package/lib/systems/animation/catalogs/AttackAnimations.js.map +1 -1
  264. package/lib/systems/animation/catalogs/BasicAnimations.js.map +1 -1
  265. package/lib/systems/animation/catalogs/ComboAnimations.js.map +1 -1
  266. package/lib/systems/animation/catalogs/DarkOpsAnimations.js.map +1 -1
  267. package/lib/systems/animation/catalogs/DefensiveAnimations.js.map +1 -1
  268. package/lib/systems/animation/catalogs/ElbowKneeAnimations.js.map +1 -1
  269. package/lib/systems/animation/catalogs/EnhancedAttackAnimations.js.map +1 -1
  270. package/lib/systems/animation/catalogs/EnhancedElbowKneeAnimations.js.map +1 -1
  271. package/lib/systems/animation/catalogs/FootworkSkeletalAnimations.js.map +1 -1
  272. package/lib/systems/animation/catalogs/GamRedirectionAnimations.js.map +1 -1
  273. package/lib/systems/animation/catalogs/GamStanceAnimations.js +21 -0
  274. package/lib/systems/animation/catalogs/GamStanceAnimations.js.map +1 -0
  275. package/lib/systems/animation/catalogs/GamTechniqueAnimations.js +34 -2
  276. package/lib/systems/animation/catalogs/GamTechniqueAnimations.js.map +1 -1
  277. package/lib/systems/animation/catalogs/GanStanceAnimations.js.map +1 -1
  278. package/lib/systems/animation/catalogs/GanTechniqueAnimations.js.map +1 -1
  279. package/lib/systems/animation/catalogs/GeonStanceAnimations.js.map +1 -1
  280. package/lib/systems/animation/catalogs/GonTechniqueAnimations.d.ts +9 -0
  281. package/lib/systems/animation/catalogs/GonTechniqueAnimations.d.ts.map +1 -1
  282. package/lib/systems/animation/catalogs/GonTechniqueAnimations.js +288 -0
  283. package/lib/systems/animation/catalogs/GonTechniqueAnimations.js.map +1 -0
  284. package/lib/systems/animation/catalogs/GrapplingAnimations.js.map +1 -1
  285. package/lib/systems/animation/catalogs/JinStanceAnimations.js.map +1 -1
  286. package/lib/systems/animation/catalogs/JinTechniqueAnimations.js.map +1 -1
  287. package/lib/systems/animation/catalogs/KickAnimations.d.ts +2 -2
  288. package/lib/systems/animation/catalogs/KickAnimations.js +2 -2
  289. package/lib/systems/animation/catalogs/KickAnimations.js.map +1 -1
  290. package/lib/systems/animation/catalogs/LiStanceAnimations.js +14 -1
  291. package/lib/systems/animation/catalogs/LiStanceAnimations.js.map +1 -1
  292. package/lib/systems/animation/catalogs/LiTechniqueAnimations.js.map +1 -1
  293. package/lib/systems/animation/catalogs/MovementAnimations.js.map +1 -1
  294. package/lib/systems/animation/catalogs/PunchAnimations.d.ts +1 -1
  295. package/lib/systems/animation/catalogs/PunchAnimations.js +1 -1
  296. package/lib/systems/animation/catalogs/PunchAnimations.js.map +1 -1
  297. package/lib/systems/animation/catalogs/RecoveryAnimations.js.map +1 -1
  298. package/lib/systems/animation/catalogs/SonStanceAnimations.js.map +1 -1
  299. package/lib/systems/animation/catalogs/SonTechniqueAnimations.js.map +1 -1
  300. package/lib/systems/animation/catalogs/SpecializedPunchAnimations.js.map +1 -1
  301. package/lib/systems/animation/catalogs/StanceAnimations.js.map +1 -1
  302. package/lib/systems/animation/catalogs/StanceAttackAnimations.js.map +1 -1
  303. package/lib/systems/animation/catalogs/StanceGuardPoses.d.ts +6 -6
  304. package/lib/systems/animation/catalogs/StanceGuardPoses.js +36 -36
  305. package/lib/systems/animation/catalogs/StanceGuardPoses.js.map +1 -1
  306. package/lib/systems/animation/catalogs/StanceIdleAnimations.js.map +1 -1
  307. package/lib/systems/animation/catalogs/StanceLocomotionAnimations.js.map +1 -1
  308. package/lib/systems/animation/catalogs/StepSkeletalAnimations.js.map +1 -1
  309. package/lib/systems/animation/catalogs/TaeJointLockAnimations.js.map +1 -1
  310. package/lib/systems/animation/catalogs/TaeStanceAnimations.js.map +1 -1
  311. package/lib/systems/animation/constants/AnatomicalLimits.js.map +1 -1
  312. package/lib/systems/animation/core/AnimationHitTiming.js.map +1 -1
  313. package/lib/systems/animation/core/AnimationOptimizations.js.map +1 -1
  314. package/lib/systems/animation/core/AnimationPriority.js +15 -15
  315. package/lib/systems/animation/core/AnimationPriority.js.map +1 -1
  316. package/lib/systems/animation/core/AnimationRegistry.d.ts +30 -0
  317. package/lib/systems/animation/core/AnimationRegistry.d.ts.map +1 -1
  318. package/lib/systems/animation/core/AnimationRegistry.js +74 -12
  319. package/lib/systems/animation/core/AnimationRegistry.js.map +1 -1
  320. package/lib/systems/animation/core/AnimationStateMachine.js +16 -16
  321. package/lib/systems/animation/core/AnimationStateMachine.js.map +1 -1
  322. package/lib/systems/animation/core/AnimationTransitions.d.ts.map +1 -1
  323. package/lib/systems/animation/core/AnimationTransitions.js +34 -0
  324. package/lib/systems/animation/core/AnimationTransitions.js.map +1 -1
  325. package/lib/systems/animation/core/LateralityTransform.js.map +1 -1
  326. package/lib/systems/animation/core/RecoveryPhaseEnhancer.js.map +1 -1
  327. package/lib/systems/animation/core/TechniqueAnimationMapper.js.map +1 -1
  328. package/lib/systems/animation/core/TechniqueAnimationMapping.js.map +1 -1
  329. package/lib/systems/animation/core/index.d.ts +1 -1
  330. package/lib/systems/animation/core/index.d.ts.map +1 -1
  331. package/lib/systems/animation/core/types.d.ts +24 -0
  332. package/lib/systems/animation/core/types.d.ts.map +1 -1
  333. package/lib/systems/animation/core/types.js +27 -11
  334. package/lib/systems/animation/core/types.js.map +1 -1
  335. package/lib/systems/animation/systems/AdvancedJointMovements.js.map +1 -1
  336. package/lib/systems/animation/systems/BodyFacingSystem.js.map +1 -1
  337. package/lib/systems/animation/systems/FacialExpressions.js.map +1 -1
  338. package/lib/systems/animation/systems/FallAnimations.js.map +1 -1
  339. package/lib/systems/animation/systems/MuscleActivation.js.map +1 -1
  340. package/lib/systems/bodypart/BodyPartDamageIntegration.js.map +1 -1
  341. package/lib/systems/bodypart/BodyPartHealthSystem.js.map +1 -1
  342. package/lib/systems/bodypart/BodyPartPositionMapping.js.map +1 -1
  343. package/lib/systems/bodypart/CombatInjuryIntegration.js.map +1 -1
  344. package/lib/systems/bodypart/InjuryIntegration.js.map +1 -1
  345. package/lib/systems/bodypart/InjuryTracker.js.map +1 -1
  346. package/lib/systems/bodypart/MovementPenaltySystem.js.map +1 -1
  347. package/lib/systems/bodypart/PlayerInjuryTrackingManager.js.map +1 -1
  348. package/lib/systems/bodypart/types.js.map +1 -1
  349. package/lib/systems/breathing/BreathingDisruptionSystem.js +19 -19
  350. package/lib/systems/breathing/BreathingDisruptionSystem.js.map +1 -1
  351. package/lib/systems/breathing/feedback.js.map +1 -1
  352. package/lib/systems/breathing/integration.js.map +1 -1
  353. package/lib/systems/combat/BalanceSystem.js +19 -19
  354. package/lib/systems/combat/BalanceSystem.js.map +1 -1
  355. package/lib/systems/combat/BreakingStatusEffects.js.map +1 -1
  356. package/lib/systems/combat/CombatStateSystem.js +17 -17
  357. package/lib/systems/combat/CombatStateSystem.js.map +1 -1
  358. package/lib/systems/combat/ConsciousnessSystem.js +24 -24
  359. package/lib/systems/combat/ConsciousnessSystem.js.map +1 -1
  360. package/lib/systems/combat/FallIntegration.js.map +1 -1
  361. package/lib/systems/combat/GrappleSystem.js.map +1 -1
  362. package/lib/systems/combat/LimbExposureSystem.js.map +1 -1
  363. package/lib/systems/combat/PainResponseSystem.js +21 -21
  364. package/lib/systems/combat/PainResponseSystem.js.map +1 -1
  365. package/lib/systems/combat/TrainingCombatSystem.js.map +1 -1
  366. package/lib/systems/combat/painConsciousnessUtils.js.map +1 -1
  367. package/lib/systems/combat/typeGuards.js.map +1 -1
  368. package/lib/systems/effects.js.map +1 -1
  369. package/lib/systems/game.js.map +1 -1
  370. package/lib/systems/movement/InjuryMovementModifier.js.map +1 -1
  371. package/lib/systems/movement/helpers/AccelerationUpdater.js.map +1 -1
  372. package/lib/systems/movement/helpers/accelerationUtils.js.map +1 -1
  373. package/lib/systems/movement/integration.js.map +1 -1
  374. package/lib/systems/physics/AttackMovementPhysics.js.map +1 -1
  375. package/lib/systems/physics/CollisionDetection.js.map +1 -1
  376. package/lib/systems/physics/CoordinateMapper.js.map +1 -1
  377. package/lib/systems/physics/KnockbackPhysics.js.map +1 -1
  378. package/lib/systems/physics/MovementPhysics.js.map +1 -1
  379. package/lib/systems/physics/PhysicalReachCalculator.js.map +1 -1
  380. package/lib/systems/physics/SpeedModifierSystem.js +6 -6
  381. package/lib/systems/physics/SpeedModifierSystem.js.map +1 -1
  382. package/lib/systems/trigram/KoreanCulture.js.map +1 -1
  383. package/lib/systems/trigram/KoreanTechniques.js.map +1 -1
  384. package/lib/systems/trigram/StanceManager.js.map +1 -1
  385. package/lib/systems/trigram/TransitionCalculator.js.map +1 -1
  386. package/lib/systems/trigram/TrigramCalculator.js.map +1 -1
  387. package/lib/systems/trigram/techniques/DarkOpsTechniques.js.map +1 -1
  388. package/lib/systems/trigram/techniques/GamTechniques.js.map +1 -1
  389. package/lib/systems/trigram/techniques/GanTechniques.js.map +1 -1
  390. package/lib/systems/trigram/techniques/GeonTechniques.js.map +1 -1
  391. package/lib/systems/trigram/techniques/GonTechniques.js.map +1 -1
  392. package/lib/systems/trigram/techniques/JinTechniques.js.map +1 -1
  393. package/lib/systems/trigram/techniques/LiTechniques.js.map +1 -1
  394. package/lib/systems/trigram/techniques/SonTechniques.js.map +1 -1
  395. package/lib/systems/trigram/techniques/TaeTechniques.js.map +1 -1
  396. package/lib/systems/trigram/techniques/TechniqueConfig.js.map +1 -1
  397. package/lib/systems/trigram/techniques/index.js.map +1 -1
  398. package/lib/systems/trigram/types/GonTechniqueExtensions.js.map +1 -1
  399. package/lib/systems/trigram/types.js.map +1 -1
  400. package/lib/systems/types.js.map +1 -1
  401. package/lib/systems/vitalpoint/DamageCalculator.js.map +1 -1
  402. package/lib/systems/vitalpoint/HitDetection.js.map +1 -1
  403. package/lib/systems/vitalpoint/KoreanAnatomy.js.map +1 -1
  404. package/lib/systems/vitalpoint/KoreanVitalPoints.js.map +1 -1
  405. package/lib/systems/vitalpoint/MeridianVitalPointMapping.js.map +1 -1
  406. package/lib/systems/vitalpoint/VitalPointsData.js.map +1 -1
  407. package/lib/types/AccessibilityTypes.js.map +1 -1
  408. package/lib/types/LayoutTypes.js.map +1 -1
  409. package/lib/types/PhysicsTypes.js.map +1 -1
  410. package/lib/types/common.js.map +1 -1
  411. package/lib/types/constants/animations.js.map +1 -1
  412. package/lib/types/constants/colors.js.map +1 -1
  413. package/lib/types/constants/designSystem.js.map +1 -1
  414. package/lib/types/constants/index.js.map +1 -1
  415. package/lib/types/constants/layout.d.ts +21 -0
  416. package/lib/types/constants/layout.d.ts.map +1 -1
  417. package/lib/types/constants/layout.js +22 -1
  418. package/lib/types/constants/layout.js.map +1 -1
  419. package/lib/types/constants/performance.js.map +1 -1
  420. package/lib/types/constants/typography.js.map +1 -1
  421. package/lib/types/constants/ui.js.map +1 -1
  422. package/lib/types/facial.js +19 -19
  423. package/lib/types/facial.js.map +1 -1
  424. package/lib/types/hand-animation.js.map +1 -1
  425. package/lib/types/injury.js.map +1 -1
  426. package/lib/types/muscle.js.map +1 -1
  427. package/lib/types/physics.js.map +1 -1
  428. package/lib/types/physicsConstants.js.map +1 -1
  429. package/lib/types/player-visual.d.ts +1 -1
  430. package/lib/types/player-visual.d.ts.map +1 -1
  431. package/lib/types/skeletal.js.map +1 -1
  432. package/lib/types/techniqueId.js.map +1 -1
  433. package/lib/utils/accessibility.js.map +1 -1
  434. package/lib/utils/arenaWorldDimensions.js.map +1 -1
  435. package/lib/utils/assetConfig.js.map +1 -1
  436. package/lib/utils/characterScaling.js.map +1 -1
  437. package/lib/utils/colorHelpers.js.map +1 -1
  438. package/lib/utils/colorUtils.js.map +1 -1
  439. package/lib/utils/combatReadiness.js.map +1 -1
  440. package/lib/utils/controlMapping.js.map +1 -1
  441. package/lib/utils/deviceDetection.js +6 -7
  442. package/lib/utils/deviceDetection.js.map +1 -1
  443. package/lib/utils/effectUtils.js.map +1 -1
  444. package/lib/utils/fabricTextures.js.map +1 -1
  445. package/lib/utils/hapticFeedback.js.map +1 -1
  446. package/lib/utils/haptics.js.map +1 -1
  447. package/lib/utils/htmlOverlayHelpers.js.map +1 -1
  448. package/lib/utils/inputSystem.js.map +1 -1
  449. package/lib/utils/koreanThemeHelpers.js.map +1 -1
  450. package/lib/utils/math.js.map +1 -1
  451. package/lib/utils/mobileLayoutHelpers.js.map +1 -1
  452. package/lib/utils/mobileUIUtils.js.map +1 -1
  453. package/lib/utils/performance/PerformanceMonitor.js.map +1 -1
  454. package/lib/utils/performance/PerformanceOverlay3D.js.map +1 -1
  455. package/lib/utils/performance/usePerformanceMonitor.js.map +1 -1
  456. package/lib/utils/performanceOptimization.js.map +1 -1
  457. package/lib/utils/player3DHelpers.js.map +1 -1
  458. package/lib/utils/playerUtils.js.map +1 -1
  459. package/lib/utils/responsiveLayout.js.map +1 -1
  460. package/lib/utils/responsiveLayoutHelpers.d.ts +7 -0
  461. package/lib/utils/responsiveLayoutHelpers.d.ts.map +1 -1
  462. package/lib/utils/responsiveLayoutHelpers.js +16 -2
  463. package/lib/utils/responsiveLayoutHelpers.js.map +1 -1
  464. package/lib/utils/responsiveOrientationConstants.js.map +1 -1
  465. package/lib/utils/safeAreaUtils.js.map +1 -1
  466. package/lib/utils/sharedPhysicsConfig.js.map +1 -1
  467. package/lib/utils/skeletonScaling.js.map +1 -1
  468. package/lib/utils/stanceHelpers.js.map +1 -1
  469. package/lib/utils/threeObjectPool.js.map +1 -1
  470. package/lib/utils/visualEffects.js.map +1 -1
  471. package/package.json +7 -7
@@ -1 +1 @@
1
- {"version":3,"file":"accelerationUtils.js","names":[],"sources":["../../../../src/systems/movement/helpers/accelerationUtils.ts"],"sourcesContent":["/**\n * Acceleration Utilities for Movement System\n *\n * Pure functions for calculating acceleration-based running speeds.\n * Extracted for testability and reusability across training and combat screens.\n *\n * @module systems/movement/helpers/accelerationUtils\n * @category Movement\n * @korean 가속 유틸리티\n */\n\n/**\n * Step distance thresholds for foot laterality alternation\n * 발 측면성 교대를 위한 걸음 거리 임계값\n */\nexport const STEP_DISTANCE_THRESHOLDS = {\n /**\n * Average step length when walking (meters)\n * 걷기 시 평균 걸음 길이\n */\n WALK: 0.7,\n\n /**\n * Average step length when running (meters)\n * 달리기 시 평균 걸음 길이\n */\n RUN: 1.0,\n} as const;\n\n/**\n * Constants for acceleration-based running (defaults for non-archetype usage)\n */\nexport const ACCELERATION_CONSTANTS = {\n /** Default walking speed in m/s (when no archetype speed provided) */\n DEFAULT_WALK_SPEED: 6.0,\n /** Default running speed in m/s (when no archetype speed provided) */\n DEFAULT_RUN_SPEED: 10.0,\n /** Time to reach running speed in seconds */\n TIME_TO_RUN: 1.5,\n /** Threshold for considering direction \"same\" (cos(45°) = √2/2) */\n DIRECTION_THRESHOLD: Math.cos(Math.PI / 4),\n /** Running threshold as percentage of max speed (0-1) */\n RUN_THRESHOLD_PERCENT: 0.9,\n /** Epsilon for speed change detection (m/s) */\n SPEED_CHANGE_EPSILON: 0.05,\n} as const;\n\n/**\n * Calculate running threshold speed\n * @param runSpeed - Maximum running speed (from archetype or default)\n * @returns Speed at which movement is considered running (m/s)\n */\nexport function calculateRunThreshold(\n runSpeed: number = ACCELERATION_CONSTANTS.DEFAULT_RUN_SPEED\n): number {\n return runSpeed * ACCELERATION_CONSTANTS.RUN_THRESHOLD_PERCENT;\n}\n\n/**\n * Check if two directions are consistent (within threshold angle)\n * @param currentDir Current direction vector\n * @param lastDir Previous direction vector\n * @returns True if directions are within 45° of each other\n */\nexport function isDirectionConsistent(\n currentDir: { x: number; y: number },\n lastDir: { x: number; y: number }\n): boolean {\n // If last direction is zero, consider any movement as consistent\n if (lastDir.x === 0 && lastDir.y === 0) {\n return true;\n }\n\n // Calculate dot product\n const dot = currentDir.x * lastDir.x + currentDir.y * lastDir.y;\n const magCurrent = Math.sqrt(currentDir.x ** 2 + currentDir.y ** 2);\n const magLast = Math.sqrt(lastDir.x ** 2 + lastDir.y ** 2);\n\n if (magCurrent === 0 || magLast === 0) {\n return false;\n }\n\n // Clamp cosAngle to [-1, 1] to handle floating-point edge cases\n const cosAngleRaw = dot / (magCurrent * magLast);\n const cosAngle = Math.max(-1, Math.min(1, cosAngleRaw));\n \n // Use >= to include exactly 45° as consistent (not trigger reset)\n return cosAngle >= ACCELERATION_CONSTANTS.DIRECTION_THRESHOLD;\n}\n\n/**\n * Calculate acceleration-based speed\n * @param movementTime Accumulated movement time in same direction (seconds)\n * @param walkSpeed - Walking speed in m/s (from archetype or default)\n * @param runSpeed - Running speed in m/s (from archetype or default)\n * @returns Interpolated speed between walk and run (m/s)\n */\nexport function calculateAcceleratedSpeed(\n movementTime: number,\n walkSpeed: number = ACCELERATION_CONSTANTS.DEFAULT_WALK_SPEED,\n runSpeed: number = ACCELERATION_CONSTANTS.DEFAULT_RUN_SPEED\n): number {\n const progress = Math.min(movementTime / ACCELERATION_CONSTANTS.TIME_TO_RUN, 1.0);\n return walkSpeed + (runSpeed - walkSpeed) * progress;\n}\n\n/**\n * Check if speed change is meaningful (exceeds epsilon)\n * @param oldSpeed Previous speed (m/s)\n * @param newSpeed New speed (m/s)\n * @returns True if change exceeds threshold\n */\nexport function isSpeedChangeMeaningful(oldSpeed: number, newSpeed: number): boolean {\n return Math.abs(newSpeed - oldSpeed) >= ACCELERATION_CONSTANTS.SPEED_CHANGE_EPSILON;\n}\n\n/**\n * Determine if player is running based on current speed\n * @param speed Current speed (m/s)\n * @param runSpeed - Maximum running speed (from archetype or default)\n * @returns True if speed exceeds running threshold\n */\nexport function isRunningSpeed(\n speed: number,\n runSpeed: number = ACCELERATION_CONSTANTS.DEFAULT_RUN_SPEED\n): boolean {\n return speed >= calculateRunThreshold(runSpeed);\n}\n"],"mappings":";;;;;;;;;;;;;;;AAeA,IAAa,2BAA2B;;;;;CAKtC,MAAM;;;;;CAMN,KAAK;CACN;;;;AAKD,IAAa,yBAAyB;;CAEpC,oBAAoB;;CAEpB,mBAAmB;;CAEnB,aAAa;;CAEb,qBAAqB,KAAK,IAAI,KAAK,KAAK,EAAE;;CAE1C,uBAAuB;;CAEvB,sBAAsB;CACvB;;;;;;AAOD,SAAgB,sBACd,WAAmB,uBAAuB,mBAClC;CACR,OAAO,WAAW,uBAAuB;;;;;;;;AAS3C,SAAgB,sBACd,YACA,SACS;CAET,IAAI,QAAQ,MAAM,KAAK,QAAQ,MAAM,GACnC,OAAO;CAIT,MAAM,MAAM,WAAW,IAAI,QAAQ,IAAI,WAAW,IAAI,QAAQ;CAC9D,MAAM,aAAa,KAAK,KAAK,WAAW,KAAK,IAAI,WAAW,KAAK,EAAE;CACnE,MAAM,UAAU,KAAK,KAAK,QAAQ,KAAK,IAAI,QAAQ,KAAK,EAAE;CAE1D,IAAI,eAAe,KAAK,YAAY,GAClC,OAAO;CAIT,MAAM,cAAc,OAAO,aAAa;CAIxC,OAHiB,KAAK,IAAI,IAAI,KAAK,IAAI,GAAG,YAAY,CAG/C,IAAY,uBAAuB;;;;;;;;;AAU5C,SAAgB,0BACd,cACA,YAAoB,uBAAuB,oBAC3C,WAAmB,uBAAuB,mBAClC;CACR,MAAM,WAAW,KAAK,IAAI,eAAe,uBAAuB,aAAa,EAAI;CACjF,OAAO,aAAa,WAAW,aAAa;;;;;;;;AAS9C,SAAgB,wBAAwB,UAAkB,UAA2B;CACnF,OAAO,KAAK,IAAI,WAAW,SAAS,IAAI,uBAAuB;;;;;;;;AASjE,SAAgB,eACd,OACA,WAAmB,uBAAuB,mBACjC;CACT,OAAO,SAAS,sBAAsB,SAAS"}
1
+ {"version":3,"file":"accelerationUtils.js","names":[],"sources":["../../../../src/systems/movement/helpers/accelerationUtils.ts"],"sourcesContent":["/**\n * Acceleration Utilities for Movement System\n *\n * Pure functions for calculating acceleration-based running speeds.\n * Extracted for testability and reusability across training and combat screens.\n *\n * @module systems/movement/helpers/accelerationUtils\n * @category Movement\n * @korean 가속 유틸리티\n */\n\n/**\n * Step distance thresholds for foot laterality alternation\n * 발 측면성 교대를 위한 걸음 거리 임계값\n */\nexport const STEP_DISTANCE_THRESHOLDS = {\n /**\n * Average step length when walking (meters)\n * 걷기 시 평균 걸음 길이\n */\n WALK: 0.7,\n\n /**\n * Average step length when running (meters)\n * 달리기 시 평균 걸음 길이\n */\n RUN: 1.0,\n} as const;\n\n/**\n * Constants for acceleration-based running (defaults for non-archetype usage)\n */\nexport const ACCELERATION_CONSTANTS = {\n /** Default walking speed in m/s (when no archetype speed provided) */\n DEFAULT_WALK_SPEED: 6.0,\n /** Default running speed in m/s (when no archetype speed provided) */\n DEFAULT_RUN_SPEED: 10.0,\n /** Time to reach running speed in seconds */\n TIME_TO_RUN: 1.5,\n /** Threshold for considering direction \"same\" (cos(45°) = √2/2) */\n DIRECTION_THRESHOLD: Math.cos(Math.PI / 4),\n /** Running threshold as percentage of max speed (0-1) */\n RUN_THRESHOLD_PERCENT: 0.9,\n /** Epsilon for speed change detection (m/s) */\n SPEED_CHANGE_EPSILON: 0.05,\n} as const;\n\n/**\n * Calculate running threshold speed\n * @param runSpeed - Maximum running speed (from archetype or default)\n * @returns Speed at which movement is considered running (m/s)\n */\nexport function calculateRunThreshold(\n runSpeed: number = ACCELERATION_CONSTANTS.DEFAULT_RUN_SPEED\n): number {\n return runSpeed * ACCELERATION_CONSTANTS.RUN_THRESHOLD_PERCENT;\n}\n\n/**\n * Check if two directions are consistent (within threshold angle)\n * @param currentDir Current direction vector\n * @param lastDir Previous direction vector\n * @returns True if directions are within 45° of each other\n */\nexport function isDirectionConsistent(\n currentDir: { x: number; y: number },\n lastDir: { x: number; y: number }\n): boolean {\n // If last direction is zero, consider any movement as consistent\n if (lastDir.x === 0 && lastDir.y === 0) {\n return true;\n }\n\n // Calculate dot product\n const dot = currentDir.x * lastDir.x + currentDir.y * lastDir.y;\n const magCurrent = Math.sqrt(currentDir.x ** 2 + currentDir.y ** 2);\n const magLast = Math.sqrt(lastDir.x ** 2 + lastDir.y ** 2);\n\n if (magCurrent === 0 || magLast === 0) {\n return false;\n }\n\n // Clamp cosAngle to [-1, 1] to handle floating-point edge cases\n const cosAngleRaw = dot / (magCurrent * magLast);\n const cosAngle = Math.max(-1, Math.min(1, cosAngleRaw));\n \n // Use >= to include exactly 45° as consistent (not trigger reset)\n return cosAngle >= ACCELERATION_CONSTANTS.DIRECTION_THRESHOLD;\n}\n\n/**\n * Calculate acceleration-based speed\n * @param movementTime Accumulated movement time in same direction (seconds)\n * @param walkSpeed - Walking speed in m/s (from archetype or default)\n * @param runSpeed - Running speed in m/s (from archetype or default)\n * @returns Interpolated speed between walk and run (m/s)\n */\nexport function calculateAcceleratedSpeed(\n movementTime: number,\n walkSpeed: number = ACCELERATION_CONSTANTS.DEFAULT_WALK_SPEED,\n runSpeed: number = ACCELERATION_CONSTANTS.DEFAULT_RUN_SPEED\n): number {\n const progress = Math.min(movementTime / ACCELERATION_CONSTANTS.TIME_TO_RUN, 1.0);\n return walkSpeed + (runSpeed - walkSpeed) * progress;\n}\n\n/**\n * Check if speed change is meaningful (exceeds epsilon)\n * @param oldSpeed Previous speed (m/s)\n * @param newSpeed New speed (m/s)\n * @returns True if change exceeds threshold\n */\nexport function isSpeedChangeMeaningful(oldSpeed: number, newSpeed: number): boolean {\n return Math.abs(newSpeed - oldSpeed) >= ACCELERATION_CONSTANTS.SPEED_CHANGE_EPSILON;\n}\n\n/**\n * Determine if player is running based on current speed\n * @param speed Current speed (m/s)\n * @param runSpeed - Maximum running speed (from archetype or default)\n * @returns True if speed exceeds running threshold\n */\nexport function isRunningSpeed(\n speed: number,\n runSpeed: number = ACCELERATION_CONSTANTS.DEFAULT_RUN_SPEED\n): boolean {\n return speed >= calculateRunThreshold(runSpeed);\n}\n"],"mappings":";;;;;;;;;;;;;;;AAeA,IAAa,2BAA2B;;;;;CAKtC,MAAM;;;;;CAMN,KAAK;AACP;;;;AAKA,IAAa,yBAAyB;;CAEpC,oBAAoB;;CAEpB,mBAAmB;;CAEnB,aAAa;;CAEb,qBAAqB,KAAK,IAAI,KAAK,KAAK,CAAC;;CAEzC,uBAAuB;;CAEvB,sBAAsB;AACxB;;;;;;AAOA,SAAgB,sBACd,WAAmB,uBAAuB,mBAClC;CACR,OAAO,WAAW,uBAAuB;AAC3C;;;;;;;AAQA,SAAgB,sBACd,YACA,SACS;CAET,IAAI,QAAQ,MAAM,KAAK,QAAQ,MAAM,GACnC,OAAO;CAIT,MAAM,MAAM,WAAW,IAAI,QAAQ,IAAI,WAAW,IAAI,QAAQ;CAC9D,MAAM,aAAa,KAAK,KAAK,WAAW,KAAK,IAAI,WAAW,KAAK,CAAC;CAClE,MAAM,UAAU,KAAK,KAAK,QAAQ,KAAK,IAAI,QAAQ,KAAK,CAAC;CAEzD,IAAI,eAAe,KAAK,YAAY,GAClC,OAAO;CAIT,MAAM,cAAc,OAAO,aAAa;CAIxC,OAHiB,KAAK,IAAI,IAAI,KAAK,IAAI,GAAG,WAAW,CAG9C,KAAY,uBAAuB;AAC5C;;;;;;;;AASA,SAAgB,0BACd,cACA,YAAoB,uBAAuB,oBAC3C,WAAmB,uBAAuB,mBAClC;CACR,MAAM,WAAW,KAAK,IAAI,eAAe,uBAAuB,aAAa,CAAG;CAChF,OAAO,aAAa,WAAW,aAAa;AAC9C;;;;;;;AAQA,SAAgB,wBAAwB,UAAkB,UAA2B;CACnF,OAAO,KAAK,IAAI,WAAW,QAAQ,KAAK,uBAAuB;AACjE;;;;;;;AAQA,SAAgB,eACd,OACA,WAAmB,uBAAuB,mBACjC;CACT,OAAO,SAAS,sBAAsB,QAAQ;AAChD"}
@@ -1 +1 @@
1
- {"version":3,"file":"integration.js","names":[],"sources":["../../../src/systems/movement/integration.ts"],"sourcesContent":["/**\n * Integration module for InjuryMovementModifier with MovementPhysics\n * \n * **Korean**: 손상 이동 통합 (Injury Movement Integration)\n * \n * Provides helper functions to integrate the InjuryMovementModifier system\n * with the existing MovementPhysics system.\n * \n * @module systems/movement/integration\n * @category Movement System\n * @korean 손상이동통합\n */\n\nimport { injuryMovementModifier } from \"./InjuryMovementModifier\";\nimport type { BodyPartHealth } from \"../bodypart/types\";\nimport { TrigramStance } from \"@/types/common\";\n\n/**\n * Calculate leg injury factor for MovementPhysics from body part health.\n * \n * **Korean**: 다리 손상 요소 계산\n * \n * Converts detailed body part health into a simple 0-1 injury factor\n * that MovementPhysics.MovementState expects. This allows gradual\n * migration from the old simple system to the new detailed system.\n * \n * Uses the **worst** (minimum) leg health to match the main injury system's behavior,\n * ensuring asymmetric injuries (e.g., one leg at 0%, other at 100%) are properly represented.\n * \n * @param bodyPartHealth - Current body part health\n * @returns Leg injury factor (0 = healthy, 1 = fully injured), clamped to 0-1 range\n * \n * @example\n * ```typescript\n * const legInjuryFactor = calculateLegInjuryFactor(bodyPartHealth);\n * movementState.legInjuryFactor = legInjuryFactor;\n * ```\n * \n * @korean 다리손상요소계산\n */\nexport function calculateLegInjuryFactor(\n bodyPartHealth: BodyPartHealth\n): number {\n // Use worst (minimum) leg health to match main injury system behavior\n // This ensures asymmetric injuries are properly represented\n const worstLegHealth = Math.min(bodyPartHealth.legLeft, bodyPartHealth.legRight);\n \n // Clamp worst leg health to valid 0-100 range to avoid invalid injury factors\n const clampedLegHealth = Math.min(100, Math.max(0, worstLegHealth));\n\n // Normalize to 0-1 health ratio (1 = fully healthy, 0 = no health)\n const healthRatio = clampedLegHealth / 100;\n\n // Convert to 0-1 injury factor (0 = healthy, 1 = fully injured)\n // Use inverse of health ratio and clamp defensively to 0-1\n const injuryFactor = 1.0 - healthRatio;\n return Math.min(1, Math.max(0, injuryFactor));\n}\n\n/**\n * Calculate comprehensive movement speed with all injury modifiers.\n * \n * **Korean**: 종합 이동 속도 계산\n * \n * This is the recommended function for full integration with the new\n * injury system. It applies all modifiers including leg injuries,\n * torso damage, stance bonuses, and pain penalties.\n * \n * Uses the shared singleton instance to avoid allocations in per-frame loops.\n * \n * @param baseSpeed - Base movement speed (m/s)\n * @param bodyPartHealth - Current body part health\n * @param stance - Current trigram stance\n * @param painLevel - Current pain level (0-100)\n * @returns Final calculated speed in m/s\n * \n * @example\n * ```typescript\n * const finalSpeed = calculateMovementSpeed(\n * 5.0,\n * playerBodyHealth,\n * TrigramStance.GEON,\n * 65\n * );\n * \n * // Use this speed to override MovementPhysics\n * movementPhysics.setMaxSpeed(finalSpeed);\n * ```\n * \n * @korean 종합이동속도계산\n */\nexport function calculateMovementSpeed(\n baseSpeed: number,\n bodyPartHealth: BodyPartHealth,\n stance: TrigramStance,\n painLevel: number\n): number {\n const result = injuryMovementModifier.calculateMovementSpeed(\n baseSpeed,\n bodyPartHealth,\n stance,\n painLevel\n );\n \n return result.finalSpeed;\n}\n\n/**\n * Calculate speed multiplier from injuries (without stance or pain).\n * \n * **Korean**: 손상 속도 배수 계산\n * \n * Useful when you want to apply injury penalties separately from\n * stance and pain modifiers.\n * \n * Uses the shared singleton instance to avoid allocations in per-frame loops.\n * \n * @param bodyPartHealth - Current body part health\n * @returns Speed multiplier (0.1-1.0)\n * \n * @korean 손상속도배수계산\n */\nexport function calculateInjuryMultiplier(\n bodyPartHealth: BodyPartHealth\n): number {\n // Calculate without stance or pain modifiers\n const result = injuryMovementModifier.calculateMovementSpeed(\n 1.0, // Base speed of 1.0 to get pure multiplier\n bodyPartHealth,\n TrigramStance.GEON, // Neutral stance (1.0x)\n 0 // No pain\n );\n \n // The result will be the pure injury multiplier\n return result.speedMultiplier;\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAwCA,SAAgB,yBACd,gBACQ;CAGR,MAAM,iBAAiB,KAAK,IAAI,eAAe,SAAS,eAAe,SAAS;CAUhF,MAAM,eAAe,IAPI,KAAK,IAAI,KAAK,KAAK,IAAI,GAAG,eAAe,CAG9C,GAAmB;CAKvC,OAAO,KAAK,IAAI,GAAG,KAAK,IAAI,GAAG,aAAa,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAmC/C,SAAgB,uBACd,WACA,gBACA,QACA,WACQ;CAQR,OAPe,uBAAuB,uBACpC,WACA,gBACA,QACA,UAGK,CAAO;;;;;;;;;;;;;;;;;AAkBhB,SAAgB,0BACd,gBACQ;CAUR,OARe,uBAAuB,uBACpC,GACA,gBACA,cAAc,MACd,EAIK,CAAO"}
1
+ {"version":3,"file":"integration.js","names":[],"sources":["../../../src/systems/movement/integration.ts"],"sourcesContent":["/**\n * Integration module for InjuryMovementModifier with MovementPhysics\n * \n * **Korean**: 손상 이동 통합 (Injury Movement Integration)\n * \n * Provides helper functions to integrate the InjuryMovementModifier system\n * with the existing MovementPhysics system.\n * \n * @module systems/movement/integration\n * @category Movement System\n * @korean 손상이동통합\n */\n\nimport { injuryMovementModifier } from \"./InjuryMovementModifier\";\nimport type { BodyPartHealth } from \"../bodypart/types\";\nimport { TrigramStance } from \"@/types/common\";\n\n/**\n * Calculate leg injury factor for MovementPhysics from body part health.\n * \n * **Korean**: 다리 손상 요소 계산\n * \n * Converts detailed body part health into a simple 0-1 injury factor\n * that MovementPhysics.MovementState expects. This allows gradual\n * migration from the old simple system to the new detailed system.\n * \n * Uses the **worst** (minimum) leg health to match the main injury system's behavior,\n * ensuring asymmetric injuries (e.g., one leg at 0%, other at 100%) are properly represented.\n * \n * @param bodyPartHealth - Current body part health\n * @returns Leg injury factor (0 = healthy, 1 = fully injured), clamped to 0-1 range\n * \n * @example\n * ```typescript\n * const legInjuryFactor = calculateLegInjuryFactor(bodyPartHealth);\n * movementState.legInjuryFactor = legInjuryFactor;\n * ```\n * \n * @korean 다리손상요소계산\n */\nexport function calculateLegInjuryFactor(\n bodyPartHealth: BodyPartHealth\n): number {\n // Use worst (minimum) leg health to match main injury system behavior\n // This ensures asymmetric injuries are properly represented\n const worstLegHealth = Math.min(bodyPartHealth.legLeft, bodyPartHealth.legRight);\n \n // Clamp worst leg health to valid 0-100 range to avoid invalid injury factors\n const clampedLegHealth = Math.min(100, Math.max(0, worstLegHealth));\n\n // Normalize to 0-1 health ratio (1 = fully healthy, 0 = no health)\n const healthRatio = clampedLegHealth / 100;\n\n // Convert to 0-1 injury factor (0 = healthy, 1 = fully injured)\n // Use inverse of health ratio and clamp defensively to 0-1\n const injuryFactor = 1.0 - healthRatio;\n return Math.min(1, Math.max(0, injuryFactor));\n}\n\n/**\n * Calculate comprehensive movement speed with all injury modifiers.\n * \n * **Korean**: 종합 이동 속도 계산\n * \n * This is the recommended function for full integration with the new\n * injury system. It applies all modifiers including leg injuries,\n * torso damage, stance bonuses, and pain penalties.\n * \n * Uses the shared singleton instance to avoid allocations in per-frame loops.\n * \n * @param baseSpeed - Base movement speed (m/s)\n * @param bodyPartHealth - Current body part health\n * @param stance - Current trigram stance\n * @param painLevel - Current pain level (0-100)\n * @returns Final calculated speed in m/s\n * \n * @example\n * ```typescript\n * const finalSpeed = calculateMovementSpeed(\n * 5.0,\n * playerBodyHealth,\n * TrigramStance.GEON,\n * 65\n * );\n * \n * // Use this speed to override MovementPhysics\n * movementPhysics.setMaxSpeed(finalSpeed);\n * ```\n * \n * @korean 종합이동속도계산\n */\nexport function calculateMovementSpeed(\n baseSpeed: number,\n bodyPartHealth: BodyPartHealth,\n stance: TrigramStance,\n painLevel: number\n): number {\n const result = injuryMovementModifier.calculateMovementSpeed(\n baseSpeed,\n bodyPartHealth,\n stance,\n painLevel\n );\n \n return result.finalSpeed;\n}\n\n/**\n * Calculate speed multiplier from injuries (without stance or pain).\n * \n * **Korean**: 손상 속도 배수 계산\n * \n * Useful when you want to apply injury penalties separately from\n * stance and pain modifiers.\n * \n * Uses the shared singleton instance to avoid allocations in per-frame loops.\n * \n * @param bodyPartHealth - Current body part health\n * @returns Speed multiplier (0.1-1.0)\n * \n * @korean 손상속도배수계산\n */\nexport function calculateInjuryMultiplier(\n bodyPartHealth: BodyPartHealth\n): number {\n // Calculate without stance or pain modifiers\n const result = injuryMovementModifier.calculateMovementSpeed(\n 1.0, // Base speed of 1.0 to get pure multiplier\n bodyPartHealth,\n TrigramStance.GEON, // Neutral stance (1.0x)\n 0 // No pain\n );\n \n // The result will be the pure injury multiplier\n return result.speedMultiplier;\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAwCA,SAAgB,yBACd,gBACQ;CAGR,MAAM,iBAAiB,KAAK,IAAI,eAAe,SAAS,eAAe,QAAQ;CAU/E,MAAM,eAAe,IAPI,KAAK,IAAI,KAAK,KAAK,IAAI,GAAG,cAAc,CAG7C,IAAmB;CAKvC,OAAO,KAAK,IAAI,GAAG,KAAK,IAAI,GAAG,YAAY,CAAC;AAC9C;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAkCA,SAAgB,uBACd,WACA,gBACA,QACA,WACQ;CAQR,OAPe,uBAAuB,uBACpC,WACA,gBACA,QACA,SAGK,EAAO;AAChB;;;;;;;;;;;;;;;;AAiBA,SAAgB,0BACd,gBACQ;CAUR,OARe,uBAAuB,uBACpC,GACA,gBACA,cAAc,MACd,CAIK,EAAO;AAChB"}
@@ -1 +1 @@
1
- {"version":3,"file":"AttackMovementPhysics.js","names":[],"sources":["../../../src/systems/physics/AttackMovementPhysics.ts"],"sourcesContent":["/**\n * Attack Movement Physics System for realistic forward momentum during attacks.\n *\n * **Korean**: 공격 이동 물리 시스템 (Attack Movement Physics System)\n *\n * Implements realistic forward movement physics when fighters execute attacks.\n * Kicks naturally extend the body forward, punches lunge with body weight,\n * and spinning techniques carry rotational momentum.\n *\n * ## Attack Movement Mechanics\n *\n * Forward movement distance is determined by:\n * - Animation type (kicks > punches > elbows/knees)\n * - Stance modifiers (8 trigram stances affect aggression)\n * - Animation phase timing (extension → peak → recovery)\n *\n * ## Movement Integration\n *\n * - Extension phase (frames 3-6): Forward lunge with ease-out\n * - Peak phase (frame 6): Maximum extension reached\n * - Recovery phase (frames 7-10): Return to original stance\n * - Arena bounds: Automatically clamp movement to valid zone\n *\n * ## Performance\n *\n * Optimized for 60fps with efficient vector calculations and\n * smooth easing curves for realistic attack momentum feel.\n *\n * @module systems/physics/AttackMovementPhysics\n * @category Physics System\n * @korean 공격이동물리\n */\n\nimport * as THREE from \"three\";\nimport { TrigramStance } from \"@/types/common\";\nimport { AnimationType } from \"@/systems/animation\";\n\n/**\n * Configuration for attack movement calculation.\n *\n * **Korean**: 공격 이동 설정 (Attack Movement Configuration)\n *\n * @korean 공격이동설정\n */\nexport interface AttackMovementConfig {\n /** Animation type being executed */\n readonly animationType: AnimationType;\n /** Current trigram stance */\n readonly currentStance: TrigramStance;\n /** Normalized attack direction vector (attacker → target) */\n readonly direction: THREE.Vector3;\n /** Total attack animation duration in seconds */\n readonly animationDuration: number;\n}\n\n/**\n * Result of attack movement calculation.\n *\n * **Korean**: 공격 이동 결과 (Attack Movement Result)\n *\n * Contains displacement vector and timing for attack lunge.\n *\n * @korean 공격이동결과\n */\nexport interface AttackMovementResult {\n /** Total forward displacement vector in world space */\n readonly displacement: THREE.Vector3;\n /** Duration of forward lunge phase in seconds */\n readonly lungeDuration: number;\n /** Duration of recovery return phase in seconds */\n readonly recoveryDuration: number;\n /** Total movement cycle duration in seconds */\n readonly totalDuration: number;\n}\n\n/**\n * Attack Movement Physics Engine.\n *\n * **Korean**: 공격 이동 물리 엔진\n *\n * Calculates realistic forward movement during attack animations\n * based on technique type, stance modifiers, and animation timing.\n * Provides smooth lunge and recovery phases for authentic martial arts feel.\n *\n * @example\n * ```typescript\n * const physics = new AttackMovementPhysics();\n *\n * // Calculate movement for roundhouse kick\n * const config: AttackMovementConfig = {\n * animationType: AnimationType.ROUNDHOUSE_KICK,\n * currentStance: TrigramStance.LI, // Fire stance (aggressive)\n * direction: new THREE.Vector3(1, 0, 0).normalize(),\n * animationDuration: 0.48, // 480ms kick animation\n * };\n *\n * const result = physics.calculateAttackMovement(config);\n * // Result: 1.0m base * 1.3 (Fire bonus) = ~1.3m forward lunge\n * // lungeDuration: 0.24s (50% of animation)\n * // recoveryDuration: 0.24s (50% of animation)\n * ```\n *\n * @korean 공격이동물리\n */\nexport class AttackMovementPhysics {\n /**\n * Calculates attack movement displacement and timing.\n *\n * **Korean**: 공격 이동 계산 (Calculate Attack Movement)\n *\n * Determines forward lunge distance and recovery timing based on:\n * 1. Base movement from animation type\n * 2. Stance movement modifier (8 trigram effects)\n * 3. Animation phase durations (lunge vs recovery)\n *\n * @param config - Attack movement configuration\n * @returns Attack movement result with displacement and timing\n *\n * @example\n * ```typescript\n * // Front kick with Heaven stance\n * const kick = physics.calculateAttackMovement({\n * animationType: AnimationType.FRONT_KICK,\n * currentStance: TrigramStance.GEON,\n * direction: attackVector,\n * animationDuration: 0.4,\n * });\n * // Result: ~0.88m forward (0.8m * 1.1 Geon modifier)\n *\n * // Jab with Mountain stance\n * const jab = physics.calculateAttackMovement({\n * animationType: AnimationType.JAB,\n * currentStance: TrigramStance.GAN,\n * direction: attackVector,\n * animationDuration: 0.25,\n * });\n * // Result: ~0.24m forward (0.3m * 0.8 Gan modifier)\n * ```\n *\n * @korean 공격이동계산\n */\n calculateAttackMovement(\n config: AttackMovementConfig\n ): AttackMovementResult {\n // 1. Get base movement distance for animation type\n const baseDistance = this.getBaseMovementDistance(config.animationType);\n\n // 2. Apply stance movement modifier\n const stanceModifier = this.getStanceMovementModifier(config.currentStance);\n const finalDistance = baseDistance * stanceModifier;\n\n // 3. Calculate displacement vector\n const displacement = config.direction.clone().multiplyScalar(finalDistance);\n\n // 4. Calculate phase durations (lunge + recovery)\n const lungeDuration = config.animationDuration * 0.5; // First 50% = forward\n const recoveryDuration = config.animationDuration * 0.5; // Last 50% = return\n\n return {\n displacement,\n lungeDuration,\n recoveryDuration,\n totalDuration: config.animationDuration,\n };\n }\n\n /**\n * Gets base forward movement distance for animation type.\n *\n * **Korean**: 기본 이동 거리 (Base Movement Distance)\n *\n * Movement distances by technique category:\n * - Kicks: 0.8-1.2m (leg extension, longest reach)\n * - Punches: 0.3-0.5m (arm extension, body lunge)\n * - Elbows/Knees: 0.2-0.3m (close range techniques)\n * - Spinning: 0.5-0.8m (rotation carries momentum)\n *\n * @param animationType - Type of attack animation\n * @returns Base movement distance in meters\n *\n * @private\n * @korean 기본이동거리\n */\n private getBaseMovementDistance(animationType: AnimationType): number {\n // Kicks - highest forward movement (leg extension)\n if (\n animationType === AnimationType.ROUNDHOUSE_KICK ||\n animationType === AnimationType.SIDE_KICK ||\n animationType === AnimationType.BACK_KICK\n ) {\n return 1.0; // 1.0m forward lunge\n }\n\n if (\n animationType === AnimationType.FRONT_KICK ||\n animationType === AnimationType.PUSH_KICK\n ) {\n return 0.8; // 0.8m forward lunge\n }\n\n if (\n animationType === AnimationType.AXE_KICK ||\n animationType === AnimationType.CRESCENT_KICK ||\n animationType === AnimationType.LOW_KICK\n ) {\n return 0.9; // 0.9m forward lunge\n }\n\n // Spinning kicks - momentum carries body forward\n if (\n animationType === AnimationType.SPINNING_HOOK ||\n animationType === AnimationType.SPINNING_HEEL_KICK ||\n animationType === AnimationType.TORNADO_KICK\n ) {\n return 0.8; // 0.8m with rotation\n }\n\n // Jumping/flying kicks - aerial momentum\n if (\n animationType === AnimationType.FLYING_KICK ||\n animationType === AnimationType.JUMPING_KICK\n ) {\n return 1.2; // 1.2m maximum extension\n }\n\n // Punches - moderate forward movement (arm + body lunge)\n if (\n animationType === AnimationType.CROSS ||\n animationType === AnimationType.OVERHAND ||\n animationType === AnimationType.HOOK\n ) {\n return 0.5; // 0.5m forward lunge\n }\n\n if (\n animationType === AnimationType.JAB ||\n animationType === AnimationType.BACKFIST\n ) {\n return 0.3; // 0.3m forward lunge\n }\n\n if (\n animationType === AnimationType.UPPERCUT ||\n animationType === AnimationType.HAMMER_FIST ||\n animationType === AnimationType.PALM_STRIKE\n ) {\n return 0.4; // 0.4m forward lunge\n }\n\n // Elbow/Knee - minimal movement (close range)\n if (\n animationType === AnimationType.ELBOW_STRIKE ||\n animationType === AnimationType.KNEE_STRIKE ||\n animationType === AnimationType.KNEE_KICK ||\n animationType === AnimationType.ELBOW_UPPERCUT ||\n animationType === AnimationType.FLYING_KNEE ||\n animationType === AnimationType.CLINCH_KNEE ||\n animationType === AnimationType.SPINNING_ELBOW ||\n animationType === AnimationType.TEMPLE_ELBOW ||\n animationType === AnimationType.SPINNING_BACK_ELBOW ||\n animationType === AnimationType.SPINAL_ELBOW ||\n animationType === AnimationType.BRACHIAL_ELBOW ||\n animationType === AnimationType.KIDNEY_KNEE ||\n animationType === AnimationType.FEMORAL_KNEE\n ) {\n return 0.2; // 0.2m forward lunge\n }\n\n // Specialized jab variants - similar to jab (quick extension)\n if (\n animationType === AnimationType.SPEAR_HAND_STRIKE ||\n animationType === AnimationType.NERVE_STRIKE ||\n animationType === AnimationType.PRESSURE_POINT_STRIKE ||\n animationType === AnimationType.LIGHTNING_STRIKE ||\n animationType === AnimationType.RAPID_BARRAGE ||\n animationType === AnimationType.RHYTHMIC_STRIKES ||\n animationType === AnimationType.NERVE_PARALYSIS ||\n animationType === AnimationType.THROAT_STRIKE ||\n animationType === AnimationType.EYE_GOUGE\n ) {\n return 0.3; // 0.3m forward lunge (jab-like)\n }\n\n // Specialized cross variants - similar to cross (body weight transfer)\n if (\n animationType === AnimationType.HEAVEN_STRIKE ||\n animationType === AnimationType.FLOWING_CROSS\n ) {\n return 0.5; // 0.5m forward lunge (cross-like)\n }\n\n // Specialized palm strikes - similar to palm strike\n if (\n animationType === AnimationType.SOLAR_PLEXUS_STRIKE ||\n animationType === AnimationType.FLOWING_PUSH ||\n animationType === AnimationType.LIVER_DISRUPTION ||\n animationType === AnimationType.EAR_STRIKE\n ) {\n return 0.4; // 0.4m forward lunge (palm-like)\n }\n\n // Grappling - minimal forward movement (rotation/off-balancing in place)\n if (\n animationType === AnimationType.THROW ||\n animationType === AnimationType.JOINT_LOCK ||\n animationType === AnimationType.TAKEDOWN ||\n animationType === AnimationType.SWEEP ||\n animationType === AnimationType.CLINCH ||\n animationType === AnimationType.GRAPPLE ||\n animationType === AnimationType.SLAM ||\n animationType === AnimationType.WRIST_LOCK ||\n animationType === AnimationType.ARM_BAR ||\n animationType === AnimationType.SHOULDER_LOCK ||\n animationType === AnimationType.HIP_THROW ||\n animationType === AnimationType.LEG_REAP ||\n animationType === AnimationType.SMALL_CIRCLE_LOCK ||\n animationType === AnimationType.FINGER_LOCK ||\n animationType === AnimationType.ELBOW_LOCK ||\n animationType === AnimationType.SHOULDER_MANIPULATION ||\n animationType === AnimationType.MOUNTAIN_LOCK ||\n animationType === AnimationType.EARTH_EMBRACE ||\n animationType === AnimationType.CAROTID_CHOKE\n ) {\n return 0.05; // 0.05m forward movement for grappling entries\n }\n\n // Default - conservative minimal movement for any unmapped types\n // Additional AnimationType values can be grouped with the closest category above\n // to fine-tune their forward displacement without changing overall behavior\n return 0.2;\n }\n\n /**\n * Gets stance movement modifier for attack lunge.\n *\n * **Korean**: 자세 이동 배율 (Stance Movement Modifier)\n *\n * Trigram stance movement modifiers based on combat philosophy:\n * - ☰ 건 (Geon/Heaven): +30% movement (aggressive, drives forward with pure yang)\n * - ☳ 진 (Jin/Thunder): +20% movement (explosive power, shocking force)\n * - ☴ 손 (Son/Wind): +15% movement (continuous flow, mobile)\n * - ☲ 리 (Li/Fire): +10% movement (precision strikes, controlled aggression)\n * - ☱ 태 (Tae/Lake): 0% (neutral, fluid and adaptive)\n * - ☵ 감 (Gam/Water): 0% (neutral, flowing and flexible)\n * - ☷ 곤 (Gon/Earth): -10% movement (grounded, stable techniques)\n * - ☶ 간 (Gan/Mountain): -20% movement (defensive mastery, minimal advance)\n *\n * @param stance - Current trigram stance\n * @returns Movement multiplier (0.8 to 1.3)\n *\n * @private\n * @korean 자세이동배율\n */\n private getStanceMovementModifier(stance: TrigramStance): number {\n const modifiers: Record<TrigramStance, number> = {\n [TrigramStance.GEON]: 1.3, // Heaven: +30% aggressive forward pressure (pure yang drives forward)\n [TrigramStance.JIN]: 1.2, // Thunder: +20% explosive movement\n [TrigramStance.SON]: 1.15, // Wind: +15% flowing movement\n [TrigramStance.LI]: 1.1, // Fire: +10% precision strikes with controlled forward movement\n [TrigramStance.TAE]: 1.0, // Lake: neutral movement\n [TrigramStance.GAM]: 1.0, // Water: neutral movement\n [TrigramStance.GON]: 0.9, // Earth: -10% grounded movement\n [TrigramStance.GAN]: 0.8, // Mountain: -20% defensive movement\n };\n return modifiers[stance];\n }\n\n /**\n * Applies attack movement force to attacker position over time.\n *\n * **Korean**: 공격 이동 적용 (Apply Attack Movement)\n *\n * Uses smooth ease-out cubic curve for realistic lunge feel:\n * - Fast initial forward movement (attack commitment)\n * - Gradual deceleration at peak extension\n * - Smooth return during recovery phase\n *\n * @param basePosition - Original stance position (does not change during attack)\n * @param result - Attack movement result with displacement\n * @param elapsedTime - Time elapsed since attack started\n * @param isRecoveryPhase - Whether in recovery (return) phase\n * @returns New position with attack displacement applied\n *\n * @example\n * ```typescript\n * // In animation update loop (60fps)\n * const basePos = new THREE.Vector3(0, 0, 0); // Original stance position\n * \n * if (elapsedTime < result.lungeDuration) {\n * // Lunge forward phase\n * const newPosition = physics.applyAttackMovement(\n * basePos,\n * result,\n * elapsedTime,\n * false\n * );\n * } else if (elapsedTime < result.totalDuration) {\n * // Recovery return phase\n * const newPosition = physics.applyAttackMovement(\n * basePos,\n * result,\n * elapsedTime,\n * true\n * );\n * }\n * ```\n *\n * @korean 공격이동적용\n */\n applyAttackMovement(\n basePosition: THREE.Vector3,\n result: AttackMovementResult,\n elapsedTime: number,\n isRecoveryPhase: boolean\n ): THREE.Vector3 {\n let progress: number;\n\n if (!isRecoveryPhase) {\n // Lunge forward phase\n progress = Math.min(1.0, elapsedTime / result.lungeDuration);\n\n // Smooth forward lunge curve (ease-out cubic)\n // Fast initial commitment, gradual deceleration at peak\n const easedProgress = 1 - Math.pow(1 - progress, 3);\n\n // Calculate position along forward path\n const currentDisplacement = result.displacement\n .clone()\n .multiplyScalar(easedProgress);\n\n return basePosition.clone().add(currentDisplacement);\n } else {\n // Recovery return phase\n const recoveryTime = elapsedTime - result.lungeDuration;\n progress = Math.min(1.0, recoveryTime / result.recoveryDuration);\n\n // Smooth return curve (ease-in cubic)\n // Gradual start, faster finish back to stance\n const easedProgress = Math.pow(progress, 3);\n\n // Calculate return position (from peak back to origin)\n const returnDisplacement = result.displacement\n .clone()\n .multiplyScalar(1.0 - easedProgress);\n\n return basePosition.clone().add(returnDisplacement);\n }\n }\n\n /**\n * Checks if attacker is currently in lunge phase.\n *\n * **Korean**: 돌진 상태 확인 (Check Lunge State)\n *\n * @param elapsedTime - Time elapsed since attack started\n * @param lungeDuration - Total lunge phase duration\n * @returns True if in forward lunge phase\n *\n * @korean 돌진상태확인\n */\n isInLungePhase(elapsedTime: number, lungeDuration: number): boolean {\n return elapsedTime < lungeDuration;\n }\n\n /**\n * Checks if attacker is in recovery return phase.\n *\n * **Korean**: 회복 상태 확인 (Check Recovery State)\n *\n * @param elapsedTime - Time elapsed since attack started\n * @param result - Attack movement result with timing\n * @returns True if in recovery return phase\n *\n * @korean 회복상태확인\n */\n isInRecoveryPhase(\n elapsedTime: number,\n result: AttackMovementResult\n ): boolean {\n return (\n elapsedTime >= result.lungeDuration &&\n elapsedTime < result.totalDuration\n );\n }\n\n /**\n * Gets bilingual Korean-English name for lunge phase.\n *\n * **Korean**: 돌진 단계 이름 (Lunge Phase Name)\n *\n * @returns Korean and English phase names\n *\n * @korean 돌진단계이름\n */\n getLungePhaseName(): { korean: string; english: string } {\n return {\n korean: \"돌진\",\n english: \"Lunge\",\n };\n }\n\n /**\n * Gets bilingual Korean-English name for recovery phase.\n *\n * **Korean**: 복귀 단계 이름 (Recovery Phase Name)\n *\n * @returns Korean and English phase names\n *\n * @korean 복귀단계이름\n */\n getRecoveryPhaseName(): { korean: string; english: string } {\n return {\n korean: \"복귀\",\n english: \"Recovery\",\n };\n }\n}\n\nexport default AttackMovementPhysics;\n"],"mappings":"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"}
1
+ {"version":3,"file":"AttackMovementPhysics.js","names":[],"sources":["../../../src/systems/physics/AttackMovementPhysics.ts"],"sourcesContent":["/**\n * Attack Movement Physics System for realistic forward momentum during attacks.\n *\n * **Korean**: 공격 이동 물리 시스템 (Attack Movement Physics System)\n *\n * Implements realistic forward movement physics when fighters execute attacks.\n * Kicks naturally extend the body forward, punches lunge with body weight,\n * and spinning techniques carry rotational momentum.\n *\n * ## Attack Movement Mechanics\n *\n * Forward movement distance is determined by:\n * - Animation type (kicks > punches > elbows/knees)\n * - Stance modifiers (8 trigram stances affect aggression)\n * - Animation phase timing (extension → peak → recovery)\n *\n * ## Movement Integration\n *\n * - Extension phase (frames 3-6): Forward lunge with ease-out\n * - Peak phase (frame 6): Maximum extension reached\n * - Recovery phase (frames 7-10): Return to original stance\n * - Arena bounds: Automatically clamp movement to valid zone\n *\n * ## Performance\n *\n * Optimized for 60fps with efficient vector calculations and\n * smooth easing curves for realistic attack momentum feel.\n *\n * @module systems/physics/AttackMovementPhysics\n * @category Physics System\n * @korean 공격이동물리\n */\n\nimport * as THREE from \"three\";\nimport { TrigramStance } from \"@/types/common\";\nimport { AnimationType } from \"@/systems/animation\";\n\n/**\n * Configuration for attack movement calculation.\n *\n * **Korean**: 공격 이동 설정 (Attack Movement Configuration)\n *\n * @korean 공격이동설정\n */\nexport interface AttackMovementConfig {\n /** Animation type being executed */\n readonly animationType: AnimationType;\n /** Current trigram stance */\n readonly currentStance: TrigramStance;\n /** Normalized attack direction vector (attacker → target) */\n readonly direction: THREE.Vector3;\n /** Total attack animation duration in seconds */\n readonly animationDuration: number;\n}\n\n/**\n * Result of attack movement calculation.\n *\n * **Korean**: 공격 이동 결과 (Attack Movement Result)\n *\n * Contains displacement vector and timing for attack lunge.\n *\n * @korean 공격이동결과\n */\nexport interface AttackMovementResult {\n /** Total forward displacement vector in world space */\n readonly displacement: THREE.Vector3;\n /** Duration of forward lunge phase in seconds */\n readonly lungeDuration: number;\n /** Duration of recovery return phase in seconds */\n readonly recoveryDuration: number;\n /** Total movement cycle duration in seconds */\n readonly totalDuration: number;\n}\n\n/**\n * Attack Movement Physics Engine.\n *\n * **Korean**: 공격 이동 물리 엔진\n *\n * Calculates realistic forward movement during attack animations\n * based on technique type, stance modifiers, and animation timing.\n * Provides smooth lunge and recovery phases for authentic martial arts feel.\n *\n * @example\n * ```typescript\n * const physics = new AttackMovementPhysics();\n *\n * // Calculate movement for roundhouse kick\n * const config: AttackMovementConfig = {\n * animationType: AnimationType.ROUNDHOUSE_KICK,\n * currentStance: TrigramStance.LI, // Fire stance (aggressive)\n * direction: new THREE.Vector3(1, 0, 0).normalize(),\n * animationDuration: 0.48, // 480ms kick animation\n * };\n *\n * const result = physics.calculateAttackMovement(config);\n * // Result: 1.0m base * 1.3 (Fire bonus) = ~1.3m forward lunge\n * // lungeDuration: 0.24s (50% of animation)\n * // recoveryDuration: 0.24s (50% of animation)\n * ```\n *\n * @korean 공격이동물리\n */\nexport class AttackMovementPhysics {\n /**\n * Calculates attack movement displacement and timing.\n *\n * **Korean**: 공격 이동 계산 (Calculate Attack Movement)\n *\n * Determines forward lunge distance and recovery timing based on:\n * 1. Base movement from animation type\n * 2. Stance movement modifier (8 trigram effects)\n * 3. Animation phase durations (lunge vs recovery)\n *\n * @param config - Attack movement configuration\n * @returns Attack movement result with displacement and timing\n *\n * @example\n * ```typescript\n * // Front kick with Heaven stance\n * const kick = physics.calculateAttackMovement({\n * animationType: AnimationType.FRONT_KICK,\n * currentStance: TrigramStance.GEON,\n * direction: attackVector,\n * animationDuration: 0.4,\n * });\n * // Result: ~0.88m forward (0.8m * 1.1 Geon modifier)\n *\n * // Jab with Mountain stance\n * const jab = physics.calculateAttackMovement({\n * animationType: AnimationType.JAB,\n * currentStance: TrigramStance.GAN,\n * direction: attackVector,\n * animationDuration: 0.25,\n * });\n * // Result: ~0.24m forward (0.3m * 0.8 Gan modifier)\n * ```\n *\n * @korean 공격이동계산\n */\n calculateAttackMovement(\n config: AttackMovementConfig\n ): AttackMovementResult {\n // 1. Get base movement distance for animation type\n const baseDistance = this.getBaseMovementDistance(config.animationType);\n\n // 2. Apply stance movement modifier\n const stanceModifier = this.getStanceMovementModifier(config.currentStance);\n const finalDistance = baseDistance * stanceModifier;\n\n // 3. Calculate displacement vector\n const displacement = config.direction.clone().multiplyScalar(finalDistance);\n\n // 4. Calculate phase durations (lunge + recovery)\n const lungeDuration = config.animationDuration * 0.5; // First 50% = forward\n const recoveryDuration = config.animationDuration * 0.5; // Last 50% = return\n\n return {\n displacement,\n lungeDuration,\n recoveryDuration,\n totalDuration: config.animationDuration,\n };\n }\n\n /**\n * Gets base forward movement distance for animation type.\n *\n * **Korean**: 기본 이동 거리 (Base Movement Distance)\n *\n * Movement distances by technique category:\n * - Kicks: 0.8-1.2m (leg extension, longest reach)\n * - Punches: 0.3-0.5m (arm extension, body lunge)\n * - Elbows/Knees: 0.2-0.3m (close range techniques)\n * - Spinning: 0.5-0.8m (rotation carries momentum)\n *\n * @param animationType - Type of attack animation\n * @returns Base movement distance in meters\n *\n * @private\n * @korean 기본이동거리\n */\n private getBaseMovementDistance(animationType: AnimationType): number {\n // Kicks - highest forward movement (leg extension)\n if (\n animationType === AnimationType.ROUNDHOUSE_KICK ||\n animationType === AnimationType.SIDE_KICK ||\n animationType === AnimationType.BACK_KICK\n ) {\n return 1.0; // 1.0m forward lunge\n }\n\n if (\n animationType === AnimationType.FRONT_KICK ||\n animationType === AnimationType.PUSH_KICK\n ) {\n return 0.8; // 0.8m forward lunge\n }\n\n if (\n animationType === AnimationType.AXE_KICK ||\n animationType === AnimationType.CRESCENT_KICK ||\n animationType === AnimationType.LOW_KICK\n ) {\n return 0.9; // 0.9m forward lunge\n }\n\n // Spinning kicks - momentum carries body forward\n if (\n animationType === AnimationType.SPINNING_HOOK ||\n animationType === AnimationType.SPINNING_HEEL_KICK ||\n animationType === AnimationType.TORNADO_KICK\n ) {\n return 0.8; // 0.8m with rotation\n }\n\n // Jumping/flying kicks - aerial momentum\n if (\n animationType === AnimationType.FLYING_KICK ||\n animationType === AnimationType.JUMPING_KICK\n ) {\n return 1.2; // 1.2m maximum extension\n }\n\n // Punches - moderate forward movement (arm + body lunge)\n if (\n animationType === AnimationType.CROSS ||\n animationType === AnimationType.OVERHAND ||\n animationType === AnimationType.HOOK\n ) {\n return 0.5; // 0.5m forward lunge\n }\n\n if (\n animationType === AnimationType.JAB ||\n animationType === AnimationType.BACKFIST\n ) {\n return 0.3; // 0.3m forward lunge\n }\n\n if (\n animationType === AnimationType.UPPERCUT ||\n animationType === AnimationType.HAMMER_FIST ||\n animationType === AnimationType.PALM_STRIKE\n ) {\n return 0.4; // 0.4m forward lunge\n }\n\n // Elbow/Knee - minimal movement (close range)\n if (\n animationType === AnimationType.ELBOW_STRIKE ||\n animationType === AnimationType.KNEE_STRIKE ||\n animationType === AnimationType.KNEE_KICK ||\n animationType === AnimationType.ELBOW_UPPERCUT ||\n animationType === AnimationType.FLYING_KNEE ||\n animationType === AnimationType.CLINCH_KNEE ||\n animationType === AnimationType.SPINNING_ELBOW ||\n animationType === AnimationType.TEMPLE_ELBOW ||\n animationType === AnimationType.SPINNING_BACK_ELBOW ||\n animationType === AnimationType.SPINAL_ELBOW ||\n animationType === AnimationType.BRACHIAL_ELBOW ||\n animationType === AnimationType.KIDNEY_KNEE ||\n animationType === AnimationType.FEMORAL_KNEE\n ) {\n return 0.2; // 0.2m forward lunge\n }\n\n // Specialized jab variants - similar to jab (quick extension)\n if (\n animationType === AnimationType.SPEAR_HAND_STRIKE ||\n animationType === AnimationType.NERVE_STRIKE ||\n animationType === AnimationType.PRESSURE_POINT_STRIKE ||\n animationType === AnimationType.LIGHTNING_STRIKE ||\n animationType === AnimationType.RAPID_BARRAGE ||\n animationType === AnimationType.RHYTHMIC_STRIKES ||\n animationType === AnimationType.NERVE_PARALYSIS ||\n animationType === AnimationType.THROAT_STRIKE ||\n animationType === AnimationType.EYE_GOUGE\n ) {\n return 0.3; // 0.3m forward lunge (jab-like)\n }\n\n // Specialized cross variants - similar to cross (body weight transfer)\n if (\n animationType === AnimationType.HEAVEN_STRIKE ||\n animationType === AnimationType.FLOWING_CROSS\n ) {\n return 0.5; // 0.5m forward lunge (cross-like)\n }\n\n // Specialized palm strikes - similar to palm strike\n if (\n animationType === AnimationType.SOLAR_PLEXUS_STRIKE ||\n animationType === AnimationType.FLOWING_PUSH ||\n animationType === AnimationType.LIVER_DISRUPTION ||\n animationType === AnimationType.EAR_STRIKE\n ) {\n return 0.4; // 0.4m forward lunge (palm-like)\n }\n\n // Grappling - minimal forward movement (rotation/off-balancing in place)\n if (\n animationType === AnimationType.THROW ||\n animationType === AnimationType.JOINT_LOCK ||\n animationType === AnimationType.TAKEDOWN ||\n animationType === AnimationType.SWEEP ||\n animationType === AnimationType.CLINCH ||\n animationType === AnimationType.GRAPPLE ||\n animationType === AnimationType.SLAM ||\n animationType === AnimationType.WRIST_LOCK ||\n animationType === AnimationType.ARM_BAR ||\n animationType === AnimationType.SHOULDER_LOCK ||\n animationType === AnimationType.HIP_THROW ||\n animationType === AnimationType.LEG_REAP ||\n animationType === AnimationType.SMALL_CIRCLE_LOCK ||\n animationType === AnimationType.FINGER_LOCK ||\n animationType === AnimationType.ELBOW_LOCK ||\n animationType === AnimationType.SHOULDER_MANIPULATION ||\n animationType === AnimationType.MOUNTAIN_LOCK ||\n animationType === AnimationType.EARTH_EMBRACE ||\n animationType === AnimationType.CAROTID_CHOKE\n ) {\n return 0.05; // 0.05m forward movement for grappling entries\n }\n\n // Default - conservative minimal movement for any unmapped types\n // Additional AnimationType values can be grouped with the closest category above\n // to fine-tune their forward displacement without changing overall behavior\n return 0.2;\n }\n\n /**\n * Gets stance movement modifier for attack lunge.\n *\n * **Korean**: 자세 이동 배율 (Stance Movement Modifier)\n *\n * Trigram stance movement modifiers based on combat philosophy:\n * - ☰ 건 (Geon/Heaven): +30% movement (aggressive, drives forward with pure yang)\n * - ☳ 진 (Jin/Thunder): +20% movement (explosive power, shocking force)\n * - ☴ 손 (Son/Wind): +15% movement (continuous flow, mobile)\n * - ☲ 리 (Li/Fire): +10% movement (precision strikes, controlled aggression)\n * - ☱ 태 (Tae/Lake): 0% (neutral, fluid and adaptive)\n * - ☵ 감 (Gam/Water): 0% (neutral, flowing and flexible)\n * - ☷ 곤 (Gon/Earth): -10% movement (grounded, stable techniques)\n * - ☶ 간 (Gan/Mountain): -20% movement (defensive mastery, minimal advance)\n *\n * @param stance - Current trigram stance\n * @returns Movement multiplier (0.8 to 1.3)\n *\n * @private\n * @korean 자세이동배율\n */\n private getStanceMovementModifier(stance: TrigramStance): number {\n const modifiers: Record<TrigramStance, number> = {\n [TrigramStance.GEON]: 1.3, // Heaven: +30% aggressive forward pressure (pure yang drives forward)\n [TrigramStance.JIN]: 1.2, // Thunder: +20% explosive movement\n [TrigramStance.SON]: 1.15, // Wind: +15% flowing movement\n [TrigramStance.LI]: 1.1, // Fire: +10% precision strikes with controlled forward movement\n [TrigramStance.TAE]: 1.0, // Lake: neutral movement\n [TrigramStance.GAM]: 1.0, // Water: neutral movement\n [TrigramStance.GON]: 0.9, // Earth: -10% grounded movement\n [TrigramStance.GAN]: 0.8, // Mountain: -20% defensive movement\n };\n return modifiers[stance];\n }\n\n /**\n * Applies attack movement force to attacker position over time.\n *\n * **Korean**: 공격 이동 적용 (Apply Attack Movement)\n *\n * Uses smooth ease-out cubic curve for realistic lunge feel:\n * - Fast initial forward movement (attack commitment)\n * - Gradual deceleration at peak extension\n * - Smooth return during recovery phase\n *\n * @param basePosition - Original stance position (does not change during attack)\n * @param result - Attack movement result with displacement\n * @param elapsedTime - Time elapsed since attack started\n * @param isRecoveryPhase - Whether in recovery (return) phase\n * @returns New position with attack displacement applied\n *\n * @example\n * ```typescript\n * // In animation update loop (60fps)\n * const basePos = new THREE.Vector3(0, 0, 0); // Original stance position\n * \n * if (elapsedTime < result.lungeDuration) {\n * // Lunge forward phase\n * const newPosition = physics.applyAttackMovement(\n * basePos,\n * result,\n * elapsedTime,\n * false\n * );\n * } else if (elapsedTime < result.totalDuration) {\n * // Recovery return phase\n * const newPosition = physics.applyAttackMovement(\n * basePos,\n * result,\n * elapsedTime,\n * true\n * );\n * }\n * ```\n *\n * @korean 공격이동적용\n */\n applyAttackMovement(\n basePosition: THREE.Vector3,\n result: AttackMovementResult,\n elapsedTime: number,\n isRecoveryPhase: boolean\n ): THREE.Vector3 {\n let progress: number;\n\n if (!isRecoveryPhase) {\n // Lunge forward phase\n progress = Math.min(1.0, elapsedTime / result.lungeDuration);\n\n // Smooth forward lunge curve (ease-out cubic)\n // Fast initial commitment, gradual deceleration at peak\n const easedProgress = 1 - Math.pow(1 - progress, 3);\n\n // Calculate position along forward path\n const currentDisplacement = result.displacement\n .clone()\n .multiplyScalar(easedProgress);\n\n return basePosition.clone().add(currentDisplacement);\n } else {\n // Recovery return phase\n const recoveryTime = elapsedTime - result.lungeDuration;\n progress = Math.min(1.0, recoveryTime / result.recoveryDuration);\n\n // Smooth return curve (ease-in cubic)\n // Gradual start, faster finish back to stance\n const easedProgress = Math.pow(progress, 3);\n\n // Calculate return position (from peak back to origin)\n const returnDisplacement = result.displacement\n .clone()\n .multiplyScalar(1.0 - easedProgress);\n\n return basePosition.clone().add(returnDisplacement);\n }\n }\n\n /**\n * Checks if attacker is currently in lunge phase.\n *\n * **Korean**: 돌진 상태 확인 (Check Lunge State)\n *\n * @param elapsedTime - Time elapsed since attack started\n * @param lungeDuration - Total lunge phase duration\n * @returns True if in forward lunge phase\n *\n * @korean 돌진상태확인\n */\n isInLungePhase(elapsedTime: number, lungeDuration: number): boolean {\n return elapsedTime < lungeDuration;\n }\n\n /**\n * Checks if attacker is in recovery return phase.\n *\n * **Korean**: 회복 상태 확인 (Check Recovery State)\n *\n * @param elapsedTime - Time elapsed since attack started\n * @param result - Attack movement result with timing\n * @returns True if in recovery return phase\n *\n * @korean 회복상태확인\n */\n isInRecoveryPhase(\n elapsedTime: number,\n result: AttackMovementResult\n ): boolean {\n return (\n elapsedTime >= result.lungeDuration &&\n elapsedTime < result.totalDuration\n );\n }\n\n /**\n * Gets bilingual Korean-English name for lunge phase.\n *\n * **Korean**: 돌진 단계 이름 (Lunge Phase Name)\n *\n * @returns Korean and English phase names\n *\n * @korean 돌진단계이름\n */\n getLungePhaseName(): { korean: string; english: string } {\n return {\n korean: \"돌진\",\n english: \"Lunge\",\n };\n }\n\n /**\n * Gets bilingual Korean-English name for recovery phase.\n *\n * **Korean**: 복귀 단계 이름 (Recovery Phase Name)\n *\n * @returns Korean and English phase names\n *\n * @korean 복귀단계이름\n */\n getRecoveryPhaseName(): { korean: string; english: string } {\n return {\n korean: \"복귀\",\n english: \"Recovery\",\n };\n }\n}\n\nexport default AttackMovementPhysics;\n"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"CollisionDetection.js","names":[],"sources":["../../../src/systems/physics/CollisionDetection.ts"],"sourcesContent":["/**\n * Collision Detection System for Black Trigram combat physics.\n *\n * **Korean**: 충돌 감지 시스템\n *\n * Implements precise collision detection for the 70 vital points combat system using:\n * - Broad-phase AABB (Axis-Aligned Bounding Box) checks for performance\n * - Narrow-phase raycasting for precise vital point detection\n * - Attack reach validation based on technique and stance\n * - Bounding boxes for 5 anatomical regions\n *\n * ## Architecture\n *\n * The system uses a two-phase collision detection approach:\n *\n * 1. **Broad-phase**: Fast AABB checks to eliminate impossible collisions\n * 2. **Narrow-phase**: Precise raycasting to identify specific vital points\n *\n * This ensures 60fps performance even with complex collision queries.\n *\n * ## Performance\n *\n * - Broad-phase: O(1) per check (simple distance comparison)\n * - Narrow-phase: O(n) where n = vital points in region (typically 8-20)\n * - Target: <100 collision checks per frame for 60fps\n *\n * @example\n * ```typescript\n * const collision = new CollisionDetection();\n *\n * const result = collision.checkAttackHit(\n * { x: 0, y: 0, z: 5 }, // Attacker position\n * { x: 0, y: 0, z: 6.5 }, // Defender position\n * { id: \"kick\", type: \"kick\" }, // Technique\n * TrigramStance.LI, // Attacker stance\n * \"torso\" // Target region\n * );\n *\n * if (result.hit) {\n * console.log(`Hit ${result.vitalPoint?.names.english}!`);\n * console.log(`Accuracy: ${(result.accuracy * 100).toFixed(1)}%`);\n * }\n * ```\n *\n * @module systems/physics/CollisionDetection\n * @category Combat Systems\n * @korean 충돌감지시스템\n */\n\nimport * as THREE from \"three\";\nimport { TrigramStance } from \"../../types/common\";\nimport type {\n AnatomicalRegionPhysics,\n BoundingBox,\n CollisionResult,\n Position3D,\n RaycastQuery,\n TechniqueType,\n} from \"../../types/physics\";\nimport {\n ANATOMICAL_DIMENSIONS,\n BASE_REACH,\n STANCE_REACH_MODIFIERS,\n} from \"../../types/physics\";\nimport type { VitalPoint } from \"../vitalpoint/types\";\nimport { VITAL_POINTS_DATA } from \"../vitalpoint/VitalPointsData\";\nimport { CoordinateMapper } from \"./CoordinateMapper\";\nimport { ThreeObjectPools } from \"../../utils/threeObjectPool\";\n\n/**\n * Collision Detection Engine for combat physics.\n *\n * **Korean**: 충돌 감지 엔진\n *\n * Provides efficient collision detection for combat using bounding boxes and raycasting.\n * Optimized for 60fps performance with multiple simultaneous collision checks.\n *\n * @category Combat Systems\n * @korean 충돌감지엔진\n */\nexport class CollisionDetection {\n private readonly boundingBoxes: Map<AnatomicalRegionPhysics, BoundingBox> =\n new Map();\n private readonly raycaster: THREE.Raycaster = new THREE.Raycaster();\n private vitalPointsByRegion: Map<AnatomicalRegionPhysics, VitalPoint[]> =\n new Map();\n\n // Geometry cache for object pooling to avoid repeated allocations during combat\n private readonly geometryCache: Map<string, THREE.BufferGeometry> = new Map();\n\n // Coordinate mapper for 2D→3D conversion\n private readonly coordinateMapper: CoordinateMapper;\n\n /**\n * Creates a new CollisionDetection instance.\n *\n * Initializes bounding boxes for all anatomical regions and organizes\n * vital points by region for efficient lookup.\n */\n constructor() {\n this.coordinateMapper = new CoordinateMapper();\n this.initializeBoundingBoxes();\n this.organizeVitalPointsByRegion();\n this.initializeGeometryCache();\n }\n\n /**\n * Cleans up Three.js resources.\n *\n * **Korean**: 자원 정리\n *\n * Disposes of cached geometries and releases memory to prevent leaks.\n * Should be called when the CollisionDetection instance is no longer needed.\n *\n * @korean 자원정리\n */\n public dispose(): void {\n // Dispose all cached geometries\n for (const geometry of this.geometryCache.values()) {\n geometry.dispose();\n }\n this.geometryCache.clear();\n }\n\n /**\n * Checks if an attack hits the defender.\n *\n * **Korean**: 공격 타격 확인\n *\n * Performs two-phase collision detection:\n * 1. Calculate effective attack reach based on technique and stance\n * 2. Broad-phase: Check if defender is within reach\n * 3. Broad-phase: Check if target region's AABB is within reach\n * 4. Narrow-phase: Raycast to find precise vital point hit\n *\n * @param attackerPosition - 3D position of the attacker\n * @param defenderPosition - 3D position of the defender\n * @param technique - Technique being used with type information\n * @param attackerStance - Attacker's current trigram stance\n * @param targetRegion - Anatomical region being targeted\n * @returns Collision result with hit status, vital point, distance, and accuracy\n *\n * @example\n * ```typescript\n * const result = collision.checkAttackHit(\n * { x: 0, y: 0, z: 5 },\n * { x: 0, y: 0, z: 6 },\n * { type: \"punch\" },\n * TrigramStance.GEON,\n * \"head\"\n * );\n * ```\n *\n * @korean 공격타격확인\n */\n checkAttackHit(\n attackerPosition: Position3D,\n defenderPosition: Position3D,\n // eslint-disable-next-line @typescript-eslint/no-explicit-any -- Technique object has dynamic properties from combat system\n technique: { type?: string; [key: string]: any },\n attackerStance: TrigramStance,\n targetRegion: AnatomicalRegionPhysics,\n ): CollisionResult {\n // Calculate effective attack reach\n const techniqueType = this.parseTechniqueType(technique.type);\n const attackReach = this.calculateAttackReach(\n techniqueType,\n attackerStance,\n );\n\n // Calculate distance between attacker and defender\n const distance = this.calculateDistance3D(\n attackerPosition,\n defenderPosition,\n );\n\n // Broad-phase: Check if defender is within reach\n if (distance > attackReach.effectiveReach) {\n return {\n hit: false,\n distance,\n accuracy: 0,\n };\n }\n\n // Get target region bounding box\n const targetBox = this.boundingBoxes.get(targetRegion);\n if (!targetBox) {\n return {\n hit: false,\n distance,\n accuracy: 0,\n };\n }\n\n // Narrow-phase: Raycast from attacker to target region\n const attackDirection = this.normalizeVector3D(\n this.subtractVectors3D(defenderPosition, attackerPosition),\n );\n\n const raycastQuery: RaycastQuery = {\n origin: attackerPosition,\n direction: attackDirection,\n maxDistance: attackReach.effectiveReach,\n targetRegion,\n };\n\n // Check intersection with target bounding box\n const intersection = this.raycastBoundingBox(\n raycastQuery,\n targetBox,\n defenderPosition,\n );\n\n if (!intersection) {\n return {\n hit: false,\n distance,\n accuracy: 0,\n };\n }\n\n // Determine specific vital point within region\n const vitalPoint = this.identifyVitalPoint(\n targetRegion,\n intersection.point,\n defenderPosition,\n );\n\n if (!vitalPoint) {\n return {\n hit: false,\n distance,\n accuracy: 0,\n };\n }\n\n // Calculate accuracy (how close to vital point center)\n const accuracy = this.calculateHitAccuracy(\n intersection.point,\n vitalPoint,\n defenderPosition,\n );\n\n return {\n hit: true,\n region: targetRegion,\n vitalPoint,\n distance,\n accuracy,\n hitPoint: intersection.point,\n };\n }\n\n /**\n * Calculates effective attack reach for a technique.\n *\n * **Korean**: 공격 범위 계산\n *\n * Applies stance modifiers to base technique reach.\n *\n * @param techniqueType - Type of technique\n * @param stance - Current trigram stance\n * @returns Attack reach with all modifiers applied\n *\n * @private\n * @korean 공격범위계산\n */\n private calculateAttackReach(\n techniqueType: TechniqueType,\n stance: TrigramStance,\n ): {\n technique: TechniqueType;\n baseReach: number;\n stance: TrigramStance;\n stanceModifier: number;\n effectiveReach: number;\n } {\n // Use BASE_REACH constant from physics types\n const baseReach = BASE_REACH[techniqueType];\n\n // Use STANCE_REACH_MODIFIERS constant from physics types\n const stanceModifier = STANCE_REACH_MODIFIERS[stance];\n const effectiveReach = baseReach * stanceModifier;\n\n return {\n technique: techniqueType,\n baseReach,\n stance,\n stanceModifier,\n effectiveReach,\n };\n }\n\n /**\n * Performs raycasting against a bounding box.\n *\n * **Korean**: 경계 상자 광선 투사\n *\n * Uses cached geometries from object pool to avoid repeated allocations\n * during combat. Creates a Three.js mesh for the bounding box and performs\n * raycasting to detect intersection points.\n *\n * Uses object pooling for Vector3 allocations to reduce GC pressure.\n *\n * @param query - Raycast query parameters\n * @param box - Bounding box to test\n * @param defenderPosition - Position of the defender\n * @returns Intersection point or null if no hit\n *\n * @private\n * @korean 경계상자광선투사\n */\n private raycastBoundingBox(\n query: RaycastQuery,\n box: BoundingBox,\n defenderPosition: Position3D,\n ): { point: Position3D } | null {\n // Get cached geometry from pool to avoid repeated allocations\n const cacheKey = `${box.type}-${box.region}`;\n let geometry = this.geometryCache.get(cacheKey);\n\n // If not cached (shouldn't happen after initialization), create it\n if (!geometry) {\n geometry = this.createGeometryForBox(box);\n this.geometryCache.set(cacheKey, geometry);\n }\n\n // Create temporary mesh for raycasting (mesh is lightweight, geometry is cached)\n const mesh = new THREE.Mesh(geometry);\n mesh.position.set(\n defenderPosition.x + box.center.x,\n defenderPosition.y + box.center.y,\n defenderPosition.z + box.center.z,\n );\n\n // Setup raycaster using pooled Vector3 objects to reduce GC pressure\n // Wrap in try-finally to ensure pooled objects are always released\n const origin = ThreeObjectPools.vector3.acquire();\n const direction = ThreeObjectPools.vector3.acquire();\n\n try {\n origin.set(query.origin.x, query.origin.y, query.origin.z);\n direction.set(query.direction.x, query.direction.y, query.direction.z);\n\n this.raycaster.set(origin, direction);\n this.raycaster.far = query.maxDistance;\n\n // Perform raycast\n const intersections = this.raycaster.intersectObject(mesh);\n\n // Clean up temporary mesh (geometry remains cached)\n // Note: mesh.material is undefined, no need to dispose\n\n if (intersections.length > 0) {\n const point = intersections[0].point;\n return {\n point: { x: point.x, y: point.y, z: point.z },\n };\n }\n\n return null;\n } finally {\n // Release pooled objects back to pool (guaranteed even if exception occurs)\n ThreeObjectPools.vector3.release(origin);\n ThreeObjectPools.vector3.release(direction);\n }\n }\n\n /**\n * Creates Three.js geometry for a bounding box.\n *\n * Helper method for geometry cache initialization.\n *\n * @param box - Bounding box specification\n * @returns Three.js geometry\n *\n * @private\n * @korean 경계상자지오메트리생성\n */\n private createGeometryForBox(box: BoundingBox): THREE.BufferGeometry {\n switch (box.type) {\n case \"sphere\":\n return new THREE.SphereGeometry(box.dimensions.x, 8, 8);\n case \"box\":\n return new THREE.BoxGeometry(\n box.dimensions.x,\n box.dimensions.y,\n box.dimensions.z,\n );\n case \"capsule\":\n return new THREE.CapsuleGeometry(\n box.dimensions.x,\n box.dimensions.y,\n 4,\n 8,\n );\n }\n }\n\n /**\n * Identifies the specific vital point hit within a region.\n *\n * **Korean**: 급소 식별\n *\n * Finds the closest vital point to the hit location within the targeted region.\n *\n * @param region - Anatomical region hit\n * @param hitPoint - 3D point where attack intersected\n * @param defenderPosition - Position of the defender\n * @returns Closest vital point or null if none found\n *\n * @private\n * @korean 급소식별\n */\n private identifyVitalPoint(\n region: AnatomicalRegionPhysics,\n hitPoint: Position3D,\n _defenderPosition: Position3D, // Prefixed with _ to indicate intentionally unused\n ): VitalPoint | null {\n // Get all vital points for this region\n const vitalPoints = this.vitalPointsByRegion.get(region);\n if (!vitalPoints || vitalPoints.length === 0) {\n return null;\n }\n\n // Use CoordinateMapper to find the closest vital point in 3D space\n const result = this.coordinateMapper.findClosestVitalPoint(\n hitPoint,\n vitalPoints,\n region,\n );\n\n // For collision detection, we accept any vital point in the region\n // even if it's not very close, as long as the region was hit\n // This provides more forgiving collision detection while still\n // using accurate 3D positioning for scoring/accuracy\n return result\n ? result.vitalPoint\n : vitalPoints.length > 0\n ? vitalPoints[0]\n : null;\n }\n\n /**\n * Calculates hit accuracy based on distance to vital point center.\n *\n * **Korean**: 타격 정확도 계산\n *\n * Accuracy decreases linearly with distance from vital point center.\n * Perfect accuracy (1.0) at center, decreasing to 0 at 5cm radius.\n *\n * @param hitPoint - Point where attack landed\n * @param vitalPoint - Target vital point\n * @param defenderPosition - Position of the defender\n * @returns Accuracy value from 0 to 1\n *\n * @private\n * @korean 타격정확도계산\n */\n private calculateHitAccuracy(\n hitPoint: Position3D,\n vitalPoint: VitalPoint,\n _defenderPosition: Position3D, // Prefixed with _ to indicate intentionally unused\n ): number {\n // Use CoordinateMapper to convert vital point to 3D and calculate distance\n const distance = this.coordinateMapper.distanceToVitalPoint(\n hitPoint,\n vitalPoint,\n );\n\n // Accuracy calculation: Perfect (1.0) at center, decreasing to 0 at 5cm radius\n const maxDistance = 0.05; // 5cm radius for vital points\n const accuracy = Math.max(0, 1 - distance / maxDistance);\n\n return accuracy;\n }\n\n /**\n * Calculates Euclidean distance between two 3D points.\n *\n * @param pos1 - First position\n * @param pos2 - Second position\n * @returns Distance in meters\n *\n * @private\n * @korean 3D거리계산\n */\n private calculateDistance3D(pos1: Position3D, pos2: Position3D): number {\n const dx = pos1.x - pos2.x;\n const dy = pos1.y - pos2.y;\n const dz = pos1.z - pos2.z;\n return Math.sqrt(dx * dx + dy * dy + dz * dz);\n }\n\n /**\n * Subtracts one 3D vector from another.\n *\n * @param v1 - First vector\n * @param v2 - Second vector\n * @returns Resulting vector\n *\n * @private\n * @korean 벡터빼기\n */\n private subtractVectors3D(v1: Position3D, v2: Position3D): Position3D {\n return {\n x: v1.x - v2.x,\n y: v1.y - v2.y,\n z: v1.z - v2.z,\n };\n }\n\n /**\n * Normalizes a 3D vector to unit length.\n *\n * @param vec - Vector to normalize\n * @returns Normalized vector\n *\n * @private\n * @korean 벡터정규화\n */\n private normalizeVector3D(vec: Position3D): Position3D {\n const length = Math.sqrt(vec.x * vec.x + vec.y * vec.y + vec.z * vec.z);\n if (length === 0) {\n return { x: 0, y: 0, z: 1 }; // Default forward direction\n }\n return {\n x: vec.x / length,\n y: vec.y / length,\n z: vec.z / length,\n };\n }\n\n /**\n * Parses technique type from string.\n *\n * @param techniqueTypeStr - Technique type as string\n * @returns Parsed technique type\n *\n * @private\n * @korean 기술유형파싱\n */\n private parseTechniqueType(techniqueTypeStr?: string): TechniqueType {\n const typeMap: Record<string, TechniqueType> = {\n punch: \"punch\",\n kick: \"kick\",\n elbow: \"elbow\",\n knee: \"knee\",\n pressure_point: \"pressure_point\",\n strike: \"punch\", // Default strike to punch\n };\n\n return typeMap[techniqueTypeStr ?? \"punch\"] ?? \"punch\";\n }\n\n /**\n * Initializes bounding boxes for all anatomical regions.\n *\n * Creates collision volumes for the 5 anatomical regions using the\n * ANATOMICAL_DIMENSIONS constants from the physics types module.\n *\n * @private\n * @korean 경계상자초기화\n */\n private initializeBoundingBoxes(): void {\n // Head: Sphere (from ANATOMICAL_DIMENSIONS)\n this.boundingBoxes.set(\"head\", {\n type: ANATOMICAL_DIMENSIONS.head.type,\n center: ANATOMICAL_DIMENSIONS.head.center,\n dimensions: {\n x: ANATOMICAL_DIMENSIONS.head.radius,\n y: 0,\n z: 0,\n }, // radius only\n region: \"head\",\n });\n\n // Neck: Capsule (from ANATOMICAL_DIMENSIONS)\n this.boundingBoxes.set(\"neck\", {\n type: ANATOMICAL_DIMENSIONS.neck.type,\n center: ANATOMICAL_DIMENSIONS.neck.center,\n dimensions: {\n x: ANATOMICAL_DIMENSIONS.neck.radius,\n y: ANATOMICAL_DIMENSIONS.neck.height,\n z: 0,\n }, // radius and height\n region: \"neck\",\n });\n\n // Torso: Box (from ANATOMICAL_DIMENSIONS)\n this.boundingBoxes.set(\"torso\", {\n type: ANATOMICAL_DIMENSIONS.torso.type,\n center: ANATOMICAL_DIMENSIONS.torso.center,\n dimensions: {\n x: ANATOMICAL_DIMENSIONS.torso.width,\n y: ANATOMICAL_DIMENSIONS.torso.height,\n z: ANATOMICAL_DIMENSIONS.torso.depth,\n }, // width, height, depth\n region: \"torso\",\n });\n\n // Arms: Capsules (from ANATOMICAL_DIMENSIONS)\n this.boundingBoxes.set(\"arms\", {\n type: ANATOMICAL_DIMENSIONS.arms.type,\n center: ANATOMICAL_DIMENSIONS.arms.center,\n dimensions: {\n x: ANATOMICAL_DIMENSIONS.arms.radius,\n y: ANATOMICAL_DIMENSIONS.arms.height,\n z: 0,\n }, // radius and length\n region: \"arms\",\n });\n\n // Legs: Capsules (from ANATOMICAL_DIMENSIONS)\n this.boundingBoxes.set(\"legs\", {\n type: ANATOMICAL_DIMENSIONS.legs.type,\n center: ANATOMICAL_DIMENSIONS.legs.center,\n dimensions: {\n x: ANATOMICAL_DIMENSIONS.legs.radius,\n y: ANATOMICAL_DIMENSIONS.legs.height,\n z: 0,\n }, // radius and length\n region: \"legs\",\n });\n }\n\n /**\n * Initializes geometry cache for object pooling.\n *\n * Pre-creates all geometries needed for raycasting to avoid repeated\n * allocations during combat. Critical for maintaining 60fps with up to\n * 100 collision checks per frame.\n *\n * @private\n * @korean 지오메트리캐시초기화\n */\n private initializeGeometryCache(): void {\n // Pre-create and cache geometries for all bounding boxes\n for (const box of this.boundingBoxes.values()) {\n const cacheKey = `${box.type}-${box.region}`;\n const geometry = this.createGeometryForBox(box);\n this.geometryCache.set(cacheKey, geometry);\n }\n }\n\n /**\n * Organizes vital points by anatomical region for efficient lookup.\n *\n * NOTE: This categorization currently uses y-coordinate thresholds that assume\n * positions are in meters. However, VITAL_POINTS_DATA uses pixel coordinates\n * (e.g., y: 50), which will cause incorrect categorization. Most vital points\n * will end up in the \"legs\" region since pixel y-coordinates are typically less\n * than 0.8.\n *\n * TODO: After implementing 2D→3D coordinate mapping, update this method to use\n * the converted 3D positions, or use the anatomical region data that may already\n * exist in the vital points data structure.\n *\n * Categorizes the 70 vital points into their respective regions:\n * - Head: 10 vital points\n * - Neck: 8 vital points\n * - Torso: 20 vital points\n * - Arms: 16 vital points\n * - Legs: 16 vital points\n *\n * @private\n * @korean 급소영역별정리\n */\n private organizeVitalPointsByRegion(): void {\n const regionMap: Map<AnatomicalRegionPhysics, VitalPoint[]> = new Map([\n [\"head\", []],\n [\"neck\", []],\n [\"torso\", []],\n [\"arms\", []],\n [\"legs\", []],\n ]);\n\n // Use existing VitalPointsData categorization by ID prefix\n // This is more reliable than coordinate-based heuristics\n for (const vp of VITAL_POINTS_DATA) {\n let region: AnatomicalRegionPhysics;\n\n // Categorize by ID prefix (existing VitalPointsData convention)\n // Check neck-related patterns first (more specific) before head (broader)\n if (\n vp.id.includes(\"_neck\") ||\n vp.id.includes(\"_throat\") ||\n vp.id === \"head_side_neck\" ||\n vp.id === \"head_throat\"\n ) {\n region = \"neck\";\n } else if (vp.id.startsWith(\"head_\")) {\n region = \"head\";\n } else if (vp.id.startsWith(\"torso_\")) {\n region = \"torso\";\n } else if (\n vp.id.startsWith(\"arm_left_\") ||\n vp.id.startsWith(\"arm_right_\")\n ) {\n region = \"arms\";\n } else if (\n vp.id.startsWith(\"leg_left_\") ||\n vp.id.startsWith(\"leg_right_\")\n ) {\n region = \"legs\";\n } else {\n // Default to torso for uncategorized points (core region)\n region = \"torso\";\n }\n\n const list = regionMap.get(region);\n if (list) {\n list.push(vp);\n }\n }\n\n this.vitalPointsByRegion = regionMap;\n }\n\n /**\n * Gets the bounding box for an anatomical region.\n *\n * @param region - Anatomical region\n * @returns Bounding box or undefined if not found\n *\n * @korean 경계상자조회\n */\n getBoundingBox(region: AnatomicalRegionPhysics): BoundingBox | undefined {\n return this.boundingBoxes.get(region);\n }\n\n /**\n * Gets all bounding boxes.\n *\n * @returns Map of all bounding boxes by region\n *\n * @korean 모든경계상자조회\n */\n getAllBoundingBoxes(): ReadonlyMap<AnatomicalRegionPhysics, BoundingBox> {\n return this.boundingBoxes;\n }\n\n /**\n * Gets vital points for a specific region.\n *\n * @param region - Anatomical region\n * @returns Array of vital points in that region\n *\n * @korean 영역별급소조회\n */\n getVitalPointsInRegion(\n region: AnatomicalRegionPhysics,\n ): readonly VitalPoint[] {\n return this.vitalPointsByRegion.get(region) ?? [];\n }\n}\n\nexport default CollisionDetection;\n"],"mappings":"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"}
1
+ {"version":3,"file":"CollisionDetection.js","names":[],"sources":["../../../src/systems/physics/CollisionDetection.ts"],"sourcesContent":["/**\n * Collision Detection System for Black Trigram combat physics.\n *\n * **Korean**: 충돌 감지 시스템\n *\n * Implements precise collision detection for the 70 vital points combat system using:\n * - Broad-phase AABB (Axis-Aligned Bounding Box) checks for performance\n * - Narrow-phase raycasting for precise vital point detection\n * - Attack reach validation based on technique and stance\n * - Bounding boxes for 5 anatomical regions\n *\n * ## Architecture\n *\n * The system uses a two-phase collision detection approach:\n *\n * 1. **Broad-phase**: Fast AABB checks to eliminate impossible collisions\n * 2. **Narrow-phase**: Precise raycasting to identify specific vital points\n *\n * This ensures 60fps performance even with complex collision queries.\n *\n * ## Performance\n *\n * - Broad-phase: O(1) per check (simple distance comparison)\n * - Narrow-phase: O(n) where n = vital points in region (typically 8-20)\n * - Target: <100 collision checks per frame for 60fps\n *\n * @example\n * ```typescript\n * const collision = new CollisionDetection();\n *\n * const result = collision.checkAttackHit(\n * { x: 0, y: 0, z: 5 }, // Attacker position\n * { x: 0, y: 0, z: 6.5 }, // Defender position\n * { id: \"kick\", type: \"kick\" }, // Technique\n * TrigramStance.LI, // Attacker stance\n * \"torso\" // Target region\n * );\n *\n * if (result.hit) {\n * console.log(`Hit ${result.vitalPoint?.names.english}!`);\n * console.log(`Accuracy: ${(result.accuracy * 100).toFixed(1)}%`);\n * }\n * ```\n *\n * @module systems/physics/CollisionDetection\n * @category Combat Systems\n * @korean 충돌감지시스템\n */\n\nimport * as THREE from \"three\";\nimport { TrigramStance } from \"../../types/common\";\nimport type {\n AnatomicalRegionPhysics,\n BoundingBox,\n CollisionResult,\n Position3D,\n RaycastQuery,\n TechniqueType,\n} from \"../../types/physics\";\nimport {\n ANATOMICAL_DIMENSIONS,\n BASE_REACH,\n STANCE_REACH_MODIFIERS,\n} from \"../../types/physics\";\nimport type { VitalPoint } from \"../vitalpoint/types\";\nimport { VITAL_POINTS_DATA } from \"../vitalpoint/VitalPointsData\";\nimport { CoordinateMapper } from \"./CoordinateMapper\";\nimport { ThreeObjectPools } from \"../../utils/threeObjectPool\";\n\n/**\n * Collision Detection Engine for combat physics.\n *\n * **Korean**: 충돌 감지 엔진\n *\n * Provides efficient collision detection for combat using bounding boxes and raycasting.\n * Optimized for 60fps performance with multiple simultaneous collision checks.\n *\n * @category Combat Systems\n * @korean 충돌감지엔진\n */\nexport class CollisionDetection {\n private readonly boundingBoxes: Map<AnatomicalRegionPhysics, BoundingBox> =\n new Map();\n private readonly raycaster: THREE.Raycaster = new THREE.Raycaster();\n private vitalPointsByRegion: Map<AnatomicalRegionPhysics, VitalPoint[]> =\n new Map();\n\n // Geometry cache for object pooling to avoid repeated allocations during combat\n private readonly geometryCache: Map<string, THREE.BufferGeometry> = new Map();\n\n // Coordinate mapper for 2D→3D conversion\n private readonly coordinateMapper: CoordinateMapper;\n\n /**\n * Creates a new CollisionDetection instance.\n *\n * Initializes bounding boxes for all anatomical regions and organizes\n * vital points by region for efficient lookup.\n */\n constructor() {\n this.coordinateMapper = new CoordinateMapper();\n this.initializeBoundingBoxes();\n this.organizeVitalPointsByRegion();\n this.initializeGeometryCache();\n }\n\n /**\n * Cleans up Three.js resources.\n *\n * **Korean**: 자원 정리\n *\n * Disposes of cached geometries and releases memory to prevent leaks.\n * Should be called when the CollisionDetection instance is no longer needed.\n *\n * @korean 자원정리\n */\n public dispose(): void {\n // Dispose all cached geometries\n for (const geometry of this.geometryCache.values()) {\n geometry.dispose();\n }\n this.geometryCache.clear();\n }\n\n /**\n * Checks if an attack hits the defender.\n *\n * **Korean**: 공격 타격 확인\n *\n * Performs two-phase collision detection:\n * 1. Calculate effective attack reach based on technique and stance\n * 2. Broad-phase: Check if defender is within reach\n * 3. Broad-phase: Check if target region's AABB is within reach\n * 4. Narrow-phase: Raycast to find precise vital point hit\n *\n * @param attackerPosition - 3D position of the attacker\n * @param defenderPosition - 3D position of the defender\n * @param technique - Technique being used with type information\n * @param attackerStance - Attacker's current trigram stance\n * @param targetRegion - Anatomical region being targeted\n * @returns Collision result with hit status, vital point, distance, and accuracy\n *\n * @example\n * ```typescript\n * const result = collision.checkAttackHit(\n * { x: 0, y: 0, z: 5 },\n * { x: 0, y: 0, z: 6 },\n * { type: \"punch\" },\n * TrigramStance.GEON,\n * \"head\"\n * );\n * ```\n *\n * @korean 공격타격확인\n */\n checkAttackHit(\n attackerPosition: Position3D,\n defenderPosition: Position3D,\n // eslint-disable-next-line @typescript-eslint/no-explicit-any -- Technique object has dynamic properties from combat system\n technique: { type?: string; [key: string]: any },\n attackerStance: TrigramStance,\n targetRegion: AnatomicalRegionPhysics,\n ): CollisionResult {\n // Calculate effective attack reach\n const techniqueType = this.parseTechniqueType(technique.type);\n const attackReach = this.calculateAttackReach(\n techniqueType,\n attackerStance,\n );\n\n // Calculate distance between attacker and defender\n const distance = this.calculateDistance3D(\n attackerPosition,\n defenderPosition,\n );\n\n // Broad-phase: Check if defender is within reach\n if (distance > attackReach.effectiveReach) {\n return {\n hit: false,\n distance,\n accuracy: 0,\n };\n }\n\n // Get target region bounding box\n const targetBox = this.boundingBoxes.get(targetRegion);\n if (!targetBox) {\n return {\n hit: false,\n distance,\n accuracy: 0,\n };\n }\n\n // Narrow-phase: Raycast from attacker to target region\n const attackDirection = this.normalizeVector3D(\n this.subtractVectors3D(defenderPosition, attackerPosition),\n );\n\n const raycastQuery: RaycastQuery = {\n origin: attackerPosition,\n direction: attackDirection,\n maxDistance: attackReach.effectiveReach,\n targetRegion,\n };\n\n // Check intersection with target bounding box\n const intersection = this.raycastBoundingBox(\n raycastQuery,\n targetBox,\n defenderPosition,\n );\n\n if (!intersection) {\n return {\n hit: false,\n distance,\n accuracy: 0,\n };\n }\n\n // Determine specific vital point within region\n const vitalPoint = this.identifyVitalPoint(\n targetRegion,\n intersection.point,\n defenderPosition,\n );\n\n if (!vitalPoint) {\n return {\n hit: false,\n distance,\n accuracy: 0,\n };\n }\n\n // Calculate accuracy (how close to vital point center)\n const accuracy = this.calculateHitAccuracy(\n intersection.point,\n vitalPoint,\n defenderPosition,\n );\n\n return {\n hit: true,\n region: targetRegion,\n vitalPoint,\n distance,\n accuracy,\n hitPoint: intersection.point,\n };\n }\n\n /**\n * Calculates effective attack reach for a technique.\n *\n * **Korean**: 공격 범위 계산\n *\n * Applies stance modifiers to base technique reach.\n *\n * @param techniqueType - Type of technique\n * @param stance - Current trigram stance\n * @returns Attack reach with all modifiers applied\n *\n * @private\n * @korean 공격범위계산\n */\n private calculateAttackReach(\n techniqueType: TechniqueType,\n stance: TrigramStance,\n ): {\n technique: TechniqueType;\n baseReach: number;\n stance: TrigramStance;\n stanceModifier: number;\n effectiveReach: number;\n } {\n // Use BASE_REACH constant from physics types\n const baseReach = BASE_REACH[techniqueType];\n\n // Use STANCE_REACH_MODIFIERS constant from physics types\n const stanceModifier = STANCE_REACH_MODIFIERS[stance];\n const effectiveReach = baseReach * stanceModifier;\n\n return {\n technique: techniqueType,\n baseReach,\n stance,\n stanceModifier,\n effectiveReach,\n };\n }\n\n /**\n * Performs raycasting against a bounding box.\n *\n * **Korean**: 경계 상자 광선 투사\n *\n * Uses cached geometries from object pool to avoid repeated allocations\n * during combat. Creates a Three.js mesh for the bounding box and performs\n * raycasting to detect intersection points.\n *\n * Uses object pooling for Vector3 allocations to reduce GC pressure.\n *\n * @param query - Raycast query parameters\n * @param box - Bounding box to test\n * @param defenderPosition - Position of the defender\n * @returns Intersection point or null if no hit\n *\n * @private\n * @korean 경계상자광선투사\n */\n private raycastBoundingBox(\n query: RaycastQuery,\n box: BoundingBox,\n defenderPosition: Position3D,\n ): { point: Position3D } | null {\n // Get cached geometry from pool to avoid repeated allocations\n const cacheKey = `${box.type}-${box.region}`;\n let geometry = this.geometryCache.get(cacheKey);\n\n // If not cached (shouldn't happen after initialization), create it\n if (!geometry) {\n geometry = this.createGeometryForBox(box);\n this.geometryCache.set(cacheKey, geometry);\n }\n\n // Create temporary mesh for raycasting (mesh is lightweight, geometry is cached)\n const mesh = new THREE.Mesh(geometry);\n mesh.position.set(\n defenderPosition.x + box.center.x,\n defenderPosition.y + box.center.y,\n defenderPosition.z + box.center.z,\n );\n\n // Setup raycaster using pooled Vector3 objects to reduce GC pressure\n // Wrap in try-finally to ensure pooled objects are always released\n const origin = ThreeObjectPools.vector3.acquire();\n const direction = ThreeObjectPools.vector3.acquire();\n\n try {\n origin.set(query.origin.x, query.origin.y, query.origin.z);\n direction.set(query.direction.x, query.direction.y, query.direction.z);\n\n this.raycaster.set(origin, direction);\n this.raycaster.far = query.maxDistance;\n\n // Perform raycast\n const intersections = this.raycaster.intersectObject(mesh);\n\n // Clean up temporary mesh (geometry remains cached)\n // Note: mesh.material is undefined, no need to dispose\n\n if (intersections.length > 0) {\n const point = intersections[0].point;\n return {\n point: { x: point.x, y: point.y, z: point.z },\n };\n }\n\n return null;\n } finally {\n // Release pooled objects back to pool (guaranteed even if exception occurs)\n ThreeObjectPools.vector3.release(origin);\n ThreeObjectPools.vector3.release(direction);\n }\n }\n\n /**\n * Creates Three.js geometry for a bounding box.\n *\n * Helper method for geometry cache initialization.\n *\n * @param box - Bounding box specification\n * @returns Three.js geometry\n *\n * @private\n * @korean 경계상자지오메트리생성\n */\n private createGeometryForBox(box: BoundingBox): THREE.BufferGeometry {\n switch (box.type) {\n case \"sphere\":\n return new THREE.SphereGeometry(box.dimensions.x, 8, 8);\n case \"box\":\n return new THREE.BoxGeometry(\n box.dimensions.x,\n box.dimensions.y,\n box.dimensions.z,\n );\n case \"capsule\":\n return new THREE.CapsuleGeometry(\n box.dimensions.x,\n box.dimensions.y,\n 4,\n 8,\n );\n }\n }\n\n /**\n * Identifies the specific vital point hit within a region.\n *\n * **Korean**: 급소 식별\n *\n * Finds the closest vital point to the hit location within the targeted region.\n *\n * @param region - Anatomical region hit\n * @param hitPoint - 3D point where attack intersected\n * @param defenderPosition - Position of the defender\n * @returns Closest vital point or null if none found\n *\n * @private\n * @korean 급소식별\n */\n private identifyVitalPoint(\n region: AnatomicalRegionPhysics,\n hitPoint: Position3D,\n _defenderPosition: Position3D, // Prefixed with _ to indicate intentionally unused\n ): VitalPoint | null {\n // Get all vital points for this region\n const vitalPoints = this.vitalPointsByRegion.get(region);\n if (!vitalPoints || vitalPoints.length === 0) {\n return null;\n }\n\n // Use CoordinateMapper to find the closest vital point in 3D space\n const result = this.coordinateMapper.findClosestVitalPoint(\n hitPoint,\n vitalPoints,\n region,\n );\n\n // For collision detection, we accept any vital point in the region\n // even if it's not very close, as long as the region was hit\n // This provides more forgiving collision detection while still\n // using accurate 3D positioning for scoring/accuracy\n return result\n ? result.vitalPoint\n : vitalPoints.length > 0\n ? vitalPoints[0]\n : null;\n }\n\n /**\n * Calculates hit accuracy based on distance to vital point center.\n *\n * **Korean**: 타격 정확도 계산\n *\n * Accuracy decreases linearly with distance from vital point center.\n * Perfect accuracy (1.0) at center, decreasing to 0 at 5cm radius.\n *\n * @param hitPoint - Point where attack landed\n * @param vitalPoint - Target vital point\n * @param defenderPosition - Position of the defender\n * @returns Accuracy value from 0 to 1\n *\n * @private\n * @korean 타격정확도계산\n */\n private calculateHitAccuracy(\n hitPoint: Position3D,\n vitalPoint: VitalPoint,\n _defenderPosition: Position3D, // Prefixed with _ to indicate intentionally unused\n ): number {\n // Use CoordinateMapper to convert vital point to 3D and calculate distance\n const distance = this.coordinateMapper.distanceToVitalPoint(\n hitPoint,\n vitalPoint,\n );\n\n // Accuracy calculation: Perfect (1.0) at center, decreasing to 0 at 5cm radius\n const maxDistance = 0.05; // 5cm radius for vital points\n const accuracy = Math.max(0, 1 - distance / maxDistance);\n\n return accuracy;\n }\n\n /**\n * Calculates Euclidean distance between two 3D points.\n *\n * @param pos1 - First position\n * @param pos2 - Second position\n * @returns Distance in meters\n *\n * @private\n * @korean 3D거리계산\n */\n private calculateDistance3D(pos1: Position3D, pos2: Position3D): number {\n const dx = pos1.x - pos2.x;\n const dy = pos1.y - pos2.y;\n const dz = pos1.z - pos2.z;\n return Math.sqrt(dx * dx + dy * dy + dz * dz);\n }\n\n /**\n * Subtracts one 3D vector from another.\n *\n * @param v1 - First vector\n * @param v2 - Second vector\n * @returns Resulting vector\n *\n * @private\n * @korean 벡터빼기\n */\n private subtractVectors3D(v1: Position3D, v2: Position3D): Position3D {\n return {\n x: v1.x - v2.x,\n y: v1.y - v2.y,\n z: v1.z - v2.z,\n };\n }\n\n /**\n * Normalizes a 3D vector to unit length.\n *\n * @param vec - Vector to normalize\n * @returns Normalized vector\n *\n * @private\n * @korean 벡터정규화\n */\n private normalizeVector3D(vec: Position3D): Position3D {\n const length = Math.sqrt(vec.x * vec.x + vec.y * vec.y + vec.z * vec.z);\n if (length === 0) {\n return { x: 0, y: 0, z: 1 }; // Default forward direction\n }\n return {\n x: vec.x / length,\n y: vec.y / length,\n z: vec.z / length,\n };\n }\n\n /**\n * Parses technique type from string.\n *\n * @param techniqueTypeStr - Technique type as string\n * @returns Parsed technique type\n *\n * @private\n * @korean 기술유형파싱\n */\n private parseTechniqueType(techniqueTypeStr?: string): TechniqueType {\n const typeMap: Record<string, TechniqueType> = {\n punch: \"punch\",\n kick: \"kick\",\n elbow: \"elbow\",\n knee: \"knee\",\n pressure_point: \"pressure_point\",\n strike: \"punch\", // Default strike to punch\n };\n\n return typeMap[techniqueTypeStr ?? \"punch\"] ?? \"punch\";\n }\n\n /**\n * Initializes bounding boxes for all anatomical regions.\n *\n * Creates collision volumes for the 5 anatomical regions using the\n * ANATOMICAL_DIMENSIONS constants from the physics types module.\n *\n * @private\n * @korean 경계상자초기화\n */\n private initializeBoundingBoxes(): void {\n // Head: Sphere (from ANATOMICAL_DIMENSIONS)\n this.boundingBoxes.set(\"head\", {\n type: ANATOMICAL_DIMENSIONS.head.type,\n center: ANATOMICAL_DIMENSIONS.head.center,\n dimensions: {\n x: ANATOMICAL_DIMENSIONS.head.radius,\n y: 0,\n z: 0,\n }, // radius only\n region: \"head\",\n });\n\n // Neck: Capsule (from ANATOMICAL_DIMENSIONS)\n this.boundingBoxes.set(\"neck\", {\n type: ANATOMICAL_DIMENSIONS.neck.type,\n center: ANATOMICAL_DIMENSIONS.neck.center,\n dimensions: {\n x: ANATOMICAL_DIMENSIONS.neck.radius,\n y: ANATOMICAL_DIMENSIONS.neck.height,\n z: 0,\n }, // radius and height\n region: \"neck\",\n });\n\n // Torso: Box (from ANATOMICAL_DIMENSIONS)\n this.boundingBoxes.set(\"torso\", {\n type: ANATOMICAL_DIMENSIONS.torso.type,\n center: ANATOMICAL_DIMENSIONS.torso.center,\n dimensions: {\n x: ANATOMICAL_DIMENSIONS.torso.width,\n y: ANATOMICAL_DIMENSIONS.torso.height,\n z: ANATOMICAL_DIMENSIONS.torso.depth,\n }, // width, height, depth\n region: \"torso\",\n });\n\n // Arms: Capsules (from ANATOMICAL_DIMENSIONS)\n this.boundingBoxes.set(\"arms\", {\n type: ANATOMICAL_DIMENSIONS.arms.type,\n center: ANATOMICAL_DIMENSIONS.arms.center,\n dimensions: {\n x: ANATOMICAL_DIMENSIONS.arms.radius,\n y: ANATOMICAL_DIMENSIONS.arms.height,\n z: 0,\n }, // radius and length\n region: \"arms\",\n });\n\n // Legs: Capsules (from ANATOMICAL_DIMENSIONS)\n this.boundingBoxes.set(\"legs\", {\n type: ANATOMICAL_DIMENSIONS.legs.type,\n center: ANATOMICAL_DIMENSIONS.legs.center,\n dimensions: {\n x: ANATOMICAL_DIMENSIONS.legs.radius,\n y: ANATOMICAL_DIMENSIONS.legs.height,\n z: 0,\n }, // radius and length\n region: \"legs\",\n });\n }\n\n /**\n * Initializes geometry cache for object pooling.\n *\n * Pre-creates all geometries needed for raycasting to avoid repeated\n * allocations during combat. Critical for maintaining 60fps with up to\n * 100 collision checks per frame.\n *\n * @private\n * @korean 지오메트리캐시초기화\n */\n private initializeGeometryCache(): void {\n // Pre-create and cache geometries for all bounding boxes\n for (const box of this.boundingBoxes.values()) {\n const cacheKey = `${box.type}-${box.region}`;\n const geometry = this.createGeometryForBox(box);\n this.geometryCache.set(cacheKey, geometry);\n }\n }\n\n /**\n * Organizes vital points by anatomical region for efficient lookup.\n *\n * NOTE: This categorization currently uses y-coordinate thresholds that assume\n * positions are in meters. However, VITAL_POINTS_DATA uses pixel coordinates\n * (e.g., y: 50), which will cause incorrect categorization. Most vital points\n * will end up in the \"legs\" region since pixel y-coordinates are typically less\n * than 0.8.\n *\n * TODO: After implementing 2D→3D coordinate mapping, update this method to use\n * the converted 3D positions, or use the anatomical region data that may already\n * exist in the vital points data structure.\n *\n * Categorizes the 70 vital points into their respective regions:\n * - Head: 10 vital points\n * - Neck: 8 vital points\n * - Torso: 20 vital points\n * - Arms: 16 vital points\n * - Legs: 16 vital points\n *\n * @private\n * @korean 급소영역별정리\n */\n private organizeVitalPointsByRegion(): void {\n const regionMap: Map<AnatomicalRegionPhysics, VitalPoint[]> = new Map([\n [\"head\", []],\n [\"neck\", []],\n [\"torso\", []],\n [\"arms\", []],\n [\"legs\", []],\n ]);\n\n // Use existing VitalPointsData categorization by ID prefix\n // This is more reliable than coordinate-based heuristics\n for (const vp of VITAL_POINTS_DATA) {\n let region: AnatomicalRegionPhysics;\n\n // Categorize by ID prefix (existing VitalPointsData convention)\n // Check neck-related patterns first (more specific) before head (broader)\n if (\n vp.id.includes(\"_neck\") ||\n vp.id.includes(\"_throat\") ||\n vp.id === \"head_side_neck\" ||\n vp.id === \"head_throat\"\n ) {\n region = \"neck\";\n } else if (vp.id.startsWith(\"head_\")) {\n region = \"head\";\n } else if (vp.id.startsWith(\"torso_\")) {\n region = \"torso\";\n } else if (\n vp.id.startsWith(\"arm_left_\") ||\n vp.id.startsWith(\"arm_right_\")\n ) {\n region = \"arms\";\n } else if (\n vp.id.startsWith(\"leg_left_\") ||\n vp.id.startsWith(\"leg_right_\")\n ) {\n region = \"legs\";\n } else {\n // Default to torso for uncategorized points (core region)\n region = \"torso\";\n }\n\n const list = regionMap.get(region);\n if (list) {\n list.push(vp);\n }\n }\n\n this.vitalPointsByRegion = regionMap;\n }\n\n /**\n * Gets the bounding box for an anatomical region.\n *\n * @param region - Anatomical region\n * @returns Bounding box or undefined if not found\n *\n * @korean 경계상자조회\n */\n getBoundingBox(region: AnatomicalRegionPhysics): BoundingBox | undefined {\n return this.boundingBoxes.get(region);\n }\n\n /**\n * Gets all bounding boxes.\n *\n * @returns Map of all bounding boxes by region\n *\n * @korean 모든경계상자조회\n */\n getAllBoundingBoxes(): ReadonlyMap<AnatomicalRegionPhysics, BoundingBox> {\n return this.boundingBoxes;\n }\n\n /**\n * Gets vital points for a specific region.\n *\n * @param region - Anatomical region\n * @returns Array of vital points in that region\n *\n * @korean 영역별급소조회\n */\n getVitalPointsInRegion(\n region: AnatomicalRegionPhysics,\n ): readonly VitalPoint[] {\n return this.vitalPointsByRegion.get(region) ?? [];\n }\n}\n\nexport default CollisionDetection;\n"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"CoordinateMapper.js","names":[],"sources":["../../../src/systems/physics/CoordinateMapper.ts"],"sourcesContent":["/**\n * Coordinate Mapper for 2D→3D conversion in Black Trigram.\n * \n * **Korean**: 좌표 변환기 (2D → 3D)\n * \n * Maps 2D pixel coordinates from the vital points data (UI overlay) to 3D world\n * coordinates for physics-based collision detection. This allows the UI-based\n * vital point system to work seamlessly with Three.js 3D collision detection.\n * \n * ## Coordinate Systems\n * \n * - **2D Pixel Space**: Origin (0,0) at top-left, used by vital points UI overlay\n * - **3D World Space**: Origin (0,0,0) at character center, used by collision detection\n * \n * ## Character Model Assumptions\n * \n * - Character height: 1.75m (average adult)\n * - Character width: 0.5m (shoulder width)\n * - Character depth: 0.3m (chest to back)\n * - Standing position: feet at y=0, head at y=1.75\n * \n * ## Mapping Strategy\n * \n * The mapper uses anatomical regions to determine the Z-depth (front-to-back position)\n * of vital points, ensuring physically accurate 3D positions for collision detection.\n * \n * @module systems/physics/CoordinateMapper\n * @category Physics System\n * @korean 좌표변환\n */\n\nimport type { Position } from \"@/types/common\";\nimport type { Position3D, AnatomicalRegionPhysics } from \"@/types/physics\";\nimport type { VitalPoint } from \"../vitalpoint/types\";\n\n/**\n * Configuration for the character model coordinate mapping.\n * \n * **Korean**: 캐릭터 모델 설정\n */\nexport interface CharacterModelConfig {\n /** Character height in meters (default: 1.75m) */\n readonly height: number;\n \n /** Character shoulder width in meters (default: 0.5m) */\n readonly width: number;\n \n /** Character depth (chest to back) in meters (default: 0.3m) */\n readonly depth: number;\n \n /** UI overlay width in pixels (default: 200px) */\n readonly overlayWidth: number;\n \n /** UI overlay height in pixels (default: 300px) */\n readonly overlayHeight: number;\n}\n\n/**\n * Average adult standing height used for physics mapping (meters).\n * \n * This value is based on common anthropometric data and provides a\n * reasonable default for a neutral, unarmed character in Black Trigram.\n */\nconst AVERAGE_ADULT_HEIGHT_M = 1.75; // 175cm average adult height\n\n/**\n * Average shoulder width used for frontal collision and UI mapping (meters).\n * \n * This approximates the biacromial (shoulder-to-shoulder) breadth for an\n * adult, which is sufficient for our hitbox and overlay alignment needs.\n */\nconst AVERAGE_SHOULDER_WIDTH_M = 0.5; // 50cm shoulder width\n\n/**\n * Average torso depth (chest to back) used for front/back hit placement (meters).\n */\nconst AVERAGE_TORSO_DEPTH_M = 0.3; // 30cm chest depth\n\n/**\n * Default UI overlay width in pixels.\n * \n * This matches the designed vital-point sprite asset width, ensuring that\n * pixel coordinates from the overlay map consistently to the 3D character.\n */\nconst DEFAULT_OVERLAY_WIDTH_PX = 200; // Standard UI overlay width\n\n/**\n * Default UI overlay height in pixels.\n * \n * This matches the designed vital-point sprite asset height, keeping the\n * 2D→3D mapping resolution-independent but asset-consistent.\n */\nconst DEFAULT_OVERLAY_HEIGHT_PX = 300; // Standard UI overlay height\n\n/**\n * Default character model configuration based on average adult proportions.\n */\nconst DEFAULT_CHARACTER_CONFIG: CharacterModelConfig = {\n height: AVERAGE_ADULT_HEIGHT_M,\n width: AVERAGE_SHOULDER_WIDTH_M,\n depth: AVERAGE_TORSO_DEPTH_M,\n overlayWidth: DEFAULT_OVERLAY_WIDTH_PX,\n overlayHeight: DEFAULT_OVERLAY_HEIGHT_PX,\n};\n\n/**\n * Z-depth (front-to-back) offsets for different anatomical regions.\n * \n * Units are **meters in model space**, measured along the character's local Z axis,\n * and are applied relative to the character's center plane (Z = 0) and the\n * configured {@link CharacterModelConfig.depth}. With the default depth of\n * `0.3m`, an offset of `0.05` corresponds to ≈5 cm of forward displacement.\n *\n * These values were chosen as simple, stable approximations based on average\n * adult proportions and validated visually against the default rig:\n *\n * - `head: 0.05` → head center sits slightly in front of the torso plane\n * (≈1/6 of torso depth) to match typical forward head posture.\n * - `neck: 0.02` → neck base is close to the torso plane but not perfectly\n * centered, acknowledging slight anterior offset of the trachea/sternum.\n * - `torso: 0.0` → torso vital points lie on the reference center plane that\n * approximates the mid‑chest / spine midpoint.\n * - `arms: 0.05` → relaxed arms hang slightly in front of the torso plane,\n * using the same forward offset as the head for consistency.\n * - `legs: 0.0` → leg targets (front of thighs/shins) are modeled on the\n * center plane; depth variation is instead captured by vertical placement.\n *\n * If you change {@link CharacterModelConfig.depth}, you may keep these absolute\n * meter values (for physical fidelity) or re‑express them as fractions of the\n * new depth, but they should continue to represent small (≈0–5 cm) anatomical\n * offsets from the torso midline.\n */\nconst REGION_DEPTH_OFFSETS: Record<AnatomicalRegionPhysics, number> = {\n head: 0.05, // ≈5 cm forward from torso mid-plane to match cranial alignment\n neck: 0.02, // ≈2 cm forward; neck base is near, but not exactly on, center\n torso: 0.0, // Torso reference plane (mid‑chest / spine midpoint)\n arms: 0.05, // ≈5 cm forward; relaxed arms rest slightly in front of torso\n legs: 0.0, // Legs aligned to torso center plane; front/back handled elsewhere\n};\n\n/**\n * Coordinate Mapper for converting 2D pixel coordinates to 3D world coordinates.\n * \n * **Korean**: 좌표 변환기\n * \n * Provides bidirectional mapping between the 2D UI overlay coordinate system\n * and the 3D physics world coordinate system.\n * \n * @category Physics System\n * @korean 좌표변환기\n */\nexport class CoordinateMapper {\n private readonly config: CharacterModelConfig;\n \n /**\n * Creates a new CoordinateMapper with optional custom configuration.\n * \n * @param config - Optional character model configuration\n */\n constructor(config: Partial<CharacterModelConfig> = {}) {\n this.config = { ...DEFAULT_CHARACTER_CONFIG, ...config };\n }\n \n /**\n * Converts a 2D pixel position to a 3D world position.\n * \n * **Korean**: 2D → 3D 변환\n * \n * Maps UI overlay pixel coordinates to 3D world space coordinates suitable\n * for physics collision detection.\n * \n * @param pixel2D - 2D pixel position (origin top-left)\n * @param region - Anatomical region for Z-depth calculation\n * @returns 3D world position in meters (origin at character center)\n * \n * @example\n * ```typescript\n * const mapper = new CoordinateMapper();\n * const vitalPoint = { position: { x: 100, y: 50 } };\n * const worldPos = mapper.pixel2DToWorld3D(vitalPoint.position, \"head\");\n * // worldPos: { x: 0, y: 1.45, z: 0.05 }\n * ```\n */\n pixel2DToWorld3D(pixel2D: Position, region: AnatomicalRegionPhysics): Position3D {\n // Normalize pixel coordinates to 0-1 range\n const normalizedX = pixel2D.x / this.config.overlayWidth;\n const normalizedY = pixel2D.y / this.config.overlayHeight;\n \n // Convert to world coordinates\n // X: Map from pixel space (0 = left, overlayWidth = right) to world space (-width/2 to +width/2)\n const worldX = (normalizedX - 0.5) * this.config.width;\n \n // Y: Map from pixel space (0 = top, overlayHeight = bottom) to world space (height to 0)\n // Invert Y-axis: pixel 0 = top = character head, pixel overlayHeight = bottom = character feet\n const worldY = (1 - normalizedY) * this.config.height;\n \n // Z: Use anatomical region to determine depth offset\n const worldZ = REGION_DEPTH_OFFSETS[region];\n \n return {\n x: worldX,\n y: worldY,\n z: worldZ,\n };\n }\n \n /**\n * Converts a 3D world position to a 2D pixel position.\n * \n * **Korean**: 3D → 2D 변환\n * \n * Maps 3D world space coordinates back to UI overlay pixel coordinates.\n * Useful for debugging and visual feedback.\n * \n * @param world3D - 3D world position in meters\n * @returns 2D pixel position (origin top-left)\n * \n * @example\n * ```typescript\n * const mapper = new CoordinateMapper();\n * const worldPos = { x: 0, y: 1.45, z: 0.05 };\n * const pixelPos = mapper.world3DToPixel2D(worldPos);\n * // pixelPos: { x: 100, y: 50 }\n * ```\n */\n world3DToPixel2D(world3D: Position3D): Position {\n // Convert world X to normalized 0-1 range\n const normalizedX = (world3D.x / this.config.width) + 0.5;\n \n // Convert world Y to normalized 0-1 range (inverted)\n const normalizedY = 1 - (world3D.y / this.config.height);\n \n // Convert to pixel coordinates\n return {\n x: Math.round(normalizedX * this.config.overlayWidth),\n y: Math.round(normalizedY * this.config.overlayHeight),\n };\n }\n \n /**\n * Converts a vital point's 2D position to 3D world position.\n * \n * **Korean**: 급소 위치 변환\n * \n * Convenience method that extracts position and region from a VitalPoint object.\n * \n * @param vitalPoint - Vital point with 2D position\n * @returns 3D world position\n * \n * @example\n * ```typescript\n * const mapper = new CoordinateMapper();\n * const temple = VITAL_POINTS_DATA.find(vp => vp.id === \"head_temple\");\n * const worldPos = mapper.vitalPointToWorld3D(temple);\n * ```\n */\n vitalPointToWorld3D(vitalPoint: VitalPoint): Position3D {\n // Infer region from vital point ID prefix\n const region = this.inferRegionFromId(vitalPoint.id);\n return this.pixel2DToWorld3D(vitalPoint.position, region);\n }\n \n /**\n * Infers the anatomical region from a vital point ID.\n * \n * **Korean**: 급소 ID에서 영역 추론\n * \n * @param vitalPointId - Vital point ID (e.g., \"head_temple\", \"torso_solar_plexus\")\n * @returns Anatomical region\n * \n * @private\n */\n private inferRegionFromId(vitalPointId: string): AnatomicalRegionPhysics {\n if (vitalPointId.startsWith(\"head_\")) return \"head\";\n if (vitalPointId.startsWith(\"neck_\")) return \"neck\";\n if (vitalPointId.startsWith(\"torso_\")) return \"torso\";\n if (vitalPointId.startsWith(\"arm_\")) return \"arms\";\n if (vitalPointId.startsWith(\"leg_\")) return \"legs\";\n \n // Default to torso if no clear region prefix\n return \"torso\";\n }\n \n /**\n * Calculates the distance between a 3D point and a vital point's 3D position.\n * \n * **Korean**: 거리 계산\n * \n * @param point3D - Point in 3D world space\n * @param vitalPoint - Vital point to measure distance to\n * @returns Distance in meters\n * \n * @example\n * ```typescript\n * const mapper = new CoordinateMapper();\n * const attackPoint = { x: 0, y: 1.5, z: 0.1 };\n * const temple = VITAL_POINTS_DATA.find(vp => vp.id === \"head_temple\");\n * const distance = mapper.distanceToVitalPoint(attackPoint, temple);\n * ```\n */\n distanceToVitalPoint(point3D: Position3D, vitalPoint: VitalPoint): number {\n const vitalPoint3D = this.vitalPointToWorld3D(vitalPoint);\n \n const dx = point3D.x - vitalPoint3D.x;\n const dy = point3D.y - vitalPoint3D.y;\n const dz = point3D.z - vitalPoint3D.z;\n \n return Math.sqrt(dx * dx + dy * dy + dz * dz);\n }\n \n /**\n * Finds the closest vital point to a 3D position within a given region.\n * \n * **Korean**: 가장 가까운 급소 찾기\n * \n * @param point3D - Point in 3D world space\n * @param vitalPoints - Array of vital points to search\n * @param region - Optional region filter\n * @returns Closest vital point and distance, or null if none found\n * \n * @example\n * ```typescript\n * const mapper = new CoordinateMapper();\n * const hitPoint = { x: 0, y: 1.7, z: 0.05 };\n * const result = mapper.findClosestVitalPoint(hitPoint, VITAL_POINTS_DATA, \"head\");\n * if (result) {\n * console.log(`Hit ${result.vitalPoint.names.english} at ${result.distance}m`);\n * }\n * ```\n */\n findClosestVitalPoint(\n point3D: Position3D,\n vitalPoints: readonly VitalPoint[],\n region?: AnatomicalRegionPhysics\n ): { vitalPoint: VitalPoint; distance: number } | null {\n let closestVitalPoint: VitalPoint | null = null;\n let closestDistance = Infinity;\n \n for (const vitalPoint of vitalPoints) {\n // Filter by region if specified\n if (region) {\n const vpRegion = this.inferRegionFromId(vitalPoint.id);\n if (vpRegion !== region) continue;\n }\n \n const distance = this.distanceToVitalPoint(point3D, vitalPoint);\n \n if (distance < closestDistance) {\n closestDistance = distance;\n closestVitalPoint = vitalPoint;\n }\n }\n \n if (closestVitalPoint === null) {\n return null;\n }\n \n return {\n vitalPoint: closestVitalPoint,\n distance: closestDistance,\n };\n }\n \n /**\n * Gets the current character model configuration.\n * \n * @returns Current configuration\n */\n getConfig(): CharacterModelConfig {\n return { ...this.config };\n }\n}\n\n/**\n * Default singleton instance for convenient access.\n * \n * **Korean**: 기본 인스턴스\n * \n * @example\n * ```typescript\n * import { defaultCoordinateMapper } from './CoordinateMapper';\n * \n * const worldPos = defaultCoordinateMapper.pixel2DToWorld3D({ x: 100, y: 50 }, \"head\");\n * ```\n */\nexport const defaultCoordinateMapper = new CoordinateMapper();\n"],"mappings":";;;;AAiGA,IAAM,2BAAiD;CACrD,QAAQ;CACR,OAAO;CACP,OAAO;CACP,cAAc;CACd,eAAe;CAChB;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA6BD,IAAM,uBAAgE;CACpE,MAAM;CACN,MAAM;CACN,OAAO;CACP,MAAM;CACN,MAAM;CACP;;;;;;;;;;;;AAaD,IAAa,mBAAb,MAA8B;CAC5B;;;;;;CAOA,YAAY,SAAwC,EAAE,EAAE;EACtD,KAAK,SAAS;GAAE,GAAG;GAA0B,GAAG;GAAQ;;;;;;;;;;;;;;;;;;;;;;CAuB1D,iBAAiB,SAAmB,QAA6C;EAE/E,MAAM,cAAc,QAAQ,IAAI,KAAK,OAAO;EAC5C,MAAM,cAAc,QAAQ,IAAI,KAAK,OAAO;EAa5C,OAAO;GACL,IAVc,cAAc,MAAO,KAAK,OAAO;GAW/C,IAPc,IAAI,eAAe,KAAK,OAAO;GAQ7C,GALa,qBAAqB;GAMnC;;;;;;;;;;;;;;;;;;;;;CAsBH,iBAAiB,SAA+B;EAE9C,MAAM,cAAe,QAAQ,IAAI,KAAK,OAAO,QAAS;EAGtD,MAAM,cAAc,IAAK,QAAQ,IAAI,KAAK,OAAO;EAGjD,OAAO;GACL,GAAG,KAAK,MAAM,cAAc,KAAK,OAAO,aAAa;GACrD,GAAG,KAAK,MAAM,cAAc,KAAK,OAAO,cAAc;GACvD;;;;;;;;;;;;;;;;;;;CAoBH,oBAAoB,YAAoC;EAEtD,MAAM,SAAS,KAAK,kBAAkB,WAAW,GAAG;EACpD,OAAO,KAAK,iBAAiB,WAAW,UAAU,OAAO;;;;;;;;;;;;CAa3D,kBAA0B,cAA+C;EACvE,IAAI,aAAa,WAAW,QAAQ,EAAE,OAAO;EAC7C,IAAI,aAAa,WAAW,QAAQ,EAAE,OAAO;EAC7C,IAAI,aAAa,WAAW,SAAS,EAAE,OAAO;EAC9C,IAAI,aAAa,WAAW,OAAO,EAAE,OAAO;EAC5C,IAAI,aAAa,WAAW,OAAO,EAAE,OAAO;EAG5C,OAAO;;;;;;;;;;;;;;;;;;;CAoBT,qBAAqB,SAAqB,YAAgC;EACxE,MAAM,eAAe,KAAK,oBAAoB,WAAW;EAEzD,MAAM,KAAK,QAAQ,IAAI,aAAa;EACpC,MAAM,KAAK,QAAQ,IAAI,aAAa;EACpC,MAAM,KAAK,QAAQ,IAAI,aAAa;EAEpC,OAAO,KAAK,KAAK,KAAK,KAAK,KAAK,KAAK,KAAK,GAAG;;;;;;;;;;;;;;;;;;;;;;CAuB/C,sBACE,SACA,aACA,QACqD;EACrD,IAAI,oBAAuC;EAC3C,IAAI,kBAAkB;EAEtB,KAAK,MAAM,cAAc,aAAa;GAEpC,IAAI;QACe,KAAK,kBAAkB,WAAW,GAC/C,KAAa,QAAQ;;GAG3B,MAAM,WAAW,KAAK,qBAAqB,SAAS,WAAW;GAE/D,IAAI,WAAW,iBAAiB;IAC9B,kBAAkB;IAClB,oBAAoB;;;EAIxB,IAAI,sBAAsB,MACxB,OAAO;EAGT,OAAO;GACL,YAAY;GACZ,UAAU;GACX;;;;;;;CAQH,YAAkC;EAChC,OAAO,EAAE,GAAG,KAAK,QAAQ;;;AAgBU,IAAI,kBAAkB"}
1
+ {"version":3,"file":"CoordinateMapper.js","names":[],"sources":["../../../src/systems/physics/CoordinateMapper.ts"],"sourcesContent":["/**\n * Coordinate Mapper for 2D→3D conversion in Black Trigram.\n * \n * **Korean**: 좌표 변환기 (2D → 3D)\n * \n * Maps 2D pixel coordinates from the vital points data (UI overlay) to 3D world\n * coordinates for physics-based collision detection. This allows the UI-based\n * vital point system to work seamlessly with Three.js 3D collision detection.\n * \n * ## Coordinate Systems\n * \n * - **2D Pixel Space**: Origin (0,0) at top-left, used by vital points UI overlay\n * - **3D World Space**: Origin (0,0,0) at character center, used by collision detection\n * \n * ## Character Model Assumptions\n * \n * - Character height: 1.75m (average adult)\n * - Character width: 0.5m (shoulder width)\n * - Character depth: 0.3m (chest to back)\n * - Standing position: feet at y=0, head at y=1.75\n * \n * ## Mapping Strategy\n * \n * The mapper uses anatomical regions to determine the Z-depth (front-to-back position)\n * of vital points, ensuring physically accurate 3D positions for collision detection.\n * \n * @module systems/physics/CoordinateMapper\n * @category Physics System\n * @korean 좌표변환\n */\n\nimport type { Position } from \"@/types/common\";\nimport type { Position3D, AnatomicalRegionPhysics } from \"@/types/physics\";\nimport type { VitalPoint } from \"../vitalpoint/types\";\n\n/**\n * Configuration for the character model coordinate mapping.\n * \n * **Korean**: 캐릭터 모델 설정\n */\nexport interface CharacterModelConfig {\n /** Character height in meters (default: 1.75m) */\n readonly height: number;\n \n /** Character shoulder width in meters (default: 0.5m) */\n readonly width: number;\n \n /** Character depth (chest to back) in meters (default: 0.3m) */\n readonly depth: number;\n \n /** UI overlay width in pixels (default: 200px) */\n readonly overlayWidth: number;\n \n /** UI overlay height in pixels (default: 300px) */\n readonly overlayHeight: number;\n}\n\n/**\n * Average adult standing height used for physics mapping (meters).\n * \n * This value is based on common anthropometric data and provides a\n * reasonable default for a neutral, unarmed character in Black Trigram.\n */\nconst AVERAGE_ADULT_HEIGHT_M = 1.75; // 175cm average adult height\n\n/**\n * Average shoulder width used for frontal collision and UI mapping (meters).\n * \n * This approximates the biacromial (shoulder-to-shoulder) breadth for an\n * adult, which is sufficient for our hitbox and overlay alignment needs.\n */\nconst AVERAGE_SHOULDER_WIDTH_M = 0.5; // 50cm shoulder width\n\n/**\n * Average torso depth (chest to back) used for front/back hit placement (meters).\n */\nconst AVERAGE_TORSO_DEPTH_M = 0.3; // 30cm chest depth\n\n/**\n * Default UI overlay width in pixels.\n * \n * This matches the designed vital-point sprite asset width, ensuring that\n * pixel coordinates from the overlay map consistently to the 3D character.\n */\nconst DEFAULT_OVERLAY_WIDTH_PX = 200; // Standard UI overlay width\n\n/**\n * Default UI overlay height in pixels.\n * \n * This matches the designed vital-point sprite asset height, keeping the\n * 2D→3D mapping resolution-independent but asset-consistent.\n */\nconst DEFAULT_OVERLAY_HEIGHT_PX = 300; // Standard UI overlay height\n\n/**\n * Default character model configuration based on average adult proportions.\n */\nconst DEFAULT_CHARACTER_CONFIG: CharacterModelConfig = {\n height: AVERAGE_ADULT_HEIGHT_M,\n width: AVERAGE_SHOULDER_WIDTH_M,\n depth: AVERAGE_TORSO_DEPTH_M,\n overlayWidth: DEFAULT_OVERLAY_WIDTH_PX,\n overlayHeight: DEFAULT_OVERLAY_HEIGHT_PX,\n};\n\n/**\n * Z-depth (front-to-back) offsets for different anatomical regions.\n * \n * Units are **meters in model space**, measured along the character's local Z axis,\n * and are applied relative to the character's center plane (Z = 0) and the\n * configured {@link CharacterModelConfig.depth}. With the default depth of\n * `0.3m`, an offset of `0.05` corresponds to ≈5 cm of forward displacement.\n *\n * These values were chosen as simple, stable approximations based on average\n * adult proportions and validated visually against the default rig:\n *\n * - `head: 0.05` → head center sits slightly in front of the torso plane\n * (≈1/6 of torso depth) to match typical forward head posture.\n * - `neck: 0.02` → neck base is close to the torso plane but not perfectly\n * centered, acknowledging slight anterior offset of the trachea/sternum.\n * - `torso: 0.0` → torso vital points lie on the reference center plane that\n * approximates the mid‑chest / spine midpoint.\n * - `arms: 0.05` → relaxed arms hang slightly in front of the torso plane,\n * using the same forward offset as the head for consistency.\n * - `legs: 0.0` → leg targets (front of thighs/shins) are modeled on the\n * center plane; depth variation is instead captured by vertical placement.\n *\n * If you change {@link CharacterModelConfig.depth}, you may keep these absolute\n * meter values (for physical fidelity) or re‑express them as fractions of the\n * new depth, but they should continue to represent small (≈0–5 cm) anatomical\n * offsets from the torso midline.\n */\nconst REGION_DEPTH_OFFSETS: Record<AnatomicalRegionPhysics, number> = {\n head: 0.05, // ≈5 cm forward from torso mid-plane to match cranial alignment\n neck: 0.02, // ≈2 cm forward; neck base is near, but not exactly on, center\n torso: 0.0, // Torso reference plane (mid‑chest / spine midpoint)\n arms: 0.05, // ≈5 cm forward; relaxed arms rest slightly in front of torso\n legs: 0.0, // Legs aligned to torso center plane; front/back handled elsewhere\n};\n\n/**\n * Coordinate Mapper for converting 2D pixel coordinates to 3D world coordinates.\n * \n * **Korean**: 좌표 변환기\n * \n * Provides bidirectional mapping between the 2D UI overlay coordinate system\n * and the 3D physics world coordinate system.\n * \n * @category Physics System\n * @korean 좌표변환기\n */\nexport class CoordinateMapper {\n private readonly config: CharacterModelConfig;\n \n /**\n * Creates a new CoordinateMapper with optional custom configuration.\n * \n * @param config - Optional character model configuration\n */\n constructor(config: Partial<CharacterModelConfig> = {}) {\n this.config = { ...DEFAULT_CHARACTER_CONFIG, ...config };\n }\n \n /**\n * Converts a 2D pixel position to a 3D world position.\n * \n * **Korean**: 2D → 3D 변환\n * \n * Maps UI overlay pixel coordinates to 3D world space coordinates suitable\n * for physics collision detection.\n * \n * @param pixel2D - 2D pixel position (origin top-left)\n * @param region - Anatomical region for Z-depth calculation\n * @returns 3D world position in meters (origin at character center)\n * \n * @example\n * ```typescript\n * const mapper = new CoordinateMapper();\n * const vitalPoint = { position: { x: 100, y: 50 } };\n * const worldPos = mapper.pixel2DToWorld3D(vitalPoint.position, \"head\");\n * // worldPos: { x: 0, y: 1.45, z: 0.05 }\n * ```\n */\n pixel2DToWorld3D(pixel2D: Position, region: AnatomicalRegionPhysics): Position3D {\n // Normalize pixel coordinates to 0-1 range\n const normalizedX = pixel2D.x / this.config.overlayWidth;\n const normalizedY = pixel2D.y / this.config.overlayHeight;\n \n // Convert to world coordinates\n // X: Map from pixel space (0 = left, overlayWidth = right) to world space (-width/2 to +width/2)\n const worldX = (normalizedX - 0.5) * this.config.width;\n \n // Y: Map from pixel space (0 = top, overlayHeight = bottom) to world space (height to 0)\n // Invert Y-axis: pixel 0 = top = character head, pixel overlayHeight = bottom = character feet\n const worldY = (1 - normalizedY) * this.config.height;\n \n // Z: Use anatomical region to determine depth offset\n const worldZ = REGION_DEPTH_OFFSETS[region];\n \n return {\n x: worldX,\n y: worldY,\n z: worldZ,\n };\n }\n \n /**\n * Converts a 3D world position to a 2D pixel position.\n * \n * **Korean**: 3D → 2D 변환\n * \n * Maps 3D world space coordinates back to UI overlay pixel coordinates.\n * Useful for debugging and visual feedback.\n * \n * @param world3D - 3D world position in meters\n * @returns 2D pixel position (origin top-left)\n * \n * @example\n * ```typescript\n * const mapper = new CoordinateMapper();\n * const worldPos = { x: 0, y: 1.45, z: 0.05 };\n * const pixelPos = mapper.world3DToPixel2D(worldPos);\n * // pixelPos: { x: 100, y: 50 }\n * ```\n */\n world3DToPixel2D(world3D: Position3D): Position {\n // Convert world X to normalized 0-1 range\n const normalizedX = (world3D.x / this.config.width) + 0.5;\n \n // Convert world Y to normalized 0-1 range (inverted)\n const normalizedY = 1 - (world3D.y / this.config.height);\n \n // Convert to pixel coordinates\n return {\n x: Math.round(normalizedX * this.config.overlayWidth),\n y: Math.round(normalizedY * this.config.overlayHeight),\n };\n }\n \n /**\n * Converts a vital point's 2D position to 3D world position.\n * \n * **Korean**: 급소 위치 변환\n * \n * Convenience method that extracts position and region from a VitalPoint object.\n * \n * @param vitalPoint - Vital point with 2D position\n * @returns 3D world position\n * \n * @example\n * ```typescript\n * const mapper = new CoordinateMapper();\n * const temple = VITAL_POINTS_DATA.find(vp => vp.id === \"head_temple\");\n * const worldPos = mapper.vitalPointToWorld3D(temple);\n * ```\n */\n vitalPointToWorld3D(vitalPoint: VitalPoint): Position3D {\n // Infer region from vital point ID prefix\n const region = this.inferRegionFromId(vitalPoint.id);\n return this.pixel2DToWorld3D(vitalPoint.position, region);\n }\n \n /**\n * Infers the anatomical region from a vital point ID.\n * \n * **Korean**: 급소 ID에서 영역 추론\n * \n * @param vitalPointId - Vital point ID (e.g., \"head_temple\", \"torso_solar_plexus\")\n * @returns Anatomical region\n * \n * @private\n */\n private inferRegionFromId(vitalPointId: string): AnatomicalRegionPhysics {\n if (vitalPointId.startsWith(\"head_\")) return \"head\";\n if (vitalPointId.startsWith(\"neck_\")) return \"neck\";\n if (vitalPointId.startsWith(\"torso_\")) return \"torso\";\n if (vitalPointId.startsWith(\"arm_\")) return \"arms\";\n if (vitalPointId.startsWith(\"leg_\")) return \"legs\";\n \n // Default to torso if no clear region prefix\n return \"torso\";\n }\n \n /**\n * Calculates the distance between a 3D point and a vital point's 3D position.\n * \n * **Korean**: 거리 계산\n * \n * @param point3D - Point in 3D world space\n * @param vitalPoint - Vital point to measure distance to\n * @returns Distance in meters\n * \n * @example\n * ```typescript\n * const mapper = new CoordinateMapper();\n * const attackPoint = { x: 0, y: 1.5, z: 0.1 };\n * const temple = VITAL_POINTS_DATA.find(vp => vp.id === \"head_temple\");\n * const distance = mapper.distanceToVitalPoint(attackPoint, temple);\n * ```\n */\n distanceToVitalPoint(point3D: Position3D, vitalPoint: VitalPoint): number {\n const vitalPoint3D = this.vitalPointToWorld3D(vitalPoint);\n \n const dx = point3D.x - vitalPoint3D.x;\n const dy = point3D.y - vitalPoint3D.y;\n const dz = point3D.z - vitalPoint3D.z;\n \n return Math.sqrt(dx * dx + dy * dy + dz * dz);\n }\n \n /**\n * Finds the closest vital point to a 3D position within a given region.\n * \n * **Korean**: 가장 가까운 급소 찾기\n * \n * @param point3D - Point in 3D world space\n * @param vitalPoints - Array of vital points to search\n * @param region - Optional region filter\n * @returns Closest vital point and distance, or null if none found\n * \n * @example\n * ```typescript\n * const mapper = new CoordinateMapper();\n * const hitPoint = { x: 0, y: 1.7, z: 0.05 };\n * const result = mapper.findClosestVitalPoint(hitPoint, VITAL_POINTS_DATA, \"head\");\n * if (result) {\n * console.log(`Hit ${result.vitalPoint.names.english} at ${result.distance}m`);\n * }\n * ```\n */\n findClosestVitalPoint(\n point3D: Position3D,\n vitalPoints: readonly VitalPoint[],\n region?: AnatomicalRegionPhysics\n ): { vitalPoint: VitalPoint; distance: number } | null {\n let closestVitalPoint: VitalPoint | null = null;\n let closestDistance = Infinity;\n \n for (const vitalPoint of vitalPoints) {\n // Filter by region if specified\n if (region) {\n const vpRegion = this.inferRegionFromId(vitalPoint.id);\n if (vpRegion !== region) continue;\n }\n \n const distance = this.distanceToVitalPoint(point3D, vitalPoint);\n \n if (distance < closestDistance) {\n closestDistance = distance;\n closestVitalPoint = vitalPoint;\n }\n }\n \n if (closestVitalPoint === null) {\n return null;\n }\n \n return {\n vitalPoint: closestVitalPoint,\n distance: closestDistance,\n };\n }\n \n /**\n * Gets the current character model configuration.\n * \n * @returns Current configuration\n */\n getConfig(): CharacterModelConfig {\n return { ...this.config };\n }\n}\n\n/**\n * Default singleton instance for convenient access.\n * \n * **Korean**: 기본 인스턴스\n * \n * @example\n * ```typescript\n * import { defaultCoordinateMapper } from './CoordinateMapper';\n * \n * const worldPos = defaultCoordinateMapper.pixel2DToWorld3D({ x: 100, y: 50 }, \"head\");\n * ```\n */\nexport const defaultCoordinateMapper = new CoordinateMapper();\n"],"mappings":";;;;AAiGA,IAAM,2BAAiD;CACrD,QAAQ;CACR,OAAO;CACP,OAAO;CACP,cAAc;CACd,eAAe;AACjB;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA6BA,IAAM,uBAAgE;CACpE,MAAM;CACN,MAAM;CACN,OAAO;CACP,MAAM;CACN,MAAM;AACR;;;;;;;;;;;;AAaA,IAAa,mBAAb,MAA8B;CAC5B;;;;;;CAOA,YAAY,SAAwC,CAAC,GAAG;EACtD,KAAK,SAAS;GAAE,GAAG;GAA0B,GAAG;EAAO;CACzD;;;;;;;;;;;;;;;;;;;;;CAsBA,iBAAiB,SAAmB,QAA6C;EAE/E,MAAM,cAAc,QAAQ,IAAI,KAAK,OAAO;EAC5C,MAAM,cAAc,QAAQ,IAAI,KAAK,OAAO;EAa5C,OAAO;GACL,IAVc,cAAc,MAAO,KAAK,OAAO;GAW/C,IAPc,IAAI,eAAe,KAAK,OAAO;GAQ7C,GALa,qBAAqB;EAMpC;CACF;;;;;;;;;;;;;;;;;;;;CAqBA,iBAAiB,SAA+B;EAE9C,MAAM,cAAe,QAAQ,IAAI,KAAK,OAAO,QAAS;EAGtD,MAAM,cAAc,IAAK,QAAQ,IAAI,KAAK,OAAO;EAGjD,OAAO;GACL,GAAG,KAAK,MAAM,cAAc,KAAK,OAAO,YAAY;GACpD,GAAG,KAAK,MAAM,cAAc,KAAK,OAAO,aAAa;EACvD;CACF;;;;;;;;;;;;;;;;;;CAmBA,oBAAoB,YAAoC;EAEtD,MAAM,SAAS,KAAK,kBAAkB,WAAW,EAAE;EACnD,OAAO,KAAK,iBAAiB,WAAW,UAAU,MAAM;CAC1D;;;;;;;;;;;CAYA,kBAA0B,cAA+C;EACvE,IAAI,aAAa,WAAW,OAAO,GAAG,OAAO;EAC7C,IAAI,aAAa,WAAW,OAAO,GAAG,OAAO;EAC7C,IAAI,aAAa,WAAW,QAAQ,GAAG,OAAO;EAC9C,IAAI,aAAa,WAAW,MAAM,GAAG,OAAO;EAC5C,IAAI,aAAa,WAAW,MAAM,GAAG,OAAO;EAG5C,OAAO;CACT;;;;;;;;;;;;;;;;;;CAmBA,qBAAqB,SAAqB,YAAgC;EACxE,MAAM,eAAe,KAAK,oBAAoB,UAAU;EAExD,MAAM,KAAK,QAAQ,IAAI,aAAa;EACpC,MAAM,KAAK,QAAQ,IAAI,aAAa;EACpC,MAAM,KAAK,QAAQ,IAAI,aAAa;EAEpC,OAAO,KAAK,KAAK,KAAK,KAAK,KAAK,KAAK,KAAK,EAAE;CAC9C;;;;;;;;;;;;;;;;;;;;;CAsBA,sBACE,SACA,aACA,QACqD;EACrD,IAAI,oBAAuC;EAC3C,IAAI,kBAAkB;EAEtB,KAAK,MAAM,cAAc,aAAa;GAEpC,IAAI;QACe,KAAK,kBAAkB,WAAW,EAC/C,MAAa,QAAQ;GAAA;GAG3B,MAAM,WAAW,KAAK,qBAAqB,SAAS,UAAU;GAE9D,IAAI,WAAW,iBAAiB;IAC9B,kBAAkB;IAClB,oBAAoB;GACtB;EACF;EAEA,IAAI,sBAAsB,MACxB,OAAO;EAGT,OAAO;GACL,YAAY;GACZ,UAAU;EACZ;CACF;;;;;;CAOA,YAAkC;EAChC,OAAO,EAAE,GAAG,KAAK,OAAO;CAC1B;AACF;AAcuC,IAAI,iBAAiB"}
@@ -1 +1 @@
1
- {"version":3,"file":"KnockbackPhysics.js","names":[],"sources":["../../../src/systems/physics/KnockbackPhysics.ts"],"sourcesContent":["/**\n * Knockback Physics System for realistic force-based displacement.\n * \n * **Korean**: 밀침 물리 시스템 (Knockback Physics System)\n * \n * Implements realistic combat knockback mechanics that calculate force-based\n * displacement when players are hit. The system integrates with BalanceSystem\n * for authentic stumbling/falling reactions.\n * \n * ## Knockback Mechanics\n * \n * Knockback force is determined by:\n * - Attack damage and technique type\n * - Impact angle and direction\n * - Current balance state\n * - Stance resistance modifiers (8 trigram stances)\n * \n * ## Balance Integration\n * \n * - High balance (>70%): Reduced knockback by 30%\n * - Medium balance (40-70%): Normal knockback\n * - Low balance (<40%): Increased knockback by 50%, stumbling\n * - Critical balance (<20%): Guaranteed fall, 2s recovery\n * \n * ## Performance\n * \n * Optimized for 60fps with efficient vector calculations and smooth\n * easing curves for realistic knockback feel.\n * \n * @module systems/physics/KnockbackPhysics\n * @category Physics System\n * @korean 밀침물리\n */\n\nimport * as THREE from 'three';\nimport { TrigramStance } from '@/types/common';\n\n/**\n * Balance state information for knockback calculations.\n * \n * **Korean**: 균형 상태 (Balance State)\n * \n * @korean 균형상태\n */\nexport interface BalanceState {\n /** Current balance value (0-100) */\n readonly current: number;\n /** Maximum balance value (typically 100) */\n readonly max: number;\n}\n\n/**\n * Configuration for knockback force calculation.\n * \n * **Korean**: 밀침 설정 (Knockback Configuration)\n * \n * @korean 밀침설정\n */\nexport interface KnockbackConfig {\n /** Force magnitude in Newtons */\n readonly force: number;\n /** Normalized attack direction vector (attacker → defender) */\n readonly direction: THREE.Vector3;\n /** Duration of knockback effect in seconds */\n readonly duration: number;\n /** Current balance state */\n readonly balanceState: BalanceState;\n /** Current trigram stance */\n readonly currentStance: TrigramStance;\n}\n\n/**\n * Result of knockback calculation.\n * \n * **Korean**: 밀침 결과 (Knockback Result)\n * \n * Contains displacement vector, animation timing, and fall determination.\n * \n * @korean 밀침결과\n */\nexport interface KnockbackResult {\n /** Total knockback displacement vector in world space */\n readonly displacement: THREE.Vector3;\n /** Duration of knockback animation in seconds */\n readonly duration: number;\n /** Vulnerable state duration after knockback ends in seconds */\n readonly recoveryWindow: number;\n /** Whether fall animation should trigger */\n readonly shouldFall: boolean;\n}\n\n/**\n * Knockback Physics Engine.\n * \n * **Korean**: 밀침 물리 엔진\n * \n * Calculates realistic knockback displacement based on attack force,\n * stance resistance, and balance state. Integrates with BalanceSystem\n * for stumbling and falling mechanics.\n * \n * @example\n * ```typescript\n * const physics = new KnockbackPhysics();\n * \n * // Calculate knockback from heavy strike\n * const config: KnockbackConfig = {\n * force: 800, // 80 damage\n * direction: new THREE.Vector3(1, 0, 0).normalize(),\n * duration: 0.8,\n * balanceState: { current: 35, max: 100 }, // Low balance\n * currentStance: TrigramStance.LI, // Fire stance (low resistance)\n * };\n * \n * const result = physics.calculateKnockback(config, 80);\n * // Result: 2.5m base * 1.3 (Fire penalty) * 1.5 (low balance) = ~4.9m\n * // shouldFall: false (balance > 20%)\n * // recoveryWindow: 1.05s (0.7s * 1.5 for low balance)\n * ```\n * \n * @korean 밀침물리\n */\nexport class KnockbackPhysics {\n /**\n * Calculates knockback displacement and effects.\n * \n * **Korean**: 밀침 계산 (Calculate Knockback)\n * \n * Determines knockback distance, duration, and fall state based on:\n * 1. Base knockback from damage amount\n * 2. Stance resistance modifier (8 trigram effects)\n * 3. Balance state modifier (stumbling/falling)\n * 4. Recovery window (vulnerable state after)\n * \n * @param config - Knockback configuration with force and state\n * @param attackDamage - Damage dealt by attack (0-100+)\n * @returns Knockback result with displacement and timing\n * \n * @example\n * ```typescript\n * // Light strike on stable player\n * const light = physics.calculateKnockback({\n * force: 300,\n * direction: attackVector,\n * duration: 0.3,\n * balanceState: { current: 85, max: 100 },\n * currentStance: TrigramStance.GEON,\n * }, 30);\n * // Result: ~0.3m knockback (0.5m * 0.9 Geon * 0.7 high balance)\n * \n * // Critical strike on low-balance player\n * const critical = physics.calculateKnockback({\n * force: 1200,\n * direction: attackVector,\n * duration: 1.2,\n * balanceState: { current: 15, max: 100 },\n * currentStance: TrigramStance.SON,\n * }, 110);\n * // Result: ~9.6m knockback, shouldFall: true, 3.0s recovery\n * ```\n * \n * @korean 밀침계산\n */\n calculateKnockback(config: KnockbackConfig, attackDamage: number): KnockbackResult {\n // 1. Base knockback distance from damage\n let baseDistance = this.getBaseKnockbackDistance(attackDamage);\n \n // 2. Apply stance resistance modifier\n const stanceResistance = this.getStanceResistanceModifier(config.currentStance);\n baseDistance *= (1 - stanceResistance);\n \n // 3. Apply balance modifier (stumbling/falling)\n const balanceModifier = this.getBalanceModifier(config.balanceState);\n const finalDistance = baseDistance * balanceModifier;\n \n // 4. Calculate displacement vector\n const displacement = config.direction.clone().multiplyScalar(finalDistance);\n \n // 5. Determine duration based on distance\n const duration = this.calculateKnockbackDuration(finalDistance);\n \n // 6. Calculate recovery window (vulnerable state)\n const recoveryWindow = this.calculateRecoveryWindow(attackDamage, config.balanceState);\n \n // 7. Check if balance is critical (should fall)\n const shouldFall = config.balanceState.current < 20;\n \n return {\n displacement,\n duration,\n recoveryWindow,\n shouldFall,\n };\n }\n\n /**\n * Gets base knockback distance from damage amount.\n * \n * **Korean**: 기본 밀침 거리 (Base Knockback Distance)\n * \n * Damage thresholds:\n * - Light (20-40): 0.5m knockback, 0.3s duration\n * - Medium (40-70): 1.2m knockback, 0.5s duration\n * - Heavy (70-100): 2.5m knockback, 0.8s duration\n * - Critical (100+): 4.0m knockback, 1.2s duration + fall\n * \n * @param damage - Attack damage amount\n * @returns Base knockback distance in meters\n * \n * @private\n * @korean 기본밀침거리\n */\n private getBaseKnockbackDistance(damage: number): number {\n if (damage < 40) return 0.5; // Light strike\n if (damage < 70) return 1.2; // Medium strike\n if (damage < 100) return 2.5; // Heavy strike\n return 4.0; // Critical strike\n }\n\n /**\n * Gets stance resistance modifier for knockback.\n * \n * **Korean**: 자세 저항 배율 (Stance Resistance Modifier)\n * \n * Trigram stance resistance modifiers:\n * - ☶ 간 (Gan/Mountain): +40% resistance (defensive mastery)\n * - ☷ 곤 (Gon/Earth): +30% resistance (grounded techniques)\n * - ☰ 건 (Geon/Heaven): +10% resistance (balanced force)\n * - ☵ 감 (Gam/Water): 0% (neutral, adaptive)\n * - ☱ 태 (Tae/Lake): 0% (neutral, fluid)\n * - ☳ 진 (Jin/Thunder): -10% resistance (explosive power)\n * - ☴ 손 (Son/Wind): -20% resistance (fluid, mobile)\n * - ☲ 리 (Li/Fire): -30% resistance (aggressive, exposed)\n * \n * @param stance - Current trigram stance\n * @returns Resistance modifier (-0.3 to +0.4)\n * \n * @private\n * @korean 자세저항배율\n */\n private getStanceResistanceModifier(stance: TrigramStance): number {\n const resistances: Record<TrigramStance, number> = {\n [TrigramStance.GAN]: 0.40, // Mountain: +40% resistance\n [TrigramStance.GON]: 0.30, // Earth: +30% resistance\n [TrigramStance.GEON]: 0.10, // Heaven: +10% resistance\n [TrigramStance.GAM]: 0.00, // Water: neutral\n [TrigramStance.TAE]: 0.00, // Lake: neutral\n [TrigramStance.JIN]: -0.10, // Thunder: -10% resistance\n [TrigramStance.SON]: -0.20, // Wind: -20% resistance (fluid)\n [TrigramStance.LI]: -0.30, // Fire: -30% resistance (aggressive)\n };\n return resistances[stance];\n }\n\n /**\n * Gets balance modifier for knockback distance.\n * \n * **Korean**: 균형 배율 (Balance Modifier)\n * \n * Balance thresholds:\n * - High balance (>70%): 0.70x (30% reduction)\n * - Medium balance (40-70%): 1.00x (normal)\n * - Low balance (20-40%): 1.50x (50% increase, stumbling)\n * - Critical balance (<20%): 2.00x (100% increase, falling)\n * \n * @param balanceState - Current balance state\n * @returns Balance multiplier (0.7 to 2.0)\n * \n * @private\n * @korean 균형배율\n */\n private getBalanceModifier(balanceState: BalanceState): number {\n const balancePercent = (balanceState.current / balanceState.max) * 100;\n \n if (balancePercent > 70) return 0.70; // High balance: -30% knockback\n if (balancePercent > 40) return 1.00; // Normal knockback\n if (balancePercent > 20) return 1.50; // Low balance: +50% knockback\n return 2.00; // Critical balance: +100% knockback\n }\n\n /**\n * Calculates knockback animation duration.\n * \n * **Korean**: 밀침 지속시간 계산 (Calculate Knockback Duration)\n * \n * Linear relationship between distance and duration:\n * - 0.5m = 0.3s (light)\n * - 1.2m = 0.48s\n * - 2.5m = 0.86s\n * - 4.0m = 1.2s (critical)\n * \n * @param distance - Knockback distance in meters\n * @returns Duration in seconds\n * \n * @private\n * @korean 밀침지속시간계산\n */\n private calculateKnockbackDuration(distance: number): number {\n // Linear relationship: 0.5m = 0.3s, 4.0m = 1.2s\n return 0.3 + (distance / 4.0) * 0.9;\n }\n\n /**\n * Calculates recovery window (vulnerability duration).\n * \n * **Korean**: 회복 대기시간 계산 (Calculate Recovery Window)\n * \n * Recovery windows by damage:\n * - Light (<40): 0.2s vulnerability\n * - Medium (40-70): 0.4s vulnerability\n * - Heavy (70-100): 0.7s vulnerability\n * - Critical (100+): 1.5s vulnerability + grounded\n * \n * Low balance increases vulnerability by 50%.\n * \n * @param damage - Attack damage amount\n * @param balanceState - Current balance state\n * @returns Vulnerability duration in seconds\n * \n * @private\n * @korean 회복대기시간계산\n */\n private calculateRecoveryWindow(damage: number, balanceState: BalanceState): number {\n // Base recovery time from damage\n const baseRecoveryTime = damage < 40 ? 0.2 : damage < 70 ? 0.4 : damage < 100 ? 0.7 : 1.5;\n \n // Increase recovery time if balance is low (more vulnerable)\n const balancePercent = (balanceState.current / balanceState.max) * 100;\n const balanceModifier = balancePercent < 40 ? 1.5 : 1.0;\n \n return baseRecoveryTime * balanceModifier;\n }\n\n /**\n * Applies knockback force to player position over time.\n * \n * **Korean**: 밀침 힘 적용 (Apply Knockback Force)\n * \n * Uses smooth ease-out cubic curve for realistic knockback feel:\n * - Fast initial displacement (impact)\n * - Gradual deceleration (friction/resistance)\n * - Smooth stop at final position\n * \n * @param playerPosition - Current player position\n * @param result - Knockback result with displacement\n * @param deltaTime - Time elapsed since knockback started\n * @param progress - Knockback progress (0 to 1)\n * @returns New player position\n * \n * @example\n * ```typescript\n * // In game loop (60fps)\n * const newPosition = physics.applyKnockbackForce(\n * playerPosition,\n * knockbackResult,\n * deltaTime,\n * elapsedTime / knockbackResult.duration\n * );\n * \n * // Progress 0.0: At original position\n * // Progress 0.5: ~88% of total displacement (ease-out)\n * // Progress 1.0: At final position\n * ```\n * \n * @korean 밀침힘적용\n */\n applyKnockbackForce(\n playerPosition: THREE.Vector3,\n result: KnockbackResult,\n _deltaTime: number,\n progress: number\n ): THREE.Vector3 {\n // Clamp progress to [0, 1]\n const clampedProgress = Math.max(0, Math.min(1, progress));\n \n // Smooth knockback curve (ease-out cubic)\n // Fast initial impact, gradual deceleration\n const easedProgress = 1 - Math.pow(1 - clampedProgress, 3);\n \n // Calculate position along knockback path\n const currentDisplacement = result.displacement.clone().multiplyScalar(easedProgress);\n \n return playerPosition.clone().add(currentDisplacement);\n }\n\n /**\n * Checks if player is currently in knockback state.\n * \n * **Korean**: 밀침 상태 확인 (Check Knockback State)\n * \n * @param elapsedTime - Time elapsed since knockback started\n * @param duration - Total knockback duration\n * @returns True if still in knockback animation\n * \n * @korean 밀침상태확인\n */\n isInKnockback(elapsedTime: number, duration: number): boolean {\n return elapsedTime < duration;\n }\n\n /**\n * Checks if player is in recovery window (vulnerable state).\n * \n * **Korean**: 회복 대기 확인 (Check Recovery Window)\n * \n * @param elapsedTime - Time elapsed since knockback ended\n * @param recoveryWindow - Total recovery window duration\n * @returns True if in vulnerable recovery state\n * \n * @korean 회복대기확인\n */\n isInRecoveryWindow(elapsedTime: number, recoveryWindow: number): boolean {\n return elapsedTime < recoveryWindow;\n }\n\n /**\n * Gets bilingual Korean-English name for knockback state.\n * \n * **Korean**: 밀침 상태 이름 (Knockback State Name)\n * \n * @param shouldFall - Whether knockback causes fall\n * @returns Korean and English state names\n * \n * @korean 밀침상태이름\n */\n static getKnockbackStateName(shouldFall: boolean): { korean: string; english: string } {\n if (shouldFall) {\n return {\n korean: \"넘어짐\",\n english: \"Falling\",\n };\n }\n \n return {\n korean: \"밀침\",\n english: \"Knockback\",\n };\n }\n\n /**\n * Gets bilingual Korean-English name for recovery state.\n * \n * **Korean**: 회복 상태 이름 (Recovery State Name)\n * \n * @returns Korean and English state names\n * \n * @korean 회복상태이름\n */\n getRecoveryStateName(): { korean: string; english: string } {\n return {\n korean: \"회복\",\n english: \"Recovery\",\n };\n }\n\n /**\n * Gets bilingual Korean-English name for stumbling state.\n * \n * **Korean**: 휘청거림 상태 이름 (Stumbling State Name)\n * \n * @returns Korean and English state names\n * \n * @korean 휘청거림상태이름\n */\n getStumblingStateName(): { korean: string; english: string } {\n return {\n korean: \"휘청거림\",\n english: \"Stumbling\",\n };\n }\n}\n\nexport default KnockbackPhysics;\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAyHA,IAAa,mBAAb,MAA8B;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAyC5B,mBAAmB,QAAyB,cAAuC;EAEjF,IAAI,eAAe,KAAK,yBAAyB,aAAa;EAG9D,MAAM,mBAAmB,KAAK,4BAA4B,OAAO,cAAc;EAC/E,gBAAiB,IAAI;EAGrB,MAAM,kBAAkB,KAAK,mBAAmB,OAAO,aAAa;EACpE,MAAM,gBAAgB,eAAe;EAcrC,OAAO;GACL,cAZmB,OAAO,UAAU,OAAO,CAAC,eAAe,cAY3D;GACA,UAVe,KAAK,2BAA2B,cAU/C;GACA,gBARqB,KAAK,wBAAwB,cAAc,OAAO,aAQvE;GACA,YANiB,OAAO,aAAa,UAAU;GAOhD;;;;;;;;;;;;;;;;;;;CAoBH,yBAAiC,QAAwB;EACvD,IAAI,SAAS,IAAI,OAAO;EACxB,IAAI,SAAS,IAAI,OAAO;EACxB,IAAI,SAAS,KAAK,OAAO;EACzB,OAAO;;;;;;;;;;;;;;;;;;;;;;;CAwBT,4BAAoC,QAA+B;EAWjE,OAAO;IATJ,cAAc,MAAM;IACpB,cAAc,MAAM;IACpB,cAAc,OAAO;IACrB,cAAc,MAAM;IACpB,cAAc,MAAM;IACpB,cAAc,MAAM;IACpB,cAAc,MAAM;IACpB,cAAc,KAAK;GAEf,CAAY;;;;;;;;;;;;;;;;;;;CAoBrB,mBAA2B,cAAoC;EAC7D,MAAM,iBAAkB,aAAa,UAAU,aAAa,MAAO;EAEnE,IAAI,iBAAiB,IAAI,OAAO;EAChC,IAAI,iBAAiB,IAAI,OAAO;EAChC,IAAI,iBAAiB,IAAI,OAAO;EAChC,OAAO;;;;;;;;;;;;;;;;;;;CAoBT,2BAAmC,UAA0B;EAE3D,OAAO,KAAO,WAAW,IAAO;;;;;;;;;;;;;;;;;;;;;;CAuBlC,wBAAgC,QAAgB,cAAoC;EAQlF,QANyB,SAAS,KAAK,KAAM,SAAS,KAAK,KAAM,SAAS,MAAM,KAAM,QAG9D,aAAa,UAAU,aAAa,MAAO,MAC1B,KAAK,MAAM;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAsCtD,oBACE,gBACA,QACA,YACA,UACe;EAMf,MAAM,gBAAgB,IAAI,KAAK,IAAI,IAJX,KAAK,IAAI,GAAG,KAAK,IAAI,GAAG,SAAS,CAIlB,EAAiB,EAAE;EAG1D,MAAM,sBAAsB,OAAO,aAAa,OAAO,CAAC,eAAe,cAAc;EAErF,OAAO,eAAe,OAAO,CAAC,IAAI,oBAAoB;;;;;;;;;;;;;CAcxD,cAAc,aAAqB,UAA2B;EAC5D,OAAO,cAAc;;;;;;;;;;;;;CAcvB,mBAAmB,aAAqB,gBAAiC;EACvE,OAAO,cAAc;;;;;;;;;;;;CAavB,OAAO,sBAAsB,YAA0D;EACrF,IAAI,YACF,OAAO;GACL,QAAQ;GACR,SAAS;GACV;EAGH,OAAO;GACL,QAAQ;GACR,SAAS;GACV;;;;;;;;;;;CAYH,uBAA4D;EAC1D,OAAO;GACL,QAAQ;GACR,SAAS;GACV;;;;;;;;;;;CAYH,wBAA6D;EAC3D,OAAO;GACL,QAAQ;GACR,SAAS;GACV"}
1
+ {"version":3,"file":"KnockbackPhysics.js","names":[],"sources":["../../../src/systems/physics/KnockbackPhysics.ts"],"sourcesContent":["/**\n * Knockback Physics System for realistic force-based displacement.\n * \n * **Korean**: 밀침 물리 시스템 (Knockback Physics System)\n * \n * Implements realistic combat knockback mechanics that calculate force-based\n * displacement when players are hit. The system integrates with BalanceSystem\n * for authentic stumbling/falling reactions.\n * \n * ## Knockback Mechanics\n * \n * Knockback force is determined by:\n * - Attack damage and technique type\n * - Impact angle and direction\n * - Current balance state\n * - Stance resistance modifiers (8 trigram stances)\n * \n * ## Balance Integration\n * \n * - High balance (>70%): Reduced knockback by 30%\n * - Medium balance (40-70%): Normal knockback\n * - Low balance (<40%): Increased knockback by 50%, stumbling\n * - Critical balance (<20%): Guaranteed fall, 2s recovery\n * \n * ## Performance\n * \n * Optimized for 60fps with efficient vector calculations and smooth\n * easing curves for realistic knockback feel.\n * \n * @module systems/physics/KnockbackPhysics\n * @category Physics System\n * @korean 밀침물리\n */\n\nimport * as THREE from 'three';\nimport { TrigramStance } from '@/types/common';\n\n/**\n * Balance state information for knockback calculations.\n * \n * **Korean**: 균형 상태 (Balance State)\n * \n * @korean 균형상태\n */\nexport interface BalanceState {\n /** Current balance value (0-100) */\n readonly current: number;\n /** Maximum balance value (typically 100) */\n readonly max: number;\n}\n\n/**\n * Configuration for knockback force calculation.\n * \n * **Korean**: 밀침 설정 (Knockback Configuration)\n * \n * @korean 밀침설정\n */\nexport interface KnockbackConfig {\n /** Force magnitude in Newtons */\n readonly force: number;\n /** Normalized attack direction vector (attacker → defender) */\n readonly direction: THREE.Vector3;\n /** Duration of knockback effect in seconds */\n readonly duration: number;\n /** Current balance state */\n readonly balanceState: BalanceState;\n /** Current trigram stance */\n readonly currentStance: TrigramStance;\n}\n\n/**\n * Result of knockback calculation.\n * \n * **Korean**: 밀침 결과 (Knockback Result)\n * \n * Contains displacement vector, animation timing, and fall determination.\n * \n * @korean 밀침결과\n */\nexport interface KnockbackResult {\n /** Total knockback displacement vector in world space */\n readonly displacement: THREE.Vector3;\n /** Duration of knockback animation in seconds */\n readonly duration: number;\n /** Vulnerable state duration after knockback ends in seconds */\n readonly recoveryWindow: number;\n /** Whether fall animation should trigger */\n readonly shouldFall: boolean;\n}\n\n/**\n * Knockback Physics Engine.\n * \n * **Korean**: 밀침 물리 엔진\n * \n * Calculates realistic knockback displacement based on attack force,\n * stance resistance, and balance state. Integrates with BalanceSystem\n * for stumbling and falling mechanics.\n * \n * @example\n * ```typescript\n * const physics = new KnockbackPhysics();\n * \n * // Calculate knockback from heavy strike\n * const config: KnockbackConfig = {\n * force: 800, // 80 damage\n * direction: new THREE.Vector3(1, 0, 0).normalize(),\n * duration: 0.8,\n * balanceState: { current: 35, max: 100 }, // Low balance\n * currentStance: TrigramStance.LI, // Fire stance (low resistance)\n * };\n * \n * const result = physics.calculateKnockback(config, 80);\n * // Result: 2.5m base * 1.3 (Fire penalty) * 1.5 (low balance) = ~4.9m\n * // shouldFall: false (balance > 20%)\n * // recoveryWindow: 1.05s (0.7s * 1.5 for low balance)\n * ```\n * \n * @korean 밀침물리\n */\nexport class KnockbackPhysics {\n /**\n * Calculates knockback displacement and effects.\n * \n * **Korean**: 밀침 계산 (Calculate Knockback)\n * \n * Determines knockback distance, duration, and fall state based on:\n * 1. Base knockback from damage amount\n * 2. Stance resistance modifier (8 trigram effects)\n * 3. Balance state modifier (stumbling/falling)\n * 4. Recovery window (vulnerable state after)\n * \n * @param config - Knockback configuration with force and state\n * @param attackDamage - Damage dealt by attack (0-100+)\n * @returns Knockback result with displacement and timing\n * \n * @example\n * ```typescript\n * // Light strike on stable player\n * const light = physics.calculateKnockback({\n * force: 300,\n * direction: attackVector,\n * duration: 0.3,\n * balanceState: { current: 85, max: 100 },\n * currentStance: TrigramStance.GEON,\n * }, 30);\n * // Result: ~0.3m knockback (0.5m * 0.9 Geon * 0.7 high balance)\n * \n * // Critical strike on low-balance player\n * const critical = physics.calculateKnockback({\n * force: 1200,\n * direction: attackVector,\n * duration: 1.2,\n * balanceState: { current: 15, max: 100 },\n * currentStance: TrigramStance.SON,\n * }, 110);\n * // Result: ~9.6m knockback, shouldFall: true, 3.0s recovery\n * ```\n * \n * @korean 밀침계산\n */\n calculateKnockback(config: KnockbackConfig, attackDamage: number): KnockbackResult {\n // 1. Base knockback distance from damage\n let baseDistance = this.getBaseKnockbackDistance(attackDamage);\n \n // 2. Apply stance resistance modifier\n const stanceResistance = this.getStanceResistanceModifier(config.currentStance);\n baseDistance *= (1 - stanceResistance);\n \n // 3. Apply balance modifier (stumbling/falling)\n const balanceModifier = this.getBalanceModifier(config.balanceState);\n const finalDistance = baseDistance * balanceModifier;\n \n // 4. Calculate displacement vector\n const displacement = config.direction.clone().multiplyScalar(finalDistance);\n \n // 5. Determine duration based on distance\n const duration = this.calculateKnockbackDuration(finalDistance);\n \n // 6. Calculate recovery window (vulnerable state)\n const recoveryWindow = this.calculateRecoveryWindow(attackDamage, config.balanceState);\n \n // 7. Check if balance is critical (should fall)\n const shouldFall = config.balanceState.current < 20;\n \n return {\n displacement,\n duration,\n recoveryWindow,\n shouldFall,\n };\n }\n\n /**\n * Gets base knockback distance from damage amount.\n * \n * **Korean**: 기본 밀침 거리 (Base Knockback Distance)\n * \n * Damage thresholds:\n * - Light (20-40): 0.5m knockback, 0.3s duration\n * - Medium (40-70): 1.2m knockback, 0.5s duration\n * - Heavy (70-100): 2.5m knockback, 0.8s duration\n * - Critical (100+): 4.0m knockback, 1.2s duration + fall\n * \n * @param damage - Attack damage amount\n * @returns Base knockback distance in meters\n * \n * @private\n * @korean 기본밀침거리\n */\n private getBaseKnockbackDistance(damage: number): number {\n if (damage < 40) return 0.5; // Light strike\n if (damage < 70) return 1.2; // Medium strike\n if (damage < 100) return 2.5; // Heavy strike\n return 4.0; // Critical strike\n }\n\n /**\n * Gets stance resistance modifier for knockback.\n * \n * **Korean**: 자세 저항 배율 (Stance Resistance Modifier)\n * \n * Trigram stance resistance modifiers:\n * - ☶ 간 (Gan/Mountain): +40% resistance (defensive mastery)\n * - ☷ 곤 (Gon/Earth): +30% resistance (grounded techniques)\n * - ☰ 건 (Geon/Heaven): +10% resistance (balanced force)\n * - ☵ 감 (Gam/Water): 0% (neutral, adaptive)\n * - ☱ 태 (Tae/Lake): 0% (neutral, fluid)\n * - ☳ 진 (Jin/Thunder): -10% resistance (explosive power)\n * - ☴ 손 (Son/Wind): -20% resistance (fluid, mobile)\n * - ☲ 리 (Li/Fire): -30% resistance (aggressive, exposed)\n * \n * @param stance - Current trigram stance\n * @returns Resistance modifier (-0.3 to +0.4)\n * \n * @private\n * @korean 자세저항배율\n */\n private getStanceResistanceModifier(stance: TrigramStance): number {\n const resistances: Record<TrigramStance, number> = {\n [TrigramStance.GAN]: 0.40, // Mountain: +40% resistance\n [TrigramStance.GON]: 0.30, // Earth: +30% resistance\n [TrigramStance.GEON]: 0.10, // Heaven: +10% resistance\n [TrigramStance.GAM]: 0.00, // Water: neutral\n [TrigramStance.TAE]: 0.00, // Lake: neutral\n [TrigramStance.JIN]: -0.10, // Thunder: -10% resistance\n [TrigramStance.SON]: -0.20, // Wind: -20% resistance (fluid)\n [TrigramStance.LI]: -0.30, // Fire: -30% resistance (aggressive)\n };\n return resistances[stance];\n }\n\n /**\n * Gets balance modifier for knockback distance.\n * \n * **Korean**: 균형 배율 (Balance Modifier)\n * \n * Balance thresholds:\n * - High balance (>70%): 0.70x (30% reduction)\n * - Medium balance (40-70%): 1.00x (normal)\n * - Low balance (20-40%): 1.50x (50% increase, stumbling)\n * - Critical balance (<20%): 2.00x (100% increase, falling)\n * \n * @param balanceState - Current balance state\n * @returns Balance multiplier (0.7 to 2.0)\n * \n * @private\n * @korean 균형배율\n */\n private getBalanceModifier(balanceState: BalanceState): number {\n const balancePercent = (balanceState.current / balanceState.max) * 100;\n \n if (balancePercent > 70) return 0.70; // High balance: -30% knockback\n if (balancePercent > 40) return 1.00; // Normal knockback\n if (balancePercent > 20) return 1.50; // Low balance: +50% knockback\n return 2.00; // Critical balance: +100% knockback\n }\n\n /**\n * Calculates knockback animation duration.\n * \n * **Korean**: 밀침 지속시간 계산 (Calculate Knockback Duration)\n * \n * Linear relationship between distance and duration:\n * - 0.5m = 0.3s (light)\n * - 1.2m = 0.48s\n * - 2.5m = 0.86s\n * - 4.0m = 1.2s (critical)\n * \n * @param distance - Knockback distance in meters\n * @returns Duration in seconds\n * \n * @private\n * @korean 밀침지속시간계산\n */\n private calculateKnockbackDuration(distance: number): number {\n // Linear relationship: 0.5m = 0.3s, 4.0m = 1.2s\n return 0.3 + (distance / 4.0) * 0.9;\n }\n\n /**\n * Calculates recovery window (vulnerability duration).\n * \n * **Korean**: 회복 대기시간 계산 (Calculate Recovery Window)\n * \n * Recovery windows by damage:\n * - Light (<40): 0.2s vulnerability\n * - Medium (40-70): 0.4s vulnerability\n * - Heavy (70-100): 0.7s vulnerability\n * - Critical (100+): 1.5s vulnerability + grounded\n * \n * Low balance increases vulnerability by 50%.\n * \n * @param damage - Attack damage amount\n * @param balanceState - Current balance state\n * @returns Vulnerability duration in seconds\n * \n * @private\n * @korean 회복대기시간계산\n */\n private calculateRecoveryWindow(damage: number, balanceState: BalanceState): number {\n // Base recovery time from damage\n const baseRecoveryTime = damage < 40 ? 0.2 : damage < 70 ? 0.4 : damage < 100 ? 0.7 : 1.5;\n \n // Increase recovery time if balance is low (more vulnerable)\n const balancePercent = (balanceState.current / balanceState.max) * 100;\n const balanceModifier = balancePercent < 40 ? 1.5 : 1.0;\n \n return baseRecoveryTime * balanceModifier;\n }\n\n /**\n * Applies knockback force to player position over time.\n * \n * **Korean**: 밀침 힘 적용 (Apply Knockback Force)\n * \n * Uses smooth ease-out cubic curve for realistic knockback feel:\n * - Fast initial displacement (impact)\n * - Gradual deceleration (friction/resistance)\n * - Smooth stop at final position\n * \n * @param playerPosition - Current player position\n * @param result - Knockback result with displacement\n * @param deltaTime - Time elapsed since knockback started\n * @param progress - Knockback progress (0 to 1)\n * @returns New player position\n * \n * @example\n * ```typescript\n * // In game loop (60fps)\n * const newPosition = physics.applyKnockbackForce(\n * playerPosition,\n * knockbackResult,\n * deltaTime,\n * elapsedTime / knockbackResult.duration\n * );\n * \n * // Progress 0.0: At original position\n * // Progress 0.5: ~88% of total displacement (ease-out)\n * // Progress 1.0: At final position\n * ```\n * \n * @korean 밀침힘적용\n */\n applyKnockbackForce(\n playerPosition: THREE.Vector3,\n result: KnockbackResult,\n _deltaTime: number,\n progress: number\n ): THREE.Vector3 {\n // Clamp progress to [0, 1]\n const clampedProgress = Math.max(0, Math.min(1, progress));\n \n // Smooth knockback curve (ease-out cubic)\n // Fast initial impact, gradual deceleration\n const easedProgress = 1 - Math.pow(1 - clampedProgress, 3);\n \n // Calculate position along knockback path\n const currentDisplacement = result.displacement.clone().multiplyScalar(easedProgress);\n \n return playerPosition.clone().add(currentDisplacement);\n }\n\n /**\n * Checks if player is currently in knockback state.\n * \n * **Korean**: 밀침 상태 확인 (Check Knockback State)\n * \n * @param elapsedTime - Time elapsed since knockback started\n * @param duration - Total knockback duration\n * @returns True if still in knockback animation\n * \n * @korean 밀침상태확인\n */\n isInKnockback(elapsedTime: number, duration: number): boolean {\n return elapsedTime < duration;\n }\n\n /**\n * Checks if player is in recovery window (vulnerable state).\n * \n * **Korean**: 회복 대기 확인 (Check Recovery Window)\n * \n * @param elapsedTime - Time elapsed since knockback ended\n * @param recoveryWindow - Total recovery window duration\n * @returns True if in vulnerable recovery state\n * \n * @korean 회복대기확인\n */\n isInRecoveryWindow(elapsedTime: number, recoveryWindow: number): boolean {\n return elapsedTime < recoveryWindow;\n }\n\n /**\n * Gets bilingual Korean-English name for knockback state.\n * \n * **Korean**: 밀침 상태 이름 (Knockback State Name)\n * \n * @param shouldFall - Whether knockback causes fall\n * @returns Korean and English state names\n * \n * @korean 밀침상태이름\n */\n static getKnockbackStateName(shouldFall: boolean): { korean: string; english: string } {\n if (shouldFall) {\n return {\n korean: \"넘어짐\",\n english: \"Falling\",\n };\n }\n \n return {\n korean: \"밀침\",\n english: \"Knockback\",\n };\n }\n\n /**\n * Gets bilingual Korean-English name for recovery state.\n * \n * **Korean**: 회복 상태 이름 (Recovery State Name)\n * \n * @returns Korean and English state names\n * \n * @korean 회복상태이름\n */\n getRecoveryStateName(): { korean: string; english: string } {\n return {\n korean: \"회복\",\n english: \"Recovery\",\n };\n }\n\n /**\n * Gets bilingual Korean-English name for stumbling state.\n * \n * **Korean**: 휘청거림 상태 이름 (Stumbling State Name)\n * \n * @returns Korean and English state names\n * \n * @korean 휘청거림상태이름\n */\n getStumblingStateName(): { korean: string; english: string } {\n return {\n korean: \"휘청거림\",\n english: \"Stumbling\",\n };\n }\n}\n\nexport default KnockbackPhysics;\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAyHA,IAAa,mBAAb,MAA8B;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAyC5B,mBAAmB,QAAyB,cAAuC;EAEjF,IAAI,eAAe,KAAK,yBAAyB,YAAY;EAG7D,MAAM,mBAAmB,KAAK,4BAA4B,OAAO,aAAa;EAC9E,gBAAiB,IAAI;EAGrB,MAAM,kBAAkB,KAAK,mBAAmB,OAAO,YAAY;EACnE,MAAM,gBAAgB,eAAe;EAcrC,OAAO;GACL,cAZmB,OAAO,UAAU,MAAM,EAAE,eAAe,aAY3D;GACA,UAVe,KAAK,2BAA2B,aAU/C;GACA,gBARqB,KAAK,wBAAwB,cAAc,OAAO,YAQvE;GACA,YANiB,OAAO,aAAa,UAAU;EAOjD;CACF;;;;;;;;;;;;;;;;;;CAmBA,yBAAiC,QAAwB;EACvD,IAAI,SAAS,IAAI,OAAO;EACxB,IAAI,SAAS,IAAI,OAAO;EACxB,IAAI,SAAS,KAAK,OAAO;EACzB,OAAO;CACT;;;;;;;;;;;;;;;;;;;;;;CAuBA,4BAAoC,QAA+B;EAWjE,OAAO;IATJ,cAAc,MAAM;IACpB,cAAc,MAAM;IACpB,cAAc,OAAO;IACrB,cAAc,MAAM;IACpB,cAAc,MAAM;IACpB,cAAc,MAAM;IACpB,cAAc,MAAM;IACpB,cAAc,KAAK;EAEf,EAAY;CACrB;;;;;;;;;;;;;;;;;;CAmBA,mBAA2B,cAAoC;EAC7D,MAAM,iBAAkB,aAAa,UAAU,aAAa,MAAO;EAEnE,IAAI,iBAAiB,IAAI,OAAO;EAChC,IAAI,iBAAiB,IAAI,OAAO;EAChC,IAAI,iBAAiB,IAAI,OAAO;EAChC,OAAO;CACT;;;;;;;;;;;;;;;;;;CAmBA,2BAAmC,UAA0B;EAE3D,OAAO,KAAO,WAAW,IAAO;CAClC;;;;;;;;;;;;;;;;;;;;;CAsBA,wBAAgC,QAAgB,cAAoC;EAQlF,QANyB,SAAS,KAAK,KAAM,SAAS,KAAK,KAAM,SAAS,MAAM,KAAM,QAG9D,aAAa,UAAU,aAAa,MAAO,MAC1B,KAAK,MAAM;CAGtD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAmCA,oBACE,gBACA,QACA,YACA,UACe;EAMf,MAAM,gBAAgB,IAAI,KAAK,IAAI,IAJX,KAAK,IAAI,GAAG,KAAK,IAAI,GAAG,QAAQ,CAIjB,GAAiB,CAAC;EAGzD,MAAM,sBAAsB,OAAO,aAAa,MAAM,EAAE,eAAe,aAAa;EAEpF,OAAO,eAAe,MAAM,EAAE,IAAI,mBAAmB;CACvD;;;;;;;;;;;;CAaA,cAAc,aAAqB,UAA2B;EAC5D,OAAO,cAAc;CACvB;;;;;;;;;;;;CAaA,mBAAmB,aAAqB,gBAAiC;EACvE,OAAO,cAAc;CACvB;;;;;;;;;;;CAYA,OAAO,sBAAsB,YAA0D;EACrF,IAAI,YACF,OAAO;GACL,QAAQ;GACR,SAAS;EACX;EAGF,OAAO;GACL,QAAQ;GACR,SAAS;EACX;CACF;;;;;;;;;;CAWA,uBAA4D;EAC1D,OAAO;GACL,QAAQ;GACR,SAAS;EACX;CACF;;;;;;;;;;CAWA,wBAA6D;EAC3D,OAAO;GACL,QAAQ;GACR,SAAS;EACX;CACF;AACF"}