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":"useWebGLContextLossHandler.js","names":[],"sources":["../../src/hooks/useWebGLContextLossHandler.ts"],"sourcesContent":["/**\n * useWebGLContextLossHandler - React hook for handling WebGL context loss\n *\n * This hook sets up event listeners for WebGL context loss and restoration,\n * which can occur due to GPU issues, memory pressure, or browser tab switching.\n *\n * @example\n * ```tsx\n * const Canvas = () => {\n * useWebGLContextLossHandler();\n * return <Canvas>...</Canvas>;\n * };\n * ```\n */\n\nimport type React from \"react\";\nimport { useCallback, useEffect, useRef, useState } from \"react\";\n\n/**\n * Global WebGL context state tracking\n * Helps coordinate context recovery across multiple Canvas components\n */\nlet globalContextLossCount = 0;\nlet globalIsContextLost = false;\nlet lastContextLossTime = 0;\n\n/**\n * Minimum delay between context loss events to prevent thrashing\n */\nexport const MIN_CONTEXT_RECOVERY_DELAY = 100; // ms\n\n/**\n * Get global WebGL context state\n */\nexport const getWebGLContextState = (): {\n isLost: boolean;\n lossCount: number;\n timeSinceLastLoss: number;\n} => ({\n isLost: globalIsContextLost,\n lossCount: globalContextLossCount,\n timeSinceLastLoss: Date.now() - lastContextLossTime,\n});\n\nexport interface WebGLContextLossOptions {\n /**\n * Callback when context is lost\n */\n readonly onContextLost?: () => void;\n\n /**\n * Callback when context is restored\n */\n readonly onContextRestored?: () => void;\n\n /**\n * Whether to attempt automatic restoration (default: true)\n */\n readonly autoRestore?: boolean;\n\n /**\n * Optional canvas ref to attach to a specific canvas element\n * If not provided, will query for the first canvas in the document\n */\n readonly canvasRef?: React.RefObject<HTMLCanvasElement>;\n\n /**\n * Delay in ms before attempting to query for canvas (default: 50)\n * Helps avoid race conditions during screen transitions\n */\n readonly mountDelay?: number;\n\n /**\n * Maximum attempts to find canvas element (default: 5)\n */\n readonly maxRetries?: number;\n}\n\nexport interface WebGLContextState {\n /** Whether context is currently lost */\n readonly isContextLost: boolean;\n /** Number of times context has been lost */\n readonly lossCount: number;\n /** Whether the canvas element has been found */\n readonly isCanvasMounted: boolean;\n}\n\n/**\n * Hook to handle WebGL context loss and restoration\n * Returns state information about the WebGL context\n */\nexport const useWebGLContextLossHandler = (\n options: WebGLContextLossOptions = {}\n): WebGLContextState => {\n const {\n onContextLost,\n onContextRestored,\n autoRestore = true,\n canvasRef,\n mountDelay = 50,\n maxRetries = 5,\n } = options;\n\n const [contextState, setContextState] = useState<WebGLContextState>({\n isContextLost: globalIsContextLost,\n lossCount: globalContextLossCount,\n isCanvasMounted: false,\n });\n\n // Use refs to store the latest callbacks to avoid re-registering event listeners\n const onContextLostRef = useRef(onContextLost);\n const onContextRestoredRef = useRef(onContextRestored);\n const retryCountRef = useRef(0);\n const mountedRef = useRef(true);\n\n // Update refs when callbacks change\n useEffect(() => {\n onContextLostRef.current = onContextLost;\n onContextRestoredRef.current = onContextRestored;\n });\n\n // Track component mount state\n useEffect(() => {\n mountedRef.current = true;\n return () => {\n mountedRef.current = false;\n };\n }, []);\n\n const handleContextLost = useCallback(\n (event: Event) => {\n const now = Date.now();\n globalIsContextLost = true;\n globalContextLossCount++;\n lastContextLossTime = now;\n\n console.warn(\n `⚠️ WebGL context lost (count: ${globalContextLossCount}, time since mount: ${\n now - lastContextLossTime\n }ms)`\n );\n\n // Prevent default behavior to allow restoration\n if (autoRestore) {\n event.preventDefault();\n }\n\n if (mountedRef.current) {\n setContextState({\n isContextLost: true,\n lossCount: globalContextLossCount,\n isCanvasMounted: true,\n });\n }\n\n onContextLostRef.current?.();\n },\n [autoRestore]\n );\n\n const handleContextRestored = useCallback(() => {\n globalIsContextLost = false;\n console.log(\"✅ WebGL context restored successfully\");\n\n if (mountedRef.current) {\n setContextState((prev) => ({\n ...prev,\n isContextLost: false,\n }));\n }\n\n onContextRestoredRef.current?.();\n }, []);\n\n useEffect(() => {\n let canvas = canvasRef?.current ?? document.querySelector(\"canvas\");\n let cleanupFn: (() => void) | undefined;\n let retryTimeout: ReturnType<typeof setTimeout> | undefined;\n let observer: MutationObserver | undefined;\n\n const attachListeners = (canvasEl: HTMLCanvasElement) => {\n canvasEl.addEventListener(\"webglcontextlost\", handleContextLost, false);\n canvasEl.addEventListener(\n \"webglcontextrestored\",\n handleContextRestored,\n false\n );\n\n if (mountedRef.current) {\n setContextState((prev) => ({\n ...prev,\n isCanvasMounted: true,\n }));\n }\n\n return () => {\n canvasEl.removeEventListener(\"webglcontextlost\", handleContextLost);\n canvasEl.removeEventListener(\n \"webglcontextrestored\",\n handleContextRestored\n );\n };\n };\n\n // Retry finding canvas with delay to handle screen transitions\n const tryFindCanvas = () => {\n if (!mountedRef.current) return;\n\n canvas = canvasRef?.current ?? document.querySelector(\"canvas\");\n if (canvas) {\n cleanupFn = attachListeners(canvas);\n retryCountRef.current = 0;\n } else if (retryCountRef.current < maxRetries) {\n retryCountRef.current++;\n retryTimeout = setTimeout(tryFindCanvas, mountDelay);\n } else {\n // Fall back to MutationObserver\n observer = new MutationObserver(() => {\n canvas = document.querySelector(\"canvas\");\n if (canvas && mountedRef.current) {\n observer?.disconnect();\n cleanupFn = attachListeners(canvas);\n }\n });\n\n observer.observe(document.body, {\n childList: true,\n subtree: true,\n });\n }\n };\n\n // Attach immediately if a canvas is already mounted, otherwise retry\n canvas = canvasRef?.current ?? document.querySelector(\"canvas\");\n if (canvas) {\n cleanupFn = attachListeners(canvas);\n } else {\n retryTimeout = setTimeout(tryFindCanvas, mountDelay);\n }\n\n // Cleanup\n return () => {\n if (retryTimeout) clearTimeout(retryTimeout);\n observer?.disconnect();\n cleanupFn?.();\n };\n }, [\n autoRestore,\n canvasRef,\n handleContextLost,\n handleContextRestored,\n maxRetries,\n mountDelay,\n ]);\n\n return contextState;\n};\n\n/**\n * Check if WebGL is available in the current browser\n */\nexport const isWebGLAvailable = (): boolean => {\n try {\n const canvas = document.createElement(\"canvas\");\n const gl =\n canvas.getContext(\"webgl\") ?? canvas.getContext(\"experimental-webgl\");\n const available = gl !== null;\n // Help GC by cleaning up WebGL context\n if (gl && \"getExtension\" in gl) {\n const loseContext = gl.getExtension(\"WEBGL_lose_context\");\n loseContext?.loseContext();\n }\n return available;\n } catch {\n return false;\n }\n};\n\n/**\n * Check if WebGL2 is available in the current browser\n */\nexport const isWebGL2Available = (): boolean => {\n try {\n const canvas = document.createElement(\"canvas\");\n const gl = canvas.getContext(\"webgl2\");\n const available = gl !== null;\n // Help GC by cleaning up WebGL context\n if (gl && \"getExtension\" in gl) {\n const loseContext = gl.getExtension(\"WEBGL_lose_context\");\n loseContext?.loseContext();\n }\n return available;\n } catch {\n return false;\n }\n};\n"],"mappings":"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"}
1
+ {"version":3,"file":"useWebGLContextLossHandler.js","names":[],"sources":["../../src/hooks/useWebGLContextLossHandler.ts"],"sourcesContent":["/**\n * useWebGLContextLossHandler - React hook for handling WebGL context loss\n *\n * This hook sets up event listeners for WebGL context loss and restoration,\n * which can occur due to GPU issues, memory pressure, or browser tab switching.\n *\n * @example\n * ```tsx\n * const Canvas = () => {\n * useWebGLContextLossHandler();\n * return <Canvas>...</Canvas>;\n * };\n * ```\n */\n\nimport type React from \"react\";\nimport { useCallback, useEffect, useRef, useState } from \"react\";\n\n/**\n * Global WebGL context state tracking\n * Helps coordinate context recovery across multiple Canvas components\n */\nlet globalContextLossCount = 0;\nlet globalIsContextLost = false;\nlet lastContextLossTime = 0;\n\n/**\n * Minimum delay between context loss events to prevent thrashing\n */\nexport const MIN_CONTEXT_RECOVERY_DELAY = 100; // ms\n\n/**\n * Get global WebGL context state\n */\nexport const getWebGLContextState = (): {\n isLost: boolean;\n lossCount: number;\n timeSinceLastLoss: number;\n} => ({\n isLost: globalIsContextLost,\n lossCount: globalContextLossCount,\n timeSinceLastLoss: Date.now() - lastContextLossTime,\n});\n\nexport interface WebGLContextLossOptions {\n /**\n * Callback when context is lost\n */\n readonly onContextLost?: () => void;\n\n /**\n * Callback when context is restored\n */\n readonly onContextRestored?: () => void;\n\n /**\n * Whether to attempt automatic restoration (default: true)\n */\n readonly autoRestore?: boolean;\n\n /**\n * Optional canvas ref to attach to a specific canvas element\n * If not provided, will query for the first canvas in the document\n */\n readonly canvasRef?: React.RefObject<HTMLCanvasElement>;\n\n /**\n * Delay in ms before attempting to query for canvas (default: 50)\n * Helps avoid race conditions during screen transitions\n */\n readonly mountDelay?: number;\n\n /**\n * Maximum attempts to find canvas element (default: 5)\n */\n readonly maxRetries?: number;\n}\n\nexport interface WebGLContextState {\n /** Whether context is currently lost */\n readonly isContextLost: boolean;\n /** Number of times context has been lost */\n readonly lossCount: number;\n /** Whether the canvas element has been found */\n readonly isCanvasMounted: boolean;\n}\n\n/**\n * Hook to handle WebGL context loss and restoration\n * Returns state information about the WebGL context\n */\nexport const useWebGLContextLossHandler = (\n options: WebGLContextLossOptions = {}\n): WebGLContextState => {\n const {\n onContextLost,\n onContextRestored,\n autoRestore = true,\n canvasRef,\n mountDelay = 50,\n maxRetries = 5,\n } = options;\n\n const [contextState, setContextState] = useState<WebGLContextState>({\n isContextLost: globalIsContextLost,\n lossCount: globalContextLossCount,\n isCanvasMounted: false,\n });\n\n // Use refs to store the latest callbacks to avoid re-registering event listeners\n const onContextLostRef = useRef(onContextLost);\n const onContextRestoredRef = useRef(onContextRestored);\n const retryCountRef = useRef(0);\n const mountedRef = useRef(true);\n\n // Update refs when callbacks change\n useEffect(() => {\n onContextLostRef.current = onContextLost;\n onContextRestoredRef.current = onContextRestored;\n });\n\n // Track component mount state\n useEffect(() => {\n mountedRef.current = true;\n return () => {\n mountedRef.current = false;\n };\n }, []);\n\n const handleContextLost = useCallback(\n (event: Event) => {\n const now = Date.now();\n globalIsContextLost = true;\n globalContextLossCount++;\n lastContextLossTime = now;\n\n console.warn(\n `⚠️ WebGL context lost (count: ${globalContextLossCount}, time since mount: ${\n now - lastContextLossTime\n }ms)`\n );\n\n // Prevent default behavior to allow restoration\n if (autoRestore) {\n event.preventDefault();\n }\n\n if (mountedRef.current) {\n setContextState({\n isContextLost: true,\n lossCount: globalContextLossCount,\n isCanvasMounted: true,\n });\n }\n\n onContextLostRef.current?.();\n },\n [autoRestore]\n );\n\n const handleContextRestored = useCallback(() => {\n globalIsContextLost = false;\n console.log(\"✅ WebGL context restored successfully\");\n\n if (mountedRef.current) {\n setContextState((prev) => ({\n ...prev,\n isContextLost: false,\n }));\n }\n\n onContextRestoredRef.current?.();\n }, []);\n\n useEffect(() => {\n let canvas = canvasRef?.current ?? document.querySelector(\"canvas\");\n let cleanupFn: (() => void) | undefined;\n let retryTimeout: ReturnType<typeof setTimeout> | undefined;\n let observer: MutationObserver | undefined;\n\n const attachListeners = (canvasEl: HTMLCanvasElement) => {\n canvasEl.addEventListener(\"webglcontextlost\", handleContextLost, false);\n canvasEl.addEventListener(\n \"webglcontextrestored\",\n handleContextRestored,\n false\n );\n\n if (mountedRef.current) {\n setContextState((prev) => ({\n ...prev,\n isCanvasMounted: true,\n }));\n }\n\n return () => {\n canvasEl.removeEventListener(\"webglcontextlost\", handleContextLost);\n canvasEl.removeEventListener(\n \"webglcontextrestored\",\n handleContextRestored\n );\n };\n };\n\n // Retry finding canvas with delay to handle screen transitions\n const tryFindCanvas = () => {\n if (!mountedRef.current) return;\n\n canvas = canvasRef?.current ?? document.querySelector(\"canvas\");\n if (canvas) {\n cleanupFn = attachListeners(canvas);\n retryCountRef.current = 0;\n } else if (retryCountRef.current < maxRetries) {\n retryCountRef.current++;\n retryTimeout = setTimeout(tryFindCanvas, mountDelay);\n } else {\n // Fall back to MutationObserver\n observer = new MutationObserver(() => {\n canvas = document.querySelector(\"canvas\");\n if (canvas && mountedRef.current) {\n observer?.disconnect();\n cleanupFn = attachListeners(canvas);\n }\n });\n\n observer.observe(document.body, {\n childList: true,\n subtree: true,\n });\n }\n };\n\n // Attach immediately if a canvas is already mounted, otherwise retry\n canvas = canvasRef?.current ?? document.querySelector(\"canvas\");\n if (canvas) {\n cleanupFn = attachListeners(canvas);\n } else {\n retryTimeout = setTimeout(tryFindCanvas, mountDelay);\n }\n\n // Cleanup\n return () => {\n if (retryTimeout) clearTimeout(retryTimeout);\n observer?.disconnect();\n cleanupFn?.();\n };\n }, [\n autoRestore,\n canvasRef,\n handleContextLost,\n handleContextRestored,\n maxRetries,\n mountDelay,\n ]);\n\n return contextState;\n};\n\n/**\n * Check if WebGL is available in the current browser\n */\nexport const isWebGLAvailable = (): boolean => {\n try {\n const canvas = document.createElement(\"canvas\");\n const gl =\n canvas.getContext(\"webgl\") ?? canvas.getContext(\"experimental-webgl\");\n const available = gl !== null;\n // Help GC by cleaning up WebGL context\n if (gl && \"getExtension\" in gl) {\n const loseContext = gl.getExtension(\"WEBGL_lose_context\");\n loseContext?.loseContext();\n }\n return available;\n } catch {\n return false;\n }\n};\n\n/**\n * Check if WebGL2 is available in the current browser\n */\nexport const isWebGL2Available = (): boolean => {\n try {\n const canvas = document.createElement(\"canvas\");\n const gl = canvas.getContext(\"webgl2\");\n const available = gl !== null;\n // Help GC by cleaning up WebGL context\n if (gl && \"getExtension\" in gl) {\n const loseContext = gl.getExtension(\"WEBGL_lose_context\");\n loseContext?.loseContext();\n }\n return available;\n } catch {\n return false;\n }\n};\n"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"useWindowSize.js","names":[],"sources":["../../src/hooks/useWindowSize.ts"],"sourcesContent":["/**\n * useWindowSize - Shared hook for responsive window dimensions\n *\n * @korean 윈도우크기훅 - 반응형 창 크기를 위한 공유 훅\n *\n * Eliminates duplication across screen components\n */\n\nimport { useCallback, useEffect, useState } from \"react\";\n\nexport interface WindowSize {\n readonly width: number;\n readonly height: number;\n}\n\nexport interface UseWindowSizeOptions {\n /**\n * Initial width if window is not available (SSR)\n * @default 1200\n */\n readonly initialWidth?: number;\n\n /**\n * Initial height if window is not available (SSR)\n * @default 800\n */\n readonly initialHeight?: number;\n\n /**\n * Debounce delay in milliseconds\n * @default 0\n */\n readonly debounceMs?: number;\n}\n\n/**\n * Hook to track window dimensions with optional debouncing\n *\n * @korean 윈도우 크기를 추적하는 훅 (선택적 디바운싱 지원)\n *\n * @param options - Configuration options\n * @returns Current window dimensions\n *\n * @example\n * ```tsx\n * const { width, height } = useWindowSize();\n * const isMobile = width < 768;\n * ```\n */\nexport function useWindowSize(options: UseWindowSizeOptions = {}): WindowSize {\n const { initialWidth = 1200, initialHeight = 800, debounceMs = 0 } = options;\n\n const [size, setSize] = useState<WindowSize>(() => {\n // Check if window is available (SSR safety)\n if (typeof window !== \"undefined\") {\n return {\n width: window.innerWidth,\n height: window.innerHeight,\n };\n }\n return {\n width: initialWidth,\n height: initialHeight,\n };\n });\n\n const handleResize = useCallback(() => {\n setSize({\n width: window.innerWidth,\n height: window.innerHeight,\n });\n }, []);\n\n useEffect(() => {\n // Safety check for SSR\n if (typeof window === \"undefined\") return;\n\n let timeoutId: ReturnType<typeof setTimeout> | null = null;\n\n const debouncedResize = () => {\n if (debounceMs > 0) {\n if (timeoutId) clearTimeout(timeoutId);\n timeoutId = setTimeout(handleResize, debounceMs);\n } else {\n handleResize();\n }\n };\n\n window.addEventListener(\"resize\", debouncedResize);\n\n return () => {\n window.removeEventListener(\"resize\", debouncedResize);\n if (timeoutId) clearTimeout(timeoutId);\n };\n }, [handleResize, debounceMs]);\n\n return size;\n}\n\n/**\n * Hook to determine if current viewport is mobile-sized\n *\n * @korean 현재 뷰포트가 모바일 크기인지 확인하는 훅\n *\n * @param breakpoint - Width threshold for mobile (default: 768)\n * @returns True if viewport width is less than breakpoint\n *\n * @example\n * ```tsx\n * const isMobile = useIsMobile();\n * const isMobileCustom = useIsMobile(640);\n * ```\n */\nexport function useIsMobile(breakpoint = 768): boolean {\n const { width } = useWindowSize();\n return width < breakpoint;\n}\n\nexport default useWindowSize;\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;AAiDA,SAAgB,cAAc,UAAgC,EAAE,EAAc;CAC5E,MAAM,EAAE,eAAe,MAAM,gBAAgB,KAAK,aAAa,MAAM;CAErE,MAAM,CAAC,MAAM,WAAW,eAA2B;EAEjD,IAAI,OAAO,WAAW,aACpB,OAAO;GACL,OAAO,OAAO;GACd,QAAQ,OAAO;GAChB;EAEH,OAAO;GACL,OAAO;GACP,QAAQ;GACT;GACD;CAEF,MAAM,eAAe,kBAAkB;EACrC,QAAQ;GACN,OAAO,OAAO;GACd,QAAQ,OAAO;GAChB,CAAC;IACD,EAAE,CAAC;CAEN,gBAAgB;EAEd,IAAI,OAAO,WAAW,aAAa;EAEnC,IAAI,YAAkD;EAEtD,MAAM,wBAAwB;GAC5B,IAAI,aAAa,GAAG;IAClB,IAAI,WAAW,aAAa,UAAU;IACtC,YAAY,WAAW,cAAc,WAAW;UAEhD,cAAc;;EAIlB,OAAO,iBAAiB,UAAU,gBAAgB;EAElD,aAAa;GACX,OAAO,oBAAoB,UAAU,gBAAgB;GACrD,IAAI,WAAW,aAAa,UAAU;;IAEvC,CAAC,cAAc,WAAW,CAAC;CAE9B,OAAO;;;;;;;;;;;;;;;;AAiBT,SAAgB,YAAY,aAAa,KAAc;CACrD,MAAM,EAAE,UAAU,eAAe;CACjC,OAAO,QAAQ"}
1
+ {"version":3,"file":"useWindowSize.js","names":[],"sources":["../../src/hooks/useWindowSize.ts"],"sourcesContent":["/**\n * useWindowSize - Shared hook for responsive window dimensions\n *\n * @korean 윈도우크기훅 - 반응형 창 크기를 위한 공유 훅\n *\n * Eliminates duplication across screen components\n */\n\nimport { useCallback, useEffect, useState } from \"react\";\n\nexport interface WindowSize {\n readonly width: number;\n readonly height: number;\n}\n\nexport interface UseWindowSizeOptions {\n /**\n * Initial width if window is not available (SSR)\n * @default 1200\n */\n readonly initialWidth?: number;\n\n /**\n * Initial height if window is not available (SSR)\n * @default 800\n */\n readonly initialHeight?: number;\n\n /**\n * Debounce delay in milliseconds\n * @default 0\n */\n readonly debounceMs?: number;\n}\n\n/**\n * Hook to track window dimensions with optional debouncing\n *\n * @korean 윈도우 크기를 추적하는 훅 (선택적 디바운싱 지원)\n *\n * @param options - Configuration options\n * @returns Current window dimensions\n *\n * @example\n * ```tsx\n * const { width, height } = useWindowSize();\n * const isMobile = width < 768;\n * ```\n */\nexport function useWindowSize(options: UseWindowSizeOptions = {}): WindowSize {\n const { initialWidth = 1200, initialHeight = 800, debounceMs = 0 } = options;\n\n const [size, setSize] = useState<WindowSize>(() => {\n // Check if window is available (SSR safety)\n if (typeof window !== \"undefined\") {\n return {\n width: window.innerWidth,\n height: window.innerHeight,\n };\n }\n return {\n width: initialWidth,\n height: initialHeight,\n };\n });\n\n const handleResize = useCallback(() => {\n setSize({\n width: window.innerWidth,\n height: window.innerHeight,\n });\n }, []);\n\n useEffect(() => {\n // Safety check for SSR\n if (typeof window === \"undefined\") return;\n\n let timeoutId: ReturnType<typeof setTimeout> | null = null;\n\n const debouncedResize = () => {\n if (debounceMs > 0) {\n if (timeoutId) clearTimeout(timeoutId);\n timeoutId = setTimeout(handleResize, debounceMs);\n } else {\n handleResize();\n }\n };\n\n window.addEventListener(\"resize\", debouncedResize);\n\n return () => {\n window.removeEventListener(\"resize\", debouncedResize);\n if (timeoutId) clearTimeout(timeoutId);\n };\n }, [handleResize, debounceMs]);\n\n return size;\n}\n\n/**\n * Hook to determine if current viewport is mobile-sized\n *\n * @korean 현재 뷰포트가 모바일 크기인지 확인하는 훅\n *\n * @param breakpoint - Width threshold for mobile (default: 768)\n * @returns True if viewport width is less than breakpoint\n *\n * @example\n * ```tsx\n * const isMobile = useIsMobile();\n * const isMobileCustom = useIsMobile(640);\n * ```\n */\nexport function useIsMobile(breakpoint = 768): boolean {\n const { width } = useWindowSize();\n return width < breakpoint;\n}\n\nexport default useWindowSize;\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;AAiDA,SAAgB,cAAc,UAAgC,CAAC,GAAe;CAC5E,MAAM,EAAE,eAAe,MAAM,gBAAgB,KAAK,aAAa,MAAM;CAErE,MAAM,CAAC,MAAM,WAAW,eAA2B;EAEjD,IAAI,OAAO,WAAW,aACpB,OAAO;GACL,OAAO,OAAO;GACd,QAAQ,OAAO;EACjB;EAEF,OAAO;GACL,OAAO;GACP,QAAQ;EACV;CACF,CAAC;CAED,MAAM,eAAe,kBAAkB;EACrC,QAAQ;GACN,OAAO,OAAO;GACd,QAAQ,OAAO;EACjB,CAAC;CACH,GAAG,CAAC,CAAC;CAEL,gBAAgB;EAEd,IAAI,OAAO,WAAW,aAAa;EAEnC,IAAI,YAAkD;EAEtD,MAAM,wBAAwB;GAC5B,IAAI,aAAa,GAAG;IAClB,IAAI,WAAW,aAAa,SAAS;IACrC,YAAY,WAAW,cAAc,UAAU;GACjD,OACE,aAAa;EAEjB;EAEA,OAAO,iBAAiB,UAAU,eAAe;EAEjD,aAAa;GACX,OAAO,oBAAoB,UAAU,eAAe;GACpD,IAAI,WAAW,aAAa,SAAS;EACvC;CACF,GAAG,CAAC,cAAc,UAAU,CAAC;CAE7B,OAAO;AACT;;;;;;;;;;;;;;;AAgBA,SAAgB,YAAY,aAAa,KAAc;CACrD,MAAM,EAAE,UAAU,cAAc;CAChC,OAAO,QAAQ;AACjB"}
@@ -1 +1 @@
1
- {"version":3,"file":"CombatSystem.js","names":[],"sources":["../../src/systems/CombatSystem.ts"],"sourcesContent":["import * as THREE from \"three\";\nimport { getArchetypePhysicalAttributes } from \"../data/archetypePhysicalAttributes\";\nimport { getTechniqueById } from \"../data/techniques\";\nimport { BodyRegion, CombatAttackType, CombatState, DamageType, GrappleTarget } from \"../types\";\nimport { VitalPointCategory, VitalPointSeverity } from \"../types/common\";\nimport { BASE_STAMINA_REGEN_RATE } from \"../types/physicsConstants\";\nimport { Technique } from \"../types/technique\";\nimport { calculateDistance3D } from \"../utils/math\";\nimport { calculateBodyRadius } from \"../utils/skeletonScaling\";\nimport type { DefensiveAnimationType } from \"./animation\";\nimport {\n AnimationType,\n calculateSpeedModifierForDamage,\n determineAnimationTypeForTechnique,\n getAdjustedAnimationDuration,\n getAnimationNameForType,\n isWithinHitWindow,\n} from \"./animation\";\nimport { applyDamageToBodyParts } from \"./bodypart/BodyPartDamageIntegration\";\nimport { playerInjuryManager } from \"./bodypart\";\nimport {\n applyBreathingDisruptionFromVitalPoint,\n applyBreathingDisruptionFromTorsoDamage,\n BreathingDisruptionSystem,\n causesBreathingDisruption,\n updateBreathingDisruption,\n} from \"./breathing\";\nimport BalanceSystem from \"./combat/BalanceSystem\";\nimport ConsciousnessSystem from \"./combat/ConsciousnessSystem\";\nimport {\n extractVitalPointCategory,\n isHeadTraumaHit,\n} from \"./combat/painConsciousnessUtils\";\nimport PainResponseSystem, {\n ShockPainEffect,\n} from \"./combat/PainResponseSystem\";\nimport { CombatResult, CombatSystemInterface } from \"./combat/types\";\nimport {\n CollisionDetection,\n KnockbackPhysics,\n physicalReachCalculator,\n type KnockbackConfig,\n} from \"./physics\";\nimport { PlayerState } from \"./player\";\nimport {\n addEffectsToPlayer,\n getEffectModifiers,\n removeExpiredEffects,\n} from \"./PlayerEffectManager\";\nimport { TRIGRAM_TECHNIQUES } from \"./trigram\";\nimport { TrigramSystem } from \"./TrigramSystem\";\nimport { StatusEffect } from \"./types\";\nimport { KoreanTechnique, VitalPointHitResult } from \"./vitalpoint/types\";\nimport { VitalPointSystem } from \"./VitalPointSystem\";\nimport { GrappleSystem } from \"./combat/GrappleSystem\";\n\n/**\n * Enhanced Combat System with Pain Response and Consciousness integration.\n *\n * Integrates realistic pain accumulation and consciousness tracking for\n * progressive combat impairment.\n */\nexport class CombatSystem implements CombatSystemInterface {\n private vitalPointSystem: VitalPointSystem;\n protected trigramSystem: TrigramSystem;\n private painSystem: PainResponseSystem;\n private consciousnessSystem: ConsciousnessSystem;\n private balanceSystem: BalanceSystem;\n private knockbackPhysics: KnockbackPhysics;\n private collisionDetection: CollisionDetection;\n private grappleSystem: GrappleSystem;\n\n // Track shock pain effects per player\n private shockPainEffects: Map<string, ShockPainEffect>;\n // Track last head trauma time per player for consciousness recovery\n private lastHeadTraumaTime: Map<string, number>;\n\n // Vital point severity thresholds\n private readonly SEVERITY_MAJOR_THRESHOLD = 30;\n private readonly SEVERITY_MODERATE_THRESHOLD = 20;\n\n constructor() {\n this.vitalPointSystem = new VitalPointSystem();\n this.trigramSystem = new TrigramSystem();\n this.painSystem = new PainResponseSystem();\n this.consciousnessSystem = new ConsciousnessSystem();\n this.balanceSystem = new BalanceSystem();\n this.knockbackPhysics = new KnockbackPhysics();\n this.collisionDetection = new CollisionDetection();\n this.grappleSystem = new GrappleSystem();\n this.shockPainEffects = new Map();\n this.lastHeadTraumaTime = new Map();\n }\n\n /**\n * Cleanup per-player combat state.\n *\n * Call this when a player permanently leaves the match or when\n * match-level cleanup is performed to avoid unbounded Map growth.\n *\n * @param playerId - ID of the player to cleanup\n *\n * @korean 플레이어데이터정리\n */\n public cleanupPlayerData(playerId: string): void {\n this.shockPainEffects.delete(playerId);\n this.lastHeadTraumaTime.delete(playerId);\n }\n\n /**\n * Dispose of all combat system resources.\n *\n * **Korean**: 전투 시스템 자원 정리\n *\n * Cleans up Three.js resources (geometries, raycaster) used by the collision\n * detection system to prevent memory leaks. Should be called when the\n * CombatSystem is destroyed or reinitialized.\n *\n * @korean 전투시스템자원정리\n */\n public dispose(): void {\n // Dispose collision detection resources (cached geometries, raycaster)\n this.collisionDetection.dispose();\n }\n\n /**\n * Get the balance system instance for fall checking.\n *\n * @returns BalanceSystem instance\n * @korean 균형시스템가져오기\n */\n public getBalanceSystem(): BalanceSystem {\n return this.balanceSystem;\n }\n\n /**\n * Get the consciousness system instance for fall checking.\n *\n * @returns ConsciousnessSystem instance\n * @korean 의식시스템가져오기\n */\n public getConsciousnessSystem(): ConsciousnessSystem {\n return this.consciousnessSystem;\n }\n\n /**\n * Get the collision detection system instance.\n *\n * @returns CollisionDetection instance\n * @korean 충돌감지시스템가져오기\n */\n public getCollisionDetection(): CollisionDetection {\n return this.collisionDetection;\n }\n\n /**\n * Calculate knockback physics for combat hit.\n *\n * **Korean**: 밀침 계산 (Calculate Knockback)\n *\n * Determines knockback displacement, duration, and fall state based on:\n * - Attack damage amount\n * - Defender's balance state\n * - Defender's stance resistance\n * - Attack direction vector\n *\n * @param attacker - Attacking player state\n * @param defender - Defending player state\n * @param damage - Total damage dealt\n * @returns Knockback information or undefined if no knockback\n *\n * @example\n * ```typescript\n * const knockback = this.calculateKnockback(attacker, defender, 80);\n * // Returns: { displacement: {x:2.5,y:0,z:0}, duration:0.8, recoveryWindow:0.7, shouldFall:false }\n * ```\n *\n * @private\n * @korean 밀침계산\n */\n private calculateKnockback(\n attacker: PlayerState,\n defender: PlayerState,\n damage: number,\n ): CombatResult[\"knockback\"] {\n const attackDirection = new THREE.Vector3(\n defender.position.x - attacker.position.x,\n 0, // Keep knockback on horizontal plane\n defender.position.y - attacker.position.y,\n );\n\n // If attacker and defender are at the exact same position, skip knockback to avoid NaN direction\n if (attackDirection.lengthSq() === 0) {\n return undefined;\n }\n\n attackDirection.normalize();\n\n const config: KnockbackConfig = {\n force: damage * 10, // Convert damage to force (arbitrary scaling)\n direction: attackDirection,\n duration: 0, // Will be calculated by physics engine\n balanceState: {\n current: defender.balance,\n max: 100, // Assuming max balance is always 100\n },\n currentStance: defender.currentStance,\n };\n\n const result = this.knockbackPhysics.calculateKnockback(config, damage);\n\n return {\n displacement: {\n x: result.displacement.x,\n y: result.displacement.y,\n z: result.displacement.z,\n },\n duration: result.duration,\n recoveryWindow: result.recoveryWindow,\n shouldFall: result.shouldFall,\n };\n }\n\n /**\n * Fix: Update resolveAttack to match interface signature and add animation-aware hit detection\n *\n * **Korean**: 공격 해결 (애니메이션 인식)\n *\n * Integrates animation timing and physical reach calculation for reality-based hit detection.\n * Hits only register when:\n * 1. Animation is in hit window (extension phase)\n * 2. Attacker is within effective reach based on limb length and animation\n * 3. Existing accuracy and stance checks pass\n *\n * @param attacker - Attacking player state\n * @param defender - Defending player state\n * @param technique - Technique being executed\n * @param targetedVitalPointId - Optional specific vital point target\n * @param animationContext - Optional animation timing context for reality-based hit detection\n * @returns Combat result with hit/miss and damage information\n */\n resolveAttack(\n attacker: PlayerState,\n defender: PlayerState,\n technique: KoreanTechnique,\n targetedVitalPointId?: string,\n animationContext?: {\n animationType: AnimationType;\n currentTime: number;\n },\n ): CombatResult {\n const timestamp = Date.now();\n\n if (!this.canExecuteTechnique(attacker, technique)) {\n return {\n hit: false,\n damage: 0,\n criticalHit: false,\n vitalPointHit: false,\n effects: [],\n timestamp,\n technique,\n attacker,\n defender,\n success: false,\n isCritical: false,\n isBlocked: false,\n };\n }\n\n if (attacker.combatState === CombatState.GRAPPLED) {\n return {\n hit: false,\n damage: 0,\n criticalHit: false,\n vitalPointHit: false,\n effects: [],\n timestamp,\n technique,\n attacker,\n defender,\n success: false,\n isCritical: false,\n isBlocked: false,\n };\n }\n\n if (animationContext) {\n const { animationType, currentTime } = animationContext;\n\n if (!isWithinHitWindow(animationType, currentTime)) {\n return {\n hit: false,\n damage: 0,\n criticalHit: false,\n vitalPointHit: false,\n effects: [],\n timestamp,\n technique,\n attacker,\n defender,\n success: false,\n isCritical: false,\n isBlocked: false,\n };\n }\n\n const attackerPhysical = getArchetypePhysicalAttributes(\n attacker.archetype,\n );\n const reachResult = physicalReachCalculator.calculateReach(\n attackerPhysical,\n animationType,\n currentTime,\n attacker.currentStance,\n technique.reachConfig, // Pass reachConfig for hybrid reach calculation\n );\n\n // Physics-first: Position type is 2D in METERS\n // No conversion needed - positions are already in meters\n const centerToCenterDistance = calculateDistance3D(\n [attacker.position.x, attacker.position.y, 0],\n [defender.position.x, defender.position.y, 0],\n );\n\n // Note: PhysicalReachCalculator already includes attacker body pivot/offset in reach calculation\n // (shoulder offset for punches, hip rotation for kicks), so we only subtract defender radius\n // to avoid double-counting the attacker's body dimension.\n // 타격 거리 계산: 수비자 몸체 반경만 제외 (공격자 몸체 오프셋은 이미 도달 거리에 포함됨)\n const defenderPhysical = getArchetypePhysicalAttributes(\n defender.archetype,\n );\n const defenderBodyRadius = calculateBodyRadius(defenderPhysical);\n\n // Reach is calculated from attacker center to striking limb surface (includes body pivot),\n // so we measure from attacker center to defender surface (subtracting defender radius only).\n // 유효 거리 = 중심간 거리 - 수비자 몸체 반경\n const distance = Math.max(0, centerToCenterDistance - defenderBodyRadius);\n\n if (distance > reachResult.effectiveReach) {\n return {\n hit: false,\n damage: 0,\n criticalHit: false,\n vitalPointHit: false,\n effects: [],\n timestamp,\n technique,\n attacker,\n defender,\n success: false,\n isCritical: false,\n isBlocked: false,\n };\n }\n }\n\n // Fix: Use correct method signature\n const stanceEffectiveness = this.trigramSystem.calculateStanceEffectiveness(\n attacker.currentStance,\n defender.currentStance,\n );\n\n // Calculate base hit chance\n const baseAccuracy = technique.accuracy * stanceEffectiveness;\n const hitRoll = Math.random();\n const hit = hitRoll <= baseAccuracy;\n\n if (!hit) {\n return {\n hit: false,\n damage: 0,\n criticalHit: false,\n vitalPointHit: false,\n effects: [],\n timestamp,\n technique,\n attacker,\n defender,\n success: false,\n isCritical: false,\n isBlocked: false,\n };\n }\n\n // Special handling for grapple techniques\n if (technique.type === CombatAttackType.GRAPPLE) {\n const grappleResult = this.handleGrappleTechnique(\n attacker,\n defender,\n technique,\n timestamp\n );\n\n // Grapples don't deal immediate damage, they establish control\n // Small damage on successful grapple to represent the initial impact\n const grappleDamage = grappleResult.grappleSuccess\n ? technique.damage * 0.3\n : 0;\n\n return {\n hit: grappleResult.grappleSuccess,\n damage: grappleDamage,\n criticalHit: false,\n vitalPointHit: false,\n effects: [],\n timestamp,\n technique,\n attacker: grappleResult.updatedAttacker,\n defender: grappleResult.updatedDefender,\n success: grappleResult.grappleSuccess,\n isCritical: false,\n isBlocked: false,\n animation: this.getAnimationInfoForTechnique(technique),\n };\n }\n\n // Process vital point hit if targeted (with archetype parameters)\n let vitalPointResult: VitalPointHitResult | null = null;\n if (targetedVitalPointId) {\n vitalPointResult = this.processVitalPointHit(\n targetedVitalPointId,\n technique.damage ?? 15,\n attacker,\n defender,\n );\n }\n\n const damageResult = this.calculateDamage(\n technique,\n attacker,\n defender,\n vitalPointResult ?? {\n hit: false,\n damage: 0,\n effects: [],\n severity: VitalPointSeverity.MINOR,\n },\n );\n\n const critRoll = Math.random();\n const isCritical = critRoll <= (technique.critChance ?? 0.1);\n\n const animationInfo = this.getAnimationInfoForTechnique(technique);\n\n // Calculate knockback physics (밀침 물리)\n const knockbackInfo = this.calculateKnockback(\n attacker,\n defender,\n damageResult.totalDamage,\n );\n\n return {\n hit: true,\n damage: damageResult.totalDamage,\n criticalHit: isCritical,\n vitalPointHit: vitalPointResult?.hit ?? false,\n effects: damageResult.effectsApplied,\n timestamp,\n technique,\n attacker,\n defender,\n success: true,\n isCritical: vitalPointResult?.hit ?? false,\n isBlocked: false,\n targetedVitalPointId, // Pass through the targeted vital point ID\n animation: animationInfo, // Add animation information\n knockback: knockbackInfo, // Add knockback information\n };\n }\n\n /**\n * Get animation information for a technique\n *\n * Determines the skeletal animation to play, duration, and speed modifier\n * based on technique configuration or automatic determination.\n *\n * @param technique - Korean technique to execute\n * @returns Animation information or undefined\n *\n * @private\n * @korean 기술애니메이션정보가져오기\n */\n private getAnimationInfoForTechnique(\n technique: KoreanTechnique,\n ): CombatResult[\"animation\"] {\n // Check if technique has explicit animation config (from Technique interface)\n // KoreanTechnique may not have animation field, so check the technique data\n const techniqueData = this.getTechniqueData(technique);\n\n let animationType;\n let speedModifier;\n\n if (techniqueData?.animation) {\n animationType = techniqueData.animation.type;\n speedModifier = techniqueData.animation.speedModifier;\n } else {\n const techniqueName =\n technique.name?.english || technique.englishName || \"\";\n const techniqueId = technique.id || \"\";\n const damageType = technique.damageType || \"\";\n\n animationType = determineAnimationTypeForTechnique(\n techniqueName,\n techniqueId,\n damageType,\n );\n\n speedModifier = calculateSpeedModifierForDamage(technique.damage || 15);\n }\n\n const animationName = getAnimationNameForType(animationType);\n const duration = getAdjustedAnimationDuration(animationName, speedModifier);\n const techniqueDisplayName =\n technique.name?.korean || technique.koreanName || technique.id;\n\n return {\n animationName,\n duration,\n speedModifier,\n techniqueDisplayName,\n };\n }\n\n /**\n * Get Technique data if this KoreanTechnique has an associated Technique definition\n *\n * @param technique - Korean technique\n * @returns Technique data or null\n *\n * @private\n * @korean 기술데이터가져오기\n */\n private getTechniqueData(technique: KoreanTechnique): Technique | null {\n return getTechniqueById(technique.id) ?? null;\n }\n\n /**\n * Handle grapple technique execution.\n *\n * **Korean**: 잡기 기술 처리 (Handle Grapple Technique)\n *\n * Initiates or transitions grapple control based on technique.\n *\n * @param attacker - Player executing grapple\n * @param defender - Target player\n * @param technique - Grapple technique being used\n * @param currentTime - Current game time in milliseconds\n * @returns Updated player states with grapple control\n */\n handleGrappleTechnique(\n attacker: PlayerState,\n defender: PlayerState,\n technique: KoreanTechnique,\n currentTime: number\n ): {\n updatedAttacker: PlayerState;\n updatedDefender: PlayerState;\n grappleSuccess: boolean;\n } {\n // Determine grapple target from technique\n const target = this.getGrappleTargetFromTechnique(technique);\n\n // Attempt grapple\n const result = this.grappleSystem.attemptGrapple(\n attacker,\n defender,\n target,\n currentTime\n );\n\n if (result.success && result.grappleControl) {\n // Grapple succeeded - update both players\n return {\n updatedAttacker: {\n ...attacker,\n combatState: CombatState.GRAPPLING,\n grappleControl: result.grappleControl,\n stamina: Math.max(0, attacker.stamina - result.staminaCost),\n },\n updatedDefender: {\n ...defender,\n combatState: CombatState.GRAPPLED,\n grappleControl: result.grappleControl,\n },\n grappleSuccess: true,\n };\n }\n\n // Grapple failed\n return {\n updatedAttacker: {\n ...attacker,\n stamina: Math.max(0, attacker.stamina - result.staminaCost),\n },\n updatedDefender: defender,\n grappleSuccess: false,\n };\n }\n\n /**\n * Determine grapple target from technique characteristics.\n *\n * **Korean**: 기술에서 잡기 목표 결정 (Determine Grapple Target from Technique)\n *\n * @private\n */\n private getGrappleTargetFromTechnique(\n technique: KoreanTechnique\n ): GrappleTarget {\n // Check technique name/ID for hints (both English and Korean)\n const techName = (\n technique.name?.english ||\n technique.englishName ||\n \"\"\n ).toLowerCase();\n const techNameKorean = (\n technique.name?.korean ||\n technique.koreanName ||\n \"\"\n );\n const techId = technique.id.toLowerCase();\n\n // Check for wrist/hand - 손목 (sonmok)\n if (\n techName.includes(\"wrist\") ||\n techId.includes(\"wrist\") ||\n techNameKorean.includes(\"손목\")\n ) {\n return GrappleTarget.HAND;\n }\n\n // Check for arm - 팔 (pal)\n if (\n techName.includes(\"arm\") ||\n techId.includes(\"arm\") ||\n techNameKorean.includes(\"팔\")\n ) {\n return GrappleTarget.ARM;\n }\n\n // Check for leg - 다리 (dari)\n if (\n techName.includes(\"leg\") ||\n techId.includes(\"leg\") ||\n techNameKorean.includes(\"다리\")\n ) {\n return GrappleTarget.LEG;\n }\n\n // Check for neck - 목 (mok)\n if (\n techName.includes(\"neck\") ||\n techId.includes(\"neck\") ||\n techNameKorean.includes(\"목\")\n ) {\n return GrappleTarget.NECK;\n }\n\n // Check for both arms - 양팔 (yangpal)\n if (\n techName.includes(\"both\") ||\n techName.includes(\"double\") ||\n techId.includes(\"both\") ||\n techNameKorean.includes(\"양팔\") ||\n techNameKorean.includes(\"쌍\")\n ) {\n return GrappleTarget.BOTH_ARMS;\n }\n\n // Check for torso/body/hip - 몸통 (momtong), 허리 (heori), 몸 (mom)\n if (\n techName.includes(\"torso\") ||\n techName.includes(\"body\") ||\n techName.includes(\"hip\") ||\n techId.includes(\"torso\") ||\n techNameKorean.includes(\"몸통\") ||\n techNameKorean.includes(\"허리\") ||\n techNameKorean.includes(\"몸\")\n ) {\n return GrappleTarget.TORSO;\n }\n\n // Default to arm for unspecified grapples\n return GrappleTarget.ARM;\n }\n\n /**\n * Update grapple state for players over time.\n *\n * **Korean**: 잡기 상태 업데이트 (Update Grapple State)\n *\n * @param controller - Player maintaining control\n * @param target - Player being controlled\n * @param deltaTime - Time elapsed in seconds\n * @param currentTime - Current game time in milliseconds\n * @returns Updated player states\n */\n updateGrappleState(\n controller: PlayerState,\n target: PlayerState,\n deltaTime: number,\n currentTime: number\n ): {\n updatedController: PlayerState;\n updatedTarget: PlayerState;\n } {\n if (!controller.grappleControl) {\n // No active grapple\n return {\n updatedController: controller,\n updatedTarget: target,\n };\n }\n\n // Validate grapple control consistency\n if (\n controller.grappleControl.controllerId !== controller.id ||\n controller.grappleControl.targetId !== target.id\n ) {\n // Inconsistent state - break grapple\n return {\n updatedController: {\n ...controller,\n combatState: CombatState.IDLE,\n grappleControl: null,\n },\n updatedTarget: {\n ...target,\n combatState: CombatState.IDLE,\n grappleControl: null,\n },\n };\n }\n\n // Update grapple control\n const updatedControl = this.grappleSystem.updateGrapple(\n controller.grappleControl,\n controller,\n target,\n deltaTime,\n currentTime\n );\n\n if (!updatedControl) {\n // Grapple broken - reset both players\n return {\n updatedController: {\n ...controller,\n combatState: CombatState.IDLE,\n grappleControl: null,\n },\n updatedTarget: {\n ...target,\n combatState: CombatState.IDLE,\n grappleControl: null,\n },\n };\n }\n\n // Deduct stamina from controller\n const staminaCost = updatedControl.staminaCostPerSecond * deltaTime;\n\n return {\n updatedController: {\n ...controller,\n grappleControl: updatedControl,\n stamina: Math.max(0, controller.stamina - staminaCost),\n },\n updatedTarget: {\n ...target,\n grappleControl: updatedControl,\n },\n };\n }\n\n /**\n * Fix: Make applyCombatResult non-static instance method with effect application\n * Enhanced with Pain Response and Consciousness System integration\n */\n applyCombatResult(\n result: CombatResult,\n attacker: PlayerState,\n defender: PlayerState,\n ): { updatedAttacker: PlayerState; updatedDefender: PlayerState } {\n // Start with base result\n const { updatedAttacker, updatedDefender: initialDefender } =\n CombatSystem.applyCombatResult(result, attacker, defender);\n let updatedDefender = initialDefender;\n\n if (result.hit && result.damage > 0) {\n // Determine vital point category and severity from hit result\n const category = this.getVitalPointCategory(result);\n const severity = this.getVitalPointSeverity(result);\n\n // Apply pain from damage\n const { player: defenderWithPain, shockEffect: newShockEffect } =\n this.painSystem.applyPain(\n updatedDefender,\n result.damage,\n severity,\n category,\n );\n updatedDefender = defenderWithPain;\n\n // Store shock pain effect if triggered\n if (newShockEffect) {\n this.shockPainEffects.set(updatedDefender.id, newShockEffect);\n }\n\n // Check for pain overload stun\n if (this.painSystem.shouldTriggerStun(updatedDefender)) {\n updatedDefender = {\n ...updatedDefender,\n isStunned: true,\n };\n }\n\n // Apply consciousness damage for head/neurological hits\n if (this.isHeadTrauma(result, category)) {\n updatedDefender = this.consciousnessSystem.applyDamage(\n updatedDefender,\n result.damage,\n category,\n );\n\n // Track head trauma time for recovery gating\n this.lastHeadTraumaTime.set(updatedDefender.id, Date.now());\n\n // Check incapacitation threshold\n if (\n this.consciousnessSystem.isAtIncapacitationThreshold(updatedDefender)\n ) {\n updatedDefender = {\n ...updatedDefender,\n isStunned: true,\n // Could add helpless duration tracking here if needed\n };\n }\n }\n\n // Apply pain and consciousness effects to stats\n const currentShockEffect = this.shockPainEffects.get(updatedDefender.id);\n updatedDefender = this.painSystem.applyEffects(\n updatedDefender,\n currentShockEffect,\n );\n updatedDefender = this.consciousnessSystem.applyEffects(updatedDefender);\n\n // Apply balance disruption from the hit\n // Determine body region from vital point or use default\n const bodyRegion = this.getBodyRegionFromResult(result);\n updatedDefender = this.balanceSystem.disruptBalance(\n updatedDefender,\n result.damage,\n bodyRegion,\n );\n\n // Apply breathing disruption for torso strikes\n if (result.vitalPointHit && result.targetedVitalPointId) {\n // Vital point strike to torso\n const vitalPoint = this.vitalPointSystem.getVitalPointById(\n result.targetedVitalPointId,\n );\n if (vitalPoint && causesBreathingDisruption(vitalPoint.id)) {\n updatedDefender = applyBreathingDisruptionFromVitalPoint(\n updatedDefender,\n vitalPoint,\n Date.now(),\n );\n }\n } else if (bodyRegion === BodyRegion.TORSO && result.damage >= 10) {\n // General torso damage (non-vital point hits)\n // Apply breathing disruption if damage is significant enough\n // Note: Solar plexus detection is best-effort; vital point system is primary mechanism\n const techniqueId = (result.technique?.id ?? \"\").toLowerCase();\n const isSolarPlexusArea = \n techniqueId.includes(\"solar\") ||\n techniqueId.includes(\"myeongchi\");\n updatedDefender = applyBreathingDisruptionFromTorsoDamage(\n updatedDefender,\n result.damage,\n isSolarPlexusArea,\n Date.now(),\n );\n }\n }\n\n return { updatedAttacker, updatedDefender };\n }\n\n /**\n * Determines if a hit caused head trauma (affects consciousness).\n *\n * @param result - Combat result\n * @param category - Vital point category\n * @returns True if hit should affect consciousness\n */\n private isHeadTrauma(\n result: CombatResult,\n category?: VitalPointCategory,\n ): boolean {\n return isHeadTraumaHit(result, category);\n }\n\n /**\n * Extracts vital point category from combat result.\n *\n * @param result - Combat result\n * @returns Vital point category if available\n */\n private getVitalPointCategory(\n result: CombatResult,\n ): VitalPointCategory | undefined {\n return extractVitalPointCategory(result);\n }\n\n /**\n * Extracts vital point severity from combat result.\n *\n * @param result - Combat result\n * @returns Vital point severity if critical hit\n */\n private getVitalPointSeverity(\n result: CombatResult,\n ): VitalPointSeverity | undefined {\n if (result.vitalPointHit) {\n if (result.isCritical) {\n return VitalPointSeverity.CRITICAL;\n }\n if (result.damage > this.SEVERITY_MAJOR_THRESHOLD) {\n return VitalPointSeverity.MAJOR;\n }\n if (result.damage > this.SEVERITY_MODERATE_THRESHOLD) {\n return VitalPointSeverity.MODERATE;\n }\n return VitalPointSeverity.MINOR;\n }\n return undefined;\n }\n\n /**\n * Determines body region from combat result for balance disruption.\n *\n * Maps vital points to body regions for balance system integration.\n * Uses string matching on vital point IDs as a pragmatic heuristic since\n * VitalPoint interface doesn't currently include a bodyRegion property.\n *\n * Future improvement: Add bodyRegion: BodyRegion to VitalPoint interface\n * for more robust region mapping without string pattern matching.\n *\n * @param result - Combat result\n * @returns Body region that was struck\n * @private\n * @korean 신체부위결정\n */\n private getBodyRegionFromResult(result: CombatResult): BodyRegion {\n // If we have a targeted vital point, try to determine region\n if (result.targetedVitalPointId) {\n const vitalPoint = this.vitalPointSystem.getVitalPointById(\n result.targetedVitalPointId,\n );\n\n if (vitalPoint) {\n const pointId = vitalPoint.id.toLowerCase();\n\n // Check for leg/lower body targets\n if (\n pointId.includes(\"leg\") ||\n pointId.includes(\"knee\") ||\n pointId.includes(\"ankle\") ||\n pointId.includes(\"thigh\")\n ) {\n return BodyRegion.LEFT_LEG; // Generic leg for balance disruption\n }\n\n // Check for head targets\n if (\n pointId.includes(\"head\") ||\n pointId.includes(\"temple\") ||\n pointId.includes(\"jaw\") ||\n pointId.includes(\"nose\")\n ) {\n return BodyRegion.HEAD;\n }\n\n // Check for arm targets\n if (\n pointId.includes(\"arm\") ||\n pointId.includes(\"elbow\") ||\n pointId.includes(\"wrist\") ||\n pointId.includes(\"shoulder\")\n ) {\n return BodyRegion.LEFT_ARM;\n }\n }\n }\n\n // Default to torso for general strikes\n return BodyRegion.TORSO;\n }\n\n /**\n * Updates player states for recovery (pain dissipation, consciousness recovery, balance recovery).\n * Call this regularly in game loop.\n *\n * @param player - Player to update\n * @param deltaTime - Time elapsed since last update (ms)\n * @returns Updated player state\n */\n applyRecovery(player: PlayerState, deltaTime: number): PlayerState {\n let updatedPlayer = player;\n\n // Apply pain recovery\n updatedPlayer = this.painSystem.applyDissipation(updatedPlayer, deltaTime);\n\n // Apply consciousness recovery (only if enough time since head trauma)\n const lastTrauma = this.lastHeadTraumaTime.get(player.id);\n updatedPlayer = this.consciousnessSystem.applyRecovery(\n updatedPlayer,\n deltaTime,\n lastTrauma,\n );\n\n // Apply balance recovery\n updatedPlayer = this.balanceSystem.applyRecovery(updatedPlayer, deltaTime);\n\n // Clean up expired shock pain effects\n const shockEffect = this.shockPainEffects.get(player.id);\n if (shockEffect) {\n const elapsed = Date.now() - shockEffect.startTime;\n if (elapsed >= shockEffect.duration) {\n this.shockPainEffects.delete(player.id);\n }\n }\n\n return updatedPlayer;\n }\n\n /**\n * Static version for backwards compatibility with comprehensive effect application\n * Enhanced with Pain Response and Consciousness System integration\n * Updated to apply damage to body parts for 8-body-part health visualization\n */\n static applyCombatResult(\n result: CombatResult,\n attacker: PlayerState,\n defender: PlayerState,\n ): { updatedAttacker: PlayerState; updatedDefender: PlayerState } {\n let updatedDefender = defender;\n let updatedAttacker = attacker;\n\n if (result.hit) {\n // Determine body region from technique or use random distribution\n const bodyRegion = CombatSystem.getBodyRegionFromTechnique(\n result.technique,\n );\n\n // Apply damage to body parts (this also updates aggregate health)\n updatedDefender = applyDamageToBodyParts(\n defender,\n result.damage,\n bodyRegion,\n );\n\n // Record injury for trauma visualization (외상 시각화)\n // Use per-player injury tracking to prevent mixing injuries between characters\n // Automatically tracks bruising, cuts, and blood effects based on damage type\n // Records injury even without specific technique (defaults to BRUISE)\n if (result.damage > 0) {\n const defenderInjuryIntegration = playerInjuryManager.getIntegrationForPlayer(defender.id);\n defenderInjuryIntegration.recordCombatDamage({\n damage: result.damage,\n bodyRegion,\n damageType: (result.technique?.damageType as DamageType) || undefined,\n });\n }\n\n // Update tracking stats (applyDamageToBodyParts already sets health)\n updatedDefender = {\n ...updatedDefender,\n totalDamageReceived:\n updatedDefender.totalDamageReceived + result.damage,\n hitsTaken: updatedDefender.hitsTaken + 1,\n };\n\n if (result.effects && result.effects.length > 0) {\n updatedDefender = addEffectsToPlayer(updatedDefender, result.effects);\n }\n\n if (result.vitalPointHit) {\n updatedAttacker = {\n ...updatedAttacker,\n vitalPointHits: attacker.vitalPointHits + 1,\n };\n }\n\n if (result.isCritical) {\n updatedAttacker = {\n ...updatedAttacker,\n perfectStrikes: attacker.perfectStrikes + 1,\n };\n }\n }\n\n updatedAttacker = {\n ...updatedAttacker,\n ki: Math.max(0, attacker.ki - 5),\n stamina: Math.max(0, attacker.stamina - 10),\n totalDamageDealt:\n attacker.totalDamageDealt + (result.hit ? result.damage : 0),\n hitsLanded: attacker.hitsLanded + (result.hit ? 1 : 0),\n };\n\n return { updatedAttacker, updatedDefender };\n }\n\n /**\n * Determine body region from technique name or type\n *\n * **Korean**: 기술에서 신체 영역 결정\n *\n * @param technique - The technique used in the attack\n * @returns Body region to apply damage to\n */\n private static getBodyRegionFromTechnique(\n technique?: KoreanTechnique,\n ): BodyRegion {\n if (!technique) {\n return BodyRegion.TORSO;\n }\n\n const techniqueName = (\n technique.name?.english ||\n technique.englishName ||\n \"\"\n ).toLowerCase();\n const techniqueId = technique.id?.toLowerCase() || \"\";\n\n // Head/face targeting techniques\n if (\n techniqueName.includes(\"head\") ||\n techniqueName.includes(\"temple\") ||\n techniqueName.includes(\"jaw\") ||\n techniqueName.includes(\"face\") ||\n techniqueId.includes(\"head\") ||\n techniqueId.includes(\"skull\")\n ) {\n return BodyRegion.HEAD;\n }\n\n // Neck targeting techniques\n if (\n techniqueName.includes(\"neck\") ||\n techniqueName.includes(\"throat\") ||\n techniqueName.includes(\"choke\") ||\n techniqueId.includes(\"neck\")\n ) {\n return BodyRegion.NECK;\n }\n\n // Leg targeting techniques\n if (\n techniqueName.includes(\"kick\") ||\n techniqueName.includes(\"sweep\") ||\n techniqueName.includes(\"leg\") ||\n techniqueName.includes(\"knee\") ||\n techniqueId.includes(\"kick\") ||\n techniqueId.includes(\"leg\")\n ) {\n return Math.random() < 0.5 ? BodyRegion.LEFT_LEG : BodyRegion.RIGHT_LEG;\n }\n\n // Arm targeting techniques\n if (\n techniqueName.includes(\"arm\") ||\n techniqueName.includes(\"shoulder\") ||\n techniqueName.includes(\"elbow\") ||\n techniqueId.includes(\"arm\")\n ) {\n return Math.random() < 0.5 ? BodyRegion.LEFT_ARM : BodyRegion.RIGHT_ARM;\n }\n\n // Punch/strike techniques typically target torso\n if (\n techniqueName.includes(\"punch\") ||\n techniqueName.includes(\"strike\") ||\n techniqueName.includes(\"jab\") ||\n techniqueName.includes(\"cross\") ||\n techniqueName.includes(\"hook\") ||\n techniqueName.includes(\"uppercut\")\n ) {\n // Mix of torso and head for punches\n return Math.random() < 0.7 ? BodyRegion.TORSO : BodyRegion.HEAD;\n }\n\n // Body/core targeting techniques\n if (\n techniqueName.includes(\"body\") ||\n techniqueName.includes(\"solar\") ||\n techniqueName.includes(\"liver\") ||\n techniqueName.includes(\"ribs\") ||\n techniqueName.includes(\"abdomen\") ||\n techniqueId.includes(\"torso\") ||\n techniqueId.includes(\"core\")\n ) {\n return BodyRegion.TORSO;\n }\n\n // Default to torso for unmatched techniques\n return BodyRegion.TORSO;\n }\n\n /**\n * Fix: Add missing getAvailableTechniques method required by interface\n */\n getAvailableTechniques(player: PlayerState): readonly KoreanTechnique[] {\n const allTechniques = TRIGRAM_TECHNIQUES[player.currentStance] ?? [];\n\n // Filter techniques based on available resources using canExecuteTechnique\n return allTechniques.filter((technique) =>\n this.canExecuteTechnique(player, technique as KoreanTechnique),\n ) as readonly KoreanTechnique[];\n }\n\n /**\n * Check if attacker can execute technique\n */\n private canExecuteTechnique(\n player: PlayerState,\n technique: KoreanTechnique,\n ): boolean {\n return (\n player.ki >= technique.kiCost &&\n player.stamina >= technique.staminaCost &&\n player.currentStance === technique.stance &&\n !player.isStunned\n );\n }\n\n /**\n * Static methods for backwards compatibility\n */\n static resolveAttack(\n attacker: PlayerState,\n defender: PlayerState,\n technique: KoreanTechnique,\n ): CombatResult {\n const instance = new CombatSystem();\n return instance.executeAttack(attacker, defender, technique);\n }\n\n /**\n * Check if a player is defeated\n */\n isPlayerDefeated(player: PlayerState): boolean {\n return player.health <= 0 || player.consciousness <= 0;\n }\n\n /**\n * Update player state over time with effect management\n */\n updatePlayerState(player: PlayerState, deltaTime: number): PlayerState {\n let updatedPlayer = { ...player };\n\n updatedPlayer = removeExpiredEffects(updatedPlayer);\n\n const effectModifiers = getEffectModifiers(updatedPlayer);\n\n const regenRate = deltaTime / 1000;\n\n // Ki regeneration (slower during combat) - affected by effects\n if (updatedPlayer.ki < updatedPlayer.maxKi) {\n updatedPlayer.ki = Math.min(\n updatedPlayer.maxKi,\n updatedPlayer.ki + regenRate * 2 * effectModifiers.kiRegen,\n );\n }\n\n // Stamina regeneration - affected by effects and breathing disruption\n // Uses BASE_STAMINA_REGEN_RATE constant for better gameplay\n // Allows players to move around and attack frequently without running out\n // 체력 재생 속도 - 더 활발한 전투를 위해\n if (updatedPlayer.stamina < updatedPlayer.maxStamina) {\n const baseStaminaRegen = regenRate * BASE_STAMINA_REGEN_RATE * effectModifiers.staminaRegen;\n const modifiedStaminaRegen =\n BreathingDisruptionSystem.calculateStaminaRegen(\n updatedPlayer,\n baseStaminaRegen,\n );\n updatedPlayer.stamina = Math.min(\n updatedPlayer.maxStamina,\n updatedPlayer.stamina + modifiedStaminaRegen,\n );\n }\n\n if (\n updatedPlayer.health < updatedPlayer.maxHealth &&\n updatedPlayer.health > 0\n ) {\n updatedPlayer.health = Math.min(\n updatedPlayer.maxHealth,\n updatedPlayer.health + regenRate * 0.5,\n );\n }\n\n // Update breathing disruption effects (gradual recovery if torso health > 50%)\n updatedPlayer = updateBreathingDisruption(\n updatedPlayer,\n deltaTime,\n Date.now(),\n );\n\n const currentTime = Date.now();\n if (\n updatedPlayer.lastActionTime &&\n currentTime - updatedPlayer.lastActionTime > updatedPlayer.recoveryTime\n ) {\n updatedPlayer.isStunned = false;\n updatedPlayer.isCountering = false;\n }\n\n return updatedPlayer;\n }\n\n /**\n * Get combat statistics\n */\n getCombatStatistics(player: PlayerState): {\n healthPercent: number;\n kiPercent: number;\n staminaPercent: number;\n balancePercent: number;\n } {\n return {\n healthPercent: (player.health / player.maxHealth) * 100,\n kiPercent: (player.ki / player.maxKi) * 100,\n staminaPercent: (player.stamina / player.maxStamina) * 100,\n balancePercent: player.balance,\n };\n }\n\n /**\n * Fix: Integrate processVitalPointHit into the combat system with archetype parameters\n */\n private processVitalPointHit(\n vitalPointId: string,\n _baseDamage: number, // Unused but kept for interface compatibility\n attacker: PlayerState,\n defender: PlayerState,\n ): VitalPointHitResult {\n const vitalPoint = this.vitalPointSystem.getVitalPointById(vitalPointId);\n\n if (!vitalPoint) {\n return {\n hit: false,\n damage: 0,\n effects: [],\n severity: VitalPointSeverity.MINOR,\n };\n }\n\n return this.vitalPointSystem.processHit(\n vitalPoint.position, // Use vital point position for hit calculation\n { width: 10, height: 10 }, // Standard hit box\n vitalPointId, // Targeted vital point\n undefined, // Use current hour from system\n attacker.archetype, // Attacker archetype for offensive modifiers\n defender.archetype, // Defender archetype for defensive modifiers\n );\n }\n\n /**\n * Process defensive action and determine animation type.\n *\n * Determines the appropriate defensive animation based on:\n * - Defender's balance and stamina (defensive power)\n * - Attacker's technique power\n * - Combat readiness state\n *\n * **Korean**: 방어 행동 처리\n *\n * @param defender - Defending player state\n * @param attacker - Attacking player state (unused but kept for future enhancements)\n * @param attackPower - Power of the incoming attack\n * @returns Defensive animation type to play (parry_deflect, block_success, or guard_break)\n *\n * @example\n * ```typescript\n * const animType = combatSystem.processDefensiveAction(\n * defender,\n * attacker,\n * technique.damage\n * );\n * // Returns: 'parry_deflect', 'block_success', or 'guard_break'\n * ```\n *\n * @korean 방어행동처리\n */\n public processDefensiveAction(\n defender: PlayerState,\n _attacker: PlayerState,\n attackPower: number,\n ): Exclude<DefensiveAnimationType, \"guard_recovery\"> {\n // Guard Break: Check balance threshold first (highest priority condition)\n if (defender.balance < 30) {\n return \"guard_break\";\n }\n\n // Calculate defensive power based on balance and stamina\n // Balance represents physical stability (0-100)\n // Stamina represents energy reserves (0-100)\n const balanceFactor = defender.balance / 100;\n const staminaFactor = defender.stamina / 100;\n\n // Apply defensive modifiers from effects\n const effectModifiers = getEffectModifiers(defender);\n const defenseMultiplier = effectModifiers.defense;\n\n // Defense stat provides moderate bonus (normalized to 0.5-1.5 range for typical 5-15 defense)\n const defenseBonus = Math.max(0.5, Math.min(1.5, defender.defense / 10));\n\n const defensePower =\n balanceFactor * staminaFactor * 100 * defenseMultiplier * defenseBonus;\n\n // Determine defensive outcome based on power ratio\n // Parry: Strong defense (1.8x attack power or more) - Perfect deflection\n if (defensePower >= attackPower * 1.8) {\n return \"parry_deflect\";\n }\n // Block Success: Adequate defense (1.0x to 1.8x attack power) - Absorb impact\n else if (defensePower >= attackPower) {\n return \"block_success\";\n }\n // Guard Break: Defense insufficient against powerful attack (<60% of attack power)\n else if (defensePower < attackPower * 0.6) {\n return \"guard_break\";\n }\n // Block Success: Marginal defense (60-100% of attack power) - barely hold\n else {\n return \"block_success\";\n }\n }\n\n /**\n * Execute attack with technique\n */\n protected executeAttack(\n attacker: PlayerState,\n defender: PlayerState,\n technique: KoreanTechnique,\n ): CombatResult {\n const hitRoll = Math.random();\n const accuracy = technique.accuracy || 0.8;\n const hit = hitRoll <= accuracy;\n\n if (!hit) {\n return {\n hit: false,\n damage: 0,\n criticalHit: false,\n vitalPointHit: false,\n effects: [],\n timestamp: Date.now(),\n technique,\n attacker,\n defender,\n success: false,\n isCritical: false,\n isBlocked: false,\n };\n }\n\n const vitalPointHit: VitalPointHitResult = {\n hit: false,\n damage: 0,\n effects: [],\n severity: VitalPointSeverity.MINOR, // Use the enum directly\n };\n\n const damageResult = this.calculateDamage(\n technique,\n attacker,\n defender,\n vitalPointHit,\n );\n\n return {\n hit: true,\n damage: damageResult.totalDamage,\n criticalHit: Math.random() < (technique.critChance || 0.1),\n vitalPointHit: false,\n effects: damageResult.effectsApplied,\n timestamp: Date.now(),\n technique,\n attacker,\n defender,\n success: true,\n isCritical: false,\n isBlocked: false,\n };\n }\n\n /**\n * Fix: Add missing calculateDamage method required by interface\n */\n calculateDamage(\n technique: KoreanTechnique,\n attacker: PlayerState,\n defender: PlayerState,\n hitResult: VitalPointHitResult,\n ): {\n baseDamage: number;\n modifierDamage: number;\n totalDamage: number;\n effectsApplied: readonly StatusEffect[];\n finalDefenderState?: Partial<PlayerState>;\n } {\n const baseDamage = technique.damage || 15;\n\n const attackerBonus = attacker.attackPower * 0.1;\n\n let vitalPointMultiplier = 1.0;\n if (hitResult.hit && hitResult.vitalPointHit) {\n const severityMultipliers: Record<VitalPointSeverity, number> = {\n [VitalPointSeverity.MINOR]: 1.1,\n [VitalPointSeverity.MODERATE]: 1.3,\n [VitalPointSeverity.MAJOR]: 1.6,\n [VitalPointSeverity.CRITICAL]: 2.0,\n [VitalPointSeverity.LETHAL]: 3.0,\n };\n\n vitalPointMultiplier = severityMultipliers[hitResult.severity] ?? 1.0;\n }\n\n const modifierDamage = attackerBonus * vitalPointMultiplier;\n\n const defenseReduction = defender.defense * 0.05;\n const totalDamage = Math.max(\n 1,\n baseDamage + modifierDamage - defenseReduction,\n );\n\n const effectsApplied = [...technique.effects, ...hitResult.effects];\n\n return {\n baseDamage,\n modifierDamage,\n totalDamage: Math.floor(totalDamage),\n effectsApplied,\n finalDefenderState: {\n health: Math.max(0, defender.health - totalDamage),\n },\n };\n }\n}\n\n/**\n * Creates a standardized CombatResult with all required fields\n * Ensures both 'critical' and 'criticalHit' are present for API compatibility\n */\nexport function createCombatResult(\n partialResult: Partial<CombatResult>,\n): CombatResult {\n // Set default values\n const result: CombatResult = {\n success: partialResult.success ?? false,\n damage: partialResult.damage ?? 0,\n isCritical: partialResult.isCritical ?? partialResult.criticalHit ?? false,\n hit: partialResult.hit ?? partialResult.success ?? false,\n isBlocked: partialResult.isBlocked ?? false,\n vitalPointHit: partialResult.vitalPointHit ?? false,\n effects: partialResult.effects ?? [],\n attacker: partialResult.attacker,\n defender: partialResult.defender,\n technique: partialResult.technique,\n // Always set criticalHit to match critical for consistency\n criticalHit: partialResult.isCritical ?? partialResult.criticalHit ?? false,\n timestamp: Date.now(),\n };\n\n return result;\n}\n\nexport default CombatSystem;\n"],"mappings":"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"}
1
+ {"version":3,"file":"CombatSystem.js","names":[],"sources":["../../src/systems/CombatSystem.ts"],"sourcesContent":["import * as THREE from \"three\";\nimport { getArchetypePhysicalAttributes } from \"../data/archetypePhysicalAttributes\";\nimport { getTechniqueById } from \"../data/techniques\";\nimport { BodyRegion, CombatAttackType, CombatState, DamageType, GrappleTarget } from \"../types\";\nimport { VitalPointCategory, VitalPointSeverity } from \"../types/common\";\nimport { BASE_STAMINA_REGEN_RATE } from \"../types/physicsConstants\";\nimport { Technique } from \"../types/technique\";\nimport { calculateDistance3D } from \"../utils/math\";\nimport { calculateBodyRadius } from \"../utils/skeletonScaling\";\nimport type { DefensiveAnimationType } from \"./animation\";\nimport {\n AnimationType,\n calculateSpeedModifierForDamage,\n determineAnimationTypeForTechnique,\n getAdjustedAnimationDuration,\n getAnimationNameForType,\n isWithinHitWindow,\n} from \"./animation\";\nimport { applyDamageToBodyParts } from \"./bodypart/BodyPartDamageIntegration\";\nimport { playerInjuryManager } from \"./bodypart\";\nimport {\n applyBreathingDisruptionFromVitalPoint,\n applyBreathingDisruptionFromTorsoDamage,\n BreathingDisruptionSystem,\n causesBreathingDisruption,\n updateBreathingDisruption,\n} from \"./breathing\";\nimport BalanceSystem from \"./combat/BalanceSystem\";\nimport ConsciousnessSystem from \"./combat/ConsciousnessSystem\";\nimport {\n extractVitalPointCategory,\n isHeadTraumaHit,\n} from \"./combat/painConsciousnessUtils\";\nimport PainResponseSystem, {\n ShockPainEffect,\n} from \"./combat/PainResponseSystem\";\nimport { CombatResult, CombatSystemInterface } from \"./combat/types\";\nimport {\n CollisionDetection,\n KnockbackPhysics,\n physicalReachCalculator,\n type KnockbackConfig,\n} from \"./physics\";\nimport { PlayerState } from \"./player\";\nimport {\n addEffectsToPlayer,\n getEffectModifiers,\n removeExpiredEffects,\n} from \"./PlayerEffectManager\";\nimport { TRIGRAM_TECHNIQUES } from \"./trigram\";\nimport { TrigramSystem } from \"./TrigramSystem\";\nimport { StatusEffect } from \"./types\";\nimport { KoreanTechnique, VitalPointHitResult } from \"./vitalpoint/types\";\nimport { VitalPointSystem } from \"./VitalPointSystem\";\nimport { GrappleSystem } from \"./combat/GrappleSystem\";\n\n/**\n * Enhanced Combat System with Pain Response and Consciousness integration.\n *\n * Integrates realistic pain accumulation and consciousness tracking for\n * progressive combat impairment.\n */\nexport class CombatSystem implements CombatSystemInterface {\n private vitalPointSystem: VitalPointSystem;\n protected trigramSystem: TrigramSystem;\n private painSystem: PainResponseSystem;\n private consciousnessSystem: ConsciousnessSystem;\n private balanceSystem: BalanceSystem;\n private knockbackPhysics: KnockbackPhysics;\n private collisionDetection: CollisionDetection;\n private grappleSystem: GrappleSystem;\n\n // Track shock pain effects per player\n private shockPainEffects: Map<string, ShockPainEffect>;\n // Track last head trauma time per player for consciousness recovery\n private lastHeadTraumaTime: Map<string, number>;\n\n // Vital point severity thresholds\n private readonly SEVERITY_MAJOR_THRESHOLD = 30;\n private readonly SEVERITY_MODERATE_THRESHOLD = 20;\n\n constructor() {\n this.vitalPointSystem = new VitalPointSystem();\n this.trigramSystem = new TrigramSystem();\n this.painSystem = new PainResponseSystem();\n this.consciousnessSystem = new ConsciousnessSystem();\n this.balanceSystem = new BalanceSystem();\n this.knockbackPhysics = new KnockbackPhysics();\n this.collisionDetection = new CollisionDetection();\n this.grappleSystem = new GrappleSystem();\n this.shockPainEffects = new Map();\n this.lastHeadTraumaTime = new Map();\n }\n\n /**\n * Cleanup per-player combat state.\n *\n * Call this when a player permanently leaves the match or when\n * match-level cleanup is performed to avoid unbounded Map growth.\n *\n * @param playerId - ID of the player to cleanup\n *\n * @korean 플레이어데이터정리\n */\n public cleanupPlayerData(playerId: string): void {\n this.shockPainEffects.delete(playerId);\n this.lastHeadTraumaTime.delete(playerId);\n }\n\n /**\n * Dispose of all combat system resources.\n *\n * **Korean**: 전투 시스템 자원 정리\n *\n * Cleans up Three.js resources (geometries, raycaster) used by the collision\n * detection system to prevent memory leaks. Should be called when the\n * CombatSystem is destroyed or reinitialized.\n *\n * @korean 전투시스템자원정리\n */\n public dispose(): void {\n // Dispose collision detection resources (cached geometries, raycaster)\n this.collisionDetection.dispose();\n }\n\n /**\n * Get the balance system instance for fall checking.\n *\n * @returns BalanceSystem instance\n * @korean 균형시스템가져오기\n */\n public getBalanceSystem(): BalanceSystem {\n return this.balanceSystem;\n }\n\n /**\n * Get the consciousness system instance for fall checking.\n *\n * @returns ConsciousnessSystem instance\n * @korean 의식시스템가져오기\n */\n public getConsciousnessSystem(): ConsciousnessSystem {\n return this.consciousnessSystem;\n }\n\n /**\n * Get the collision detection system instance.\n *\n * @returns CollisionDetection instance\n * @korean 충돌감지시스템가져오기\n */\n public getCollisionDetection(): CollisionDetection {\n return this.collisionDetection;\n }\n\n /**\n * Calculate knockback physics for combat hit.\n *\n * **Korean**: 밀침 계산 (Calculate Knockback)\n *\n * Determines knockback displacement, duration, and fall state based on:\n * - Attack damage amount\n * - Defender's balance state\n * - Defender's stance resistance\n * - Attack direction vector\n *\n * @param attacker - Attacking player state\n * @param defender - Defending player state\n * @param damage - Total damage dealt\n * @returns Knockback information or undefined if no knockback\n *\n * @example\n * ```typescript\n * const knockback = this.calculateKnockback(attacker, defender, 80);\n * // Returns: { displacement: {x:2.5,y:0,z:0}, duration:0.8, recoveryWindow:0.7, shouldFall:false }\n * ```\n *\n * @private\n * @korean 밀침계산\n */\n private calculateKnockback(\n attacker: PlayerState,\n defender: PlayerState,\n damage: number,\n ): CombatResult[\"knockback\"] {\n const attackDirection = new THREE.Vector3(\n defender.position.x - attacker.position.x,\n 0, // Keep knockback on horizontal plane\n defender.position.y - attacker.position.y,\n );\n\n // If attacker and defender are at the exact same position, skip knockback to avoid NaN direction\n if (attackDirection.lengthSq() === 0) {\n return undefined;\n }\n\n attackDirection.normalize();\n\n const config: KnockbackConfig = {\n force: damage * 10, // Convert damage to force (arbitrary scaling)\n direction: attackDirection,\n duration: 0, // Will be calculated by physics engine\n balanceState: {\n current: defender.balance,\n max: 100, // Assuming max balance is always 100\n },\n currentStance: defender.currentStance,\n };\n\n const result = this.knockbackPhysics.calculateKnockback(config, damage);\n\n return {\n displacement: {\n x: result.displacement.x,\n y: result.displacement.y,\n z: result.displacement.z,\n },\n duration: result.duration,\n recoveryWindow: result.recoveryWindow,\n shouldFall: result.shouldFall,\n };\n }\n\n /**\n * Fix: Update resolveAttack to match interface signature and add animation-aware hit detection\n *\n * **Korean**: 공격 해결 (애니메이션 인식)\n *\n * Integrates animation timing and physical reach calculation for reality-based hit detection.\n * Hits only register when:\n * 1. Animation is in hit window (extension phase)\n * 2. Attacker is within effective reach based on limb length and animation\n * 3. Existing accuracy and stance checks pass\n *\n * @param attacker - Attacking player state\n * @param defender - Defending player state\n * @param technique - Technique being executed\n * @param targetedVitalPointId - Optional specific vital point target\n * @param animationContext - Optional animation timing context for reality-based hit detection\n * @returns Combat result with hit/miss and damage information\n */\n resolveAttack(\n attacker: PlayerState,\n defender: PlayerState,\n technique: KoreanTechnique,\n targetedVitalPointId?: string,\n animationContext?: {\n animationType: AnimationType;\n currentTime: number;\n },\n ): CombatResult {\n const timestamp = Date.now();\n\n if (!this.canExecuteTechnique(attacker, technique)) {\n return {\n hit: false,\n damage: 0,\n criticalHit: false,\n vitalPointHit: false,\n effects: [],\n timestamp,\n technique,\n attacker,\n defender,\n success: false,\n isCritical: false,\n isBlocked: false,\n };\n }\n\n if (attacker.combatState === CombatState.GRAPPLED) {\n return {\n hit: false,\n damage: 0,\n criticalHit: false,\n vitalPointHit: false,\n effects: [],\n timestamp,\n technique,\n attacker,\n defender,\n success: false,\n isCritical: false,\n isBlocked: false,\n };\n }\n\n if (animationContext) {\n const { animationType, currentTime } = animationContext;\n\n if (!isWithinHitWindow(animationType, currentTime)) {\n return {\n hit: false,\n damage: 0,\n criticalHit: false,\n vitalPointHit: false,\n effects: [],\n timestamp,\n technique,\n attacker,\n defender,\n success: false,\n isCritical: false,\n isBlocked: false,\n };\n }\n\n const attackerPhysical = getArchetypePhysicalAttributes(\n attacker.archetype,\n );\n const reachResult = physicalReachCalculator.calculateReach(\n attackerPhysical,\n animationType,\n currentTime,\n attacker.currentStance,\n technique.reachConfig, // Pass reachConfig for hybrid reach calculation\n );\n\n // Physics-first: Position type is 2D in METERS\n // No conversion needed - positions are already in meters\n const centerToCenterDistance = calculateDistance3D(\n [attacker.position.x, attacker.position.y, 0],\n [defender.position.x, defender.position.y, 0],\n );\n\n // Note: PhysicalReachCalculator already includes attacker body pivot/offset in reach calculation\n // (shoulder offset for punches, hip rotation for kicks), so we only subtract defender radius\n // to avoid double-counting the attacker's body dimension.\n // 타격 거리 계산: 수비자 몸체 반경만 제외 (공격자 몸체 오프셋은 이미 도달 거리에 포함됨)\n const defenderPhysical = getArchetypePhysicalAttributes(\n defender.archetype,\n );\n const defenderBodyRadius = calculateBodyRadius(defenderPhysical);\n\n // Reach is calculated from attacker center to striking limb surface (includes body pivot),\n // so we measure from attacker center to defender surface (subtracting defender radius only).\n // 유효 거리 = 중심간 거리 - 수비자 몸체 반경\n const distance = Math.max(0, centerToCenterDistance - defenderBodyRadius);\n\n if (distance > reachResult.effectiveReach) {\n return {\n hit: false,\n damage: 0,\n criticalHit: false,\n vitalPointHit: false,\n effects: [],\n timestamp,\n technique,\n attacker,\n defender,\n success: false,\n isCritical: false,\n isBlocked: false,\n };\n }\n }\n\n // Fix: Use correct method signature\n const stanceEffectiveness = this.trigramSystem.calculateStanceEffectiveness(\n attacker.currentStance,\n defender.currentStance,\n );\n\n // Calculate base hit chance\n const baseAccuracy = technique.accuracy * stanceEffectiveness;\n const hitRoll = Math.random();\n const hit = hitRoll <= baseAccuracy;\n\n if (!hit) {\n return {\n hit: false,\n damage: 0,\n criticalHit: false,\n vitalPointHit: false,\n effects: [],\n timestamp,\n technique,\n attacker,\n defender,\n success: false,\n isCritical: false,\n isBlocked: false,\n };\n }\n\n // Special handling for grapple techniques\n if (technique.type === CombatAttackType.GRAPPLE) {\n const grappleResult = this.handleGrappleTechnique(\n attacker,\n defender,\n technique,\n timestamp\n );\n\n // Grapples don't deal immediate damage, they establish control\n // Small damage on successful grapple to represent the initial impact\n const grappleDamage = grappleResult.grappleSuccess\n ? technique.damage * 0.3\n : 0;\n\n return {\n hit: grappleResult.grappleSuccess,\n damage: grappleDamage,\n criticalHit: false,\n vitalPointHit: false,\n effects: [],\n timestamp,\n technique,\n attacker: grappleResult.updatedAttacker,\n defender: grappleResult.updatedDefender,\n success: grappleResult.grappleSuccess,\n isCritical: false,\n isBlocked: false,\n animation: this.getAnimationInfoForTechnique(technique),\n };\n }\n\n // Process vital point hit if targeted (with archetype parameters)\n let vitalPointResult: VitalPointHitResult | null = null;\n if (targetedVitalPointId) {\n vitalPointResult = this.processVitalPointHit(\n targetedVitalPointId,\n technique.damage ?? 15,\n attacker,\n defender,\n );\n }\n\n const damageResult = this.calculateDamage(\n technique,\n attacker,\n defender,\n vitalPointResult ?? {\n hit: false,\n damage: 0,\n effects: [],\n severity: VitalPointSeverity.MINOR,\n },\n );\n\n const critRoll = Math.random();\n const isCritical = critRoll <= (technique.critChance ?? 0.1);\n\n const animationInfo = this.getAnimationInfoForTechnique(technique);\n\n // Calculate knockback physics (밀침 물리)\n const knockbackInfo = this.calculateKnockback(\n attacker,\n defender,\n damageResult.totalDamage,\n );\n\n return {\n hit: true,\n damage: damageResult.totalDamage,\n criticalHit: isCritical,\n vitalPointHit: vitalPointResult?.hit ?? false,\n effects: damageResult.effectsApplied,\n timestamp,\n technique,\n attacker,\n defender,\n success: true,\n isCritical: vitalPointResult?.hit ?? false,\n isBlocked: false,\n targetedVitalPointId, // Pass through the targeted vital point ID\n animation: animationInfo, // Add animation information\n knockback: knockbackInfo, // Add knockback information\n };\n }\n\n /**\n * Get animation information for a technique\n *\n * Determines the skeletal animation to play, duration, and speed modifier\n * based on technique configuration or automatic determination.\n *\n * @param technique - Korean technique to execute\n * @returns Animation information or undefined\n *\n * @private\n * @korean 기술애니메이션정보가져오기\n */\n private getAnimationInfoForTechnique(\n technique: KoreanTechnique,\n ): CombatResult[\"animation\"] {\n // Check if technique has explicit animation config (from Technique interface)\n // KoreanTechnique may not have animation field, so check the technique data\n const techniqueData = this.getTechniqueData(technique);\n\n let animationType;\n let speedModifier;\n\n if (techniqueData?.animation) {\n animationType = techniqueData.animation.type;\n speedModifier = techniqueData.animation.speedModifier;\n } else {\n const techniqueName =\n technique.name?.english || technique.englishName || \"\";\n const techniqueId = technique.id || \"\";\n const damageType = technique.damageType || \"\";\n\n animationType = determineAnimationTypeForTechnique(\n techniqueName,\n techniqueId,\n damageType,\n );\n\n speedModifier = calculateSpeedModifierForDamage(technique.damage || 15);\n }\n\n const animationName = getAnimationNameForType(animationType);\n const duration = getAdjustedAnimationDuration(animationName, speedModifier);\n const techniqueDisplayName =\n technique.name?.korean || technique.koreanName || technique.id;\n\n return {\n animationName,\n duration,\n speedModifier,\n techniqueDisplayName,\n };\n }\n\n /**\n * Get Technique data if this KoreanTechnique has an associated Technique definition\n *\n * @param technique - Korean technique\n * @returns Technique data or null\n *\n * @private\n * @korean 기술데이터가져오기\n */\n private getTechniqueData(technique: KoreanTechnique): Technique | null {\n return getTechniqueById(technique.id) ?? null;\n }\n\n /**\n * Handle grapple technique execution.\n *\n * **Korean**: 잡기 기술 처리 (Handle Grapple Technique)\n *\n * Initiates or transitions grapple control based on technique.\n *\n * @param attacker - Player executing grapple\n * @param defender - Target player\n * @param technique - Grapple technique being used\n * @param currentTime - Current game time in milliseconds\n * @returns Updated player states with grapple control\n */\n handleGrappleTechnique(\n attacker: PlayerState,\n defender: PlayerState,\n technique: KoreanTechnique,\n currentTime: number\n ): {\n updatedAttacker: PlayerState;\n updatedDefender: PlayerState;\n grappleSuccess: boolean;\n } {\n // Determine grapple target from technique\n const target = this.getGrappleTargetFromTechnique(technique);\n\n // Attempt grapple\n const result = this.grappleSystem.attemptGrapple(\n attacker,\n defender,\n target,\n currentTime\n );\n\n if (result.success && result.grappleControl) {\n // Grapple succeeded - update both players\n return {\n updatedAttacker: {\n ...attacker,\n combatState: CombatState.GRAPPLING,\n grappleControl: result.grappleControl,\n stamina: Math.max(0, attacker.stamina - result.staminaCost),\n },\n updatedDefender: {\n ...defender,\n combatState: CombatState.GRAPPLED,\n grappleControl: result.grappleControl,\n },\n grappleSuccess: true,\n };\n }\n\n // Grapple failed\n return {\n updatedAttacker: {\n ...attacker,\n stamina: Math.max(0, attacker.stamina - result.staminaCost),\n },\n updatedDefender: defender,\n grappleSuccess: false,\n };\n }\n\n /**\n * Determine grapple target from technique characteristics.\n *\n * **Korean**: 기술에서 잡기 목표 결정 (Determine Grapple Target from Technique)\n *\n * @private\n */\n private getGrappleTargetFromTechnique(\n technique: KoreanTechnique\n ): GrappleTarget {\n // Check technique name/ID for hints (both English and Korean)\n const techName = (\n technique.name?.english ||\n technique.englishName ||\n \"\"\n ).toLowerCase();\n const techNameKorean = (\n technique.name?.korean ||\n technique.koreanName ||\n \"\"\n );\n const techId = technique.id.toLowerCase();\n\n // Check for wrist/hand - 손목 (sonmok)\n if (\n techName.includes(\"wrist\") ||\n techId.includes(\"wrist\") ||\n techNameKorean.includes(\"손목\")\n ) {\n return GrappleTarget.HAND;\n }\n\n // Check for arm - 팔 (pal)\n if (\n techName.includes(\"arm\") ||\n techId.includes(\"arm\") ||\n techNameKorean.includes(\"팔\")\n ) {\n return GrappleTarget.ARM;\n }\n\n // Check for leg - 다리 (dari)\n if (\n techName.includes(\"leg\") ||\n techId.includes(\"leg\") ||\n techNameKorean.includes(\"다리\")\n ) {\n return GrappleTarget.LEG;\n }\n\n // Check for neck - 목 (mok)\n if (\n techName.includes(\"neck\") ||\n techId.includes(\"neck\") ||\n techNameKorean.includes(\"목\")\n ) {\n return GrappleTarget.NECK;\n }\n\n // Check for both arms - 양팔 (yangpal)\n if (\n techName.includes(\"both\") ||\n techName.includes(\"double\") ||\n techId.includes(\"both\") ||\n techNameKorean.includes(\"양팔\") ||\n techNameKorean.includes(\"쌍\")\n ) {\n return GrappleTarget.BOTH_ARMS;\n }\n\n // Check for torso/body/hip - 몸통 (momtong), 허리 (heori), 몸 (mom)\n if (\n techName.includes(\"torso\") ||\n techName.includes(\"body\") ||\n techName.includes(\"hip\") ||\n techId.includes(\"torso\") ||\n techNameKorean.includes(\"몸통\") ||\n techNameKorean.includes(\"허리\") ||\n techNameKorean.includes(\"몸\")\n ) {\n return GrappleTarget.TORSO;\n }\n\n // Default to arm for unspecified grapples\n return GrappleTarget.ARM;\n }\n\n /**\n * Update grapple state for players over time.\n *\n * **Korean**: 잡기 상태 업데이트 (Update Grapple State)\n *\n * @param controller - Player maintaining control\n * @param target - Player being controlled\n * @param deltaTime - Time elapsed in seconds\n * @param currentTime - Current game time in milliseconds\n * @returns Updated player states\n */\n updateGrappleState(\n controller: PlayerState,\n target: PlayerState,\n deltaTime: number,\n currentTime: number\n ): {\n updatedController: PlayerState;\n updatedTarget: PlayerState;\n } {\n if (!controller.grappleControl) {\n // No active grapple\n return {\n updatedController: controller,\n updatedTarget: target,\n };\n }\n\n // Validate grapple control consistency\n if (\n controller.grappleControl.controllerId !== controller.id ||\n controller.grappleControl.targetId !== target.id\n ) {\n // Inconsistent state - break grapple\n return {\n updatedController: {\n ...controller,\n combatState: CombatState.IDLE,\n grappleControl: null,\n },\n updatedTarget: {\n ...target,\n combatState: CombatState.IDLE,\n grappleControl: null,\n },\n };\n }\n\n // Update grapple control\n const updatedControl = this.grappleSystem.updateGrapple(\n controller.grappleControl,\n controller,\n target,\n deltaTime,\n currentTime\n );\n\n if (!updatedControl) {\n // Grapple broken - reset both players\n return {\n updatedController: {\n ...controller,\n combatState: CombatState.IDLE,\n grappleControl: null,\n },\n updatedTarget: {\n ...target,\n combatState: CombatState.IDLE,\n grappleControl: null,\n },\n };\n }\n\n // Deduct stamina from controller\n const staminaCost = updatedControl.staminaCostPerSecond * deltaTime;\n\n return {\n updatedController: {\n ...controller,\n grappleControl: updatedControl,\n stamina: Math.max(0, controller.stamina - staminaCost),\n },\n updatedTarget: {\n ...target,\n grappleControl: updatedControl,\n },\n };\n }\n\n /**\n * Fix: Make applyCombatResult non-static instance method with effect application\n * Enhanced with Pain Response and Consciousness System integration\n */\n applyCombatResult(\n result: CombatResult,\n attacker: PlayerState,\n defender: PlayerState,\n ): { updatedAttacker: PlayerState; updatedDefender: PlayerState } {\n // Start with base result\n const { updatedAttacker, updatedDefender: initialDefender } =\n CombatSystem.applyCombatResult(result, attacker, defender);\n let updatedDefender = initialDefender;\n\n if (result.hit && result.damage > 0) {\n // Determine vital point category and severity from hit result\n const category = this.getVitalPointCategory(result);\n const severity = this.getVitalPointSeverity(result);\n\n // Apply pain from damage\n const { player: defenderWithPain, shockEffect: newShockEffect } =\n this.painSystem.applyPain(\n updatedDefender,\n result.damage,\n severity,\n category,\n );\n updatedDefender = defenderWithPain;\n\n // Store shock pain effect if triggered\n if (newShockEffect) {\n this.shockPainEffects.set(updatedDefender.id, newShockEffect);\n }\n\n // Check for pain overload stun\n if (this.painSystem.shouldTriggerStun(updatedDefender)) {\n updatedDefender = {\n ...updatedDefender,\n isStunned: true,\n };\n }\n\n // Apply consciousness damage for head/neurological hits\n if (this.isHeadTrauma(result, category)) {\n updatedDefender = this.consciousnessSystem.applyDamage(\n updatedDefender,\n result.damage,\n category,\n );\n\n // Track head trauma time for recovery gating\n this.lastHeadTraumaTime.set(updatedDefender.id, Date.now());\n\n // Check incapacitation threshold\n if (\n this.consciousnessSystem.isAtIncapacitationThreshold(updatedDefender)\n ) {\n updatedDefender = {\n ...updatedDefender,\n isStunned: true,\n // Could add helpless duration tracking here if needed\n };\n }\n }\n\n // Apply pain and consciousness effects to stats\n const currentShockEffect = this.shockPainEffects.get(updatedDefender.id);\n updatedDefender = this.painSystem.applyEffects(\n updatedDefender,\n currentShockEffect,\n );\n updatedDefender = this.consciousnessSystem.applyEffects(updatedDefender);\n\n // Apply balance disruption from the hit\n // Determine body region from vital point or use default\n const bodyRegion = this.getBodyRegionFromResult(result);\n updatedDefender = this.balanceSystem.disruptBalance(\n updatedDefender,\n result.damage,\n bodyRegion,\n );\n\n // Apply breathing disruption for torso strikes\n if (result.vitalPointHit && result.targetedVitalPointId) {\n // Vital point strike to torso\n const vitalPoint = this.vitalPointSystem.getVitalPointById(\n result.targetedVitalPointId,\n );\n if (vitalPoint && causesBreathingDisruption(vitalPoint.id)) {\n updatedDefender = applyBreathingDisruptionFromVitalPoint(\n updatedDefender,\n vitalPoint,\n Date.now(),\n );\n }\n } else if (bodyRegion === BodyRegion.TORSO && result.damage >= 10) {\n // General torso damage (non-vital point hits)\n // Apply breathing disruption if damage is significant enough\n // Note: Solar plexus detection is best-effort; vital point system is primary mechanism\n const techniqueId = (result.technique?.id ?? \"\").toLowerCase();\n const isSolarPlexusArea = \n techniqueId.includes(\"solar\") ||\n techniqueId.includes(\"myeongchi\");\n updatedDefender = applyBreathingDisruptionFromTorsoDamage(\n updatedDefender,\n result.damage,\n isSolarPlexusArea,\n Date.now(),\n );\n }\n }\n\n return { updatedAttacker, updatedDefender };\n }\n\n /**\n * Determines if a hit caused head trauma (affects consciousness).\n *\n * @param result - Combat result\n * @param category - Vital point category\n * @returns True if hit should affect consciousness\n */\n private isHeadTrauma(\n result: CombatResult,\n category?: VitalPointCategory,\n ): boolean {\n return isHeadTraumaHit(result, category);\n }\n\n /**\n * Extracts vital point category from combat result.\n *\n * @param result - Combat result\n * @returns Vital point category if available\n */\n private getVitalPointCategory(\n result: CombatResult,\n ): VitalPointCategory | undefined {\n return extractVitalPointCategory(result);\n }\n\n /**\n * Extracts vital point severity from combat result.\n *\n * @param result - Combat result\n * @returns Vital point severity if critical hit\n */\n private getVitalPointSeverity(\n result: CombatResult,\n ): VitalPointSeverity | undefined {\n if (result.vitalPointHit) {\n if (result.isCritical) {\n return VitalPointSeverity.CRITICAL;\n }\n if (result.damage > this.SEVERITY_MAJOR_THRESHOLD) {\n return VitalPointSeverity.MAJOR;\n }\n if (result.damage > this.SEVERITY_MODERATE_THRESHOLD) {\n return VitalPointSeverity.MODERATE;\n }\n return VitalPointSeverity.MINOR;\n }\n return undefined;\n }\n\n /**\n * Determines body region from combat result for balance disruption.\n *\n * Maps vital points to body regions for balance system integration.\n * Uses string matching on vital point IDs as a pragmatic heuristic since\n * VitalPoint interface doesn't currently include a bodyRegion property.\n *\n * Future improvement: Add bodyRegion: BodyRegion to VitalPoint interface\n * for more robust region mapping without string pattern matching.\n *\n * @param result - Combat result\n * @returns Body region that was struck\n * @private\n * @korean 신체부위결정\n */\n private getBodyRegionFromResult(result: CombatResult): BodyRegion {\n // If we have a targeted vital point, try to determine region\n if (result.targetedVitalPointId) {\n const vitalPoint = this.vitalPointSystem.getVitalPointById(\n result.targetedVitalPointId,\n );\n\n if (vitalPoint) {\n const pointId = vitalPoint.id.toLowerCase();\n\n // Check for leg/lower body targets\n if (\n pointId.includes(\"leg\") ||\n pointId.includes(\"knee\") ||\n pointId.includes(\"ankle\") ||\n pointId.includes(\"thigh\")\n ) {\n return BodyRegion.LEFT_LEG; // Generic leg for balance disruption\n }\n\n // Check for head targets\n if (\n pointId.includes(\"head\") ||\n pointId.includes(\"temple\") ||\n pointId.includes(\"jaw\") ||\n pointId.includes(\"nose\")\n ) {\n return BodyRegion.HEAD;\n }\n\n // Check for arm targets\n if (\n pointId.includes(\"arm\") ||\n pointId.includes(\"elbow\") ||\n pointId.includes(\"wrist\") ||\n pointId.includes(\"shoulder\")\n ) {\n return BodyRegion.LEFT_ARM;\n }\n }\n }\n\n // Default to torso for general strikes\n return BodyRegion.TORSO;\n }\n\n /**\n * Updates player states for recovery (pain dissipation, consciousness recovery, balance recovery).\n * Call this regularly in game loop.\n *\n * @param player - Player to update\n * @param deltaTime - Time elapsed since last update (ms)\n * @returns Updated player state\n */\n applyRecovery(player: PlayerState, deltaTime: number): PlayerState {\n let updatedPlayer = player;\n\n // Apply pain recovery\n updatedPlayer = this.painSystem.applyDissipation(updatedPlayer, deltaTime);\n\n // Apply consciousness recovery (only if enough time since head trauma)\n const lastTrauma = this.lastHeadTraumaTime.get(player.id);\n updatedPlayer = this.consciousnessSystem.applyRecovery(\n updatedPlayer,\n deltaTime,\n lastTrauma,\n );\n\n // Apply balance recovery\n updatedPlayer = this.balanceSystem.applyRecovery(updatedPlayer, deltaTime);\n\n // Clean up expired shock pain effects\n const shockEffect = this.shockPainEffects.get(player.id);\n if (shockEffect) {\n const elapsed = Date.now() - shockEffect.startTime;\n if (elapsed >= shockEffect.duration) {\n this.shockPainEffects.delete(player.id);\n }\n }\n\n return updatedPlayer;\n }\n\n /**\n * Static version for backwards compatibility with comprehensive effect application\n * Enhanced with Pain Response and Consciousness System integration\n * Updated to apply damage to body parts for 8-body-part health visualization\n */\n static applyCombatResult(\n result: CombatResult,\n attacker: PlayerState,\n defender: PlayerState,\n ): { updatedAttacker: PlayerState; updatedDefender: PlayerState } {\n let updatedDefender = defender;\n let updatedAttacker = attacker;\n\n if (result.hit) {\n // Determine body region from technique or use random distribution\n const bodyRegion = CombatSystem.getBodyRegionFromTechnique(\n result.technique,\n );\n\n // Apply damage to body parts (this also updates aggregate health)\n updatedDefender = applyDamageToBodyParts(\n defender,\n result.damage,\n bodyRegion,\n );\n\n // Record injury for trauma visualization (외상 시각화)\n // Use per-player injury tracking to prevent mixing injuries between characters\n // Automatically tracks bruising, cuts, and blood effects based on damage type\n // Records injury even without specific technique (defaults to BRUISE)\n if (result.damage > 0) {\n const defenderInjuryIntegration = playerInjuryManager.getIntegrationForPlayer(defender.id);\n defenderInjuryIntegration.recordCombatDamage({\n damage: result.damage,\n bodyRegion,\n damageType: (result.technique?.damageType as DamageType) || undefined,\n });\n }\n\n // Update tracking stats (applyDamageToBodyParts already sets health)\n updatedDefender = {\n ...updatedDefender,\n totalDamageReceived:\n updatedDefender.totalDamageReceived + result.damage,\n hitsTaken: updatedDefender.hitsTaken + 1,\n };\n\n if (result.effects && result.effects.length > 0) {\n updatedDefender = addEffectsToPlayer(updatedDefender, result.effects);\n }\n\n if (result.vitalPointHit) {\n updatedAttacker = {\n ...updatedAttacker,\n vitalPointHits: attacker.vitalPointHits + 1,\n };\n }\n\n if (result.isCritical) {\n updatedAttacker = {\n ...updatedAttacker,\n perfectStrikes: attacker.perfectStrikes + 1,\n };\n }\n }\n\n updatedAttacker = {\n ...updatedAttacker,\n ki: Math.max(0, attacker.ki - 5),\n stamina: Math.max(0, attacker.stamina - 10),\n totalDamageDealt:\n attacker.totalDamageDealt + (result.hit ? result.damage : 0),\n hitsLanded: attacker.hitsLanded + (result.hit ? 1 : 0),\n };\n\n return { updatedAttacker, updatedDefender };\n }\n\n /**\n * Determine body region from technique name or type\n *\n * **Korean**: 기술에서 신체 영역 결정\n *\n * @param technique - The technique used in the attack\n * @returns Body region to apply damage to\n */\n private static getBodyRegionFromTechnique(\n technique?: KoreanTechnique,\n ): BodyRegion {\n if (!technique) {\n return BodyRegion.TORSO;\n }\n\n const techniqueName = (\n technique.name?.english ||\n technique.englishName ||\n \"\"\n ).toLowerCase();\n const techniqueId = technique.id?.toLowerCase() || \"\";\n\n // Head/face targeting techniques\n if (\n techniqueName.includes(\"head\") ||\n techniqueName.includes(\"temple\") ||\n techniqueName.includes(\"jaw\") ||\n techniqueName.includes(\"face\") ||\n techniqueId.includes(\"head\") ||\n techniqueId.includes(\"skull\")\n ) {\n return BodyRegion.HEAD;\n }\n\n // Neck targeting techniques\n if (\n techniqueName.includes(\"neck\") ||\n techniqueName.includes(\"throat\") ||\n techniqueName.includes(\"choke\") ||\n techniqueId.includes(\"neck\")\n ) {\n return BodyRegion.NECK;\n }\n\n // Leg targeting techniques\n if (\n techniqueName.includes(\"kick\") ||\n techniqueName.includes(\"sweep\") ||\n techniqueName.includes(\"leg\") ||\n techniqueName.includes(\"knee\") ||\n techniqueId.includes(\"kick\") ||\n techniqueId.includes(\"leg\")\n ) {\n return Math.random() < 0.5 ? BodyRegion.LEFT_LEG : BodyRegion.RIGHT_LEG;\n }\n\n // Arm targeting techniques\n if (\n techniqueName.includes(\"arm\") ||\n techniqueName.includes(\"shoulder\") ||\n techniqueName.includes(\"elbow\") ||\n techniqueId.includes(\"arm\")\n ) {\n return Math.random() < 0.5 ? BodyRegion.LEFT_ARM : BodyRegion.RIGHT_ARM;\n }\n\n // Punch/strike techniques typically target torso\n if (\n techniqueName.includes(\"punch\") ||\n techniqueName.includes(\"strike\") ||\n techniqueName.includes(\"jab\") ||\n techniqueName.includes(\"cross\") ||\n techniqueName.includes(\"hook\") ||\n techniqueName.includes(\"uppercut\")\n ) {\n // Mix of torso and head for punches\n return Math.random() < 0.7 ? BodyRegion.TORSO : BodyRegion.HEAD;\n }\n\n // Body/core targeting techniques\n if (\n techniqueName.includes(\"body\") ||\n techniqueName.includes(\"solar\") ||\n techniqueName.includes(\"liver\") ||\n techniqueName.includes(\"ribs\") ||\n techniqueName.includes(\"abdomen\") ||\n techniqueId.includes(\"torso\") ||\n techniqueId.includes(\"core\")\n ) {\n return BodyRegion.TORSO;\n }\n\n // Default to torso for unmatched techniques\n return BodyRegion.TORSO;\n }\n\n /**\n * Fix: Add missing getAvailableTechniques method required by interface\n */\n getAvailableTechniques(player: PlayerState): readonly KoreanTechnique[] {\n const allTechniques = TRIGRAM_TECHNIQUES[player.currentStance] ?? [];\n\n // Filter techniques based on available resources using canExecuteTechnique\n return allTechniques.filter((technique) =>\n this.canExecuteTechnique(player, technique as KoreanTechnique),\n ) as readonly KoreanTechnique[];\n }\n\n /**\n * Check if attacker can execute technique\n */\n private canExecuteTechnique(\n player: PlayerState,\n technique: KoreanTechnique,\n ): boolean {\n return (\n player.ki >= technique.kiCost &&\n player.stamina >= technique.staminaCost &&\n player.currentStance === technique.stance &&\n !player.isStunned\n );\n }\n\n /**\n * Static methods for backwards compatibility\n */\n static resolveAttack(\n attacker: PlayerState,\n defender: PlayerState,\n technique: KoreanTechnique,\n ): CombatResult {\n const instance = new CombatSystem();\n return instance.executeAttack(attacker, defender, technique);\n }\n\n /**\n * Check if a player is defeated\n */\n isPlayerDefeated(player: PlayerState): boolean {\n return player.health <= 0 || player.consciousness <= 0;\n }\n\n /**\n * Update player state over time with effect management\n */\n updatePlayerState(player: PlayerState, deltaTime: number): PlayerState {\n let updatedPlayer = { ...player };\n\n updatedPlayer = removeExpiredEffects(updatedPlayer);\n\n const effectModifiers = getEffectModifiers(updatedPlayer);\n\n const regenRate = deltaTime / 1000;\n\n // Ki regeneration (slower during combat) - affected by effects\n if (updatedPlayer.ki < updatedPlayer.maxKi) {\n updatedPlayer.ki = Math.min(\n updatedPlayer.maxKi,\n updatedPlayer.ki + regenRate * 2 * effectModifiers.kiRegen,\n );\n }\n\n // Stamina regeneration - affected by effects and breathing disruption\n // Uses BASE_STAMINA_REGEN_RATE constant for better gameplay\n // Allows players to move around and attack frequently without running out\n // 체력 재생 속도 - 더 활발한 전투를 위해\n if (updatedPlayer.stamina < updatedPlayer.maxStamina) {\n const baseStaminaRegen = regenRate * BASE_STAMINA_REGEN_RATE * effectModifiers.staminaRegen;\n const modifiedStaminaRegen =\n BreathingDisruptionSystem.calculateStaminaRegen(\n updatedPlayer,\n baseStaminaRegen,\n );\n updatedPlayer.stamina = Math.min(\n updatedPlayer.maxStamina,\n updatedPlayer.stamina + modifiedStaminaRegen,\n );\n }\n\n if (\n updatedPlayer.health < updatedPlayer.maxHealth &&\n updatedPlayer.health > 0\n ) {\n updatedPlayer.health = Math.min(\n updatedPlayer.maxHealth,\n updatedPlayer.health + regenRate * 0.5,\n );\n }\n\n // Update breathing disruption effects (gradual recovery if torso health > 50%)\n updatedPlayer = updateBreathingDisruption(\n updatedPlayer,\n deltaTime,\n Date.now(),\n );\n\n const currentTime = Date.now();\n if (\n updatedPlayer.lastActionTime &&\n currentTime - updatedPlayer.lastActionTime > updatedPlayer.recoveryTime\n ) {\n updatedPlayer.isStunned = false;\n updatedPlayer.isCountering = false;\n }\n\n return updatedPlayer;\n }\n\n /**\n * Get combat statistics\n */\n getCombatStatistics(player: PlayerState): {\n healthPercent: number;\n kiPercent: number;\n staminaPercent: number;\n balancePercent: number;\n } {\n return {\n healthPercent: (player.health / player.maxHealth) * 100,\n kiPercent: (player.ki / player.maxKi) * 100,\n staminaPercent: (player.stamina / player.maxStamina) * 100,\n balancePercent: player.balance,\n };\n }\n\n /**\n * Fix: Integrate processVitalPointHit into the combat system with archetype parameters\n */\n private processVitalPointHit(\n vitalPointId: string,\n _baseDamage: number, // Unused but kept for interface compatibility\n attacker: PlayerState,\n defender: PlayerState,\n ): VitalPointHitResult {\n const vitalPoint = this.vitalPointSystem.getVitalPointById(vitalPointId);\n\n if (!vitalPoint) {\n return {\n hit: false,\n damage: 0,\n effects: [],\n severity: VitalPointSeverity.MINOR,\n };\n }\n\n return this.vitalPointSystem.processHit(\n vitalPoint.position, // Use vital point position for hit calculation\n { width: 10, height: 10 }, // Standard hit box\n vitalPointId, // Targeted vital point\n undefined, // Use current hour from system\n attacker.archetype, // Attacker archetype for offensive modifiers\n defender.archetype, // Defender archetype for defensive modifiers\n );\n }\n\n /**\n * Process defensive action and determine animation type.\n *\n * Determines the appropriate defensive animation based on:\n * - Defender's balance and stamina (defensive power)\n * - Attacker's technique power\n * - Combat readiness state\n *\n * **Korean**: 방어 행동 처리\n *\n * @param defender - Defending player state\n * @param attacker - Attacking player state (unused but kept for future enhancements)\n * @param attackPower - Power of the incoming attack\n * @returns Defensive animation type to play (parry_deflect, block_success, or guard_break)\n *\n * @example\n * ```typescript\n * const animType = combatSystem.processDefensiveAction(\n * defender,\n * attacker,\n * technique.damage\n * );\n * // Returns: 'parry_deflect', 'block_success', or 'guard_break'\n * ```\n *\n * @korean 방어행동처리\n */\n public processDefensiveAction(\n defender: PlayerState,\n _attacker: PlayerState,\n attackPower: number,\n ): Exclude<DefensiveAnimationType, \"guard_recovery\"> {\n // Guard Break: Check balance threshold first (highest priority condition)\n if (defender.balance < 30) {\n return \"guard_break\";\n }\n\n // Calculate defensive power based on balance and stamina\n // Balance represents physical stability (0-100)\n // Stamina represents energy reserves (0-100)\n const balanceFactor = defender.balance / 100;\n const staminaFactor = defender.stamina / 100;\n\n // Apply defensive modifiers from effects\n const effectModifiers = getEffectModifiers(defender);\n const defenseMultiplier = effectModifiers.defense;\n\n // Defense stat provides moderate bonus (normalized to 0.5-1.5 range for typical 5-15 defense)\n const defenseBonus = Math.max(0.5, Math.min(1.5, defender.defense / 10));\n\n const defensePower =\n balanceFactor * staminaFactor * 100 * defenseMultiplier * defenseBonus;\n\n // Determine defensive outcome based on power ratio\n // Parry: Strong defense (1.8x attack power or more) - Perfect deflection\n if (defensePower >= attackPower * 1.8) {\n return \"parry_deflect\";\n }\n // Block Success: Adequate defense (1.0x to 1.8x attack power) - Absorb impact\n else if (defensePower >= attackPower) {\n return \"block_success\";\n }\n // Guard Break: Defense insufficient against powerful attack (<60% of attack power)\n else if (defensePower < attackPower * 0.6) {\n return \"guard_break\";\n }\n // Block Success: Marginal defense (60-100% of attack power) - barely hold\n else {\n return \"block_success\";\n }\n }\n\n /**\n * Execute attack with technique\n */\n protected executeAttack(\n attacker: PlayerState,\n defender: PlayerState,\n technique: KoreanTechnique,\n ): CombatResult {\n const hitRoll = Math.random();\n const accuracy = technique.accuracy || 0.8;\n const hit = hitRoll <= accuracy;\n\n if (!hit) {\n return {\n hit: false,\n damage: 0,\n criticalHit: false,\n vitalPointHit: false,\n effects: [],\n timestamp: Date.now(),\n technique,\n attacker,\n defender,\n success: false,\n isCritical: false,\n isBlocked: false,\n };\n }\n\n const vitalPointHit: VitalPointHitResult = {\n hit: false,\n damage: 0,\n effects: [],\n severity: VitalPointSeverity.MINOR, // Use the enum directly\n };\n\n const damageResult = this.calculateDamage(\n technique,\n attacker,\n defender,\n vitalPointHit,\n );\n\n return {\n hit: true,\n damage: damageResult.totalDamage,\n criticalHit: Math.random() < (technique.critChance || 0.1),\n vitalPointHit: false,\n effects: damageResult.effectsApplied,\n timestamp: Date.now(),\n technique,\n attacker,\n defender,\n success: true,\n isCritical: false,\n isBlocked: false,\n };\n }\n\n /**\n * Fix: Add missing calculateDamage method required by interface\n */\n calculateDamage(\n technique: KoreanTechnique,\n attacker: PlayerState,\n defender: PlayerState,\n hitResult: VitalPointHitResult,\n ): {\n baseDamage: number;\n modifierDamage: number;\n totalDamage: number;\n effectsApplied: readonly StatusEffect[];\n finalDefenderState?: Partial<PlayerState>;\n } {\n const baseDamage = technique.damage || 15;\n\n const attackerBonus = attacker.attackPower * 0.1;\n\n let vitalPointMultiplier = 1.0;\n if (hitResult.hit && hitResult.vitalPointHit) {\n const severityMultipliers: Record<VitalPointSeverity, number> = {\n [VitalPointSeverity.MINOR]: 1.1,\n [VitalPointSeverity.MODERATE]: 1.3,\n [VitalPointSeverity.MAJOR]: 1.6,\n [VitalPointSeverity.CRITICAL]: 2.0,\n [VitalPointSeverity.LETHAL]: 3.0,\n };\n\n vitalPointMultiplier = severityMultipliers[hitResult.severity] ?? 1.0;\n }\n\n const modifierDamage = attackerBonus * vitalPointMultiplier;\n\n const defenseReduction = defender.defense * 0.05;\n const totalDamage = Math.max(\n 1,\n baseDamage + modifierDamage - defenseReduction,\n );\n\n const effectsApplied = [...technique.effects, ...hitResult.effects];\n\n return {\n baseDamage,\n modifierDamage,\n totalDamage: Math.floor(totalDamage),\n effectsApplied,\n finalDefenderState: {\n health: Math.max(0, defender.health - totalDamage),\n },\n };\n }\n}\n\n/**\n * Creates a standardized CombatResult with all required fields\n * Ensures both 'critical' and 'criticalHit' are present for API compatibility\n */\nexport function createCombatResult(\n partialResult: Partial<CombatResult>,\n): CombatResult {\n // Set default values\n const result: CombatResult = {\n success: partialResult.success ?? false,\n damage: partialResult.damage ?? 0,\n isCritical: partialResult.isCritical ?? partialResult.criticalHit ?? false,\n hit: partialResult.hit ?? partialResult.success ?? false,\n isBlocked: partialResult.isBlocked ?? false,\n vitalPointHit: partialResult.vitalPointHit ?? false,\n effects: partialResult.effects ?? [],\n attacker: partialResult.attacker,\n defender: partialResult.defender,\n technique: partialResult.technique,\n // Always set criticalHit to match critical for consistency\n criticalHit: partialResult.isCritical ?? partialResult.criticalHit ?? false,\n timestamp: Date.now(),\n };\n\n return result;\n}\n\nexport default CombatSystem;\n"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"EffectCalculator.js","names":[],"sources":["../../src/systems/EffectCalculator.ts"],"sourcesContent":["/**\n * Effect calculation system for vital point strikes.\n * \n * **Korean**: 효과 계산 시스템 (Effect Calculation System)\n * \n * This module provides comprehensive calculations for status effect duration,\n * intensity, and application based on vital point strikes, accuracy, severity,\n * and archetype-specific modifiers.\n * \n * ## Key Features\n * \n * - **Duration Calculation**: Based on severity, accuracy, and archetype\n * - **Intensity Scaling**: Scales with hit accuracy (0.5-1.5x)\n * - **Archetype Modifiers**: Resistance and vulnerability factors\n * - **Critical Hit Enhancement**: 2x duration for accuracy > 0.9\n * - **Effect Stacking**: Manages multiple concurrent effects\n * \n * @module systems/EffectCalculator\n * @category Combat Effects\n * @korean 효과계산기\n */\n\nimport { PlayerArchetype, VitalPointSeverity } from \"../types/common\";\nimport { EffectIntensity } from \"./effects\";\nimport { StatusEffect } from \"./types\";\nimport { VitalPointEffect } from \"./vitalpoint/types\";\n\n/**\n * Severity multipliers for effect duration calculation.\n * \n * **Korean**: 심각도 배율 (Severity Multipliers)\n */\nconst SEVERITY_MULTIPLIERS: Record<VitalPointSeverity, number> = {\n [VitalPointSeverity.MINOR]: 0.5,\n [VitalPointSeverity.MODERATE]: 1.0,\n [VitalPointSeverity.MAJOR]: 1.5,\n [VitalPointSeverity.CRITICAL]: 2.0,\n [VitalPointSeverity.LETHAL]: 3.0,\n};\n\n/**\n * Critical hit accuracy threshold.\n * Strikes with accuracy >= 0.9 are considered critical.\n * \n * **Korean**: 크리티컬 정확도 기준점\n */\nconst CRITICAL_HIT_THRESHOLD = 0.9;\n\n/**\n * Critical hit duration multiplier.\n * \n * **Korean**: 크리티컬 타격 지속시간 배율\n */\nconst CRITICAL_DURATION_MULTIPLIER = 2.0;\n\n/**\n * Maximum number of concurrent status effects allowed per player.\n * \n * **Korean**: 최대 동시 효과 개수\n */\nexport const MAX_CONCURRENT_EFFECTS = 5;\n\n/**\n * Archetype resistance modifiers.\n * Positive values = resistance (effects last shorter)\n * Negative values = vulnerability (effects last longer)\n * \n * **Korean**: 원형별 저항력 배율 (Archetype Resistance Modifiers)\n */\nconst ARCHETYPE_RESISTANCE: Record<PlayerArchetype, number> = {\n [PlayerArchetype.MUSA]: 0.2, // +20% resistance (traditional warrior discipline)\n [PlayerArchetype.AMSALJA]: -0.1, // -10% resistance (glass cannon)\n [PlayerArchetype.HACKER]: 0.0, // Neutral resistance\n [PlayerArchetype.JEONGBO_YOWON]: 0.1, // +10% resistance (mental fortitude)\n [PlayerArchetype.JOJIK_POKRYEOKBAE]: 0.15, // +15% resistance (street-hardened)\n};\n\n/**\n * Archetype offensive effect modifiers.\n * Applied when archetype deals vital point strike.\n * \n * **Korean**: 원형별 공격 효과 배율 (Archetype Offensive Modifiers)\n */\nconst ARCHETYPE_OFFENSIVE: Record<PlayerArchetype, number> = {\n [PlayerArchetype.MUSA]: 1.0, // Standard effects\n [PlayerArchetype.AMSALJA]: 1.3, // +30% effect potency (assassination mastery)\n [PlayerArchetype.HACKER]: 1.15, // +15% effect potency (precision targeting)\n [PlayerArchetype.JEONGBO_YOWON]: 1.25, // +25% effect potency (anatomical knowledge)\n [PlayerArchetype.JOJIK_POKRYEOKBAE]: 1.2, // +20% effect potency (brutal efficiency)\n};\n\n/**\n * Default effect duration in milliseconds if not specified.\n * \n * **Korean**: 기본 효과 지속시간 (Default Effect Duration)\n */\nconst DEFAULT_EFFECT_DURATION = 2000;\n\n/**\n * Calculates the duration of a status effect based on multiple factors.\n * \n * **Korean**: 효과 지속시간 계산 (Calculate Effect Duration)\n * \n * Formula:\n * ```\n * duration = baseDuration * (1 + (accuracy - 0.5) * 0.5) * severityMultiplier * archetypeModifier\n * ```\n * \n * For critical hits (accuracy >= 0.9):\n * ```\n * duration = duration * 2.0\n * ```\n * \n * @param effect - Vital point effect with base duration\n * @param accuracy - Hit accuracy (0-1)\n * @param severity - Vital point severity level\n * @param attackerArchetype - Attacking player's archetype\n * @param defenderArchetype - Defending player's archetype\n * @returns Calculated duration in milliseconds\n * \n * @example\n * ```typescript\n * const duration = calculateEffectDuration(\n * { id: \"paralysis\", duration: 2000, ... },\n * 0.95, // High accuracy\n * VitalPointSeverity.MAJOR,\n * PlayerArchetype.AMSALJA, // Assassin attacker (+30%)\n * PlayerArchetype.MUSA // Warrior defender (+20% resistance)\n * );\n * // Result: 2000 * 1.225 * 1.5 * 1.3 * 0.8 * 2.0 = ~9,568ms\n * ```\n * \n * @korean 효과지속시간계산\n */\nexport function calculateEffectDuration(\n effect: VitalPointEffect,\n accuracy: number,\n severity: VitalPointSeverity,\n attackerArchetype: PlayerArchetype,\n defenderArchetype: PlayerArchetype\n): number {\n const baseDuration = effect.duration || DEFAULT_EFFECT_DURATION;\n\n const accuracyBonus = 1 + (accuracy - 0.5) * 0.5;\n\n const severityMult = SEVERITY_MULTIPLIERS[severity] || 1.0;\n\n const offensiveModifier = ARCHETYPE_OFFENSIVE[attackerArchetype] || 1.0;\n\n // Defender resistance modifier (inverted: resistance reduces duration)\n const resistanceModifier =\n 1 - (ARCHETYPE_RESISTANCE[defenderArchetype] || 0);\n\n let finalDuration =\n baseDuration * accuracyBonus * severityMult * offensiveModifier * resistanceModifier;\n\n if (accuracy >= CRITICAL_HIT_THRESHOLD) {\n finalDuration *= CRITICAL_DURATION_MULTIPLIER;\n }\n\n return Math.floor(finalDuration);\n}\n\n/**\n * Calculates the intensity of a status effect based on hit accuracy.\n * \n * **Korean**: 효과 강도 계산 (Calculate Effect Intensity)\n * \n * Scales effect intensity from 0.5x (poor accuracy) to 1.5x (perfect accuracy).\n * \n * @param baseIntensity - Base effect intensity\n * @param accuracy - Hit accuracy (0-1)\n * @returns Scaled intensity value\n * \n * @example\n * ```typescript\n * const intensity = calculateEffectIntensity(\n * EffectIntensity.MEDIUM,\n * 0.9 // High accuracy\n * );\n * // Returns scaled intensity for 90% accuracy hit\n * ```\n * \n * @korean 효과강도계산\n */\nexport function calculateEffectIntensity(\n baseIntensity: EffectIntensity,\n accuracy: number\n): EffectIntensity {\n // Map intensities to numeric scale\n const intensityMap: Record<EffectIntensity, number> = { [EffectIntensity.WEAK]: 1,\n [EffectIntensity.MINOR]: 2,\n [EffectIntensity.LOW]: 3,\n [EffectIntensity.MEDIUM]: 4,\n [EffectIntensity.MODERATE]: 5,\n [EffectIntensity.HIGH]: 6,\n [EffectIntensity.SEVERE]: 7,\n [EffectIntensity.CRITICAL]: 8,\n [EffectIntensity.EXTREME]: 9,\n };\n\n const reverseMap: EffectIntensity[] = [\n EffectIntensity.WEAK,\n EffectIntensity.MINOR,\n EffectIntensity.LOW,\n EffectIntensity.MEDIUM,\n EffectIntensity.MODERATE,\n EffectIntensity.HIGH,\n EffectIntensity.SEVERE,\n EffectIntensity.CRITICAL,\n EffectIntensity.EXTREME,\n ];\n\n const baseLevel = intensityMap[baseIntensity] || 4;\n\n // Accuracy modifier: 0.5x at 0 accuracy, 1.0x at 0.5, 1.5x at 1.0\n const accuracyModifier = 0.5 + accuracy;\n\n const scaledLevel = Math.max(\n 1,\n Math.min(9, Math.round(baseLevel * accuracyModifier))\n );\n\n return reverseMap[scaledLevel - 1];\n}\n\n/**\n * Converts a VitalPointEffect to a StatusEffect with calculated properties.\n * \n * **Korean**: 급소 효과를 상태 효과로 변환 (Convert Vital Point Effect to Status Effect)\n * \n * @param effect - Vital point effect to convert\n * @param accuracy - Hit accuracy\n * @param severity - Vital point severity\n * @param attackerArchetype - Attacker's archetype\n * @param defenderArchetype - Defender's archetype\n * @param vitalPointId - Source vital point ID\n * @param timestamp - Current timestamp (for startTime/endTime)\n * @returns StatusEffect with calculated duration and intensity\n * \n * @korean 상태효과변환\n */\nexport function convertToStatusEffect(\n effect: VitalPointEffect,\n accuracy: number,\n severity: VitalPointSeverity,\n attackerArchetype: PlayerArchetype,\n defenderArchetype: PlayerArchetype,\n vitalPointId: string,\n timestamp: number\n): StatusEffect {\n const duration = calculateEffectDuration(\n effect,\n accuracy,\n severity,\n attackerArchetype,\n defenderArchetype\n );\n\n const intensity = calculateEffectIntensity(effect.intensity, accuracy);\n\n return {\n id: `${effect.id}_${timestamp}`,\n type: effect.type,\n intensity,\n duration,\n description: effect.description,\n stackable: effect.stackable,\n source: vitalPointId,\n startTime: timestamp,\n endTime: timestamp + duration,\n };\n}\n\n/**\n * Applies effect stacking logic to a list of status effects.\n * \n * **Korean**: 효과 중첩 관리 (Manage Effect Stacking)\n * \n * Rules:\n * - Non-stackable effects: Keep only the most recent\n * - Stackable effects: Allow up to MAX_CONCURRENT_EFFECTS\n * - Expired effects: Remove automatically\n * \n * @param currentEffects - Existing active effects\n * @param newEffects - New effects to add\n * @param currentTime - Current timestamp\n * @returns Updated effect list with stacking applied\n * \n * @example\n * ```typescript\n * const updated = applyEffectStacking(\n * player.statusEffects,\n * newVitalPointEffects,\n * Date.now()\n * );\n * ```\n * \n * @korean 효과중첩적용\n */\nexport function applyEffectStacking(\n currentEffects: readonly StatusEffect[],\n newEffects: readonly StatusEffect[],\n currentTime: number\n): StatusEffect[] {\n // Remove expired effects\n let activeEffects = currentEffects.filter(\n (effect) => effect.endTime > currentTime\n );\n\n for (const newEffect of newEffects) {\n if (!newEffect.stackable) {\n // Remove existing effects of same type (non-stackable)\n activeEffects = activeEffects.filter(\n (effect) => effect.type !== newEffect.type\n );\n }\n\n activeEffects = [...activeEffects, newEffect];\n }\n\n // Limit to MAX_CONCURRENT_EFFECTS (keep most recent)\n if (activeEffects.length > MAX_CONCURRENT_EFFECTS) {\n activeEffects = activeEffects\n .sort((a, b) => b.startTime - a.startTime)\n .slice(0, MAX_CONCURRENT_EFFECTS);\n }\n\n return activeEffects;\n}\n\n/**\n * Gets the offensive effect modifier for an archetype.\n * \n * **Korean**: 원형 공격 배율 조회 (Get Archetype Offensive Modifier)\n * \n * @param archetype - Player archetype\n * @returns Offensive effect multiplier (1.0 - 1.3)\n * \n * @korean 원형공격배율조회\n */\nexport function getArchetypeOffensiveModifier(\n archetype: PlayerArchetype\n): number {\n return ARCHETYPE_OFFENSIVE[archetype] || 1.0;\n}\n\n/**\n * Gets the defensive resistance modifier for an archetype.\n * \n * **Korean**: 원형 방어 배율 조회 (Get Archetype Defensive Modifier)\n * \n * @param archetype - Player archetype\n * @returns Resistance modifier (-0.1 to 0.2)\n * \n * @korean 원형방어배율조회\n */\nexport function getArchetypeDefensiveModifier(\n archetype: PlayerArchetype\n): number {\n return ARCHETYPE_RESISTANCE[archetype] || 0;\n}\n\n/**\n * Checks if a hit qualifies as a critical hit based on accuracy.\n * \n * **Korean**: 크리티컬 타격 판정 (Check Critical Hit)\n * \n * @param accuracy - Hit accuracy (0-1)\n * @returns True if accuracy >= 0.9 (critical hit)\n * \n * @korean 크리티컬판정\n */\nexport function isCriticalHit(accuracy: number): boolean {\n return accuracy >= CRITICAL_HIT_THRESHOLD;\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAgCA,IAAM,uBAA2D;EAC9D,mBAAmB,QAAQ;EAC3B,mBAAmB,WAAW;EAC9B,mBAAmB,QAAQ;EAC3B,mBAAmB,WAAW;EAC9B,mBAAmB,SAAS;CAC9B;;;;;;;AAQD,IAAM,yBAAyB;;;;;;AAO/B,IAAM,+BAA+B;;;;;;;;AAgBrC,IAAM,uBAAwD;EAC3D,gBAAgB,OAAO;EACvB,gBAAgB,UAAU;EAC1B,gBAAgB,SAAS;EACzB,gBAAgB,gBAAgB;EAChC,gBAAgB,oBAAoB;CACtC;;;;;;;AAQD,IAAM,sBAAuD;EAC1D,gBAAgB,OAAO;EACvB,gBAAgB,UAAU;EAC1B,gBAAgB,SAAS;EACzB,gBAAgB,gBAAgB;EAChC,gBAAgB,oBAAoB;CACtC;;;;;;AAOD,IAAM,0BAA0B;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAsChC,SAAgB,wBACd,QACA,UACA,UACA,mBACA,mBACQ;CACR,MAAM,eAAe,OAAO,YAAY;CAExC,MAAM,gBAAgB,KAAK,WAAW,MAAO;CAE7C,MAAM,eAAe,qBAAqB,aAAa;CAEvD,MAAM,oBAAoB,oBAAoB,sBAAsB;CAGpE,MAAM,qBACJ,KAAK,qBAAqB,sBAAsB;CAElD,IAAI,gBACF,eAAe,gBAAgB,eAAe,oBAAoB;CAEpE,IAAI,YAAY,wBACd,iBAAiB;CAGnB,OAAO,KAAK,MAAM,cAAc;;;;;;;;;;;;;;;;;;;;;;;;AAyBlC,SAAgB,yBACd,eACA,UACiB;CAEjB,MAAM,eAAgD;GAAM,gBAAgB,OAAO;GAChF,gBAAgB,QAAQ;GACxB,gBAAgB,MAAM;GACtB,gBAAgB,SAAS;GACzB,gBAAgB,WAAW;GAC3B,gBAAgB,OAAO;GACvB,gBAAgB,SAAS;GACzB,gBAAgB,WAAW;GAC3B,gBAAgB,UAAU;EAC5B;CAED,MAAM,aAAgC;EACpC,gBAAgB;EAChB,gBAAgB;EAChB,gBAAgB;EAChB,gBAAgB;EAChB,gBAAgB;EAChB,gBAAgB;EAChB,gBAAgB;EAChB,gBAAgB;EAChB,gBAAgB;EACjB;CAED,MAAM,YAAY,aAAa,kBAAkB;CAGjD,MAAM,mBAAmB,KAAM;CAO/B,OAAO,WALa,KAAK,IACvB,GACA,KAAK,IAAI,GAAG,KAAK,MAAM,YAAY,iBAAiB,CAAC,CAGrC,GAAc;;;;;;;;;;;;;;;;;;AAmBlC,SAAgB,sBACd,QACA,UACA,UACA,mBACA,mBACA,cACA,WACc;CACd,MAAM,WAAW,wBACf,QACA,UACA,UACA,mBACA,kBACD;CAED,MAAM,YAAY,yBAAyB,OAAO,WAAW,SAAS;CAEtE,OAAO;EACL,IAAI,GAAG,OAAO,GAAG,GAAG;EACpB,MAAM,OAAO;EACb;EACA;EACA,aAAa,OAAO;EACpB,WAAW,OAAO;EAClB,QAAQ;EACR,WAAW;EACX,SAAS,YAAY;EACtB;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA6BH,SAAgB,oBACd,gBACA,YACA,aACgB;CAEhB,IAAI,gBAAgB,eAAe,QAChC,WAAW,OAAO,UAAU,YAC9B;CAED,KAAK,MAAM,aAAa,YAAY;EAClC,IAAI,CAAC,UAAU,WAEb,gBAAgB,cAAc,QAC3B,WAAW,OAAO,SAAS,UAAU,KACvC;EAGH,gBAAgB,CAAC,GAAG,eAAe,UAAU;;CAI/C,IAAI,cAAc,SAAA,GAChB,gBAAgB,cACb,MAAM,GAAG,MAAM,EAAE,YAAY,EAAE,UAAU,CACzC,MAAM,GAAA,EAA0B;CAGrC,OAAO"}
1
+ {"version":3,"file":"EffectCalculator.js","names":[],"sources":["../../src/systems/EffectCalculator.ts"],"sourcesContent":["/**\n * Effect calculation system for vital point strikes.\n * \n * **Korean**: 효과 계산 시스템 (Effect Calculation System)\n * \n * This module provides comprehensive calculations for status effect duration,\n * intensity, and application based on vital point strikes, accuracy, severity,\n * and archetype-specific modifiers.\n * \n * ## Key Features\n * \n * - **Duration Calculation**: Based on severity, accuracy, and archetype\n * - **Intensity Scaling**: Scales with hit accuracy (0.5-1.5x)\n * - **Archetype Modifiers**: Resistance and vulnerability factors\n * - **Critical Hit Enhancement**: 2x duration for accuracy > 0.9\n * - **Effect Stacking**: Manages multiple concurrent effects\n * \n * @module systems/EffectCalculator\n * @category Combat Effects\n * @korean 효과계산기\n */\n\nimport { PlayerArchetype, VitalPointSeverity } from \"../types/common\";\nimport { EffectIntensity } from \"./effects\";\nimport { StatusEffect } from \"./types\";\nimport { VitalPointEffect } from \"./vitalpoint/types\";\n\n/**\n * Severity multipliers for effect duration calculation.\n * \n * **Korean**: 심각도 배율 (Severity Multipliers)\n */\nconst SEVERITY_MULTIPLIERS: Record<VitalPointSeverity, number> = {\n [VitalPointSeverity.MINOR]: 0.5,\n [VitalPointSeverity.MODERATE]: 1.0,\n [VitalPointSeverity.MAJOR]: 1.5,\n [VitalPointSeverity.CRITICAL]: 2.0,\n [VitalPointSeverity.LETHAL]: 3.0,\n};\n\n/**\n * Critical hit accuracy threshold.\n * Strikes with accuracy >= 0.9 are considered critical.\n * \n * **Korean**: 크리티컬 정확도 기준점\n */\nconst CRITICAL_HIT_THRESHOLD = 0.9;\n\n/**\n * Critical hit duration multiplier.\n * \n * **Korean**: 크리티컬 타격 지속시간 배율\n */\nconst CRITICAL_DURATION_MULTIPLIER = 2.0;\n\n/**\n * Maximum number of concurrent status effects allowed per player.\n * \n * **Korean**: 최대 동시 효과 개수\n */\nexport const MAX_CONCURRENT_EFFECTS = 5;\n\n/**\n * Archetype resistance modifiers.\n * Positive values = resistance (effects last shorter)\n * Negative values = vulnerability (effects last longer)\n * \n * **Korean**: 원형별 저항력 배율 (Archetype Resistance Modifiers)\n */\nconst ARCHETYPE_RESISTANCE: Record<PlayerArchetype, number> = {\n [PlayerArchetype.MUSA]: 0.2, // +20% resistance (traditional warrior discipline)\n [PlayerArchetype.AMSALJA]: -0.1, // -10% resistance (glass cannon)\n [PlayerArchetype.HACKER]: 0.0, // Neutral resistance\n [PlayerArchetype.JEONGBO_YOWON]: 0.1, // +10% resistance (mental fortitude)\n [PlayerArchetype.JOJIK_POKRYEOKBAE]: 0.15, // +15% resistance (street-hardened)\n};\n\n/**\n * Archetype offensive effect modifiers.\n * Applied when archetype deals vital point strike.\n * \n * **Korean**: 원형별 공격 효과 배율 (Archetype Offensive Modifiers)\n */\nconst ARCHETYPE_OFFENSIVE: Record<PlayerArchetype, number> = {\n [PlayerArchetype.MUSA]: 1.0, // Standard effects\n [PlayerArchetype.AMSALJA]: 1.3, // +30% effect potency (assassination mastery)\n [PlayerArchetype.HACKER]: 1.15, // +15% effect potency (precision targeting)\n [PlayerArchetype.JEONGBO_YOWON]: 1.25, // +25% effect potency (anatomical knowledge)\n [PlayerArchetype.JOJIK_POKRYEOKBAE]: 1.2, // +20% effect potency (brutal efficiency)\n};\n\n/**\n * Default effect duration in milliseconds if not specified.\n * \n * **Korean**: 기본 효과 지속시간 (Default Effect Duration)\n */\nconst DEFAULT_EFFECT_DURATION = 2000;\n\n/**\n * Calculates the duration of a status effect based on multiple factors.\n * \n * **Korean**: 효과 지속시간 계산 (Calculate Effect Duration)\n * \n * Formula:\n * ```\n * duration = baseDuration * (1 + (accuracy - 0.5) * 0.5) * severityMultiplier * archetypeModifier\n * ```\n * \n * For critical hits (accuracy >= 0.9):\n * ```\n * duration = duration * 2.0\n * ```\n * \n * @param effect - Vital point effect with base duration\n * @param accuracy - Hit accuracy (0-1)\n * @param severity - Vital point severity level\n * @param attackerArchetype - Attacking player's archetype\n * @param defenderArchetype - Defending player's archetype\n * @returns Calculated duration in milliseconds\n * \n * @example\n * ```typescript\n * const duration = calculateEffectDuration(\n * { id: \"paralysis\", duration: 2000, ... },\n * 0.95, // High accuracy\n * VitalPointSeverity.MAJOR,\n * PlayerArchetype.AMSALJA, // Assassin attacker (+30%)\n * PlayerArchetype.MUSA // Warrior defender (+20% resistance)\n * );\n * // Result: 2000 * 1.225 * 1.5 * 1.3 * 0.8 * 2.0 = ~9,568ms\n * ```\n * \n * @korean 효과지속시간계산\n */\nexport function calculateEffectDuration(\n effect: VitalPointEffect,\n accuracy: number,\n severity: VitalPointSeverity,\n attackerArchetype: PlayerArchetype,\n defenderArchetype: PlayerArchetype\n): number {\n const baseDuration = effect.duration || DEFAULT_EFFECT_DURATION;\n\n const accuracyBonus = 1 + (accuracy - 0.5) * 0.5;\n\n const severityMult = SEVERITY_MULTIPLIERS[severity] || 1.0;\n\n const offensiveModifier = ARCHETYPE_OFFENSIVE[attackerArchetype] || 1.0;\n\n // Defender resistance modifier (inverted: resistance reduces duration)\n const resistanceModifier =\n 1 - (ARCHETYPE_RESISTANCE[defenderArchetype] || 0);\n\n let finalDuration =\n baseDuration * accuracyBonus * severityMult * offensiveModifier * resistanceModifier;\n\n if (accuracy >= CRITICAL_HIT_THRESHOLD) {\n finalDuration *= CRITICAL_DURATION_MULTIPLIER;\n }\n\n return Math.floor(finalDuration);\n}\n\n/**\n * Calculates the intensity of a status effect based on hit accuracy.\n * \n * **Korean**: 효과 강도 계산 (Calculate Effect Intensity)\n * \n * Scales effect intensity from 0.5x (poor accuracy) to 1.5x (perfect accuracy).\n * \n * @param baseIntensity - Base effect intensity\n * @param accuracy - Hit accuracy (0-1)\n * @returns Scaled intensity value\n * \n * @example\n * ```typescript\n * const intensity = calculateEffectIntensity(\n * EffectIntensity.MEDIUM,\n * 0.9 // High accuracy\n * );\n * // Returns scaled intensity for 90% accuracy hit\n * ```\n * \n * @korean 효과강도계산\n */\nexport function calculateEffectIntensity(\n baseIntensity: EffectIntensity,\n accuracy: number\n): EffectIntensity {\n // Map intensities to numeric scale\n const intensityMap: Record<EffectIntensity, number> = { [EffectIntensity.WEAK]: 1,\n [EffectIntensity.MINOR]: 2,\n [EffectIntensity.LOW]: 3,\n [EffectIntensity.MEDIUM]: 4,\n [EffectIntensity.MODERATE]: 5,\n [EffectIntensity.HIGH]: 6,\n [EffectIntensity.SEVERE]: 7,\n [EffectIntensity.CRITICAL]: 8,\n [EffectIntensity.EXTREME]: 9,\n };\n\n const reverseMap: EffectIntensity[] = [\n EffectIntensity.WEAK,\n EffectIntensity.MINOR,\n EffectIntensity.LOW,\n EffectIntensity.MEDIUM,\n EffectIntensity.MODERATE,\n EffectIntensity.HIGH,\n EffectIntensity.SEVERE,\n EffectIntensity.CRITICAL,\n EffectIntensity.EXTREME,\n ];\n\n const baseLevel = intensityMap[baseIntensity] || 4;\n\n // Accuracy modifier: 0.5x at 0 accuracy, 1.0x at 0.5, 1.5x at 1.0\n const accuracyModifier = 0.5 + accuracy;\n\n const scaledLevel = Math.max(\n 1,\n Math.min(9, Math.round(baseLevel * accuracyModifier))\n );\n\n return reverseMap[scaledLevel - 1];\n}\n\n/**\n * Converts a VitalPointEffect to a StatusEffect with calculated properties.\n * \n * **Korean**: 급소 효과를 상태 효과로 변환 (Convert Vital Point Effect to Status Effect)\n * \n * @param effect - Vital point effect to convert\n * @param accuracy - Hit accuracy\n * @param severity - Vital point severity\n * @param attackerArchetype - Attacker's archetype\n * @param defenderArchetype - Defender's archetype\n * @param vitalPointId - Source vital point ID\n * @param timestamp - Current timestamp (for startTime/endTime)\n * @returns StatusEffect with calculated duration and intensity\n * \n * @korean 상태효과변환\n */\nexport function convertToStatusEffect(\n effect: VitalPointEffect,\n accuracy: number,\n severity: VitalPointSeverity,\n attackerArchetype: PlayerArchetype,\n defenderArchetype: PlayerArchetype,\n vitalPointId: string,\n timestamp: number\n): StatusEffect {\n const duration = calculateEffectDuration(\n effect,\n accuracy,\n severity,\n attackerArchetype,\n defenderArchetype\n );\n\n const intensity = calculateEffectIntensity(effect.intensity, accuracy);\n\n return {\n id: `${effect.id}_${timestamp}`,\n type: effect.type,\n intensity,\n duration,\n description: effect.description,\n stackable: effect.stackable,\n source: vitalPointId,\n startTime: timestamp,\n endTime: timestamp + duration,\n };\n}\n\n/**\n * Applies effect stacking logic to a list of status effects.\n * \n * **Korean**: 효과 중첩 관리 (Manage Effect Stacking)\n * \n * Rules:\n * - Non-stackable effects: Keep only the most recent\n * - Stackable effects: Allow up to MAX_CONCURRENT_EFFECTS\n * - Expired effects: Remove automatically\n * \n * @param currentEffects - Existing active effects\n * @param newEffects - New effects to add\n * @param currentTime - Current timestamp\n * @returns Updated effect list with stacking applied\n * \n * @example\n * ```typescript\n * const updated = applyEffectStacking(\n * player.statusEffects,\n * newVitalPointEffects,\n * Date.now()\n * );\n * ```\n * \n * @korean 효과중첩적용\n */\nexport function applyEffectStacking(\n currentEffects: readonly StatusEffect[],\n newEffects: readonly StatusEffect[],\n currentTime: number\n): StatusEffect[] {\n // Remove expired effects\n let activeEffects = currentEffects.filter(\n (effect) => effect.endTime > currentTime\n );\n\n for (const newEffect of newEffects) {\n if (!newEffect.stackable) {\n // Remove existing effects of same type (non-stackable)\n activeEffects = activeEffects.filter(\n (effect) => effect.type !== newEffect.type\n );\n }\n\n activeEffects = [...activeEffects, newEffect];\n }\n\n // Limit to MAX_CONCURRENT_EFFECTS (keep most recent)\n if (activeEffects.length > MAX_CONCURRENT_EFFECTS) {\n activeEffects = activeEffects\n .sort((a, b) => b.startTime - a.startTime)\n .slice(0, MAX_CONCURRENT_EFFECTS);\n }\n\n return activeEffects;\n}\n\n/**\n * Gets the offensive effect modifier for an archetype.\n * \n * **Korean**: 원형 공격 배율 조회 (Get Archetype Offensive Modifier)\n * \n * @param archetype - Player archetype\n * @returns Offensive effect multiplier (1.0 - 1.3)\n * \n * @korean 원형공격배율조회\n */\nexport function getArchetypeOffensiveModifier(\n archetype: PlayerArchetype\n): number {\n return ARCHETYPE_OFFENSIVE[archetype] || 1.0;\n}\n\n/**\n * Gets the defensive resistance modifier for an archetype.\n * \n * **Korean**: 원형 방어 배율 조회 (Get Archetype Defensive Modifier)\n * \n * @param archetype - Player archetype\n * @returns Resistance modifier (-0.1 to 0.2)\n * \n * @korean 원형방어배율조회\n */\nexport function getArchetypeDefensiveModifier(\n archetype: PlayerArchetype\n): number {\n return ARCHETYPE_RESISTANCE[archetype] || 0;\n}\n\n/**\n * Checks if a hit qualifies as a critical hit based on accuracy.\n * \n * **Korean**: 크리티컬 타격 판정 (Check Critical Hit)\n * \n * @param accuracy - Hit accuracy (0-1)\n * @returns True if accuracy >= 0.9 (critical hit)\n * \n * @korean 크리티컬판정\n */\nexport function isCriticalHit(accuracy: number): boolean {\n return accuracy >= CRITICAL_HIT_THRESHOLD;\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAgCA,IAAM,uBAA2D;EAC9D,mBAAmB,QAAQ;EAC3B,mBAAmB,WAAW;EAC9B,mBAAmB,QAAQ;EAC3B,mBAAmB,WAAW;EAC9B,mBAAmB,SAAS;AAC/B;;;;;;;AAQA,IAAM,yBAAyB;;;;;;AAO/B,IAAM,+BAA+B;;;;;;;;AAgBrC,IAAM,uBAAwD;EAC3D,gBAAgB,OAAO;EACvB,gBAAgB,UAAU;EAC1B,gBAAgB,SAAS;EACzB,gBAAgB,gBAAgB;EAChC,gBAAgB,oBAAoB;AACvC;;;;;;;AAQA,IAAM,sBAAuD;EAC1D,gBAAgB,OAAO;EACvB,gBAAgB,UAAU;EAC1B,gBAAgB,SAAS;EACzB,gBAAgB,gBAAgB;EAChC,gBAAgB,oBAAoB;AACvC;;;;;;AAOA,IAAM,0BAA0B;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAsChC,SAAgB,wBACd,QACA,UACA,UACA,mBACA,mBACQ;CACR,MAAM,eAAe,OAAO,YAAY;CAExC,MAAM,gBAAgB,KAAK,WAAW,MAAO;CAE7C,MAAM,eAAe,qBAAqB,aAAa;CAEvD,MAAM,oBAAoB,oBAAoB,sBAAsB;CAGpE,MAAM,qBACJ,KAAK,qBAAqB,sBAAsB;CAElD,IAAI,gBACF,eAAe,gBAAgB,eAAe,oBAAoB;CAEpE,IAAI,YAAY,wBACd,iBAAiB;CAGnB,OAAO,KAAK,MAAM,aAAa;AACjC;;;;;;;;;;;;;;;;;;;;;;;AAwBA,SAAgB,yBACd,eACA,UACiB;CAEjB,MAAM,eAAgD;GAAM,gBAAgB,OAAO;GAChF,gBAAgB,QAAQ;GACxB,gBAAgB,MAAM;GACtB,gBAAgB,SAAS;GACzB,gBAAgB,WAAW;GAC3B,gBAAgB,OAAO;GACvB,gBAAgB,SAAS;GACzB,gBAAgB,WAAW;GAC3B,gBAAgB,UAAU;CAC7B;CAEA,MAAM,aAAgC;EACpC,gBAAgB;EAChB,gBAAgB;EAChB,gBAAgB;EAChB,gBAAgB;EAChB,gBAAgB;EAChB,gBAAgB;EAChB,gBAAgB;EAChB,gBAAgB;EAChB,gBAAgB;CAClB;CAEA,MAAM,YAAY,aAAa,kBAAkB;CAGjD,MAAM,mBAAmB,KAAM;CAO/B,OAAO,WALa,KAAK,IACvB,GACA,KAAK,IAAI,GAAG,KAAK,MAAM,YAAY,gBAAgB,CAAC,CAGpC,IAAc;AAClC;;;;;;;;;;;;;;;;;AAkBA,SAAgB,sBACd,QACA,UACA,UACA,mBACA,mBACA,cACA,WACc;CACd,MAAM,WAAW,wBACf,QACA,UACA,UACA,mBACA,iBACF;CAEA,MAAM,YAAY,yBAAyB,OAAO,WAAW,QAAQ;CAErE,OAAO;EACL,IAAI,GAAG,OAAO,GAAG,GAAG;EACpB,MAAM,OAAO;EACb;EACA;EACA,aAAa,OAAO;EACpB,WAAW,OAAO;EAClB,QAAQ;EACR,WAAW;EACX,SAAS,YAAY;CACvB;AACF;;;;;;;;;;;;;;;;;;;;;;;;;;;AA4BA,SAAgB,oBACd,gBACA,YACA,aACgB;CAEhB,IAAI,gBAAgB,eAAe,QAChC,WAAW,OAAO,UAAU,WAC/B;CAEA,KAAK,MAAM,aAAa,YAAY;EAClC,IAAI,CAAC,UAAU,WAEb,gBAAgB,cAAc,QAC3B,WAAW,OAAO,SAAS,UAAU,IACxC;EAGF,gBAAgB,CAAC,GAAG,eAAe,SAAS;CAC9C;CAGA,IAAI,cAAc,SAAA,GAChB,gBAAgB,cACb,MAAM,GAAG,MAAM,EAAE,YAAY,EAAE,SAAS,EACxC,MAAM,GAAA,CAAyB;CAGpC,OAAO;AACT"}
@@ -1 +1 @@
1
- {"version":3,"file":"LayoutSystem.js","names":[],"sources":["../../src/systems/LayoutSystem.ts"],"sourcesContent":["/**\n * Unified Layout System for consistent component positioning\n *\n * Provides grid-based layout calculations, responsive positioning utilities,\n * and alignment helpers for all screens in Black Trigram (흑괘).\n *\n * Features:\n * - 12-column grid system for consistent alignment\n * - Responsive positioning that adapts to screen size\n * - Safe area handling for mobile devices with notches\n * - Z-index hierarchy management\n * - Korean-English UI alignment support\n *\n * Performance target: <1ms for layout calculations\n *\n * @module systems/LayoutSystem\n * @category Systems\n * @korean 레이아웃시스템\n */\n\nimport { Position } from \"../types/common\";\nimport {\n ContainerBounds,\n HorizontalAlignment,\n ResponsivePosition,\n SafeAreaInsets,\n ScreenSize,\n VerticalAlignment,\n} from \"../types/LayoutTypes\";\n\n/**\n * Default grid configuration\n */\nconst DEFAULT_GRID_COLUMNS = 12;\nconst DEFAULT_GUTTER_SIZE = 20;\nconst BASE_DESKTOP_WIDTH = 1200;\n\n/**\n * Base width for mobile arena scaling calculations (in pixels)\n * This represents the standard desktop arena width (80% of 1200px)\n * Used to calculate scale factor for mobile devices\n */\nconst MOBILE_ARENA_BASE_WIDTH = 960;\n\n/**\n * Default row height for grid-based vertical positioning (in pixels)\n * Used when row index is specified in grid configuration\n * Can be overridden by providing explicit y position\n * \n */\nexport const DEFAULT_ROW_HEIGHT = 100;\n\n/**\n * Default safe area insets for mobile devices\n * Based on typical iOS device dimensions (iPhone 14 Pro as reference)\n */\nconst DEFAULT_SAFE_AREA: SafeAreaInsets = {\n top: 44, // Status bar + notch\n right: 0,\n bottom: 34, // Home indicator\n left: 0,\n};\n\n/**\n * LayoutSystem Class\n *\n * Provides unified layout calculations and positioning utilities\n * for consistent component placement across all screens.\n *\n * @example\n * ```typescript\n * const layout = new LayoutSystem();\n *\n * // Calculate grid position\n * const pos = layout.calculateGridPosition(2, 4, 1200);\n * // Returns { x: 200, width: 380 } for column 2, span 4\n *\n * // Calculate responsive position\n * const screenSize = { width: 375, height: 667, isMobile: true, isTablet: false, isDesktop: false, isLandscape: false };\n * const responsivePos = layout.calculateResponsivePosition(\n * { base: { x: 100, y: 50 } },\n * screenSize\n * );\n * ```\n */\nexport class LayoutSystem {\n private readonly gridColumns: number;\n private readonly gutterSize: number;\n private readonly safeArea: SafeAreaInsets;\n\n /**\n * Create a new LayoutSystem instance\n *\n * @param gridColumns - Number of grid columns (default: 12)\n * @param gutterSize - Gutter size between columns in pixels (default: 20)\n * @param safeArea - Safe area insets for mobile devices\n */\n constructor(\n gridColumns: number = DEFAULT_GRID_COLUMNS,\n gutterSize: number = DEFAULT_GUTTER_SIZE,\n safeArea: SafeAreaInsets = DEFAULT_SAFE_AREA\n ) {\n this.gridColumns = gridColumns;\n this.gutterSize = gutterSize;\n this.safeArea = safeArea;\n }\n\n /**\n * Calculate position and width for grid-based layout\n *\n * Uses 12-column grid system for consistent alignment.\n * Accounts for gutters between columns.\n *\n * @param column - Starting column (0-11)\n * @param span - Number of columns to span (1-12)\n * @param containerWidth - Total container width in pixels\n * @param customGutter - Custom gutter size (optional)\n * @returns Position and width for the grid cell\n * @throws Error if column or span are outside valid ranges\n *\n * @example\n * ```typescript\n * // Center element spanning 6 columns\n * const pos = layout.calculateGridPosition(3, 6, 1200);\n * // Returns { x: 300, width: 580 }\n * ```\n */\n calculateGridPosition(\n column: number,\n span: number,\n containerWidth: number,\n customGutter?: number\n ): { x: number; width: number } {\n if (column < 0 || column >= this.gridColumns) {\n throw new Error(\n `Invalid column: ${column}. Column must be between 0 and ${this.gridColumns - 1}`\n );\n }\n if (span < 1 || span > this.gridColumns) {\n throw new Error(\n `Invalid span: ${span}. Span must be between 1 and ${this.gridColumns}`\n );\n }\n if (column + span > this.gridColumns) {\n throw new Error(\n `Invalid grid position: column ${column} + span ${span} = ${column + span} exceeds ${this.gridColumns} columns`\n );\n }\n\n const gutter = customGutter ?? this.gutterSize;\n const columnWidth = containerWidth / this.gridColumns;\n\n const x = column * columnWidth;\n\n // Calculate width accounting for gutters\n // Width = (span * columnWidth) - gutter\n // The gutter is subtracted to create spacing between elements\n const width = span * columnWidth - gutter;\n\n return { x, width };\n }\n\n /**\n * Calculate responsive position based on screen size\n *\n * Scales position proportionally or uses specific overrides for tablet/mobile.\n *\n * @param config - Responsive position configuration\n * @param screenSize - Current screen dimensions and device type\n * @returns Calculated position for current screen size\n *\n * @example\n * ```typescript\n * const pos = layout.calculateResponsivePosition(\n * {\n * base: { x: 100, y: 50 },\n * mobile: { x: 10, y: 20 },\n * scaleProportionally: true\n * },\n * screenSize\n * );\n * ```\n */\n calculateResponsivePosition(\n config: ResponsivePosition,\n screenSize: ScreenSize\n ): Position {\n if (screenSize.isMobile && config.mobile) {\n return config.mobile;\n }\n if (screenSize.isTablet && config.tablet) {\n return config.tablet;\n }\n\n if (screenSize.isDesktop || !config.scaleProportionally) {\n return config.base;\n }\n\n // Scale proportionally for mobile/tablet if no override\n const scale = screenSize.width / BASE_DESKTOP_WIDTH;\n return {\n x: config.base.x * scale,\n y: config.base.y * scale,\n };\n }\n\n /**\n * Calculate safe position accounting for device notches and home indicators\n *\n * Ensures UI elements don't overlap with system UI on mobile devices.\n *\n * @param position - Base position\n * @param edge - Which edge to apply safe area ('top' | 'bottom' | 'left' | 'right')\n * @returns Position adjusted for safe area\n *\n * @example\n * ```typescript\n * // Adjust top position for status bar/notch\n * const safePos = layout.calculateSafePosition({ x: 0, y: 10 }, 'top');\n * // Returns { x: 0, y: 54 } (10 + 44 for notch)\n * ```\n */\n calculateSafePosition(\n position: Position,\n edge: \"top\" | \"bottom\" | \"left\" | \"right\"\n ): Position {\n const inset = this.safeArea[edge];\n switch (edge) {\n case \"top\":\n return { ...position, y: position.y + inset };\n case \"bottom\":\n return { ...position, y: position.y - inset };\n case \"left\":\n return { ...position, x: position.x + inset };\n case \"right\":\n return { ...position, x: position.x - inset };\n }\n }\n\n /**\n * Align element horizontally within container\n *\n * @param elementWidth - Width of element to align\n * @param containerWidth - Width of container\n * @param alignment - Alignment type ('left' | 'center' | 'right')\n * @param margin - Optional margin from edges\n * @returns X position for alignment\n *\n * @example\n * ```typescript\n * const x = layout.alignHorizontal(200, 800, 'center', 10);\n * // Returns 300 (centered with margins)\n * ```\n */\n alignHorizontal(\n elementWidth: number,\n containerWidth: number,\n alignment: HorizontalAlignment,\n margin: number = 0\n ): number {\n switch (alignment) {\n case \"left\":\n return margin;\n case \"center\":\n return (containerWidth - elementWidth) / 2;\n case \"right\":\n return containerWidth - elementWidth - margin;\n }\n }\n\n /**\n * Align element vertically within container\n *\n * @param elementHeight - Height of element to align\n * @param containerHeight - Height of container\n * @param alignment - Alignment type ('top' | 'middle' | 'bottom')\n * @param margin - Optional margin from edges\n * @returns Y position for alignment\n *\n * @example\n * ```typescript\n * const y = layout.alignVertical(100, 600, 'middle', 10);\n * // Returns 250 (vertically centered)\n * ```\n */\n alignVertical(\n elementHeight: number,\n containerHeight: number,\n alignment: VerticalAlignment,\n margin: number = 0\n ): number {\n switch (alignment) {\n case \"top\":\n return margin;\n case \"middle\":\n return (containerHeight - elementHeight) / 2;\n case \"bottom\":\n return containerHeight - elementHeight - margin;\n }\n }\n\n /**\n * Create screen size information from dimensions\n *\n * Determines device type and orientation based on screen dimensions.\n *\n * @param width - Screen width in pixels\n * @param height - Screen height in pixels\n * @returns ScreenSize object with device type flags\n *\n * @example\n * ```typescript\n * const screenSize = layout.getScreenSize(375, 667);\n * // Returns { width: 375, height: 667, isMobile: true, ... }\n * ```\n */\n getScreenSize(width: number, height: number): ScreenSize {\n const isMobile = width < 768;\n const isTablet = width >= 768 && width < 1200;\n const isDesktop = width >= 1200;\n const isLandscape = width > height;\n\n return {\n width,\n height,\n isMobile,\n isTablet,\n isDesktop,\n isLandscape,\n };\n }\n\n /**\n * Calculate container bounds for arena or game area\n *\n * Accounts for HUD, controls, and safe areas to determine usable space.\n *\n * @param screenWidth - Total screen width\n * @param screenHeight - Total screen height\n * @param hudHeight - Height of top HUD\n * @param controlsHeight - Height of bottom controls\n * @param padding - Padding around content\n * @returns Container bounds for game content\n *\n * @example\n * ```typescript\n * const bounds = layout.calculateContainerBounds(1200, 800, 120, 0, 10);\n * // Returns bounds for desktop game area\n * ```\n */\n calculateContainerBounds(\n screenWidth: number,\n screenHeight: number,\n hudHeight: number = 0,\n controlsHeight: number = 0,\n padding: number = 10\n ): ContainerBounds {\n const screenSize = this.getScreenSize(screenWidth, screenHeight);\n\n // Calculate available height\n const topOffset = hudHeight + padding + (screenSize.isMobile ? this.safeArea.top : 0);\n const bottomOffset = controlsHeight + padding + (screenSize.isMobile ? this.safeArea.bottom : 0);\n const availableHeight = screenHeight - topOffset - bottomOffset;\n\n // Calculate width accounting for padding\n const availableWidth = screenWidth - padding * 2;\n\n // Calculate scale for mobile (arena should be smaller on mobile)\n const scale = screenSize.isMobile ? Math.min(availableWidth / MOBILE_ARENA_BASE_WIDTH, 1.0) : 1.0;\n\n return {\n x: padding,\n y: topOffset,\n width: availableWidth,\n height: availableHeight,\n scale,\n };\n }\n}\n\n/**\n * Default singleton instance for convenience\n *\n * @example\n * ```typescript\n * import { defaultLayoutSystem } from './systems/LayoutSystem';\n *\n * const pos = defaultLayoutSystem.alignHorizontal(200, 800, 'center');\n * ```\n */\nexport const defaultLayoutSystem = new LayoutSystem();\n\n/**\n * Helper function to create grid-based position\n *\n * Convenience wrapper around LayoutSystem.calculateGridPosition\n *\n * @param column - Starting column (0-11)\n * @param span - Number of columns to span (1-12)\n * @param containerWidth - Total container width\n * @returns Position and width for grid cell\n */\nexport function calculateGridPosition(\n column: number,\n span: number,\n containerWidth: number\n): { x: number; width: number } {\n return defaultLayoutSystem.calculateGridPosition(column, span, containerWidth);\n}\n\n/**\n * Helper function to align element horizontally\n *\n * @param elementWidth - Width of element\n * @param containerWidth - Width of container\n * @param alignment - Alignment type\n * @param margin - Optional margin\n * @returns X position\n */\nexport function alignHorizontal(\n elementWidth: number,\n containerWidth: number,\n alignment: HorizontalAlignment = \"center\",\n margin: number = 0\n): number {\n return defaultLayoutSystem.alignHorizontal(elementWidth, containerWidth, alignment, margin);\n}\n\n/**\n * Helper function to align element vertically\n *\n * @param elementHeight - Height of element\n * @param containerHeight - Height of container\n * @param alignment - Alignment type\n * @param margin - Optional margin\n * @returns Y position\n */\nexport function alignVertical(\n elementHeight: number,\n containerHeight: number,\n alignment: VerticalAlignment = \"middle\",\n margin: number = 0\n): number {\n return defaultLayoutSystem.alignVertical(elementHeight, containerHeight, alignment, margin);\n}\n\n/**\n * Helper function to center element in container\n *\n * @param elementWidth - Width of element\n * @param elementHeight - Height of element\n * @param containerWidth - Width of container\n * @param containerHeight - Height of container\n * @returns Centered position\n */\nexport function centerElement(\n elementWidth: number,\n elementHeight: number,\n containerWidth: number,\n containerHeight: number\n): Position {\n return {\n x: alignHorizontal(elementWidth, containerWidth, \"center\"),\n y: alignVertical(elementHeight, containerHeight, \"middle\"),\n };\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"LayoutSystem.js","names":[],"sources":["../../src/systems/LayoutSystem.ts"],"sourcesContent":["/**\n * Unified Layout System for consistent component positioning\n *\n * Provides grid-based layout calculations, responsive positioning utilities,\n * and alignment helpers for all screens in Black Trigram (흑괘).\n *\n * Features:\n * - 12-column grid system for consistent alignment\n * - Responsive positioning that adapts to screen size\n * - Safe area handling for mobile devices with notches\n * - Z-index hierarchy management\n * - Korean-English UI alignment support\n *\n * Performance target: <1ms for layout calculations\n *\n * @module systems/LayoutSystem\n * @category Systems\n * @korean 레이아웃시스템\n */\n\nimport { Position } from \"../types/common\";\nimport {\n ContainerBounds,\n HorizontalAlignment,\n ResponsivePosition,\n SafeAreaInsets,\n ScreenSize,\n VerticalAlignment,\n} from \"../types/LayoutTypes\";\n\n/**\n * Default grid configuration\n */\nconst DEFAULT_GRID_COLUMNS = 12;\nconst DEFAULT_GUTTER_SIZE = 20;\nconst BASE_DESKTOP_WIDTH = 1200;\n\n/**\n * Base width for mobile arena scaling calculations (in pixels)\n * This represents the standard desktop arena width (80% of 1200px)\n * Used to calculate scale factor for mobile devices\n */\nconst MOBILE_ARENA_BASE_WIDTH = 960;\n\n/**\n * Default row height for grid-based vertical positioning (in pixels)\n * Used when row index is specified in grid configuration\n * Can be overridden by providing explicit y position\n * \n */\nexport const DEFAULT_ROW_HEIGHT = 100;\n\n/**\n * Default safe area insets for mobile devices\n * Based on typical iOS device dimensions (iPhone 14 Pro as reference)\n */\nconst DEFAULT_SAFE_AREA: SafeAreaInsets = {\n top: 44, // Status bar + notch\n right: 0,\n bottom: 34, // Home indicator\n left: 0,\n};\n\n/**\n * LayoutSystem Class\n *\n * Provides unified layout calculations and positioning utilities\n * for consistent component placement across all screens.\n *\n * @example\n * ```typescript\n * const layout = new LayoutSystem();\n *\n * // Calculate grid position\n * const pos = layout.calculateGridPosition(2, 4, 1200);\n * // Returns { x: 200, width: 380 } for column 2, span 4\n *\n * // Calculate responsive position\n * const screenSize = { width: 375, height: 667, isMobile: true, isTablet: false, isDesktop: false, isLandscape: false };\n * const responsivePos = layout.calculateResponsivePosition(\n * { base: { x: 100, y: 50 } },\n * screenSize\n * );\n * ```\n */\nexport class LayoutSystem {\n private readonly gridColumns: number;\n private readonly gutterSize: number;\n private readonly safeArea: SafeAreaInsets;\n\n /**\n * Create a new LayoutSystem instance\n *\n * @param gridColumns - Number of grid columns (default: 12)\n * @param gutterSize - Gutter size between columns in pixels (default: 20)\n * @param safeArea - Safe area insets for mobile devices\n */\n constructor(\n gridColumns: number = DEFAULT_GRID_COLUMNS,\n gutterSize: number = DEFAULT_GUTTER_SIZE,\n safeArea: SafeAreaInsets = DEFAULT_SAFE_AREA\n ) {\n this.gridColumns = gridColumns;\n this.gutterSize = gutterSize;\n this.safeArea = safeArea;\n }\n\n /**\n * Calculate position and width for grid-based layout\n *\n * Uses 12-column grid system for consistent alignment.\n * Accounts for gutters between columns.\n *\n * @param column - Starting column (0-11)\n * @param span - Number of columns to span (1-12)\n * @param containerWidth - Total container width in pixels\n * @param customGutter - Custom gutter size (optional)\n * @returns Position and width for the grid cell\n * @throws Error if column or span are outside valid ranges\n *\n * @example\n * ```typescript\n * // Center element spanning 6 columns\n * const pos = layout.calculateGridPosition(3, 6, 1200);\n * // Returns { x: 300, width: 580 }\n * ```\n */\n calculateGridPosition(\n column: number,\n span: number,\n containerWidth: number,\n customGutter?: number\n ): { x: number; width: number } {\n if (column < 0 || column >= this.gridColumns) {\n throw new Error(\n `Invalid column: ${column}. Column must be between 0 and ${this.gridColumns - 1}`\n );\n }\n if (span < 1 || span > this.gridColumns) {\n throw new Error(\n `Invalid span: ${span}. Span must be between 1 and ${this.gridColumns}`\n );\n }\n if (column + span > this.gridColumns) {\n throw new Error(\n `Invalid grid position: column ${column} + span ${span} = ${column + span} exceeds ${this.gridColumns} columns`\n );\n }\n\n const gutter = customGutter ?? this.gutterSize;\n const columnWidth = containerWidth / this.gridColumns;\n\n const x = column * columnWidth;\n\n // Calculate width accounting for gutters\n // Width = (span * columnWidth) - gutter\n // The gutter is subtracted to create spacing between elements\n const width = span * columnWidth - gutter;\n\n return { x, width };\n }\n\n /**\n * Calculate responsive position based on screen size\n *\n * Scales position proportionally or uses specific overrides for tablet/mobile.\n *\n * @param config - Responsive position configuration\n * @param screenSize - Current screen dimensions and device type\n * @returns Calculated position for current screen size\n *\n * @example\n * ```typescript\n * const pos = layout.calculateResponsivePosition(\n * {\n * base: { x: 100, y: 50 },\n * mobile: { x: 10, y: 20 },\n * scaleProportionally: true\n * },\n * screenSize\n * );\n * ```\n */\n calculateResponsivePosition(\n config: ResponsivePosition,\n screenSize: ScreenSize\n ): Position {\n if (screenSize.isMobile && config.mobile) {\n return config.mobile;\n }\n if (screenSize.isTablet && config.tablet) {\n return config.tablet;\n }\n\n if (screenSize.isDesktop || !config.scaleProportionally) {\n return config.base;\n }\n\n // Scale proportionally for mobile/tablet if no override\n const scale = screenSize.width / BASE_DESKTOP_WIDTH;\n return {\n x: config.base.x * scale,\n y: config.base.y * scale,\n };\n }\n\n /**\n * Calculate safe position accounting for device notches and home indicators\n *\n * Ensures UI elements don't overlap with system UI on mobile devices.\n *\n * @param position - Base position\n * @param edge - Which edge to apply safe area ('top' | 'bottom' | 'left' | 'right')\n * @returns Position adjusted for safe area\n *\n * @example\n * ```typescript\n * // Adjust top position for status bar/notch\n * const safePos = layout.calculateSafePosition({ x: 0, y: 10 }, 'top');\n * // Returns { x: 0, y: 54 } (10 + 44 for notch)\n * ```\n */\n calculateSafePosition(\n position: Position,\n edge: \"top\" | \"bottom\" | \"left\" | \"right\"\n ): Position {\n const inset = this.safeArea[edge];\n switch (edge) {\n case \"top\":\n return { ...position, y: position.y + inset };\n case \"bottom\":\n return { ...position, y: position.y - inset };\n case \"left\":\n return { ...position, x: position.x + inset };\n case \"right\":\n return { ...position, x: position.x - inset };\n }\n }\n\n /**\n * Align element horizontally within container\n *\n * @param elementWidth - Width of element to align\n * @param containerWidth - Width of container\n * @param alignment - Alignment type ('left' | 'center' | 'right')\n * @param margin - Optional margin from edges\n * @returns X position for alignment\n *\n * @example\n * ```typescript\n * const x = layout.alignHorizontal(200, 800, 'center', 10);\n * // Returns 300 (centered with margins)\n * ```\n */\n alignHorizontal(\n elementWidth: number,\n containerWidth: number,\n alignment: HorizontalAlignment,\n margin: number = 0\n ): number {\n switch (alignment) {\n case \"left\":\n return margin;\n case \"center\":\n return (containerWidth - elementWidth) / 2;\n case \"right\":\n return containerWidth - elementWidth - margin;\n }\n }\n\n /**\n * Align element vertically within container\n *\n * @param elementHeight - Height of element to align\n * @param containerHeight - Height of container\n * @param alignment - Alignment type ('top' | 'middle' | 'bottom')\n * @param margin - Optional margin from edges\n * @returns Y position for alignment\n *\n * @example\n * ```typescript\n * const y = layout.alignVertical(100, 600, 'middle', 10);\n * // Returns 250 (vertically centered)\n * ```\n */\n alignVertical(\n elementHeight: number,\n containerHeight: number,\n alignment: VerticalAlignment,\n margin: number = 0\n ): number {\n switch (alignment) {\n case \"top\":\n return margin;\n case \"middle\":\n return (containerHeight - elementHeight) / 2;\n case \"bottom\":\n return containerHeight - elementHeight - margin;\n }\n }\n\n /**\n * Create screen size information from dimensions\n *\n * Determines device type and orientation based on screen dimensions.\n *\n * @param width - Screen width in pixels\n * @param height - Screen height in pixels\n * @returns ScreenSize object with device type flags\n *\n * @example\n * ```typescript\n * const screenSize = layout.getScreenSize(375, 667);\n * // Returns { width: 375, height: 667, isMobile: true, ... }\n * ```\n */\n getScreenSize(width: number, height: number): ScreenSize {\n const isMobile = width < 768;\n const isTablet = width >= 768 && width < 1200;\n const isDesktop = width >= 1200;\n const isLandscape = width > height;\n\n return {\n width,\n height,\n isMobile,\n isTablet,\n isDesktop,\n isLandscape,\n };\n }\n\n /**\n * Calculate container bounds for arena or game area\n *\n * Accounts for HUD, controls, and safe areas to determine usable space.\n *\n * @param screenWidth - Total screen width\n * @param screenHeight - Total screen height\n * @param hudHeight - Height of top HUD\n * @param controlsHeight - Height of bottom controls\n * @param padding - Padding around content\n * @returns Container bounds for game content\n *\n * @example\n * ```typescript\n * const bounds = layout.calculateContainerBounds(1200, 800, 120, 0, 10);\n * // Returns bounds for desktop game area\n * ```\n */\n calculateContainerBounds(\n screenWidth: number,\n screenHeight: number,\n hudHeight: number = 0,\n controlsHeight: number = 0,\n padding: number = 10\n ): ContainerBounds {\n const screenSize = this.getScreenSize(screenWidth, screenHeight);\n\n // Calculate available height\n const topOffset = hudHeight + padding + (screenSize.isMobile ? this.safeArea.top : 0);\n const bottomOffset = controlsHeight + padding + (screenSize.isMobile ? this.safeArea.bottom : 0);\n const availableHeight = screenHeight - topOffset - bottomOffset;\n\n // Calculate width accounting for padding\n const availableWidth = screenWidth - padding * 2;\n\n // Calculate scale for mobile (arena should be smaller on mobile)\n const scale = screenSize.isMobile ? Math.min(availableWidth / MOBILE_ARENA_BASE_WIDTH, 1.0) : 1.0;\n\n return {\n x: padding,\n y: topOffset,\n width: availableWidth,\n height: availableHeight,\n scale,\n };\n }\n}\n\n/**\n * Default singleton instance for convenience\n *\n * @example\n * ```typescript\n * import { defaultLayoutSystem } from './systems/LayoutSystem';\n *\n * const pos = defaultLayoutSystem.alignHorizontal(200, 800, 'center');\n * ```\n */\nexport const defaultLayoutSystem = new LayoutSystem();\n\n/**\n * Helper function to create grid-based position\n *\n * Convenience wrapper around LayoutSystem.calculateGridPosition\n *\n * @param column - Starting column (0-11)\n * @param span - Number of columns to span (1-12)\n * @param containerWidth - Total container width\n * @returns Position and width for grid cell\n */\nexport function calculateGridPosition(\n column: number,\n span: number,\n containerWidth: number\n): { x: number; width: number } {\n return defaultLayoutSystem.calculateGridPosition(column, span, containerWidth);\n}\n\n/**\n * Helper function to align element horizontally\n *\n * @param elementWidth - Width of element\n * @param containerWidth - Width of container\n * @param alignment - Alignment type\n * @param margin - Optional margin\n * @returns X position\n */\nexport function alignHorizontal(\n elementWidth: number,\n containerWidth: number,\n alignment: HorizontalAlignment = \"center\",\n margin: number = 0\n): number {\n return defaultLayoutSystem.alignHorizontal(elementWidth, containerWidth, alignment, margin);\n}\n\n/**\n * Helper function to align element vertically\n *\n * @param elementHeight - Height of element\n * @param containerHeight - Height of container\n * @param alignment - Alignment type\n * @param margin - Optional margin\n * @returns Y position\n */\nexport function alignVertical(\n elementHeight: number,\n containerHeight: number,\n alignment: VerticalAlignment = \"middle\",\n margin: number = 0\n): number {\n return defaultLayoutSystem.alignVertical(elementHeight, containerHeight, alignment, margin);\n}\n\n/**\n * Helper function to center element in container\n *\n * @param elementWidth - Width of element\n * @param elementHeight - Height of element\n * @param containerWidth - Width of container\n * @param containerHeight - Height of container\n * @returns Centered position\n */\nexport function centerElement(\n elementWidth: number,\n elementHeight: number,\n containerWidth: number,\n containerHeight: number\n): Position {\n return {\n x: alignHorizontal(elementWidth, containerWidth, \"center\"),\n y: alignVertical(elementHeight, containerHeight, \"middle\"),\n };\n}\n"],"mappings":"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"}