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
@@ -44,7 +44,7 @@ var PainResponseSystem = class {
44
44
  * Pain level effects and thresholds matching requirements.
45
45
  */
46
46
  painEffects = {
47
- [PainLevel.MINIMAL]: {
47
+ ["minimal"]: {
48
48
  range: [0, 20],
49
49
  performancePenalty: 0,
50
50
  accuracyReduction: 0,
@@ -52,7 +52,7 @@ var PainResponseSystem = class {
52
52
  speedReduction: 0,
53
53
  stunChance: 0
54
54
  },
55
- [PainLevel.MODERATE]: {
55
+ ["moderate"]: {
56
56
  range: [20, 40],
57
57
  performancePenalty: .1,
58
58
  accuracyReduction: .1,
@@ -60,7 +60,7 @@ var PainResponseSystem = class {
60
60
  speedReduction: .05,
61
61
  stunChance: 0
62
62
  },
63
- [PainLevel.SIGNIFICANT]: {
63
+ ["significant"]: {
64
64
  range: [40, 60],
65
65
  performancePenalty: .2,
66
66
  accuracyReduction: .2,
@@ -68,7 +68,7 @@ var PainResponseSystem = class {
68
68
  speedReduction: .15,
69
69
  stunChance: .05
70
70
  },
71
- [PainLevel.SEVERE]: {
71
+ ["severe"]: {
72
72
  range: [60, 80],
73
73
  performancePenalty: .35,
74
74
  accuracyReduction: .35,
@@ -76,7 +76,7 @@ var PainResponseSystem = class {
76
76
  speedReduction: .25,
77
77
  stunChance: .15
78
78
  },
79
- [PainLevel.OVERLOAD]: {
79
+ ["overload"]: {
80
80
  range: [80, 100],
81
81
  performancePenalty: .5,
82
82
  accuracyReduction: .5,
@@ -231,11 +231,11 @@ var PainResponseSystem = class {
231
231
  * @korean 고통수준확인
232
232
  */
233
233
  getPainLevel(pain) {
234
- if (pain >= 80) return PainLevel.OVERLOAD;
235
- if (pain >= 60) return PainLevel.SEVERE;
236
- if (pain >= 40) return PainLevel.SIGNIFICANT;
237
- if (pain >= 20) return PainLevel.MODERATE;
238
- return PainLevel.MINIMAL;
234
+ if (pain >= 80) return "overload";
235
+ if (pain >= 60) return "severe";
236
+ if (pain >= 40) return "significant";
237
+ if (pain >= 20) return "moderate";
238
+ return "minimal";
239
239
  }
240
240
  /**
241
241
  * Gets effects for a specific pain level.
@@ -316,7 +316,7 @@ var PainResponseSystem = class {
316
316
  * @korean 고통무력화확인
317
317
  */
318
318
  isIncapacitated(player) {
319
- return this.getPainLevel(player.pain) === PainLevel.OVERLOAD;
319
+ return this.getPainLevel(player.pain) === "overload";
320
320
  }
321
321
  /**
322
322
  * Gets bilingual name for pain level.
@@ -328,23 +328,23 @@ var PainResponseSystem = class {
328
328
  */
329
329
  getLevelName(level) {
330
330
  return {
331
- [PainLevel.MINIMAL]: {
331
+ ["minimal"]: {
332
332
  korean: "최소",
333
333
  english: "Minimal"
334
334
  },
335
- [PainLevel.MODERATE]: {
335
+ ["moderate"]: {
336
336
  korean: "보통",
337
337
  english: "Moderate"
338
338
  },
339
- [PainLevel.SIGNIFICANT]: {
339
+ ["significant"]: {
340
340
  korean: "상당",
341
341
  english: "Significant"
342
342
  },
343
- [PainLevel.SEVERE]: {
343
+ ["severe"]: {
344
344
  korean: "심각",
345
345
  english: "Severe"
346
346
  },
347
- [PainLevel.OVERLOAD]: {
347
+ ["overload"]: {
348
348
  korean: "과부하",
349
349
  english: "Overload"
350
350
  }
@@ -360,23 +360,23 @@ var PainResponseSystem = class {
360
360
  */
361
361
  getLevelDescription(level) {
362
362
  return {
363
- [PainLevel.MINIMAL]: {
363
+ ["minimal"]: {
364
364
  korean: "최소한의 고통, 전투 능력에 영향 없음",
365
365
  english: "Minimal pain, no significant effects on combat"
366
366
  },
367
- [PainLevel.MODERATE]: {
367
+ ["moderate"]: {
368
368
  korean: "보통 고통, 약간의 능력 저하 (-10%)",
369
369
  english: "Moderate pain, slight performance reduction (-10%)"
370
370
  },
371
- [PainLevel.SIGNIFICANT]: {
371
+ ["significant"]: {
372
372
  korean: "상당한 고통, 명확한 손상 (-20%)",
373
373
  english: "Significant pain, noticeable impairment (-20%)"
374
374
  },
375
- [PainLevel.SEVERE]: {
375
+ ["severe"]: {
376
376
  korean: "심각한 고통, 주요 제한 (-35%)",
377
377
  english: "Severe pain, major limitations (-35%)"
378
378
  },
379
- [PainLevel.OVERLOAD]: {
379
+ ["overload"]: {
380
380
  korean: "고통 과부하, 기절 위험 (-50%, 30% 기절 확률)",
381
381
  english: "Pain overload, stun risk (-50%, 30% stun chance)"
382
382
  }
@@ -1 +1 @@
1
- {"version":3,"file":"PainResponseSystem.js","names":[],"sources":["../../../src/systems/combat/PainResponseSystem.ts"],"sourcesContent":["/**\n * Pain Response System for managing cumulative pain effects in combat.\n * \n * **Korean**: 고통 반응 시스템 (Pain Response System)\n * \n * Implements realistic pain accumulation that progressively reduces combat\n * effectiveness. Unlike health, pain represents the body's protective response\n * to damage, reducing capabilities before critical injury occurs.\n * \n * ## Pain Mechanics\n * \n * - **충격통 (Shock Pain)**: Instant reaction affecting all abilities (10-30% reduction for 2-3 seconds)\n * - **누적외상 (Cumulative Trauma)**: Progressive damage impairment\n * - **통증과부하 (Pain Overload)**: Complete incapacitation from overwhelming pain (>80 pain)\n * - **무력화한계 (Incapacitation Threshold)**: Point of complete combat inability\n * \n * ## Pain Levels\n * \n * - **0-20**: Minimal pain - no significant effects\n * - **20-40**: Moderate pain - slight performance reduction (-10%)\n * - **40-60**: Significant pain - noticeable impairment (-20%)\n * - **60-80**: Severe pain - major limitations (-35%)\n * - **80-100**: Pain overload - chance of stun/unconsciousness, extreme impairment (-50%)\n * \n * @module systems/combat/PainResponseSystem\n * @category Combat System\n * @korean 고통반응시스템\n */\n\nimport { PlayerState } from \"../player\";\nimport { VitalPointSeverity, VitalPointCategory } from \"@/types\";\n\n/**\n * Pain level categories for status indication.\n */\nexport enum PainLevel {\n /** Minimal pain (0-20) - no significant effects */\n MINIMAL = \"minimal\",\n /** Moderate pain (20-40) - slight performance reduction (-10%) */\n MODERATE = \"moderate\",\n /** Significant pain (40-60) - noticeable impairment (-20%) */\n SIGNIFICANT = \"significant\",\n /** Severe pain (60-80) - major limitations (-35%) */\n SEVERE = \"severe\",\n /** Pain overload (80-100) - chance of stun/unconsciousness, extreme impairment (-50%) */\n OVERLOAD = \"overload\",\n}\n\n/**\n * Shock pain effect from sudden trauma.\n * \n * **Korean**: 충격통 효과 (Shock Pain Effect)\n * \n * Represents instant reaction to damage affecting all abilities for 2-3 seconds.\n */\nexport interface ShockPainEffect {\n /** Intensity of shock pain (0.1-0.3 for 10-30% reduction) */\n readonly intensity: number;\n /** Duration remaining in milliseconds (2000-3000ms) */\n readonly duration: number;\n /** Timestamp when shock pain was applied */\n readonly startTime: number;\n /** Damage value that caused the shock */\n readonly causedByDamage: number;\n}\n\n/**\n * Effects of pain on combat performance.\n */\ninterface PainEffects {\n /** Pain value range */\n readonly range: readonly [number, number];\n /** Overall performance penalty multiplier */\n readonly performancePenalty: number;\n /** Accuracy reduction */\n readonly accuracyReduction: number;\n /** Damage output reduction */\n readonly damageReduction: number;\n /** Movement speed reduction */\n readonly speedReduction: number;\n /** Chance of involuntary stun (0-1) */\n readonly stunChance: number;\n}\n\n/**\n * Pain Response System managing cumulative pain effects.\n * \n * Pain accumulates from all damage sources and reduces combat effectiveness\n * progressively. High pain can incapacitate even if health remains adequate.\n * Pain dissipates slowly over time.\n * \n * @example\n * ```typescript\n * const painSystem = new PainResponseSystem();\n * \n * // Apply pain from damage\n * const newPlayer = painSystem.applyPain(player, 15, VitalPointSeverity.MAJOR);\n * \n * // Check pain level\n * const level = painSystem.getPainLevel(newPlayer.pain);\n * \n * // Apply dissipation over time\n * const recovered = painSystem.applyDissipation(newPlayer, 1000);\n * ```\n * \n * @korean 고통반응시스템\n */\nexport class PainResponseSystem {\n /**\n * Pain level effects and thresholds matching requirements.\n */\n private readonly painEffects: Record<PainLevel, PainEffects> = {\n [PainLevel.MINIMAL]: {\n range: [0, 20],\n performancePenalty: 0.0,\n accuracyReduction: 0.0,\n damageReduction: 0.0,\n speedReduction: 0.0,\n stunChance: 0.0,\n },\n [PainLevel.MODERATE]: {\n range: [20, 40],\n performancePenalty: 0.1, // -10% overall\n accuracyReduction: 0.1,\n damageReduction: 0.1,\n speedReduction: 0.05,\n stunChance: 0.0,\n },\n [PainLevel.SIGNIFICANT]: {\n range: [40, 60],\n performancePenalty: 0.2, // -20% overall\n accuracyReduction: 0.2,\n damageReduction: 0.2,\n speedReduction: 0.15,\n stunChance: 0.05, // 5% chance\n },\n [PainLevel.SEVERE]: {\n range: [60, 80],\n performancePenalty: 0.35, // -35% overall\n accuracyReduction: 0.35,\n damageReduction: 0.35,\n speedReduction: 0.25,\n stunChance: 0.15, // 15% chance\n },\n [PainLevel.OVERLOAD]: {\n range: [80, 100],\n performancePenalty: 0.5, // -50% overall\n accuracyReduction: 0.5,\n damageReduction: 0.5,\n speedReduction: 0.4,\n stunChance: 0.3, // 30% chance per hit at pain >80\n },\n };\n\n /**\n * Pain multipliers by vital point severity.\n */\n private readonly severityMultipliers: 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 * Pain multipliers by vital point category.\n */\n private readonly categoryMultipliers: Record<string, number> = {\n [VitalPointCategory.NEUROLOGICAL]: 2.5, // Nerve strikes cause intense pain\n [VitalPointCategory.VASCULAR]: 1.5, // Blood flow restriction\n [VitalPointCategory.RESPIRATORY]: 2.0, // Breathing difficulty causes panic\n [VitalPointCategory.SKELETAL]: 1.8, // Bone/structural damage\n [VitalPointCategory.MUSCULAR]: 1.4, // Muscle trauma\n [VitalPointCategory.ORGAN]: 2.2, // Internal organ trauma\n [VitalPointCategory.JOINT]: 1.9, // Joint damage\n [VitalPointCategory.CIRCULATORY]: 1.6, // Circulatory disruption\n default: 1.0, // Standard damage\n };\n\n /**\n * Base pain dissipation rate per second (requirement: -5 pain/second).\n */\n private readonly BASE_DISSIPATION_RATE = 5.0; // -5 pain per second\n\n /**\n * Shock pain duration range in milliseconds (requirement: 2-3 seconds).\n */\n private readonly SHOCK_PAIN_DURATION = {\n min: 2000, // 2 seconds\n max: 3000, // 3 seconds\n };\n\n /**\n * Minimum damage to trigger shock pain.\n */\n private readonly SHOCK_PAIN_THRESHOLD = 10;\n\n /**\n * Pain overload threshold for stun chance (requirement: >80).\n */\n private readonly PAIN_OVERLOAD_THRESHOLD = 80;\n\n /**\n * Shock pain intensity constants.\n */\n private readonly MAX_SHOCK_INTENSITY = 0.3; // 30% max reduction\n private readonly MIN_SHOCK_INTENSITY = 0.1; // 10% min reduction\n private readonly DAMAGE_SCALE_FACTOR = 100; // for normalizing damage to 0-1 range\n private readonly INTENSITY_RANGE = 0.2; // 20% variable range\n\n /**\n * Pain to damage ratio for cumulative trauma calculation.\n */\n private readonly PAIN_TO_DAMAGE_RATIO = 0.5;\n\n /**\n * Applies pain from combat damage with shock pain effects.\n * \n * Calculates pain increase based on damage amount, vital point severity,\n * and vital point category. Also determines if shock pain effect should be triggered.\n * \n * **Shock Pain**: Instant 10-30% reduction for 2-3 seconds on significant hits (>=10 damage)\n * **Cumulative Trauma**: Progressive pain accumulation that reduces performance\n * **Pain Overload**: At >80 pain, chance of stun/unconsciousness\n * \n * @param player - Current player state\n * @param damage - Base damage amount\n * @param severity - Optional vital point severity\n * @param category - Optional vital point category for more accurate pain calculation\n * @returns Updated player state with increased pain and optional shock effect\n * \n * @example\n * ```typescript\n * // Normal hit\n * const result = system.applyPain(player, 10);\n * \n * // Vital point neurological hit with shock pain\n * const result = system.applyPain(\n * player, \n * 20, \n * VitalPointSeverity.MAJOR,\n * VitalPointCategory.NEUROLOGICAL\n * );\n * if (result.shockEffect) {\n * console.log(`Shock pain active for ${result.shockEffect.duration}ms`);\n * }\n * ```\n * \n * @korean 고통적용\n */\n applyPain(\n player: PlayerState,\n damage: number,\n severity?: VitalPointSeverity,\n category?: VitalPointCategory\n ): { player: PlayerState; shockEffect?: ShockPainEffect } {\n // Get multipliers based on severity and category\n const severityMultiplier = severity\n ? this.severityMultipliers[severity]\n : 1.0;\n \n const categoryMultiplier = category\n ? this.categoryMultipliers[category] ?? this.categoryMultipliers.default\n : this.categoryMultipliers.default;\n\n // Calculate pain increase (cumulative trauma)\n const painIncrease = damage * severityMultiplier * categoryMultiplier * this.PAIN_TO_DAMAGE_RATIO;\n\n // Apply pain, clamped to 0-100\n const newPain = Math.max(0, Math.min(100, player.pain + painIncrease));\n\n let shockEffect: ShockPainEffect | undefined;\n\n // Trigger shock pain for significant hits (>=10 damage)\n if (damage >= this.SHOCK_PAIN_THRESHOLD) {\n // Calculate shock intensity: 10-30% reduction based on damage\n const shockIntensity = Math.min(\n this.MAX_SHOCK_INTENSITY,\n this.MIN_SHOCK_INTENSITY + (damage / this.DAMAGE_SCALE_FACTOR) * this.INTENSITY_RANGE\n );\n \n // Random duration between 2-3 seconds\n const shockDuration =\n this.SHOCK_PAIN_DURATION.min +\n Math.random() *\n (this.SHOCK_PAIN_DURATION.max - this.SHOCK_PAIN_DURATION.min);\n\n shockEffect = {\n intensity: shockIntensity,\n duration: shockDuration,\n startTime: Date.now(),\n causedByDamage: damage,\n };\n }\n\n const updatedPlayer: PlayerState = {\n ...player,\n pain: newPain,\n };\n\n return { player: updatedPlayer, shockEffect };\n }\n\n /**\n * Applies pain dissipation over time (requirement: -5 pain/second).\n * \n * Pain naturally dissipates when not taking damage at a constant rate\n * of -5 pain per second.\n * \n * @param player - Current player state\n * @param deltaTime - Time elapsed in milliseconds\n * @returns Updated player state with reduced pain\n * \n * @example\n * ```typescript\n * // In game loop (~60fps, 16ms per frame)\n * player = system.applyDissipation(player, 16);\n * ```\n * \n * @korean 고통감소\n */\n applyDissipation(player: PlayerState, deltaTime: number): PlayerState {\n // Already at zero pain\n if (player.pain <= 0) {\n return player;\n }\n\n const deltaSeconds = deltaTime / 1000;\n \n // Constant dissipation rate of -5 pain/second\n const dissipation = this.BASE_DISSIPATION_RATE * deltaSeconds;\n const newPain = Math.max(0, player.pain - dissipation);\n\n return {\n ...player,\n pain: newPain,\n };\n }\n\n /**\n * Determines pain level from pain value.\n * \n * @param pain - Pain value (0-100)\n * @returns Current pain level\n * \n * @korean 고통수준확인\n */\n getPainLevel(pain: number): PainLevel {\n if (pain >= 80) return PainLevel.OVERLOAD;\n if (pain >= 60) return PainLevel.SEVERE;\n if (pain >= 40) return PainLevel.SIGNIFICANT;\n if (pain >= 20) return PainLevel.MODERATE;\n return PainLevel.MINIMAL;\n }\n\n /**\n * Gets effects for a specific pain level.\n * \n * @param level - Pain level\n * @returns Effects applied at that level\n * \n * @korean 고통효과\n */\n getEffects(level: PainLevel): PainEffects {\n return this.painEffects[level];\n }\n\n /**\n * Applies pain effects to player state, including shock pain.\n * \n * Modifies player stats based on current pain level. When shock pain\n * is active, additional penalties are applied temporarily.\n * \n * @param player - Current player state\n * @param shockEffect - Optional active shock pain effect\n * @returns Modified player state with pain effects\n * \n * @korean 고통효과적용\n */\n applyEffects(\n player: PlayerState,\n shockEffect?: ShockPainEffect\n ): PlayerState {\n const level = this.getPainLevel(player.pain);\n const effects = this.getEffects(level);\n\n // Calculate total penalties (base pain + shock pain if active)\n let totalAccuracyPenalty = effects.accuracyReduction;\n let totalDamagePenalty = effects.damageReduction;\n\n // Apply shock pain if still active\n if (shockEffect) {\n const elapsed = Date.now() - shockEffect.startTime;\n if (elapsed < shockEffect.duration) {\n // Add shock pain intensity to penalties\n totalAccuracyPenalty = Math.min(\n 1.0,\n totalAccuracyPenalty + shockEffect.intensity\n );\n totalDamagePenalty = Math.min(\n 1.0,\n totalDamagePenalty + shockEffect.intensity\n );\n }\n }\n\n return {\n ...player,\n attackPower: Math.floor(\n player.attackPower * (1 - totalDamagePenalty)\n ),\n defense: Math.floor(\n player.defense * (1 - effects.performancePenalty)\n ),\n speed: Math.floor(\n player.speed * (1 - effects.speedReduction)\n ),\n technique: Math.floor(\n player.technique * (1 - totalAccuracyPenalty)\n ),\n };\n }\n\n /**\n * Checks if pain overload should trigger stun.\n * \n * At pain >80, there's a chance per hit to trigger stun based on pain level.\n * Pain Overload level has 30% chance to trigger stun.\n * \n * @param player - Current player state\n * @returns True if player should be stunned from pain\n * \n * @korean 고통기절확인\n */\n shouldTriggerStun(player: PlayerState): boolean {\n if (player.pain < this.PAIN_OVERLOAD_THRESHOLD) {\n return false;\n }\n\n const level = this.getPainLevel(player.pain);\n const effects = this.getEffects(level);\n\n // Roll for stun chance\n return Math.random() < effects.stunChance;\n }\n\n /**\n * Checks if player is in pain overload state (>80 pain).\n * \n * @param player - Current player state\n * @returns True if pain is above overload threshold\n * \n * @korean 고통과부하확인\n */\n isInPainOverload(player: PlayerState): boolean {\n return player.pain >= this.PAIN_OVERLOAD_THRESHOLD;\n }\n\n /**\n * Checks if player is incapacitated by pain.\n * \n * @param player - Current player state\n * @returns True if pain is at overload level\n * \n * @korean 고통무력화확인\n */\n isIncapacitated(player: PlayerState): boolean {\n return this.getPainLevel(player.pain) === PainLevel.OVERLOAD;\n }\n\n /**\n * Gets bilingual name for pain level.\n * \n * @param level - Pain level\n * @returns Korean and English level names\n * \n * @korean 고통이름\n */\n getLevelName(level: PainLevel): { korean: string; english: string } {\n const names: Record<PainLevel, { korean: string; english: string }> = {\n [PainLevel.MINIMAL]: {\n korean: \"최소\",\n english: \"Minimal\",\n },\n [PainLevel.MODERATE]: {\n korean: \"보통\",\n english: \"Moderate\",\n },\n [PainLevel.SIGNIFICANT]: {\n korean: \"상당\",\n english: \"Significant\",\n },\n [PainLevel.SEVERE]: {\n korean: \"심각\",\n english: \"Severe\",\n },\n [PainLevel.OVERLOAD]: {\n korean: \"과부하\",\n english: \"Overload\",\n },\n };\n\n return names[level];\n }\n\n /**\n * Gets description of pain level effects.\n * \n * @param level - Pain level\n * @returns Bilingual description\n * \n * @korean 고통설명\n */\n getLevelDescription(level: PainLevel): {\n korean: string;\n english: string;\n } {\n const descriptions: Record<\n PainLevel,\n { korean: string; english: string }\n > = {\n [PainLevel.MINIMAL]: {\n korean: \"최소한의 고통, 전투 능력에 영향 없음\",\n english: \"Minimal pain, no significant effects on combat\",\n },\n [PainLevel.MODERATE]: {\n korean: \"보통 고통, 약간의 능력 저하 (-10%)\",\n english: \"Moderate pain, slight performance reduction (-10%)\",\n },\n [PainLevel.SIGNIFICANT]: {\n korean: \"상당한 고통, 명확한 손상 (-20%)\",\n english: \"Significant pain, noticeable impairment (-20%)\",\n },\n [PainLevel.SEVERE]: {\n korean: \"심각한 고통, 주요 제한 (-35%)\",\n english: \"Severe pain, major limitations (-35%)\",\n },\n [PainLevel.OVERLOAD]: {\n korean: \"고통 과부하, 기절 위험 (-50%, 30% 기절 확률)\",\n english: \"Pain overload, stun risk (-50%, 30% stun chance)\",\n },\n };\n\n return descriptions[level];\n }\n}\n\nexport default PainResponseSystem;\n"],"mappings":"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"}
1
+ {"version":3,"file":"PainResponseSystem.js","names":[],"sources":["../../../src/systems/combat/PainResponseSystem.ts"],"sourcesContent":["/**\n * Pain Response System for managing cumulative pain effects in combat.\n * \n * **Korean**: 고통 반응 시스템 (Pain Response System)\n * \n * Implements realistic pain accumulation that progressively reduces combat\n * effectiveness. Unlike health, pain represents the body's protective response\n * to damage, reducing capabilities before critical injury occurs.\n * \n * ## Pain Mechanics\n * \n * - **충격통 (Shock Pain)**: Instant reaction affecting all abilities (10-30% reduction for 2-3 seconds)\n * - **누적외상 (Cumulative Trauma)**: Progressive damage impairment\n * - **통증과부하 (Pain Overload)**: Complete incapacitation from overwhelming pain (>80 pain)\n * - **무력화한계 (Incapacitation Threshold)**: Point of complete combat inability\n * \n * ## Pain Levels\n * \n * - **0-20**: Minimal pain - no significant effects\n * - **20-40**: Moderate pain - slight performance reduction (-10%)\n * - **40-60**: Significant pain - noticeable impairment (-20%)\n * - **60-80**: Severe pain - major limitations (-35%)\n * - **80-100**: Pain overload - chance of stun/unconsciousness, extreme impairment (-50%)\n * \n * @module systems/combat/PainResponseSystem\n * @category Combat System\n * @korean 고통반응시스템\n */\n\nimport { PlayerState } from \"../player\";\nimport { VitalPointSeverity, VitalPointCategory } from \"@/types\";\n\n/**\n * Pain level categories for status indication.\n */\nexport enum PainLevel {\n /** Minimal pain (0-20) - no significant effects */\n MINIMAL = \"minimal\",\n /** Moderate pain (20-40) - slight performance reduction (-10%) */\n MODERATE = \"moderate\",\n /** Significant pain (40-60) - noticeable impairment (-20%) */\n SIGNIFICANT = \"significant\",\n /** Severe pain (60-80) - major limitations (-35%) */\n SEVERE = \"severe\",\n /** Pain overload (80-100) - chance of stun/unconsciousness, extreme impairment (-50%) */\n OVERLOAD = \"overload\",\n}\n\n/**\n * Shock pain effect from sudden trauma.\n * \n * **Korean**: 충격통 효과 (Shock Pain Effect)\n * \n * Represents instant reaction to damage affecting all abilities for 2-3 seconds.\n */\nexport interface ShockPainEffect {\n /** Intensity of shock pain (0.1-0.3 for 10-30% reduction) */\n readonly intensity: number;\n /** Duration remaining in milliseconds (2000-3000ms) */\n readonly duration: number;\n /** Timestamp when shock pain was applied */\n readonly startTime: number;\n /** Damage value that caused the shock */\n readonly causedByDamage: number;\n}\n\n/**\n * Effects of pain on combat performance.\n */\ninterface PainEffects {\n /** Pain value range */\n readonly range: readonly [number, number];\n /** Overall performance penalty multiplier */\n readonly performancePenalty: number;\n /** Accuracy reduction */\n readonly accuracyReduction: number;\n /** Damage output reduction */\n readonly damageReduction: number;\n /** Movement speed reduction */\n readonly speedReduction: number;\n /** Chance of involuntary stun (0-1) */\n readonly stunChance: number;\n}\n\n/**\n * Pain Response System managing cumulative pain effects.\n * \n * Pain accumulates from all damage sources and reduces combat effectiveness\n * progressively. High pain can incapacitate even if health remains adequate.\n * Pain dissipates slowly over time.\n * \n * @example\n * ```typescript\n * const painSystem = new PainResponseSystem();\n * \n * // Apply pain from damage\n * const newPlayer = painSystem.applyPain(player, 15, VitalPointSeverity.MAJOR);\n * \n * // Check pain level\n * const level = painSystem.getPainLevel(newPlayer.pain);\n * \n * // Apply dissipation over time\n * const recovered = painSystem.applyDissipation(newPlayer, 1000);\n * ```\n * \n * @korean 고통반응시스템\n */\nexport class PainResponseSystem {\n /**\n * Pain level effects and thresholds matching requirements.\n */\n private readonly painEffects: Record<PainLevel, PainEffects> = {\n [PainLevel.MINIMAL]: {\n range: [0, 20],\n performancePenalty: 0.0,\n accuracyReduction: 0.0,\n damageReduction: 0.0,\n speedReduction: 0.0,\n stunChance: 0.0,\n },\n [PainLevel.MODERATE]: {\n range: [20, 40],\n performancePenalty: 0.1, // -10% overall\n accuracyReduction: 0.1,\n damageReduction: 0.1,\n speedReduction: 0.05,\n stunChance: 0.0,\n },\n [PainLevel.SIGNIFICANT]: {\n range: [40, 60],\n performancePenalty: 0.2, // -20% overall\n accuracyReduction: 0.2,\n damageReduction: 0.2,\n speedReduction: 0.15,\n stunChance: 0.05, // 5% chance\n },\n [PainLevel.SEVERE]: {\n range: [60, 80],\n performancePenalty: 0.35, // -35% overall\n accuracyReduction: 0.35,\n damageReduction: 0.35,\n speedReduction: 0.25,\n stunChance: 0.15, // 15% chance\n },\n [PainLevel.OVERLOAD]: {\n range: [80, 100],\n performancePenalty: 0.5, // -50% overall\n accuracyReduction: 0.5,\n damageReduction: 0.5,\n speedReduction: 0.4,\n stunChance: 0.3, // 30% chance per hit at pain >80\n },\n };\n\n /**\n * Pain multipliers by vital point severity.\n */\n private readonly severityMultipliers: 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 * Pain multipliers by vital point category.\n */\n private readonly categoryMultipliers: Record<string, number> = {\n [VitalPointCategory.NEUROLOGICAL]: 2.5, // Nerve strikes cause intense pain\n [VitalPointCategory.VASCULAR]: 1.5, // Blood flow restriction\n [VitalPointCategory.RESPIRATORY]: 2.0, // Breathing difficulty causes panic\n [VitalPointCategory.SKELETAL]: 1.8, // Bone/structural damage\n [VitalPointCategory.MUSCULAR]: 1.4, // Muscle trauma\n [VitalPointCategory.ORGAN]: 2.2, // Internal organ trauma\n [VitalPointCategory.JOINT]: 1.9, // Joint damage\n [VitalPointCategory.CIRCULATORY]: 1.6, // Circulatory disruption\n default: 1.0, // Standard damage\n };\n\n /**\n * Base pain dissipation rate per second (requirement: -5 pain/second).\n */\n private readonly BASE_DISSIPATION_RATE = 5.0; // -5 pain per second\n\n /**\n * Shock pain duration range in milliseconds (requirement: 2-3 seconds).\n */\n private readonly SHOCK_PAIN_DURATION = {\n min: 2000, // 2 seconds\n max: 3000, // 3 seconds\n };\n\n /**\n * Minimum damage to trigger shock pain.\n */\n private readonly SHOCK_PAIN_THRESHOLD = 10;\n\n /**\n * Pain overload threshold for stun chance (requirement: >80).\n */\n private readonly PAIN_OVERLOAD_THRESHOLD = 80;\n\n /**\n * Shock pain intensity constants.\n */\n private readonly MAX_SHOCK_INTENSITY = 0.3; // 30% max reduction\n private readonly MIN_SHOCK_INTENSITY = 0.1; // 10% min reduction\n private readonly DAMAGE_SCALE_FACTOR = 100; // for normalizing damage to 0-1 range\n private readonly INTENSITY_RANGE = 0.2; // 20% variable range\n\n /**\n * Pain to damage ratio for cumulative trauma calculation.\n */\n private readonly PAIN_TO_DAMAGE_RATIO = 0.5;\n\n /**\n * Applies pain from combat damage with shock pain effects.\n * \n * Calculates pain increase based on damage amount, vital point severity,\n * and vital point category. Also determines if shock pain effect should be triggered.\n * \n * **Shock Pain**: Instant 10-30% reduction for 2-3 seconds on significant hits (>=10 damage)\n * **Cumulative Trauma**: Progressive pain accumulation that reduces performance\n * **Pain Overload**: At >80 pain, chance of stun/unconsciousness\n * \n * @param player - Current player state\n * @param damage - Base damage amount\n * @param severity - Optional vital point severity\n * @param category - Optional vital point category for more accurate pain calculation\n * @returns Updated player state with increased pain and optional shock effect\n * \n * @example\n * ```typescript\n * // Normal hit\n * const result = system.applyPain(player, 10);\n * \n * // Vital point neurological hit with shock pain\n * const result = system.applyPain(\n * player, \n * 20, \n * VitalPointSeverity.MAJOR,\n * VitalPointCategory.NEUROLOGICAL\n * );\n * if (result.shockEffect) {\n * console.log(`Shock pain active for ${result.shockEffect.duration}ms`);\n * }\n * ```\n * \n * @korean 고통적용\n */\n applyPain(\n player: PlayerState,\n damage: number,\n severity?: VitalPointSeverity,\n category?: VitalPointCategory\n ): { player: PlayerState; shockEffect?: ShockPainEffect } {\n // Get multipliers based on severity and category\n const severityMultiplier = severity\n ? this.severityMultipliers[severity]\n : 1.0;\n \n const categoryMultiplier = category\n ? this.categoryMultipliers[category] ?? this.categoryMultipliers.default\n : this.categoryMultipliers.default;\n\n // Calculate pain increase (cumulative trauma)\n const painIncrease = damage * severityMultiplier * categoryMultiplier * this.PAIN_TO_DAMAGE_RATIO;\n\n // Apply pain, clamped to 0-100\n const newPain = Math.max(0, Math.min(100, player.pain + painIncrease));\n\n let shockEffect: ShockPainEffect | undefined;\n\n // Trigger shock pain for significant hits (>=10 damage)\n if (damage >= this.SHOCK_PAIN_THRESHOLD) {\n // Calculate shock intensity: 10-30% reduction based on damage\n const shockIntensity = Math.min(\n this.MAX_SHOCK_INTENSITY,\n this.MIN_SHOCK_INTENSITY + (damage / this.DAMAGE_SCALE_FACTOR) * this.INTENSITY_RANGE\n );\n \n // Random duration between 2-3 seconds\n const shockDuration =\n this.SHOCK_PAIN_DURATION.min +\n Math.random() *\n (this.SHOCK_PAIN_DURATION.max - this.SHOCK_PAIN_DURATION.min);\n\n shockEffect = {\n intensity: shockIntensity,\n duration: shockDuration,\n startTime: Date.now(),\n causedByDamage: damage,\n };\n }\n\n const updatedPlayer: PlayerState = {\n ...player,\n pain: newPain,\n };\n\n return { player: updatedPlayer, shockEffect };\n }\n\n /**\n * Applies pain dissipation over time (requirement: -5 pain/second).\n * \n * Pain naturally dissipates when not taking damage at a constant rate\n * of -5 pain per second.\n * \n * @param player - Current player state\n * @param deltaTime - Time elapsed in milliseconds\n * @returns Updated player state with reduced pain\n * \n * @example\n * ```typescript\n * // In game loop (~60fps, 16ms per frame)\n * player = system.applyDissipation(player, 16);\n * ```\n * \n * @korean 고통감소\n */\n applyDissipation(player: PlayerState, deltaTime: number): PlayerState {\n // Already at zero pain\n if (player.pain <= 0) {\n return player;\n }\n\n const deltaSeconds = deltaTime / 1000;\n \n // Constant dissipation rate of -5 pain/second\n const dissipation = this.BASE_DISSIPATION_RATE * deltaSeconds;\n const newPain = Math.max(0, player.pain - dissipation);\n\n return {\n ...player,\n pain: newPain,\n };\n }\n\n /**\n * Determines pain level from pain value.\n * \n * @param pain - Pain value (0-100)\n * @returns Current pain level\n * \n * @korean 고통수준확인\n */\n getPainLevel(pain: number): PainLevel {\n if (pain >= 80) return PainLevel.OVERLOAD;\n if (pain >= 60) return PainLevel.SEVERE;\n if (pain >= 40) return PainLevel.SIGNIFICANT;\n if (pain >= 20) return PainLevel.MODERATE;\n return PainLevel.MINIMAL;\n }\n\n /**\n * Gets effects for a specific pain level.\n * \n * @param level - Pain level\n * @returns Effects applied at that level\n * \n * @korean 고통효과\n */\n getEffects(level: PainLevel): PainEffects {\n return this.painEffects[level];\n }\n\n /**\n * Applies pain effects to player state, including shock pain.\n * \n * Modifies player stats based on current pain level. When shock pain\n * is active, additional penalties are applied temporarily.\n * \n * @param player - Current player state\n * @param shockEffect - Optional active shock pain effect\n * @returns Modified player state with pain effects\n * \n * @korean 고통효과적용\n */\n applyEffects(\n player: PlayerState,\n shockEffect?: ShockPainEffect\n ): PlayerState {\n const level = this.getPainLevel(player.pain);\n const effects = this.getEffects(level);\n\n // Calculate total penalties (base pain + shock pain if active)\n let totalAccuracyPenalty = effects.accuracyReduction;\n let totalDamagePenalty = effects.damageReduction;\n\n // Apply shock pain if still active\n if (shockEffect) {\n const elapsed = Date.now() - shockEffect.startTime;\n if (elapsed < shockEffect.duration) {\n // Add shock pain intensity to penalties\n totalAccuracyPenalty = Math.min(\n 1.0,\n totalAccuracyPenalty + shockEffect.intensity\n );\n totalDamagePenalty = Math.min(\n 1.0,\n totalDamagePenalty + shockEffect.intensity\n );\n }\n }\n\n return {\n ...player,\n attackPower: Math.floor(\n player.attackPower * (1 - totalDamagePenalty)\n ),\n defense: Math.floor(\n player.defense * (1 - effects.performancePenalty)\n ),\n speed: Math.floor(\n player.speed * (1 - effects.speedReduction)\n ),\n technique: Math.floor(\n player.technique * (1 - totalAccuracyPenalty)\n ),\n };\n }\n\n /**\n * Checks if pain overload should trigger stun.\n * \n * At pain >80, there's a chance per hit to trigger stun based on pain level.\n * Pain Overload level has 30% chance to trigger stun.\n * \n * @param player - Current player state\n * @returns True if player should be stunned from pain\n * \n * @korean 고통기절확인\n */\n shouldTriggerStun(player: PlayerState): boolean {\n if (player.pain < this.PAIN_OVERLOAD_THRESHOLD) {\n return false;\n }\n\n const level = this.getPainLevel(player.pain);\n const effects = this.getEffects(level);\n\n // Roll for stun chance\n return Math.random() < effects.stunChance;\n }\n\n /**\n * Checks if player is in pain overload state (>80 pain).\n * \n * @param player - Current player state\n * @returns True if pain is above overload threshold\n * \n * @korean 고통과부하확인\n */\n isInPainOverload(player: PlayerState): boolean {\n return player.pain >= this.PAIN_OVERLOAD_THRESHOLD;\n }\n\n /**\n * Checks if player is incapacitated by pain.\n * \n * @param player - Current player state\n * @returns True if pain is at overload level\n * \n * @korean 고통무력화확인\n */\n isIncapacitated(player: PlayerState): boolean {\n return this.getPainLevel(player.pain) === PainLevel.OVERLOAD;\n }\n\n /**\n * Gets bilingual name for pain level.\n * \n * @param level - Pain level\n * @returns Korean and English level names\n * \n * @korean 고통이름\n */\n getLevelName(level: PainLevel): { korean: string; english: string } {\n const names: Record<PainLevel, { korean: string; english: string }> = {\n [PainLevel.MINIMAL]: {\n korean: \"최소\",\n english: \"Minimal\",\n },\n [PainLevel.MODERATE]: {\n korean: \"보통\",\n english: \"Moderate\",\n },\n [PainLevel.SIGNIFICANT]: {\n korean: \"상당\",\n english: \"Significant\",\n },\n [PainLevel.SEVERE]: {\n korean: \"심각\",\n english: \"Severe\",\n },\n [PainLevel.OVERLOAD]: {\n korean: \"과부하\",\n english: \"Overload\",\n },\n };\n\n return names[level];\n }\n\n /**\n * Gets description of pain level effects.\n * \n * @param level - Pain level\n * @returns Bilingual description\n * \n * @korean 고통설명\n */\n getLevelDescription(level: PainLevel): {\n korean: string;\n english: string;\n } {\n const descriptions: Record<\n PainLevel,\n { korean: string; english: string }\n > = {\n [PainLevel.MINIMAL]: {\n korean: \"최소한의 고통, 전투 능력에 영향 없음\",\n english: \"Minimal pain, no significant effects on combat\",\n },\n [PainLevel.MODERATE]: {\n korean: \"보통 고통, 약간의 능력 저하 (-10%)\",\n english: \"Moderate pain, slight performance reduction (-10%)\",\n },\n [PainLevel.SIGNIFICANT]: {\n korean: \"상당한 고통, 명확한 손상 (-20%)\",\n english: \"Significant pain, noticeable impairment (-20%)\",\n },\n [PainLevel.SEVERE]: {\n korean: \"심각한 고통, 주요 제한 (-35%)\",\n english: \"Severe pain, major limitations (-35%)\",\n },\n [PainLevel.OVERLOAD]: {\n korean: \"고통 과부하, 기절 위험 (-50%, 30% 기절 확률)\",\n english: \"Pain overload, stun risk (-50%, 30% stun chance)\",\n },\n };\n\n return descriptions[level];\n }\n}\n\nexport default PainResponseSystem;\n"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"TrainingCombatSystem.js","names":[],"sources":["../../../src/systems/combat/TrainingCombatSystem.ts"],"sourcesContent":["import { PlayerArchetype, TrigramStance } from \"../../types/common\";\nimport { AnimationType } from \"../animation\";\nimport { CombatSystem } from \"../CombatSystem\";\nimport { PlayerState } from \"../player\";\nimport { KoreanTechnique } from \"../vitalpoint\";\nimport { TrainingCombatResult } from \"./types\";\n\n/**\n * Animation context for timing-based hit detection\n */\ninterface AnimationContext {\n readonly animationType: AnimationType;\n readonly currentTime: number;\n}\n\n/**\n * Training-specific combat system for Korean martial arts practice\n * Focuses on technique accuracy, form analysis, and educational feedback\n */\nexport class TrainingCombatSystem extends CombatSystem {\n private trainingDummy: PlayerState;\n private accuracyHistory: number[] = [];\n private techniqueAttempts: number = 0;\n private successfulTechniques: number = 0;\n\n constructor() {\n super();\n this.trainingDummy = this.createTrainingDummy();\n }\n\n /**\n * Create a training dummy with infinite health for practice\n */\n private createTrainingDummy(): PlayerState {\n return {\n id: \"training_dummy\",\n name: { korean: \"훈련 더미\", english: \"Training Dummy\" },\n archetype: PlayerArchetype.MUSA,\n health: 1000,\n maxHealth: 1000,\n ki: 1000,\n maxKi: 1000,\n stamina: 1000,\n maxStamina: 1000,\n energy: 1000,\n maxEnergy: 1000,\n attackPower: 50,\n defense: 100,\n speed: 30,\n technique: 50,\n pain: 0,\n consciousness: 100,\n balance: 100,\n momentum: 0,\n currentStance: TrigramStance.GAN, // Mountain stance - defensive\n // eslint-disable-next-line @typescript-eslint/no-explicit-any -- CombatState type is narrower than string literal\n combatState: \"idle\" as any,\n position: { x: 600, y: 400 },\n isBlocking: false,\n isStunned: false,\n isCountering: false,\n lastActionTime: 0,\n recoveryTime: 0,\n lastStanceChangeTime: 0,\n statusEffects: [],\n activeEffects: [],\n vitalPoints: [],\n totalDamageReceived: 0,\n totalDamageDealt: 0,\n hitsTaken: 0,\n hitsLanded: 0,\n perfectStrikes: 0,\n vitalPointHits: 0,\n };\n }\n\n /**\n * Execute a training technique with detailed analysis\n * Supports optional animation context for physics-based hit detection\n */\n executeTrainingTechnique(\n player: PlayerState,\n technique: KoreanTechnique,\n targetedVitalPointId?: string,\n animationContext?: AnimationContext,\n ): TrainingCombatResult {\n this.techniqueAttempts++;\n\n // Base combat resolution with animation context\n const baseResult = this.resolveAttack(\n player,\n this.trainingDummy,\n technique,\n targetedVitalPointId,\n animationContext,\n );\n\n // Calculate training-specific scores\n const accuracyScore = this.calculateAccuracyScore(player, technique);\n const techniqueScore = this.calculateTechniqueScore(player, technique);\n const formScore = this.calculateFormScore(player, technique);\n\n this.accuracyHistory.push(accuracyScore);\n if (this.accuracyHistory.length > 10) {\n this.accuracyHistory.shift(); // Keep last 10 attempts\n }\n\n if (baseResult.hit) {\n this.successfulTechniques++;\n }\n\n // Generate improvement feedback\n const improvementAreas = this.generateImprovementAreas(\n accuracyScore,\n techniqueScore,\n formScore,\n );\n\n const nextTrainingGoals = this.generateTrainingGoals(\n player,\n technique,\n accuracyScore,\n );\n\n return {\n ...baseResult,\n accuracyScore,\n techniqueScore,\n formScore,\n improvementAreas,\n nextTrainingGoals,\n };\n }\n\n /**\n * Calculate accuracy score based on stance, timing, and targeting\n */\n private calculateAccuracyScore(\n player: PlayerState,\n _technique: KoreanTechnique,\n ): number {\n let score = 0.5; // Base score\n\n // Player technique skill\n score += (player.technique / 100) * 0.2;\n\n return Math.min(1.0, Math.max(0.0, score));\n }\n\n /**\n * Calculate technique execution score\n */\n private calculateTechniqueScore(\n player: PlayerState,\n _technique: KoreanTechnique,\n ): number {\n let score = 0.6; // Base execution\n\n // Ki and stamina availability\n const kiRatio = player.ki / player.maxKi;\n const staminaRatio = player.stamina / player.maxStamina;\n\n score += kiRatio * 0.2;\n score += staminaRatio * 0.2;\n\n return Math.min(1.0, Math.max(0.0, score));\n }\n\n /**\n * Calculate form score based on balance and positioning\n */\n private calculateFormScore(\n _player: PlayerState,\n _technique: KoreanTechnique,\n ): number {\n const score = 0.5; // Base form\n\n // For now, return base form score\n return Math.min(1.0, Math.max(0.0, score));\n }\n\n /**\n * Generate areas for improvement based on scores\n */\n private generateImprovementAreas(\n accuracy: number,\n technique: number,\n form: number,\n ): string[] {\n const areas: string[] = [];\n\n if (accuracy < 0.7) {\n areas.push(\"targeting_precision\");\n }\n if (technique < 0.7) {\n areas.push(\"technique_execution\");\n }\n if (form < 0.7) {\n areas.push(\"stance_stability\");\n }\n\n // Korean translations\n const koreanAreas = areas.map((area) => {\n switch (area) {\n case \"targeting_precision\":\n return \"타겟팅 정확도 - Targeting Precision\";\n case \"technique_execution\":\n return \"기법 실행 - Technique Execution\";\n case \"stance_stability\":\n return \"자세 안정성 - Stance Stability\";\n default:\n return area;\n }\n });\n\n return koreanAreas.length > 0\n ? koreanAreas\n : [\"전반적 향상 - Overall Improvement\"];\n }\n\n /**\n * Generate next training goals\n */\n private generateTrainingGoals(\n // Fix: Remove unused player parameter\n _player: PlayerState,\n technique: KoreanTechnique,\n accuracy: number,\n ): string[] {\n const goals: string[] = [];\n\n if (accuracy > 0.8) {\n goals.push(\"고급 기법 연습 - Practice Advanced Techniques\");\n } else if (accuracy > 0.6) {\n goals.push(\"연속 기법 연습 - Practice Combo Techniques\");\n } else {\n goals.push(\"기본 자세 연습 - Practice Basic Stances\");\n }\n\n // Add stance-specific goals\n const stanceGoals = this.getStanceSpecificGoals(technique.stance);\n goals.push(...stanceGoals);\n\n return goals.slice(0, 3); // Limit to 3 goals\n }\n\n /**\n * Get stance-specific training goals\n */\n private getStanceSpecificGoals(stance: TrigramStance): string[] {\n const stanceGoals: Record<TrigramStance, string[]> = {\n [TrigramStance.GEON]: [\n \"천괘 직선 공격 마스터 - Master Heaven's Direct Attacks\",\n ],\n [TrigramStance.TAE]: [\"태괘 유동성 향상 - Improve Lake's Fluidity\"],\n [TrigramStance.LI]: [\"리괘 정확성 훈련 - Train Fire's Precision\"],\n [TrigramStance.JIN]: [\"진괘 폭발력 개발 - Develop Thunder's Power\"],\n [TrigramStance.SON]: [\"손괘 연속성 연습 - Practice Wind's Continuity\"],\n [TrigramStance.GAM]: [\"감괘 적응력 향상 - Improve Water's Adaptability\"],\n [TrigramStance.GAN]: [\"간괘 방어력 강화 - Strengthen Mountain's Defense\"],\n [TrigramStance.GON]: [\"곤괘 제압력 훈련 - Train Earth's Control\"],\n };\n\n return stanceGoals[stance] || [\"기본 자세 완성 - Perfect Basic Stance\"];\n }\n\n /**\n * Get training statistics\n */\n getTrainingStats() {\n const overallAccuracy =\n this.accuracyHistory.length > 0\n ? this.accuracyHistory.reduce((a, b) => a + b, 0) /\n this.accuracyHistory.length\n : 0;\n\n const successRate =\n this.techniqueAttempts > 0\n ? this.successfulTechniques / this.techniqueAttempts\n : 0;\n\n return {\n totalAttempts: this.techniqueAttempts,\n successfulTechniques: this.successfulTechniques,\n successRate,\n overallAccuracy,\n recentAccuracy: this.accuracyHistory.slice(-5),\n improvementTrend: this.calculateImprovementTrend(),\n };\n }\n\n /**\n * Calculate improvement trend from recent accuracy scores\n */\n private calculateImprovementTrend(): \"improving\" | \"stable\" | \"declining\" {\n if (this.accuracyHistory.length < 5) return \"stable\";\n\n const recent = this.accuracyHistory.slice(-3);\n const earlier = this.accuracyHistory.slice(-6, -3);\n\n if (earlier.length === 0) return \"stable\";\n\n const recentAvg = recent.reduce((a, b) => a + b, 0) / recent.length;\n const earlierAvg = earlier.reduce((a, b) => a + b, 0) / earlier.length;\n\n const diff = recentAvg - earlierAvg;\n\n if (diff > 0.1) return \"improving\";\n if (diff < -0.1) return \"declining\";\n return \"stable\";\n }\n\n /**\n * Reset training session\n */\n resetTrainingSession() {\n this.accuracyHistory = [];\n this.techniqueAttempts = 0;\n this.successfulTechniques = 0;\n this.trainingDummy = this.createTrainingDummy();\n }\n\n /**\n * Fix: Add the missing resetTrainingDummy method that tests expect\n */\n resetTrainingDummy() {\n this.trainingDummy = this.createTrainingDummy();\n }\n\n /**\n * Get training dummy for display\n */\n getTrainingDummy(): PlayerState {\n return this.trainingDummy;\n }\n\n /**\n * Update training dummy health (for visual feedback)\n */\n updateTrainingDummy(updates: Partial<PlayerState>) {\n this.trainingDummy = {\n ...this.trainingDummy,\n ...updates,\n // Always keep high health for training\n health: Math.max(this.trainingDummy.health, 900),\n };\n }\n}\n\nexport default TrainingCombatSystem;\n"],"mappings":"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"}
1
+ {"version":3,"file":"TrainingCombatSystem.js","names":[],"sources":["../../../src/systems/combat/TrainingCombatSystem.ts"],"sourcesContent":["import { PlayerArchetype, TrigramStance } from \"../../types/common\";\nimport { AnimationType } from \"../animation\";\nimport { CombatSystem } from \"../CombatSystem\";\nimport { PlayerState } from \"../player\";\nimport { KoreanTechnique } from \"../vitalpoint\";\nimport { TrainingCombatResult } from \"./types\";\n\n/**\n * Animation context for timing-based hit detection\n */\ninterface AnimationContext {\n readonly animationType: AnimationType;\n readonly currentTime: number;\n}\n\n/**\n * Training-specific combat system for Korean martial arts practice\n * Focuses on technique accuracy, form analysis, and educational feedback\n */\nexport class TrainingCombatSystem extends CombatSystem {\n private trainingDummy: PlayerState;\n private accuracyHistory: number[] = [];\n private techniqueAttempts: number = 0;\n private successfulTechniques: number = 0;\n\n constructor() {\n super();\n this.trainingDummy = this.createTrainingDummy();\n }\n\n /**\n * Create a training dummy with infinite health for practice\n */\n private createTrainingDummy(): PlayerState {\n return {\n id: \"training_dummy\",\n name: { korean: \"훈련 더미\", english: \"Training Dummy\" },\n archetype: PlayerArchetype.MUSA,\n health: 1000,\n maxHealth: 1000,\n ki: 1000,\n maxKi: 1000,\n stamina: 1000,\n maxStamina: 1000,\n energy: 1000,\n maxEnergy: 1000,\n attackPower: 50,\n defense: 100,\n speed: 30,\n technique: 50,\n pain: 0,\n consciousness: 100,\n balance: 100,\n momentum: 0,\n currentStance: TrigramStance.GAN, // Mountain stance - defensive\n // eslint-disable-next-line @typescript-eslint/no-explicit-any -- CombatState type is narrower than string literal\n combatState: \"idle\" as any,\n position: { x: 600, y: 400 },\n isBlocking: false,\n isStunned: false,\n isCountering: false,\n lastActionTime: 0,\n recoveryTime: 0,\n lastStanceChangeTime: 0,\n statusEffects: [],\n activeEffects: [],\n vitalPoints: [],\n totalDamageReceived: 0,\n totalDamageDealt: 0,\n hitsTaken: 0,\n hitsLanded: 0,\n perfectStrikes: 0,\n vitalPointHits: 0,\n };\n }\n\n /**\n * Execute a training technique with detailed analysis\n * Supports optional animation context for physics-based hit detection\n */\n executeTrainingTechnique(\n player: PlayerState,\n technique: KoreanTechnique,\n targetedVitalPointId?: string,\n animationContext?: AnimationContext,\n ): TrainingCombatResult {\n this.techniqueAttempts++;\n\n // Base combat resolution with animation context\n const baseResult = this.resolveAttack(\n player,\n this.trainingDummy,\n technique,\n targetedVitalPointId,\n animationContext,\n );\n\n // Calculate training-specific scores\n const accuracyScore = this.calculateAccuracyScore(player, technique);\n const techniqueScore = this.calculateTechniqueScore(player, technique);\n const formScore = this.calculateFormScore(player, technique);\n\n this.accuracyHistory.push(accuracyScore);\n if (this.accuracyHistory.length > 10) {\n this.accuracyHistory.shift(); // Keep last 10 attempts\n }\n\n if (baseResult.hit) {\n this.successfulTechniques++;\n }\n\n // Generate improvement feedback\n const improvementAreas = this.generateImprovementAreas(\n accuracyScore,\n techniqueScore,\n formScore,\n );\n\n const nextTrainingGoals = this.generateTrainingGoals(\n player,\n technique,\n accuracyScore,\n );\n\n return {\n ...baseResult,\n accuracyScore,\n techniqueScore,\n formScore,\n improvementAreas,\n nextTrainingGoals,\n };\n }\n\n /**\n * Calculate accuracy score based on stance, timing, and targeting\n */\n private calculateAccuracyScore(\n player: PlayerState,\n _technique: KoreanTechnique,\n ): number {\n let score = 0.5; // Base score\n\n // Player technique skill\n score += (player.technique / 100) * 0.2;\n\n return Math.min(1.0, Math.max(0.0, score));\n }\n\n /**\n * Calculate technique execution score\n */\n private calculateTechniqueScore(\n player: PlayerState,\n _technique: KoreanTechnique,\n ): number {\n let score = 0.6; // Base execution\n\n // Ki and stamina availability\n const kiRatio = player.ki / player.maxKi;\n const staminaRatio = player.stamina / player.maxStamina;\n\n score += kiRatio * 0.2;\n score += staminaRatio * 0.2;\n\n return Math.min(1.0, Math.max(0.0, score));\n }\n\n /**\n * Calculate form score based on balance and positioning\n */\n private calculateFormScore(\n _player: PlayerState,\n _technique: KoreanTechnique,\n ): number {\n const score = 0.5; // Base form\n\n // For now, return base form score\n return Math.min(1.0, Math.max(0.0, score));\n }\n\n /**\n * Generate areas for improvement based on scores\n */\n private generateImprovementAreas(\n accuracy: number,\n technique: number,\n form: number,\n ): string[] {\n const areas: string[] = [];\n\n if (accuracy < 0.7) {\n areas.push(\"targeting_precision\");\n }\n if (technique < 0.7) {\n areas.push(\"technique_execution\");\n }\n if (form < 0.7) {\n areas.push(\"stance_stability\");\n }\n\n // Korean translations\n const koreanAreas = areas.map((area) => {\n switch (area) {\n case \"targeting_precision\":\n return \"타겟팅 정확도 - Targeting Precision\";\n case \"technique_execution\":\n return \"기법 실행 - Technique Execution\";\n case \"stance_stability\":\n return \"자세 안정성 - Stance Stability\";\n default:\n return area;\n }\n });\n\n return koreanAreas.length > 0\n ? koreanAreas\n : [\"전반적 향상 - Overall Improvement\"];\n }\n\n /**\n * Generate next training goals\n */\n private generateTrainingGoals(\n // Fix: Remove unused player parameter\n _player: PlayerState,\n technique: KoreanTechnique,\n accuracy: number,\n ): string[] {\n const goals: string[] = [];\n\n if (accuracy > 0.8) {\n goals.push(\"고급 기법 연습 - Practice Advanced Techniques\");\n } else if (accuracy > 0.6) {\n goals.push(\"연속 기법 연습 - Practice Combo Techniques\");\n } else {\n goals.push(\"기본 자세 연습 - Practice Basic Stances\");\n }\n\n // Add stance-specific goals\n const stanceGoals = this.getStanceSpecificGoals(technique.stance);\n goals.push(...stanceGoals);\n\n return goals.slice(0, 3); // Limit to 3 goals\n }\n\n /**\n * Get stance-specific training goals\n */\n private getStanceSpecificGoals(stance: TrigramStance): string[] {\n const stanceGoals: Record<TrigramStance, string[]> = {\n [TrigramStance.GEON]: [\n \"천괘 직선 공격 마스터 - Master Heaven's Direct Attacks\",\n ],\n [TrigramStance.TAE]: [\"태괘 유동성 향상 - Improve Lake's Fluidity\"],\n [TrigramStance.LI]: [\"리괘 정확성 훈련 - Train Fire's Precision\"],\n [TrigramStance.JIN]: [\"진괘 폭발력 개발 - Develop Thunder's Power\"],\n [TrigramStance.SON]: [\"손괘 연속성 연습 - Practice Wind's Continuity\"],\n [TrigramStance.GAM]: [\"감괘 적응력 향상 - Improve Water's Adaptability\"],\n [TrigramStance.GAN]: [\"간괘 방어력 강화 - Strengthen Mountain's Defense\"],\n [TrigramStance.GON]: [\"곤괘 제압력 훈련 - Train Earth's Control\"],\n };\n\n return stanceGoals[stance] || [\"기본 자세 완성 - Perfect Basic Stance\"];\n }\n\n /**\n * Get training statistics\n */\n getTrainingStats() {\n const overallAccuracy =\n this.accuracyHistory.length > 0\n ? this.accuracyHistory.reduce((a, b) => a + b, 0) /\n this.accuracyHistory.length\n : 0;\n\n const successRate =\n this.techniqueAttempts > 0\n ? this.successfulTechniques / this.techniqueAttempts\n : 0;\n\n return {\n totalAttempts: this.techniqueAttempts,\n successfulTechniques: this.successfulTechniques,\n successRate,\n overallAccuracy,\n recentAccuracy: this.accuracyHistory.slice(-5),\n improvementTrend: this.calculateImprovementTrend(),\n };\n }\n\n /**\n * Calculate improvement trend from recent accuracy scores\n */\n private calculateImprovementTrend(): \"improving\" | \"stable\" | \"declining\" {\n if (this.accuracyHistory.length < 5) return \"stable\";\n\n const recent = this.accuracyHistory.slice(-3);\n const earlier = this.accuracyHistory.slice(-6, -3);\n\n if (earlier.length === 0) return \"stable\";\n\n const recentAvg = recent.reduce((a, b) => a + b, 0) / recent.length;\n const earlierAvg = earlier.reduce((a, b) => a + b, 0) / earlier.length;\n\n const diff = recentAvg - earlierAvg;\n\n if (diff > 0.1) return \"improving\";\n if (diff < -0.1) return \"declining\";\n return \"stable\";\n }\n\n /**\n * Reset training session\n */\n resetTrainingSession() {\n this.accuracyHistory = [];\n this.techniqueAttempts = 0;\n this.successfulTechniques = 0;\n this.trainingDummy = this.createTrainingDummy();\n }\n\n /**\n * Fix: Add the missing resetTrainingDummy method that tests expect\n */\n resetTrainingDummy() {\n this.trainingDummy = this.createTrainingDummy();\n }\n\n /**\n * Get training dummy for display\n */\n getTrainingDummy(): PlayerState {\n return this.trainingDummy;\n }\n\n /**\n * Update training dummy health (for visual feedback)\n */\n updateTrainingDummy(updates: Partial<PlayerState>) {\n this.trainingDummy = {\n ...this.trainingDummy,\n ...updates,\n // Always keep high health for training\n health: Math.max(this.trainingDummy.health, 900),\n };\n }\n}\n\nexport default TrainingCombatSystem;\n"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"painConsciousnessUtils.js","names":[],"sources":["../../../src/systems/combat/painConsciousnessUtils.ts"],"sourcesContent":["/**\n * Utility functions for Pain and Consciousness management in combat.\n * \n * **Korean**: 고통 의식 관리 유틸리티\n * \n * Provides helper functions for managing pain and consciousness effects\n * in combat scenarios, including status checking, effect application,\n * and recovery management.\n * \n * @module systems/combat/painConsciousnessUtils\n * @category Combat System\n */\n\nimport { PlayerState } from \"../player\";\nimport { VitalPointCategory } from \"@/types\";\nimport { CombatResult } from \"./types\";\nimport PainResponseSystem, { PainLevel, ShockPainEffect } from \"./PainResponseSystem\";\nimport ConsciousnessSystem, { ConsciousnessLevel } from \"./ConsciousnessSystem\";\n\n/**\n * Pain and consciousness status information.\n */\nexport interface PainConsciousnessStatus {\n /** Current pain level */\n readonly painLevel: PainLevel;\n /** Current consciousness level */\n readonly consciousnessLevel: ConsciousnessLevel;\n /** Whether player is in pain overload */\n readonly isInPainOverload: boolean;\n /** Whether player is at incapacitation threshold */\n readonly isIncapacitated: boolean;\n /** Whether player is combat effective */\n readonly isCombatEffective: boolean;\n /** Overall combat effectiveness percentage (0-1) */\n readonly combatEffectiveness: number;\n /** Bilingual status description */\n readonly statusDescription: {\n readonly korean: string;\n readonly english: string;\n };\n}\n\n/**\n * Gets comprehensive pain and consciousness status for a player.\n * \n * @param player - Player to check status for\n * @param painSystem - Pain response system instance\n * @param consciousnessSystem - Consciousness system instance\n * @returns Comprehensive status information\n * \n * @example\n * ```typescript\n * const status = getPainConsciousnessStatus(player, painSystem, consciousnessSystem);\n * console.log(`Combat effectiveness: ${status.combatEffectiveness * 100}%`);\n * console.log(`Status: ${status.statusDescription.english}`);\n * ```\n * \n * @korean 상태확인\n */\nexport function getPainConsciousnessStatus(\n player: PlayerState,\n painSystem: PainResponseSystem,\n consciousnessSystem: ConsciousnessSystem\n): PainConsciousnessStatus {\n const painLevel = painSystem.getPainLevel(player.pain);\n const consciousnessLevel = consciousnessSystem.getLevel(player.consciousness);\n \n const isInPainOverload = painSystem.isInPainOverload(player);\n const isIncapacitated = consciousnessSystem.isAtIncapacitationThreshold(player);\n \n // Calculate overall combat effectiveness\n const painPenalty = painSystem.getEffects(painLevel).performancePenalty;\n const consciousnessPenalty = consciousnessSystem.getEffects(consciousnessLevel).accuracyPenalty;\n const combatEffectiveness = Math.max(0, 1 - Math.max(painPenalty, consciousnessPenalty));\n \n const isCombatEffective = combatEffectiveness > 0.5;\n \n // Generate status description\n const painName = painSystem.getLevelName(painLevel);\n const consciousnessName = consciousnessSystem.getLevelName(consciousnessLevel);\n \n let statusKorean = `고통: ${painName.korean}, 의식: ${consciousnessName.korean}`;\n let statusEnglish = `Pain: ${painName.english}, Consciousness: ${consciousnessName.english}`;\n \n if (isIncapacitated) {\n statusKorean = \"무력화 상태\";\n statusEnglish = \"Incapacitated\";\n } else if (isInPainOverload) {\n statusKorean = \"고통 과부하\";\n statusEnglish = \"Pain Overload\";\n }\n \n return {\n painLevel,\n consciousnessLevel,\n isInPainOverload,\n isIncapacitated,\n isCombatEffective,\n combatEffectiveness,\n statusDescription: {\n korean: statusKorean,\n english: statusEnglish,\n },\n };\n}\n\n/**\n * Determines if a combat result should trigger head trauma.\n * \n * Head trauma occurs for:\n * - Neurological vital point hits\n * - Vascular hits to head region\n * - High damage hits (>25) that could cause concussion\n * - Critical hits to upper body\n * \n * @param result - Combat result to check\n * @param category - Vital point category if known\n * @returns True if result should cause head trauma\n * \n * @korean 두부외상확인\n */\nexport function isHeadTraumaHit(\n result: CombatResult,\n category?: VitalPointCategory\n): boolean {\n // Neurological hits often target head\n if (category === VitalPointCategory.NEUROLOGICAL) {\n return true;\n }\n \n // Vascular hits to head region\n if (category === VitalPointCategory.VASCULAR && result.damage > 15) {\n return true;\n }\n \n // High damage hits that could concuss\n if (result.damage > 25) {\n return true;\n }\n \n // Critical hits to vital points\n if (result.isCritical && result.vitalPointHit) {\n return true;\n }\n \n return false;\n}\n\n/**\n * Extracts vital point category from combat result.\n * \n * Uses heuristics to determine category from effect sources and hit data.\n * \n * @param result - Combat result\n * @returns Vital point category if determinable\n * \n * @korean 급소분류추출\n */\nexport function extractVitalPointCategory(result: CombatResult): VitalPointCategory | undefined {\n if (!result.vitalPointHit || !result.effects || result.effects.length === 0) {\n return undefined;\n }\n \n const effect = result.effects[0];\n if (!effect.source) {\n return undefined;\n }\n \n const source = effect.source.toLowerCase();\n \n // Map source strings to categories\n if (source.includes('neuro') || source.includes('nerve')) {\n return VitalPointCategory.NEUROLOGICAL;\n }\n if (source.includes('vascular') || source.includes('blood') || source.includes('artery')) {\n return VitalPointCategory.VASCULAR;\n }\n if (source.includes('respiratory') || source.includes('breath') || source.includes('lung')) {\n return VitalPointCategory.RESPIRATORY;\n }\n if (source.includes('skeletal') || source.includes('bone')) {\n return VitalPointCategory.SKELETAL;\n }\n if (source.includes('muscular') || source.includes('muscle')) {\n return VitalPointCategory.MUSCULAR;\n }\n if (source.includes('organ') || source.includes('visceral')) {\n return VitalPointCategory.ORGAN;\n }\n if (source.includes('joint')) {\n return VitalPointCategory.JOINT;\n }\n \n return undefined;\n}\n\n/**\n * Calculates recommended recovery time based on player condition.\n * \n * @param player - Player state\n * @param consciousnessSystem - Consciousness system\n * @returns Estimated recovery time in seconds\n * \n * @example\n * ```typescript\n * const recoveryTime = getRecommendedRecoveryTime(player, consciousnessSystem);\n * console.log(`Recovery needed: ${recoveryTime}s`);\n * ```\n * \n * @korean 회복시간계산\n */\nexport function getRecommendedRecoveryTime(\n player: PlayerState,\n consciousnessSystem: ConsciousnessSystem\n): number {\n let painRecoveryTime = 0;\n let consciousnessRecoveryTime = 0;\n \n // Pain recovery time (-5 pain/second)\n if (player.pain > 0) {\n painRecoveryTime = player.pain / 5;\n }\n \n // Consciousness recovery time (5 points/second after 5s delay)\n if (player.consciousness < 100) {\n const consciousnessToRecover = 100 - player.consciousness;\n const consciousnessLevel = consciousnessSystem.getLevel(player.consciousness);\n \n // Account for slower recovery at low consciousness\n let recoveryRate = 5; // Base rate\n if (consciousnessLevel === ConsciousnessLevel.STUNNED) {\n recoveryRate = 2.5; // 50% slower\n } else if (consciousnessLevel === ConsciousnessLevel.UNCONSCIOUS) {\n recoveryRate = 1; // 20% of base\n }\n \n consciousnessRecoveryTime = 5 + (consciousnessToRecover / recoveryRate);\n }\n \n // Since pain and consciousness recover in parallel (concurrently in game loop),\n // return the maximum of the two recovery times, not the sum\n return Math.ceil(Math.max(painRecoveryTime, consciousnessRecoveryTime));\n}\n\n/**\n * Checks if shock pain effect is still active.\n * \n * @param shockEffect - Shock pain effect to check\n * @returns True if effect is still active\n * \n * @korean 충격통활성확인\n */\nexport function isShockPainActive(shockEffect: ShockPainEffect): boolean {\n const elapsed = Date.now() - shockEffect.startTime;\n return elapsed < shockEffect.duration;\n}\n\n/**\n * Gets remaining shock pain duration.\n * \n * @param shockEffect - Shock pain effect\n * @returns Remaining duration in milliseconds, or 0 if expired\n * \n * @korean 충격통잔여시간\n */\nexport function getShockPainRemainingDuration(shockEffect: ShockPainEffect): number {\n const elapsed = Date.now() - shockEffect.startTime;\n return Math.max(0, shockEffect.duration - elapsed);\n}\n\n/**\n * Formats pain and consciousness values for display.\n * \n * @param player - Player state\n * @returns Formatted display strings\n * \n * @example\n * ```typescript\n * const display = formatPainConsciousnessDisplay(player);\n * console.log(display.pain); // \"Pain: 45/100\"\n * console.log(display.consciousness); // \"Consciousness: 85/100\"\n * ```\n * \n * @korean 표시형식\n */\nexport function formatPainConsciousnessDisplay(player: PlayerState): {\n readonly pain: string;\n readonly consciousness: string;\n readonly painKorean: string;\n readonly consciousnessKorean: string;\n} {\n return {\n pain: `Pain: ${Math.floor(player.pain)}/100`,\n consciousness: `Consciousness: ${Math.floor(player.consciousness)}/100`,\n painKorean: `고통: ${Math.floor(player.pain)}/100`,\n consciousnessKorean: `의식: ${Math.floor(player.consciousness)}/100`,\n };\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"painConsciousnessUtils.js","names":[],"sources":["../../../src/systems/combat/painConsciousnessUtils.ts"],"sourcesContent":["/**\n * Utility functions for Pain and Consciousness management in combat.\n * \n * **Korean**: 고통 의식 관리 유틸리티\n * \n * Provides helper functions for managing pain and consciousness effects\n * in combat scenarios, including status checking, effect application,\n * and recovery management.\n * \n * @module systems/combat/painConsciousnessUtils\n * @category Combat System\n */\n\nimport { PlayerState } from \"../player\";\nimport { VitalPointCategory } from \"@/types\";\nimport { CombatResult } from \"./types\";\nimport PainResponseSystem, { PainLevel, ShockPainEffect } from \"./PainResponseSystem\";\nimport ConsciousnessSystem, { ConsciousnessLevel } from \"./ConsciousnessSystem\";\n\n/**\n * Pain and consciousness status information.\n */\nexport interface PainConsciousnessStatus {\n /** Current pain level */\n readonly painLevel: PainLevel;\n /** Current consciousness level */\n readonly consciousnessLevel: ConsciousnessLevel;\n /** Whether player is in pain overload */\n readonly isInPainOverload: boolean;\n /** Whether player is at incapacitation threshold */\n readonly isIncapacitated: boolean;\n /** Whether player is combat effective */\n readonly isCombatEffective: boolean;\n /** Overall combat effectiveness percentage (0-1) */\n readonly combatEffectiveness: number;\n /** Bilingual status description */\n readonly statusDescription: {\n readonly korean: string;\n readonly english: string;\n };\n}\n\n/**\n * Gets comprehensive pain and consciousness status for a player.\n * \n * @param player - Player to check status for\n * @param painSystem - Pain response system instance\n * @param consciousnessSystem - Consciousness system instance\n * @returns Comprehensive status information\n * \n * @example\n * ```typescript\n * const status = getPainConsciousnessStatus(player, painSystem, consciousnessSystem);\n * console.log(`Combat effectiveness: ${status.combatEffectiveness * 100}%`);\n * console.log(`Status: ${status.statusDescription.english}`);\n * ```\n * \n * @korean 상태확인\n */\nexport function getPainConsciousnessStatus(\n player: PlayerState,\n painSystem: PainResponseSystem,\n consciousnessSystem: ConsciousnessSystem\n): PainConsciousnessStatus {\n const painLevel = painSystem.getPainLevel(player.pain);\n const consciousnessLevel = consciousnessSystem.getLevel(player.consciousness);\n \n const isInPainOverload = painSystem.isInPainOverload(player);\n const isIncapacitated = consciousnessSystem.isAtIncapacitationThreshold(player);\n \n // Calculate overall combat effectiveness\n const painPenalty = painSystem.getEffects(painLevel).performancePenalty;\n const consciousnessPenalty = consciousnessSystem.getEffects(consciousnessLevel).accuracyPenalty;\n const combatEffectiveness = Math.max(0, 1 - Math.max(painPenalty, consciousnessPenalty));\n \n const isCombatEffective = combatEffectiveness > 0.5;\n \n // Generate status description\n const painName = painSystem.getLevelName(painLevel);\n const consciousnessName = consciousnessSystem.getLevelName(consciousnessLevel);\n \n let statusKorean = `고통: ${painName.korean}, 의식: ${consciousnessName.korean}`;\n let statusEnglish = `Pain: ${painName.english}, Consciousness: ${consciousnessName.english}`;\n \n if (isIncapacitated) {\n statusKorean = \"무력화 상태\";\n statusEnglish = \"Incapacitated\";\n } else if (isInPainOverload) {\n statusKorean = \"고통 과부하\";\n statusEnglish = \"Pain Overload\";\n }\n \n return {\n painLevel,\n consciousnessLevel,\n isInPainOverload,\n isIncapacitated,\n isCombatEffective,\n combatEffectiveness,\n statusDescription: {\n korean: statusKorean,\n english: statusEnglish,\n },\n };\n}\n\n/**\n * Determines if a combat result should trigger head trauma.\n * \n * Head trauma occurs for:\n * - Neurological vital point hits\n * - Vascular hits to head region\n * - High damage hits (>25) that could cause concussion\n * - Critical hits to upper body\n * \n * @param result - Combat result to check\n * @param category - Vital point category if known\n * @returns True if result should cause head trauma\n * \n * @korean 두부외상확인\n */\nexport function isHeadTraumaHit(\n result: CombatResult,\n category?: VitalPointCategory\n): boolean {\n // Neurological hits often target head\n if (category === VitalPointCategory.NEUROLOGICAL) {\n return true;\n }\n \n // Vascular hits to head region\n if (category === VitalPointCategory.VASCULAR && result.damage > 15) {\n return true;\n }\n \n // High damage hits that could concuss\n if (result.damage > 25) {\n return true;\n }\n \n // Critical hits to vital points\n if (result.isCritical && result.vitalPointHit) {\n return true;\n }\n \n return false;\n}\n\n/**\n * Extracts vital point category from combat result.\n * \n * Uses heuristics to determine category from effect sources and hit data.\n * \n * @param result - Combat result\n * @returns Vital point category if determinable\n * \n * @korean 급소분류추출\n */\nexport function extractVitalPointCategory(result: CombatResult): VitalPointCategory | undefined {\n if (!result.vitalPointHit || !result.effects || result.effects.length === 0) {\n return undefined;\n }\n \n const effect = result.effects[0];\n if (!effect.source) {\n return undefined;\n }\n \n const source = effect.source.toLowerCase();\n \n // Map source strings to categories\n if (source.includes('neuro') || source.includes('nerve')) {\n return VitalPointCategory.NEUROLOGICAL;\n }\n if (source.includes('vascular') || source.includes('blood') || source.includes('artery')) {\n return VitalPointCategory.VASCULAR;\n }\n if (source.includes('respiratory') || source.includes('breath') || source.includes('lung')) {\n return VitalPointCategory.RESPIRATORY;\n }\n if (source.includes('skeletal') || source.includes('bone')) {\n return VitalPointCategory.SKELETAL;\n }\n if (source.includes('muscular') || source.includes('muscle')) {\n return VitalPointCategory.MUSCULAR;\n }\n if (source.includes('organ') || source.includes('visceral')) {\n return VitalPointCategory.ORGAN;\n }\n if (source.includes('joint')) {\n return VitalPointCategory.JOINT;\n }\n \n return undefined;\n}\n\n/**\n * Calculates recommended recovery time based on player condition.\n * \n * @param player - Player state\n * @param consciousnessSystem - Consciousness system\n * @returns Estimated recovery time in seconds\n * \n * @example\n * ```typescript\n * const recoveryTime = getRecommendedRecoveryTime(player, consciousnessSystem);\n * console.log(`Recovery needed: ${recoveryTime}s`);\n * ```\n * \n * @korean 회복시간계산\n */\nexport function getRecommendedRecoveryTime(\n player: PlayerState,\n consciousnessSystem: ConsciousnessSystem\n): number {\n let painRecoveryTime = 0;\n let consciousnessRecoveryTime = 0;\n \n // Pain recovery time (-5 pain/second)\n if (player.pain > 0) {\n painRecoveryTime = player.pain / 5;\n }\n \n // Consciousness recovery time (5 points/second after 5s delay)\n if (player.consciousness < 100) {\n const consciousnessToRecover = 100 - player.consciousness;\n const consciousnessLevel = consciousnessSystem.getLevel(player.consciousness);\n \n // Account for slower recovery at low consciousness\n let recoveryRate = 5; // Base rate\n if (consciousnessLevel === ConsciousnessLevel.STUNNED) {\n recoveryRate = 2.5; // 50% slower\n } else if (consciousnessLevel === ConsciousnessLevel.UNCONSCIOUS) {\n recoveryRate = 1; // 20% of base\n }\n \n consciousnessRecoveryTime = 5 + (consciousnessToRecover / recoveryRate);\n }\n \n // Since pain and consciousness recover in parallel (concurrently in game loop),\n // return the maximum of the two recovery times, not the sum\n return Math.ceil(Math.max(painRecoveryTime, consciousnessRecoveryTime));\n}\n\n/**\n * Checks if shock pain effect is still active.\n * \n * @param shockEffect - Shock pain effect to check\n * @returns True if effect is still active\n * \n * @korean 충격통활성확인\n */\nexport function isShockPainActive(shockEffect: ShockPainEffect): boolean {\n const elapsed = Date.now() - shockEffect.startTime;\n return elapsed < shockEffect.duration;\n}\n\n/**\n * Gets remaining shock pain duration.\n * \n * @param shockEffect - Shock pain effect\n * @returns Remaining duration in milliseconds, or 0 if expired\n * \n * @korean 충격통잔여시간\n */\nexport function getShockPainRemainingDuration(shockEffect: ShockPainEffect): number {\n const elapsed = Date.now() - shockEffect.startTime;\n return Math.max(0, shockEffect.duration - elapsed);\n}\n\n/**\n * Formats pain and consciousness values for display.\n * \n * @param player - Player state\n * @returns Formatted display strings\n * \n * @example\n * ```typescript\n * const display = formatPainConsciousnessDisplay(player);\n * console.log(display.pain); // \"Pain: 45/100\"\n * console.log(display.consciousness); // \"Consciousness: 85/100\"\n * ```\n * \n * @korean 표시형식\n */\nexport function formatPainConsciousnessDisplay(player: PlayerState): {\n readonly pain: string;\n readonly consciousness: string;\n readonly painKorean: string;\n readonly consciousnessKorean: string;\n} {\n return {\n pain: `Pain: ${Math.floor(player.pain)}/100`,\n consciousness: `Consciousness: ${Math.floor(player.consciousness)}/100`,\n painKorean: `고통: ${Math.floor(player.pain)}/100`,\n consciousnessKorean: `의식: ${Math.floor(player.consciousness)}/100`,\n };\n}\n"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"typeGuards.js","names":[],"sources":["../../../src/systems/combat/typeGuards.ts"],"sourcesContent":["/**\n * Type guard functions for combat system\n * Provides runtime type checking for better type safety\n */\n\nimport { PlayerArchetype, VitalPointCategory, VitalPointSeverity } from \"../../types/common\";\nimport { VitalPoint } from \"../vitalpoint/types\";\n\n/**\n * Type guard to check if a value is a valid PlayerArchetype\n * @param value - Value to check\n * @returns True if value is a valid PlayerArchetype\n */\nexport function isValidArchetype(value: unknown): value is PlayerArchetype {\n if (typeof value !== \"string\") {\n return false;\n }\n \n return Object.values(PlayerArchetype).includes(value as PlayerArchetype);\n}\n\n/**\n * Type guard to check if a value is a valid VitalPoint\n * @param value - Value to check\n * @returns True if value is a valid VitalPoint\n */\nexport function isVitalPoint(value: unknown): value is VitalPoint {\n if (!value || typeof value !== \"object\") {\n return false;\n }\n \n const obj = value as Record<string, unknown>;\n \n // Validate position structure\n const hasValidPosition = \n obj.position !== undefined &&\n typeof obj.position === \"object\" &&\n obj.position !== null &&\n typeof (obj.position as Record<string, unknown>).x === \"number\" &&\n typeof (obj.position as Record<string, unknown>).y === \"number\";\n \n return (\n typeof obj.id === \"string\" &&\n typeof obj.category === \"string\" &&\n Object.values(VitalPointCategory).includes(obj.category as VitalPointCategory) &&\n typeof obj.severity === \"string\" &&\n Object.values(VitalPointSeverity).includes(obj.severity as VitalPointSeverity) &&\n hasValidPosition &&\n Array.isArray(obj.effects) &&\n typeof obj.names === \"object\" &&\n obj.names !== null &&\n typeof obj.description === \"object\" &&\n obj.description !== null &&\n typeof obj.targetingDifficulty === \"number\" &&\n Array.isArray(obj.effectiveStances)\n );\n}\n\n/**\n * Type guard to check if a value is a valid VitalPointCategory\n * @param value - Value to check\n * @returns True if value is a valid VitalPointCategory\n */\nexport function isVitalPointCategory(value: unknown): value is VitalPointCategory {\n if (typeof value !== \"string\") {\n return false;\n }\n \n return Object.values(VitalPointCategory).includes(value as VitalPointCategory);\n}\n"],"mappings":";;;;;;;;;;;AAaA,SAAgB,iBAAiB,OAA0C;CACzE,IAAI,OAAO,UAAU,UACnB,OAAO;CAGT,OAAO,OAAO,OAAO,gBAAgB,CAAC,SAAS,MAAyB;;;;;;;AAQ1E,SAAgB,aAAa,OAAqC;CAChE,IAAI,CAAC,SAAS,OAAO,UAAU,UAC7B,OAAO;CAGT,MAAM,MAAM;CAGZ,MAAM,mBACJ,IAAI,aAAa,KAAA,KACjB,OAAO,IAAI,aAAa,YACxB,IAAI,aAAa,QACjB,OAAQ,IAAI,SAAqC,MAAM,YACvD,OAAQ,IAAI,SAAqC,MAAM;CAEzD,OACE,OAAO,IAAI,OAAO,YAClB,OAAO,IAAI,aAAa,YACxB,OAAO,OAAO,mBAAmB,CAAC,SAAS,IAAI,SAA+B,IAC9E,OAAO,IAAI,aAAa,YACxB,OAAO,OAAO,mBAAmB,CAAC,SAAS,IAAI,SAA+B,IAC9E,oBACA,MAAM,QAAQ,IAAI,QAAQ,IAC1B,OAAO,IAAI,UAAU,YACrB,IAAI,UAAU,QACd,OAAO,IAAI,gBAAgB,YAC3B,IAAI,gBAAgB,QACpB,OAAO,IAAI,wBAAwB,YACnC,MAAM,QAAQ,IAAI,iBAAiB;;;;;;;AASvC,SAAgB,qBAAqB,OAA6C;CAChF,IAAI,OAAO,UAAU,UACnB,OAAO;CAGT,OAAO,OAAO,OAAO,mBAAmB,CAAC,SAAS,MAA4B"}
1
+ {"version":3,"file":"typeGuards.js","names":[],"sources":["../../../src/systems/combat/typeGuards.ts"],"sourcesContent":["/**\n * Type guard functions for combat system\n * Provides runtime type checking for better type safety\n */\n\nimport { PlayerArchetype, VitalPointCategory, VitalPointSeverity } from \"../../types/common\";\nimport { VitalPoint } from \"../vitalpoint/types\";\n\n/**\n * Type guard to check if a value is a valid PlayerArchetype\n * @param value - Value to check\n * @returns True if value is a valid PlayerArchetype\n */\nexport function isValidArchetype(value: unknown): value is PlayerArchetype {\n if (typeof value !== \"string\") {\n return false;\n }\n \n return Object.values(PlayerArchetype).includes(value as PlayerArchetype);\n}\n\n/**\n * Type guard to check if a value is a valid VitalPoint\n * @param value - Value to check\n * @returns True if value is a valid VitalPoint\n */\nexport function isVitalPoint(value: unknown): value is VitalPoint {\n if (!value || typeof value !== \"object\") {\n return false;\n }\n \n const obj = value as Record<string, unknown>;\n \n // Validate position structure\n const hasValidPosition = \n obj.position !== undefined &&\n typeof obj.position === \"object\" &&\n obj.position !== null &&\n typeof (obj.position as Record<string, unknown>).x === \"number\" &&\n typeof (obj.position as Record<string, unknown>).y === \"number\";\n \n return (\n typeof obj.id === \"string\" &&\n typeof obj.category === \"string\" &&\n Object.values(VitalPointCategory).includes(obj.category as VitalPointCategory) &&\n typeof obj.severity === \"string\" &&\n Object.values(VitalPointSeverity).includes(obj.severity as VitalPointSeverity) &&\n hasValidPosition &&\n Array.isArray(obj.effects) &&\n typeof obj.names === \"object\" &&\n obj.names !== null &&\n typeof obj.description === \"object\" &&\n obj.description !== null &&\n typeof obj.targetingDifficulty === \"number\" &&\n Array.isArray(obj.effectiveStances)\n );\n}\n\n/**\n * Type guard to check if a value is a valid VitalPointCategory\n * @param value - Value to check\n * @returns True if value is a valid VitalPointCategory\n */\nexport function isVitalPointCategory(value: unknown): value is VitalPointCategory {\n if (typeof value !== \"string\") {\n return false;\n }\n \n return Object.values(VitalPointCategory).includes(value as VitalPointCategory);\n}\n"],"mappings":";;;;;;;;;;;AAaA,SAAgB,iBAAiB,OAA0C;CACzE,IAAI,OAAO,UAAU,UACnB,OAAO;CAGT,OAAO,OAAO,OAAO,eAAe,EAAE,SAAS,KAAwB;AACzE;;;;;;AAOA,SAAgB,aAAa,OAAqC;CAChE,IAAI,CAAC,SAAS,OAAO,UAAU,UAC7B,OAAO;CAGT,MAAM,MAAM;CAGZ,MAAM,mBACJ,IAAI,aAAa,KAAA,KACjB,OAAO,IAAI,aAAa,YACxB,IAAI,aAAa,QACjB,OAAQ,IAAI,SAAqC,MAAM,YACvD,OAAQ,IAAI,SAAqC,MAAM;CAEzD,OACE,OAAO,IAAI,OAAO,YAClB,OAAO,IAAI,aAAa,YACxB,OAAO,OAAO,kBAAkB,EAAE,SAAS,IAAI,QAA8B,KAC7E,OAAO,IAAI,aAAa,YACxB,OAAO,OAAO,kBAAkB,EAAE,SAAS,IAAI,QAA8B,KAC7E,oBACA,MAAM,QAAQ,IAAI,OAAO,KACzB,OAAO,IAAI,UAAU,YACrB,IAAI,UAAU,QACd,OAAO,IAAI,gBAAgB,YAC3B,IAAI,gBAAgB,QACpB,OAAO,IAAI,wBAAwB,YACnC,MAAM,QAAQ,IAAI,gBAAgB;AAEtC;;;;;;AAOA,SAAgB,qBAAqB,OAA6C;CAChF,IAAI,OAAO,UAAU,UACnB,OAAO;CAGT,OAAO,OAAO,OAAO,kBAAkB,EAAE,SAAS,KAA2B;AAC/E"}
@@ -1 +1 @@
1
- {"version":3,"file":"effects.js","names":[],"sources":["../../src/systems/effects.ts"],"sourcesContent":["/**\n * Combat effects and status system for Korean martial arts.\n *\n * @module systems/effects\n * @category Combat Effects\n * @korean 전투효과시스템\n */\n\n/** Hit effect type discriminator for combat feedback */\nexport enum HitEffectType {\n GENERAL_DAMAGE = \"general_damage\",\n CRITICAL_HIT = \"critical_hit\",\n VITAL_POINT_STRIKE = \"vital_point_strike\",\n STATUS_EFFECT = \"status_effect\",\n MISS = \"miss\",\n BLOCK = \"block\",\n PARRY = \"parry\",\n COUNTER = \"counter\",\n HIT = \"hit\",\n}\n\n/** Additional hit effect variants for status effects */\nexport enum HitEffectEnum {\n STUN = \"stun\",\n WEAKNESS = \"weakness\",\n STAMINA_DRAIN = \"stamina_drain\",\n VULNERABILITY = \"vulnerability\",\n BLEEDING = \"bleeding\",\n BUFF = \"buff\",\n DEBUFF = \"debuff\",\n PARALYSIS = \"paralysis\",\n POISON = \"poison\",\n BURN = \"burn\",\n FREEZE = \"freeze\",\n CONFUSION = \"confusion\",\n}\n\n/** Effect intensity levels for status effects */\nexport enum EffectIntensity {\n WEAK = \"weak\",\n MINOR = \"minor\",\n LOW = \"low\",\n MEDIUM = \"medium\",\n MODERATE = \"moderate\",\n HIGH = \"high\",\n SEVERE = \"severe\",\n CRITICAL = \"critical\",\n EXTREME = \"extreme\",\n}\n\n/** Union type of all applicable status effect identifiers */\nexport type EffectType =\n | \"stun\"\n | \"poison\"\n | \"burn\"\n | \"bleed\"\n | \"exhausted\"\n | \"focused\"\n | \"rage\"\n | \"defensive\"\n | \"weakened\"\n | \"strengthened\"\n | \"paralysis\" // Add missing paralysis\n | \"confusion\" // Add missing confusion\n | \"vulnerability\" // Add missing vulnerability\n | \"stamina_drain\"; // Add missing stamina_drain\n\n// Particle effect for visual feedback\n// Particle effect types\nexport type ParticleType =\n | \"spark\"\n | \"blood\"\n | \"energy\"\n | \"dust\"\n | \"flash\"\n | \"smoke\"\n | \"lightning\"\n | \"wind\";\n\n// Environmental effect\n// Environmental effect types\nexport type EnvironmentalEffectType =\n | \"smoke\"\n | \"fire\"\n | \"ice\"\n | \"wind\"\n | \"lightning\"\n | \"darkness\"\n | \"light\"\n | \"pressure\";\n"],"mappings":";;;;;;;;;AASA,IAAY,gBAAL,yBAAA,eAAA;CACL,cAAA,oBAAiB;CACjB,cAAA,kBAAe;CACf,cAAA,wBAAqB;CACrB,cAAA,mBAAgB;CAChB,cAAA,UAAO;CACP,cAAA,WAAQ;CACR,cAAA,WAAQ;CACR,cAAA,aAAU;CACV,cAAA,SAAM;;KACP;;AAGD,IAAY,gBAAL,yBAAA,eAAA;CACL,cAAA,UAAO;CACP,cAAA,cAAW;CACX,cAAA,mBAAgB;CAChB,cAAA,mBAAgB;CAChB,cAAA,cAAW;CACX,cAAA,UAAO;CACP,cAAA,YAAS;CACT,cAAA,eAAY;CACZ,cAAA,YAAS;CACT,cAAA,UAAO;CACP,cAAA,YAAS;CACT,cAAA,eAAY;;KACb;;AAGD,IAAY,kBAAL,yBAAA,iBAAA;CACL,gBAAA,UAAO;CACP,gBAAA,WAAQ;CACR,gBAAA,SAAM;CACN,gBAAA,YAAS;CACT,gBAAA,cAAW;CACX,gBAAA,UAAO;CACP,gBAAA,YAAS;CACT,gBAAA,cAAW;CACX,gBAAA,aAAU;;KACX"}
1
+ {"version":3,"file":"effects.js","names":[],"sources":["../../src/systems/effects.ts"],"sourcesContent":["/**\n * Combat effects and status system for Korean martial arts.\n *\n * @module systems/effects\n * @category Combat Effects\n * @korean 전투효과시스템\n */\n\n/** Hit effect type discriminator for combat feedback */\nexport enum HitEffectType {\n GENERAL_DAMAGE = \"general_damage\",\n CRITICAL_HIT = \"critical_hit\",\n VITAL_POINT_STRIKE = \"vital_point_strike\",\n STATUS_EFFECT = \"status_effect\",\n MISS = \"miss\",\n BLOCK = \"block\",\n PARRY = \"parry\",\n COUNTER = \"counter\",\n HIT = \"hit\",\n}\n\n/** Additional hit effect variants for status effects */\nexport enum HitEffectEnum {\n STUN = \"stun\",\n WEAKNESS = \"weakness\",\n STAMINA_DRAIN = \"stamina_drain\",\n VULNERABILITY = \"vulnerability\",\n BLEEDING = \"bleeding\",\n BUFF = \"buff\",\n DEBUFF = \"debuff\",\n PARALYSIS = \"paralysis\",\n POISON = \"poison\",\n BURN = \"burn\",\n FREEZE = \"freeze\",\n CONFUSION = \"confusion\",\n}\n\n/** Effect intensity levels for status effects */\nexport enum EffectIntensity {\n WEAK = \"weak\",\n MINOR = \"minor\",\n LOW = \"low\",\n MEDIUM = \"medium\",\n MODERATE = \"moderate\",\n HIGH = \"high\",\n SEVERE = \"severe\",\n CRITICAL = \"critical\",\n EXTREME = \"extreme\",\n}\n\n/** Union type of all applicable status effect identifiers */\nexport type EffectType =\n | \"stun\"\n | \"poison\"\n | \"burn\"\n | \"bleed\"\n | \"exhausted\"\n | \"focused\"\n | \"rage\"\n | \"defensive\"\n | \"weakened\"\n | \"strengthened\"\n | \"paralysis\" // Add missing paralysis\n | \"confusion\" // Add missing confusion\n | \"vulnerability\" // Add missing vulnerability\n | \"stamina_drain\"; // Add missing stamina_drain\n\n// Particle effect for visual feedback\n// Particle effect types\nexport type ParticleType =\n | \"spark\"\n | \"blood\"\n | \"energy\"\n | \"dust\"\n | \"flash\"\n | \"smoke\"\n | \"lightning\"\n | \"wind\";\n\n// Environmental effect\n// Environmental effect types\nexport type EnvironmentalEffectType =\n | \"smoke\"\n | \"fire\"\n | \"ice\"\n | \"wind\"\n | \"lightning\"\n | \"darkness\"\n | \"light\"\n | \"pressure\";\n"],"mappings":";;;;;;;;;AASA,IAAY,gBAAL,yBAAA,eAAA;CACL,cAAA,oBAAA;CACA,cAAA,kBAAA;CACA,cAAA,wBAAA;CACA,cAAA,mBAAA;CACA,cAAA,UAAA;CACA,cAAA,WAAA;CACA,cAAA,WAAA;CACA,cAAA,aAAA;CACA,cAAA,SAAA;;AACF,EAAA,CAAA,CAAA;;AAGA,IAAY,gBAAL,yBAAA,eAAA;CACL,cAAA,UAAA;CACA,cAAA,cAAA;CACA,cAAA,mBAAA;CACA,cAAA,mBAAA;CACA,cAAA,cAAA;CACA,cAAA,UAAA;CACA,cAAA,YAAA;CACA,cAAA,eAAA;CACA,cAAA,YAAA;CACA,cAAA,UAAA;CACA,cAAA,YAAA;CACA,cAAA,eAAA;;AACF,EAAA,CAAA,CAAA;;AAGA,IAAY,kBAAL,yBAAA,iBAAA;CACL,gBAAA,UAAA;CACA,gBAAA,WAAA;CACA,gBAAA,SAAA;CACA,gBAAA,YAAA;CACA,gBAAA,cAAA;CACA,gBAAA,UAAA;CACA,gBAAA,YAAA;CACA,gBAAA,cAAA;CACA,gBAAA,aAAA;;AACF,EAAA,CAAA,CAAA"}
@@ -1 +1 @@
1
- {"version":3,"file":"game.js","names":[],"sources":["../../src/systems/game.ts"],"sourcesContent":["/**\n * Core game state and flow management types.\n *\n * Defines match configuration, event system, game sessions, and\n * persistent save data for the Black Trigram combat game.\n *\n * @module systems/game\n * @category Game State\n * @korean 게임상태관리\n */\n\nimport { GameMode, KoreanText } from \"@/types\";\nimport type { PlayerState } from \"./player\";\n\n/** Configuration for a match before it begins */\nexport interface MatchConfig {\n readonly mode: GameMode;\n readonly rounds: number;\n readonly roundDuration: number; // seconds\n readonly player1Archetype: string;\n readonly player2Archetype: string;\n readonly stage: string;\n readonly difficulty?: \"easy\" | \"medium\" | \"hard\" | \"expert\";\n}\n\n/** A discrete event emitted during gameplay for logging and UI updates */\nexport interface GameEvent {\n readonly id: string;\n readonly type: GameEventType;\n readonly timestamp: number;\n readonly playerId?: string;\n // eslint-disable-next-line @typescript-eslint/no-explicit-any -- Event data can contain various types depending on event type\n readonly data: Record<string, any>;\n readonly message?: KoreanText;\n}\n\n/** All event types emitted during a match lifecycle */\nexport enum GameEventType {\n GAME_START = \"game_start\",\n ROUND_START = \"round_start\",\n ROUND_END = \"round_end\",\n MATCH_END = \"match_end\",\n PLAYER_ATTACK = \"player_attack\",\n PLAYER_HIT = \"player_hit\",\n STANCE_CHANGE = \"stance_change\",\n TECHNIQUE_EXECUTE = \"technique_execute\",\n VITAL_POINT_HIT = \"vital_point_hit\",\n STATUS_EFFECT = \"status_effect\",\n PAUSE_TOGGLE = \"pause_toggle\",\n ERROR = \"error\",\n}\n\n/** Runtime state for an active match session */\nexport interface GameSession {\n readonly id: string;\n readonly gameMode: GameMode;\n readonly players: readonly [PlayerState, PlayerState];\n readonly currentRound: number;\n readonly maxRounds: number;\n readonly roundTimeLimit: number;\n readonly timeRemaining: number;\n readonly isPaused: boolean;\n readonly isGameOver: boolean;\n readonly winner: PlayerState | null;\n}\n\n/** Global game rules and balance parameters */\nexport interface GameConfig {\n readonly maxHealth: number;\n readonly maxKi: number;\n readonly maxStamina: number;\n readonly roundDuration: number;\n readonly maxRounds: number;\n readonly difficulty: \"beginner\" | \"intermediate\" | \"expert\" | \"master\";\n readonly enableVitalPoints: boolean;\n readonly enableStatusEffects: boolean;\n readonly allowArchetypeSwitching: boolean;\n}\n\n/** Persistent player data written to save storage */\nexport interface GameSaveData {\n readonly version: string;\n readonly playerId: string;\n readonly playerProgress: {\n readonly archetypeExperience: Record<string, number>;\n readonly unlockedTechniques: readonly string[];\n readonly achievements: readonly string[];\n };\n readonly settings: {\n readonly volume: number;\n readonly difficulty: string;\n readonly controls: Record<string, string>;\n };\n readonly statistics: {\n readonly totalMatches: number;\n readonly wins: number;\n readonly losses: number;\n readonly favoriteArchetype: string;\n };\n}\n"],"mappings":";;AAqCA,IAAY,gBAAL,yBAAA,eAAA;CACL,cAAA,gBAAa;CACb,cAAA,iBAAc;CACd,cAAA,eAAY;CACZ,cAAA,eAAY;CACZ,cAAA,mBAAgB;CAChB,cAAA,gBAAa;CACb,cAAA,mBAAgB;CAChB,cAAA,uBAAoB;CACpB,cAAA,qBAAkB;CAClB,cAAA,mBAAgB;CAChB,cAAA,kBAAe;CACf,cAAA,WAAQ;;KACT"}
1
+ {"version":3,"file":"game.js","names":[],"sources":["../../src/systems/game.ts"],"sourcesContent":["/**\n * Core game state and flow management types.\n *\n * Defines match configuration, event system, game sessions, and\n * persistent save data for the Black Trigram combat game.\n *\n * @module systems/game\n * @category Game State\n * @korean 게임상태관리\n */\n\nimport { GameMode, KoreanText } from \"@/types\";\nimport type { PlayerState } from \"./player\";\n\n/** Configuration for a match before it begins */\nexport interface MatchConfig {\n readonly mode: GameMode;\n readonly rounds: number;\n readonly roundDuration: number; // seconds\n readonly player1Archetype: string;\n readonly player2Archetype: string;\n readonly stage: string;\n readonly difficulty?: \"easy\" | \"medium\" | \"hard\" | \"expert\";\n}\n\n/** A discrete event emitted during gameplay for logging and UI updates */\nexport interface GameEvent {\n readonly id: string;\n readonly type: GameEventType;\n readonly timestamp: number;\n readonly playerId?: string;\n // eslint-disable-next-line @typescript-eslint/no-explicit-any -- Event data can contain various types depending on event type\n readonly data: Record<string, any>;\n readonly message?: KoreanText;\n}\n\n/** All event types emitted during a match lifecycle */\nexport enum GameEventType {\n GAME_START = \"game_start\",\n ROUND_START = \"round_start\",\n ROUND_END = \"round_end\",\n MATCH_END = \"match_end\",\n PLAYER_ATTACK = \"player_attack\",\n PLAYER_HIT = \"player_hit\",\n STANCE_CHANGE = \"stance_change\",\n TECHNIQUE_EXECUTE = \"technique_execute\",\n VITAL_POINT_HIT = \"vital_point_hit\",\n STATUS_EFFECT = \"status_effect\",\n PAUSE_TOGGLE = \"pause_toggle\",\n ERROR = \"error\",\n}\n\n/** Runtime state for an active match session */\nexport interface GameSession {\n readonly id: string;\n readonly gameMode: GameMode;\n readonly players: readonly [PlayerState, PlayerState];\n readonly currentRound: number;\n readonly maxRounds: number;\n readonly roundTimeLimit: number;\n readonly timeRemaining: number;\n readonly isPaused: boolean;\n readonly isGameOver: boolean;\n readonly winner: PlayerState | null;\n}\n\n/** Global game rules and balance parameters */\nexport interface GameConfig {\n readonly maxHealth: number;\n readonly maxKi: number;\n readonly maxStamina: number;\n readonly roundDuration: number;\n readonly maxRounds: number;\n readonly difficulty: \"beginner\" | \"intermediate\" | \"expert\" | \"master\";\n readonly enableVitalPoints: boolean;\n readonly enableStatusEffects: boolean;\n readonly allowArchetypeSwitching: boolean;\n}\n\n/** Persistent player data written to save storage */\nexport interface GameSaveData {\n readonly version: string;\n readonly playerId: string;\n readonly playerProgress: {\n readonly archetypeExperience: Record<string, number>;\n readonly unlockedTechniques: readonly string[];\n readonly achievements: readonly string[];\n };\n readonly settings: {\n readonly volume: number;\n readonly difficulty: string;\n readonly controls: Record<string, string>;\n };\n readonly statistics: {\n readonly totalMatches: number;\n readonly wins: number;\n readonly losses: number;\n readonly favoriteArchetype: string;\n };\n}\n"],"mappings":";;AAqCA,IAAY,gBAAL,yBAAA,eAAA;CACL,cAAA,gBAAA;CACA,cAAA,iBAAA;CACA,cAAA,eAAA;CACA,cAAA,eAAA;CACA,cAAA,mBAAA;CACA,cAAA,gBAAA;CACA,cAAA,mBAAA;CACA,cAAA,uBAAA;CACA,cAAA,qBAAA;CACA,cAAA,mBAAA;CACA,cAAA,kBAAA;CACA,cAAA,WAAA;;AACF,EAAA,CAAA,CAAA"}
@@ -1 +1 @@
1
- {"version":3,"file":"InjuryMovementModifier.js","names":[],"sources":["../../../src/systems/movement/InjuryMovementModifier.ts"],"sourcesContent":["/**\n * Injury-Based Movement Modifier System\n * \n * **Korean**: 손상 기반 이동 시스템 (Injury-Based Movement System)\n * \n * Dynamically calculates movement speed modifiers based on leg injuries, torso\n * damage, stance configuration, and pain levels. Part of the 12 combat realism\n * systems for authentic Korean martial arts gameplay.\n * \n * ## Movement Speed Calculation\n * \n * Base speed is modified by multiple factors:\n * - **Leg Injuries**: 0-100% penalty based on health\n * - **Torso Injuries**: 0-30% minor penalty\n * - **Both Legs Injured**: Additional 20% cumulative penalty\n * - **Stance Modifiers**: -20% (defensive) to +25% (offensive)\n * - **Pain Overload**: -15% when pain ≥ 80\n * \n * ## Injury Severity Thresholds\n * \n * | Leg Health | State | Speed Penalty |\n * |-----------|-------|---------------|\n * | 70-100% | Normal | 0% |\n * | 30-69% | Limping | 0-40% |\n * | 10-29% | Severe Limp | 40-80% |\n * | 0-9% | Critical | 80-100% |\n * \n * @module systems/movement/InjuryMovementModifier\n * @category Movement System\n * @korean 손상기반이동\n */\n\nimport type { TrigramStance } from \"@/types/common\";\nimport type { BodyPartHealth } from \"../bodypart/types\";\nimport { STANCE_SPEED_MODIFIERS } from \"../physics/MovementPhysics\";\n\n/**\n * Configuration for injury-based movement calculations.\n * \n * **Korean**: 손상 이동 설정\n * \n * @category Movement System\n */\nexport interface InjuryMovementConfig {\n /** Leg health thresholds for injury states */\n readonly legThresholds: {\n readonly normal: number; // 70% - no penalty\n readonly limping: number; // 30% - start of severe penalty\n readonly critical: number; // 10% - near-maximum penalty\n };\n /** Maximum torso injury penalty (0.3 = 30%) */\n readonly maxTorsoPenalty: number;\n /** Both legs injured cumulative penalty multiplier (0.8 = -20%) */\n readonly bothLegsInjuredMultiplier: number;\n /** Pain overload threshold (80) */\n readonly painOverloadThreshold: number;\n /** Pain overload penalty multiplier (0.85 = -15%) */\n readonly painOverloadMultiplier: number;\n /** Minimum speed multiplier (0.1 = 10% of base speed) */\n readonly minSpeedMultiplier: number;\n}\n\n/**\n * Deep partial type for InjuryMovementConfig to support partial overrides.\n * Allows callers to override individual threshold values without providing all.\n * \n * @category Movement System\n */\nexport type PartialInjuryMovementConfig = {\n readonly legThresholds?: Partial<InjuryMovementConfig[\"legThresholds\"]>;\n readonly maxTorsoPenalty?: number;\n readonly bothLegsInjuredMultiplier?: number;\n readonly painOverloadThreshold?: number;\n readonly painOverloadMultiplier?: number;\n readonly minSpeedMultiplier?: number;\n};\n\n/**\n * Default configuration matching acceptance criteria.\n */\nexport const DEFAULT_INJURY_MOVEMENT_CONFIG: InjuryMovementConfig = {\n legThresholds: {\n normal: 70,\n limping: 30,\n critical: 10,\n },\n maxTorsoPenalty: 0.3,\n bothLegsInjuredMultiplier: 0.8,\n painOverloadThreshold: 80,\n painOverloadMultiplier: 0.85,\n minSpeedMultiplier: 0.1,\n} as const;\n\n/**\n * Result of injury-based movement calculation.\n * \n * **Korean**: 손상 이동 결과\n * \n * @category Movement System\n */\nexport interface InjuryMovementResult {\n /** Final speed in units/second */\n readonly finalSpeed: number;\n /** Combined speed multiplier (0.1-1.0+) */\n readonly speedMultiplier: number;\n /** Breakdown of penalty sources */\n readonly penalties: {\n readonly leftLegPenalty: number;\n readonly rightLegPenalty: number;\n readonly torsoPenalty: number;\n readonly bothLegsInjured: boolean;\n readonly stanceModifier: number;\n readonly painOverload: boolean;\n };\n /** Whether player is limping */\n readonly isLimping: boolean;\n /** Whether player has severe limp */\n readonly isSevereLimp: boolean;\n /** Bilingual status text */\n readonly statusText: {\n readonly korean: string;\n readonly english: string;\n };\n}\n\n/**\n * Injury-Based Movement Modifier System.\n * \n * **Korean**: 손상 기반 이동 수정자 시스템\n * \n * Calculates dynamic movement speed based on injuries, stance, and pain.\n * Integrates with MovementPhysics to apply realistic combat trauma effects.\n * \n * @example\n * ```typescript\n * const modifier = new InjuryMovementModifier();\n * \n * const result = modifier.calculateMovementSpeed(\n * 5.0, // base speed\n * bodyPartHealth, // current health\n * TrigramStance.GEON, // current stance\n * 65 // pain level\n * );\n * \n * console.log(`Speed: ${result.finalSpeed} m/s`);\n * console.log(`Status: ${result.statusText.korean} | ${result.statusText.english}`);\n * ```\n * \n * @category Movement System\n * @korean 손상기반이동수정자\n */\nexport class InjuryMovementModifier {\n private readonly config: InjuryMovementConfig;\n\n /**\n * Threshold for determining if both legs are significantly injured.\n * Uses epsilon to avoid floating-point rounding issues at boundary.\n * \n * @private\n */\n private static readonly BOTH_LEGS_THRESHOLD = 0.3 + 1e-10;\n\n /**\n * Creates a new InjuryMovementModifier with optional configuration.\n * \n * @param config - Optional configuration overrides (supports partial threshold overrides)\n */\n constructor(config?: PartialInjuryMovementConfig) {\n // Deep merge leg thresholds to prevent partial overrides from breaking the config.\n // Use ?? to avoid undefined values overwriting defaults when exactOptionalPropertyTypes is disabled.\n const mergedLegThresholds = {\n normal:\n config?.legThresholds?.normal ??\n DEFAULT_INJURY_MOVEMENT_CONFIG.legThresholds.normal,\n limping:\n config?.legThresholds?.limping ??\n DEFAULT_INJURY_MOVEMENT_CONFIG.legThresholds.limping,\n critical:\n config?.legThresholds?.critical ??\n DEFAULT_INJURY_MOVEMENT_CONFIG.legThresholds.critical,\n };\n\n const mergedConfig: InjuryMovementConfig = {\n legThresholds: mergedLegThresholds,\n maxTorsoPenalty:\n config?.maxTorsoPenalty ??\n DEFAULT_INJURY_MOVEMENT_CONFIG.maxTorsoPenalty,\n bothLegsInjuredMultiplier:\n config?.bothLegsInjuredMultiplier ??\n DEFAULT_INJURY_MOVEMENT_CONFIG.bothLegsInjuredMultiplier,\n painOverloadThreshold:\n config?.painOverloadThreshold ??\n DEFAULT_INJURY_MOVEMENT_CONFIG.painOverloadThreshold,\n painOverloadMultiplier:\n config?.painOverloadMultiplier ??\n DEFAULT_INJURY_MOVEMENT_CONFIG.painOverloadMultiplier,\n minSpeedMultiplier:\n config?.minSpeedMultiplier ??\n DEFAULT_INJURY_MOVEMENT_CONFIG.minSpeedMultiplier,\n };\n\n // Development-mode sanity check: ensure thresholds are finite and ordered\n if (process.env.NODE_ENV !== \"production\") {\n const { normal, limping, critical } = mergedConfig.legThresholds;\n const allFinite =\n Number.isFinite(normal) &&\n Number.isFinite(limping) &&\n Number.isFinite(critical);\n const ordered = normal >= limping && limping >= critical;\n\n if (!allFinite || !ordered) {\n // Non-throwing warning to avoid breaking existing behavior\n console.warn(\n \"[InjuryMovementModifier] Invalid legThresholds configuration detected:\",\n mergedConfig.legThresholds\n );\n }\n }\n\n this.config = mergedConfig;\n }\n\n /**\n * Calculate modified movement speed based on all injury factors.\n * \n * **Korean**: 이동 속도 계산\n * \n * Applies penalties from:\n * 1. Leg injuries (primary factor)\n * 2. Torso injuries (minor factor)\n * 3. Both legs injured (cumulative penalty)\n * 4. Stance modifiers\n * 5. Pain overload\n * \n * @param baseSpeed - Base movement speed (m/s)\n * @param bodyPartHealth - Current body part health values\n * @param stance - Current trigram stance\n * @param painLevel - Current pain level (0-100)\n * @returns Complete movement calculation result\n * \n * @korean 이동속도계산\n */\n public calculateMovementSpeed(\n baseSpeed: number,\n bodyPartHealth: BodyPartHealth,\n stance: TrigramStance,\n painLevel: number\n ): InjuryMovementResult {\n // Calculate individual leg penalties\n const leftLegPenalty = this.calculateLegPenalty(bodyPartHealth.legLeft);\n const rightLegPenalty = this.calculateLegPenalty(bodyPartHealth.legRight);\n\n // Use worst leg penalty as base\n const baseLegPenalty = Math.max(leftLegPenalty, rightLegPenalty);\n let speedMultiplier = 1.0 - baseLegPenalty;\n\n // Check if both legs are significantly injured using centralized method\n const bothLegsInjured = this.areBothLegsInjured(leftLegPenalty, rightLegPenalty);\n if (bothLegsInjured) {\n // Additional 20% penalty when both legs injured\n speedMultiplier *= this.config.bothLegsInjuredMultiplier;\n }\n\n // Calculate torso penalty (minor effect, 0-30% max)\n const avgTorsoHealth = (bodyPartHealth.torsoUpper + bodyPartHealth.torsoLower) / 2;\n // Clamp torso health to prevent negative penalties or exceeding max penalty\n const clampedAvgTorsoHealth = Math.min(100, Math.max(0, avgTorsoHealth));\n const torsoPenalty = ((100 - clampedAvgTorsoHealth) / 100) * this.config.maxTorsoPenalty;\n speedMultiplier *= (1 - torsoPenalty);\n\n // Apply stance modifier\n const stanceModifier = this.getStanceSpeedModifier(stance);\n speedMultiplier *= stanceModifier;\n\n // Apply pain overload penalty if applicable (>= threshold, not just >)\n const painOverload = painLevel >= this.config.painOverloadThreshold;\n if (painOverload) {\n speedMultiplier *= this.config.painOverloadMultiplier; // -15%\n }\n\n // Clamp to minimum speed (10% of base)\n speedMultiplier = Math.max(speedMultiplier, this.config.minSpeedMultiplier);\n\n // Calculate final speed\n const finalSpeed = baseSpeed * speedMultiplier;\n\n // Determine injury state for status text based on worst leg health\n // This ensures status matches the actual movement penalty (which uses worst leg)\n // Note: isLimping = true when leg health is in range [30-70) (limping range with 0-40% penalty)\n // isSevereLimp = true when leg health is < 30 (severe/critical with 40-100% penalty)\n const worstLegHealth = Math.min(bodyPartHealth.legLeft, bodyPartHealth.legRight);\n const isLimping =\n worstLegHealth < this.config.legThresholds.normal &&\n worstLegHealth >= this.config.legThresholds.limping;\n const isSevereLimp = worstLegHealth < this.config.legThresholds.limping;\n\n // Generate status text\n const statusText = this.generateStatusText(worstLegHealth, bothLegsInjured, painOverload);\n\n return {\n finalSpeed,\n speedMultiplier,\n penalties: {\n leftLegPenalty,\n rightLegPenalty,\n torsoPenalty,\n bothLegsInjured,\n stanceModifier,\n painOverload,\n },\n isLimping,\n isSevereLimp,\n statusText,\n };\n }\n\n /**\n * Calculate movement penalty from leg injury severity.\n * \n * **Korean**: 다리 부상 페널티 계산\n * \n * Progressive penalty scaling:\n * - 100-70%: No penalty (0%)\n * - 70-30%: Linear scaling (0-40%)\n * - 30-10%: Accelerated scaling (40-80%)\n * - 10-0%: Critical scaling (80-100%)\n * \n * @param legHealth - Leg health (0-100)\n * @returns Penalty factor (0.0-1.0), clamped to valid range\n * \n * @private\n */\n private calculateLegPenalty(legHealth: number): number {\n const { normal, limping, critical } = this.config.legThresholds;\n\n // Clamp input health to valid range to prevent runaway penalties\n const clampedHealth = Math.min(Math.max(legHealth, 0), 100);\n\n let penalty: number;\n\n if (clampedHealth >= normal) {\n // 70-100%: No penalty\n penalty = 0;\n } else if (clampedHealth >= limping) {\n // 30-70%: 0-40% penalty (linear)\n const healthRange = normal - limping;\n // Guard against division by zero if thresholds are misconfigured\n const healthFactor = healthRange > 0 \n ? (normal - clampedHealth) / healthRange \n : 0;\n penalty = healthFactor * 0.4;\n } else if (clampedHealth >= critical) {\n // 10-30%: 40-80% penalty (accelerated)\n const healthRange = limping - critical;\n // Guard against division by zero if thresholds are misconfigured\n const healthFactor = healthRange > 0 \n ? (limping - clampedHealth) / healthRange \n : 0;\n penalty = 0.4 + (healthFactor * 0.4);\n } else {\n // 0-10%: 80-100% penalty (critical)\n // Guard against division by zero if critical is 0\n const healthFactor = critical > 0 \n ? (critical - clampedHealth) / critical \n : 1;\n penalty = 0.8 + (healthFactor * 0.2);\n }\n\n // Ensure returned penalty is always within [0, 1]\n return Math.min(Math.max(penalty, 0), 1);\n }\n\n /**\n * Checks if both legs are significantly injured (>30% penalty each).\n * Uses epsilon-based comparison for floating-point robustness.\n * \n * @param leftLegPenalty - Penalty for left leg (0-1)\n * @param rightLegPenalty - Penalty for right leg (0-1)\n * @returns True if both legs are significantly injured\n * @private\n */\n private areBothLegsInjured(leftLegPenalty: number, rightLegPenalty: number): boolean {\n return leftLegPenalty >= InjuryMovementModifier.BOTH_LEGS_THRESHOLD && \n rightLegPenalty >= InjuryMovementModifier.BOTH_LEGS_THRESHOLD;\n }\n\n /**\n * Get stance-based speed modifier.\n * \n * **Korean**: 자세 속도 배수 가져오기\n * \n * @param stance - Current trigram stance\n * @returns Speed multiplier (0.8-1.25), defaults to 1.0 for unknown stances\n * \n */\n public getStanceSpeedModifier(stance: TrigramStance): number {\n const modifier = STANCE_SPEED_MODIFIERS[stance];\n\n // Defensive fallback for unknown stances (e.g., test/invalid values)\n if (modifier == null) {\n // Runtime-safe environment check (works in both Node and browser/Vite environments)\n if (\n typeof process !== \"undefined\" &&\n process.env?.NODE_ENV === \"development\"\n ) {\n console.warn(\n `[InjuryMovementModifier] Unknown stance \"${String(\n stance\n )}\" received in getStanceSpeedModifier; defaulting to 1.0.`\n );\n }\n return 1.0;\n }\n\n return modifier;\n }\n\n /**\n * Generate bilingual status text based on injury state.\n * \n * **Korean**: 상태 텍스트 생성\n * \n * @param legHealthForStatus - Leg health percentage used for status determination (typically worst leg)\n * @param bothLegsInjured - Whether both legs are significantly injured\n * @param painOverload - Whether pain is over threshold\n * @returns Bilingual status text\n * \n * @private\n */\n private generateStatusText(\n legHealthForStatus: number,\n bothLegsInjured: boolean,\n painOverload: boolean\n ): { korean: string; english: string } {\n const { normal, limping, critical } = this.config.legThresholds;\n\n if (legHealthForStatus < critical) {\n const painText = painOverload ? \" | 고통 과부하\" : \"\";\n const painTextEn = painOverload ? \" | Pain Overload\" : \"\";\n const baseKorean = bothLegsInjured ? \"심각한 부상 | 양 다리\" : \"심각한 부상\";\n const baseEnglish = bothLegsInjured ? \"Critical Injury | Both Legs\" : \"Critical Injury\";\n return {\n korean: `${baseKorean}${painText}`,\n english: `${baseEnglish}${painTextEn}`,\n };\n } else if (legHealthForStatus < limping) {\n const painText = painOverload ? \" | 고통 과부하\" : \"\";\n const painTextEn = painOverload ? \" | Pain Overload\" : \"\";\n const baseKorean = bothLegsInjured ? \"중증 절름거림 | 양 다리\" : \"중증 절름거림\";\n const baseEnglish = bothLegsInjured ? \"Severe Limping | Both Legs\" : \"Severe Limping\";\n return {\n korean: `${baseKorean}${painText}`,\n english: `${baseEnglish}${painTextEn}`,\n };\n } else if (legHealthForStatus < normal) {\n const statusKrParts = [\"절름거림\"];\n const statusEnParts = [\"Limping\"];\n\n if (bothLegsInjured) {\n statusKrParts.push(\"양 다리\");\n statusEnParts.push(\"Both Legs\");\n }\n\n if (painOverload) {\n statusKrParts.push(\"고통 과부하\");\n statusEnParts.push(\"Pain Overload\");\n }\n\n return {\n korean: statusKrParts.join(\" | \"),\n english: statusEnParts.join(\" | \"),\n };\n } else if (painOverload) {\n return {\n korean: \"고통 과부하\",\n english: \"Pain Overload\",\n };\n } else {\n return {\n korean: \"정상\",\n english: \"Normal\",\n };\n }\n }\n\n /**\n * Check if player should display limping animation.\n * \n * **Korean**: 절름거림 확인\n * \n * Uses worst leg health to match movement penalty behavior.\n * \n * @param bodyPartHealth - Current body part health\n * @returns True if limping animation should play\n * \n */\n public shouldLimp(bodyPartHealth: BodyPartHealth): boolean {\n const worstLegHealth = Math.min(bodyPartHealth.legLeft, bodyPartHealth.legRight);\n return worstLegHealth < this.config.legThresholds.normal;\n }\n\n /**\n * Check if player has severe limp.\n * \n * **Korean**: 중증 절름거림 확인\n * \n * Uses worst leg health to match movement penalty behavior.\n * \n * @param bodyPartHealth - Current body part health\n * @returns True if severe limp (leg health < 30%)\n * \n */\n public hasSevereLimp(bodyPartHealth: BodyPartHealth): boolean {\n const worstLegHealth = Math.min(bodyPartHealth.legLeft, bodyPartHealth.legRight);\n return worstLegHealth < this.config.legThresholds.limping;\n }\n\n /**\n * Get current injury state description.\n * \n * **Korean**: 부상 상태 설명\n * \n * Uses worst leg health to match movement penalty behavior.\n * \n * @param bodyPartHealth - Current body part health\n * @returns Bilingual injury description\n * \n */\n public getInjuryDescription(bodyPartHealth: BodyPartHealth): {\n korean: string;\n english: string;\n } {\n const worstLegHealth = Math.min(bodyPartHealth.legLeft, bodyPartHealth.legRight);\n const leftLegPenalty = this.calculateLegPenalty(bodyPartHealth.legLeft);\n const rightLegPenalty = this.calculateLegPenalty(bodyPartHealth.legRight);\n const bothLegsInjured = this.areBothLegsInjured(leftLegPenalty, rightLegPenalty);\n\n return this.generateStatusText(worstLegHealth, bothLegsInjured, false);\n }\n}\n\n/**\n * Singleton instance for global access.\n * \n */\nexport const injuryMovementModifier = new InjuryMovementModifier();\n"],"mappings":";;;;;AAgFA,IAAa,iCAAuD;CAClE,eAAe;EACb,QAAQ;EACR,SAAS;EACT,UAAU;EACX;CACD,iBAAiB;CACjB,2BAA2B;CAC3B,uBAAuB;CACvB,wBAAwB;CACxB,oBAAoB;CACrB;;;;;;;;;;;;;;;;;;;;;;;;;;;AA4DD,IAAa,yBAAb,MAAa,uBAAuB;CAClC;;;;;;;CAQA,OAAwB,sBAAsB;;;;;;CAO9C,YAAY,QAAsC;EAehD,MAAM,eAAqC;GACzC,eAAe;IAZf,QACE,QAAQ,eAAe,UACvB,+BAA+B,cAAc;IAC/C,SACE,QAAQ,eAAe,WACvB,+BAA+B,cAAc;IAC/C,UACE,QAAQ,eAAe,YACvB,+BAA+B,cAAc;IAIhC;GACf,iBACE,QAAQ,mBACR,+BAA+B;GACjC,2BACE,QAAQ,6BACR,+BAA+B;GACjC,uBACE,QAAQ,yBACR,+BAA+B;GACjC,wBACE,QAAQ,0BACR,+BAA+B;GACjC,oBACE,QAAQ,sBACR,+BAA+B;GAClC;EAGD,IAAA,QAAA,IAAA,aAA6B,cAAc;GACzC,MAAM,EAAE,QAAQ,SAAS,aAAa,aAAa;GAOnD,IAAI,EALF,OAAO,SAAS,OAAO,IACvB,OAAO,SAAS,QAAQ,IACxB,OAAO,SAAS,SAAS,KAGT,EAFF,UAAU,WAAW,WAAW,WAI9C,QAAQ,KACN,0EACA,aAAa,cACd;;EAIL,KAAK,SAAS;;;;;;;;;;;;;;;;;;;;;;CAuBhB,uBACE,WACA,gBACA,QACA,WACsB;EAEtB,MAAM,iBAAiB,KAAK,oBAAoB,eAAe,QAAQ;EACvE,MAAM,kBAAkB,KAAK,oBAAoB,eAAe,SAAS;EAIzE,IAAI,kBAAkB,IADC,KAAK,IAAI,gBAAgB,gBACpB;EAG5B,MAAM,kBAAkB,KAAK,mBAAmB,gBAAgB,gBAAgB;EAChF,IAAI,iBAEF,mBAAmB,KAAK,OAAO;EAIjC,MAAM,kBAAkB,eAAe,aAAa,eAAe,cAAc;EAGjF,MAAM,gBAAiB,MADO,KAAK,IAAI,KAAK,KAAK,IAAI,GAAG,eAAe,CAC1C,IAAyB,MAAO,KAAK,OAAO;EACzE,mBAAoB,IAAI;EAGxB,MAAM,iBAAiB,KAAK,uBAAuB,OAAO;EAC1D,mBAAmB;EAGnB,MAAM,eAAe,aAAa,KAAK,OAAO;EAC9C,IAAI,cACF,mBAAmB,KAAK,OAAO;EAIjC,kBAAkB,KAAK,IAAI,iBAAiB,KAAK,OAAO,mBAAmB;EAG3E,MAAM,aAAa,YAAY;EAM/B,MAAM,iBAAiB,KAAK,IAAI,eAAe,SAAS,eAAe,SAAS;EAChF,MAAM,YACJ,iBAAiB,KAAK,OAAO,cAAc,UAC3C,kBAAkB,KAAK,OAAO,cAAc;EAC9C,MAAM,eAAe,iBAAiB,KAAK,OAAO,cAAc;EAGhE,MAAM,aAAa,KAAK,mBAAmB,gBAAgB,iBAAiB,aAAa;EAEzF,OAAO;GACL;GACA;GACA,WAAW;IACT;IACA;IACA;IACA;IACA;IACA;IACD;GACD;GACA;GACA;GACD;;;;;;;;;;;;;;;;;;CAmBH,oBAA4B,WAA2B;EACrD,MAAM,EAAE,QAAQ,SAAS,aAAa,KAAK,OAAO;EAGlD,MAAM,gBAAgB,KAAK,IAAI,KAAK,IAAI,WAAW,EAAE,EAAE,IAAI;EAE3D,IAAI;EAEJ,IAAI,iBAAiB,QAEnB,UAAU;OACL,IAAI,iBAAiB,SAAS;GAEnC,MAAM,cAAc,SAAS;GAK7B,WAHqB,cAAc,KAC9B,SAAS,iBAAiB,cAC3B,KACqB;SACpB,IAAI,iBAAiB,UAAU;GAEpC,MAAM,cAAc,UAAU;GAK9B,UAAU,MAHW,cAAc,KAC9B,UAAU,iBAAiB,cAC5B,KAC4B;SAOhC,UAAU,MAHW,WAAW,KAC3B,WAAW,iBAAiB,WAC7B,KAC4B;EAIlC,OAAO,KAAK,IAAI,KAAK,IAAI,SAAS,EAAE,EAAE,EAAE;;;;;;;;;;;CAY1C,mBAA2B,gBAAwB,iBAAkC;EACnF,OAAO,kBAAkB,uBAAuB,uBACzC,mBAAmB,uBAAuB;;;;;;;;;;;CAYnD,uBAA8B,QAA+B;EAC3D,MAAM,WAAW,uBAAuB;EAGxC,IAAI,YAAY,MAAM;GAEpB,IACE,OAAO,YAAY,eAAA,QAAA,IAAA,aACO,eAE1B,QAAQ,KACN,4CAA4C,OAC1C,OACD,CAAC,0DACH;GAEH,OAAO;;EAGT,OAAO;;;;;;;;;;;;;;CAeT,mBACE,oBACA,iBACA,cACqC;EACrC,MAAM,EAAE,QAAQ,SAAS,aAAa,KAAK,OAAO;EAElD,IAAI,qBAAqB,UAAU;GACjC,MAAM,WAAW,eAAe,cAAc;GAC9C,MAAM,aAAa,eAAe,qBAAqB;GACvD,MAAM,aAAa,kBAAkB,kBAAkB;GACvD,MAAM,cAAc,kBAAkB,gCAAgC;GACtE,OAAO;IACL,QAAQ,GAAG,aAAa;IACxB,SAAS,GAAG,cAAc;IAC3B;SACI,IAAI,qBAAqB,SAAS;GACvC,MAAM,WAAW,eAAe,cAAc;GAC9C,MAAM,aAAa,eAAe,qBAAqB;GACvD,MAAM,aAAa,kBAAkB,mBAAmB;GACxD,MAAM,cAAc,kBAAkB,+BAA+B;GACrE,OAAO;IACL,QAAQ,GAAG,aAAa;IACxB,SAAS,GAAG,cAAc;IAC3B;SACI,IAAI,qBAAqB,QAAQ;GACtC,MAAM,gBAAgB,CAAC,OAAO;GAC9B,MAAM,gBAAgB,CAAC,UAAU;GAEjC,IAAI,iBAAiB;IACnB,cAAc,KAAK,OAAO;IAC1B,cAAc,KAAK,YAAY;;GAGjC,IAAI,cAAc;IAChB,cAAc,KAAK,SAAS;IAC5B,cAAc,KAAK,gBAAgB;;GAGrC,OAAO;IACL,QAAQ,cAAc,KAAK,MAAM;IACjC,SAAS,cAAc,KAAK,MAAM;IACnC;SACI,IAAI,cACT,OAAO;GACL,QAAQ;GACR,SAAS;GACV;OAED,OAAO;GACL,QAAQ;GACR,SAAS;GACV;;;;;;;;;;;;;CAeL,WAAkB,gBAAyC;EAEzD,OADuB,KAAK,IAAI,eAAe,SAAS,eAAe,SAChE,GAAiB,KAAK,OAAO,cAAc;;;;;;;;;;;;;CAcpD,cAAqB,gBAAyC;EAE5D,OADuB,KAAK,IAAI,eAAe,SAAS,eAAe,SAChE,GAAiB,KAAK,OAAO,cAAc;;;;;;;;;;;;;CAcpD,qBAA4B,gBAG1B;EACA,MAAM,iBAAiB,KAAK,IAAI,eAAe,SAAS,eAAe,SAAS;EAChF,MAAM,iBAAiB,KAAK,oBAAoB,eAAe,QAAQ;EACvE,MAAM,kBAAkB,KAAK,oBAAoB,eAAe,SAAS;EACzE,MAAM,kBAAkB,KAAK,mBAAmB,gBAAgB,gBAAgB;EAEhF,OAAO,KAAK,mBAAmB,gBAAgB,iBAAiB,MAAM;;;;;;;AAQ1E,IAAa,yBAAyB,IAAI,wBAAwB"}
1
+ {"version":3,"file":"InjuryMovementModifier.js","names":[],"sources":["../../../src/systems/movement/InjuryMovementModifier.ts"],"sourcesContent":["/**\n * Injury-Based Movement Modifier System\n * \n * **Korean**: 손상 기반 이동 시스템 (Injury-Based Movement System)\n * \n * Dynamically calculates movement speed modifiers based on leg injuries, torso\n * damage, stance configuration, and pain levels. Part of the 12 combat realism\n * systems for authentic Korean martial arts gameplay.\n * \n * ## Movement Speed Calculation\n * \n * Base speed is modified by multiple factors:\n * - **Leg Injuries**: 0-100% penalty based on health\n * - **Torso Injuries**: 0-30% minor penalty\n * - **Both Legs Injured**: Additional 20% cumulative penalty\n * - **Stance Modifiers**: -20% (defensive) to +25% (offensive)\n * - **Pain Overload**: -15% when pain ≥ 80\n * \n * ## Injury Severity Thresholds\n * \n * | Leg Health | State | Speed Penalty |\n * |-----------|-------|---------------|\n * | 70-100% | Normal | 0% |\n * | 30-69% | Limping | 0-40% |\n * | 10-29% | Severe Limp | 40-80% |\n * | 0-9% | Critical | 80-100% |\n * \n * @module systems/movement/InjuryMovementModifier\n * @category Movement System\n * @korean 손상기반이동\n */\n\nimport type { TrigramStance } from \"@/types/common\";\nimport type { BodyPartHealth } from \"../bodypart/types\";\nimport { STANCE_SPEED_MODIFIERS } from \"../physics/MovementPhysics\";\n\n/**\n * Configuration for injury-based movement calculations.\n * \n * **Korean**: 손상 이동 설정\n * \n * @category Movement System\n */\nexport interface InjuryMovementConfig {\n /** Leg health thresholds for injury states */\n readonly legThresholds: {\n readonly normal: number; // 70% - no penalty\n readonly limping: number; // 30% - start of severe penalty\n readonly critical: number; // 10% - near-maximum penalty\n };\n /** Maximum torso injury penalty (0.3 = 30%) */\n readonly maxTorsoPenalty: number;\n /** Both legs injured cumulative penalty multiplier (0.8 = -20%) */\n readonly bothLegsInjuredMultiplier: number;\n /** Pain overload threshold (80) */\n readonly painOverloadThreshold: number;\n /** Pain overload penalty multiplier (0.85 = -15%) */\n readonly painOverloadMultiplier: number;\n /** Minimum speed multiplier (0.1 = 10% of base speed) */\n readonly minSpeedMultiplier: number;\n}\n\n/**\n * Deep partial type for InjuryMovementConfig to support partial overrides.\n * Allows callers to override individual threshold values without providing all.\n * \n * @category Movement System\n */\nexport type PartialInjuryMovementConfig = {\n readonly legThresholds?: Partial<InjuryMovementConfig[\"legThresholds\"]>;\n readonly maxTorsoPenalty?: number;\n readonly bothLegsInjuredMultiplier?: number;\n readonly painOverloadThreshold?: number;\n readonly painOverloadMultiplier?: number;\n readonly minSpeedMultiplier?: number;\n};\n\n/**\n * Default configuration matching acceptance criteria.\n */\nexport const DEFAULT_INJURY_MOVEMENT_CONFIG: InjuryMovementConfig = {\n legThresholds: {\n normal: 70,\n limping: 30,\n critical: 10,\n },\n maxTorsoPenalty: 0.3,\n bothLegsInjuredMultiplier: 0.8,\n painOverloadThreshold: 80,\n painOverloadMultiplier: 0.85,\n minSpeedMultiplier: 0.1,\n} as const;\n\n/**\n * Result of injury-based movement calculation.\n * \n * **Korean**: 손상 이동 결과\n * \n * @category Movement System\n */\nexport interface InjuryMovementResult {\n /** Final speed in units/second */\n readonly finalSpeed: number;\n /** Combined speed multiplier (0.1-1.0+) */\n readonly speedMultiplier: number;\n /** Breakdown of penalty sources */\n readonly penalties: {\n readonly leftLegPenalty: number;\n readonly rightLegPenalty: number;\n readonly torsoPenalty: number;\n readonly bothLegsInjured: boolean;\n readonly stanceModifier: number;\n readonly painOverload: boolean;\n };\n /** Whether player is limping */\n readonly isLimping: boolean;\n /** Whether player has severe limp */\n readonly isSevereLimp: boolean;\n /** Bilingual status text */\n readonly statusText: {\n readonly korean: string;\n readonly english: string;\n };\n}\n\n/**\n * Injury-Based Movement Modifier System.\n * \n * **Korean**: 손상 기반 이동 수정자 시스템\n * \n * Calculates dynamic movement speed based on injuries, stance, and pain.\n * Integrates with MovementPhysics to apply realistic combat trauma effects.\n * \n * @example\n * ```typescript\n * const modifier = new InjuryMovementModifier();\n * \n * const result = modifier.calculateMovementSpeed(\n * 5.0, // base speed\n * bodyPartHealth, // current health\n * TrigramStance.GEON, // current stance\n * 65 // pain level\n * );\n * \n * console.log(`Speed: ${result.finalSpeed} m/s`);\n * console.log(`Status: ${result.statusText.korean} | ${result.statusText.english}`);\n * ```\n * \n * @category Movement System\n * @korean 손상기반이동수정자\n */\nexport class InjuryMovementModifier {\n private readonly config: InjuryMovementConfig;\n\n /**\n * Threshold for determining if both legs are significantly injured.\n * Uses epsilon to avoid floating-point rounding issues at boundary.\n * \n * @private\n */\n private static readonly BOTH_LEGS_THRESHOLD = 0.3 + 1e-10;\n\n /**\n * Creates a new InjuryMovementModifier with optional configuration.\n * \n * @param config - Optional configuration overrides (supports partial threshold overrides)\n */\n constructor(config?: PartialInjuryMovementConfig) {\n // Deep merge leg thresholds to prevent partial overrides from breaking the config.\n // Use ?? to avoid undefined values overwriting defaults when exactOptionalPropertyTypes is disabled.\n const mergedLegThresholds = {\n normal:\n config?.legThresholds?.normal ??\n DEFAULT_INJURY_MOVEMENT_CONFIG.legThresholds.normal,\n limping:\n config?.legThresholds?.limping ??\n DEFAULT_INJURY_MOVEMENT_CONFIG.legThresholds.limping,\n critical:\n config?.legThresholds?.critical ??\n DEFAULT_INJURY_MOVEMENT_CONFIG.legThresholds.critical,\n };\n\n const mergedConfig: InjuryMovementConfig = {\n legThresholds: mergedLegThresholds,\n maxTorsoPenalty:\n config?.maxTorsoPenalty ??\n DEFAULT_INJURY_MOVEMENT_CONFIG.maxTorsoPenalty,\n bothLegsInjuredMultiplier:\n config?.bothLegsInjuredMultiplier ??\n DEFAULT_INJURY_MOVEMENT_CONFIG.bothLegsInjuredMultiplier,\n painOverloadThreshold:\n config?.painOverloadThreshold ??\n DEFAULT_INJURY_MOVEMENT_CONFIG.painOverloadThreshold,\n painOverloadMultiplier:\n config?.painOverloadMultiplier ??\n DEFAULT_INJURY_MOVEMENT_CONFIG.painOverloadMultiplier,\n minSpeedMultiplier:\n config?.minSpeedMultiplier ??\n DEFAULT_INJURY_MOVEMENT_CONFIG.minSpeedMultiplier,\n };\n\n // Development-mode sanity check: ensure thresholds are finite and ordered\n if (process.env.NODE_ENV !== \"production\") {\n const { normal, limping, critical } = mergedConfig.legThresholds;\n const allFinite =\n Number.isFinite(normal) &&\n Number.isFinite(limping) &&\n Number.isFinite(critical);\n const ordered = normal >= limping && limping >= critical;\n\n if (!allFinite || !ordered) {\n // Non-throwing warning to avoid breaking existing behavior\n console.warn(\n \"[InjuryMovementModifier] Invalid legThresholds configuration detected:\",\n mergedConfig.legThresholds\n );\n }\n }\n\n this.config = mergedConfig;\n }\n\n /**\n * Calculate modified movement speed based on all injury factors.\n * \n * **Korean**: 이동 속도 계산\n * \n * Applies penalties from:\n * 1. Leg injuries (primary factor)\n * 2. Torso injuries (minor factor)\n * 3. Both legs injured (cumulative penalty)\n * 4. Stance modifiers\n * 5. Pain overload\n * \n * @param baseSpeed - Base movement speed (m/s)\n * @param bodyPartHealth - Current body part health values\n * @param stance - Current trigram stance\n * @param painLevel - Current pain level (0-100)\n * @returns Complete movement calculation result\n * \n * @korean 이동속도계산\n */\n public calculateMovementSpeed(\n baseSpeed: number,\n bodyPartHealth: BodyPartHealth,\n stance: TrigramStance,\n painLevel: number\n ): InjuryMovementResult {\n // Calculate individual leg penalties\n const leftLegPenalty = this.calculateLegPenalty(bodyPartHealth.legLeft);\n const rightLegPenalty = this.calculateLegPenalty(bodyPartHealth.legRight);\n\n // Use worst leg penalty as base\n const baseLegPenalty = Math.max(leftLegPenalty, rightLegPenalty);\n let speedMultiplier = 1.0 - baseLegPenalty;\n\n // Check if both legs are significantly injured using centralized method\n const bothLegsInjured = this.areBothLegsInjured(leftLegPenalty, rightLegPenalty);\n if (bothLegsInjured) {\n // Additional 20% penalty when both legs injured\n speedMultiplier *= this.config.bothLegsInjuredMultiplier;\n }\n\n // Calculate torso penalty (minor effect, 0-30% max)\n const avgTorsoHealth = (bodyPartHealth.torsoUpper + bodyPartHealth.torsoLower) / 2;\n // Clamp torso health to prevent negative penalties or exceeding max penalty\n const clampedAvgTorsoHealth = Math.min(100, Math.max(0, avgTorsoHealth));\n const torsoPenalty = ((100 - clampedAvgTorsoHealth) / 100) * this.config.maxTorsoPenalty;\n speedMultiplier *= (1 - torsoPenalty);\n\n // Apply stance modifier\n const stanceModifier = this.getStanceSpeedModifier(stance);\n speedMultiplier *= stanceModifier;\n\n // Apply pain overload penalty if applicable (>= threshold, not just >)\n const painOverload = painLevel >= this.config.painOverloadThreshold;\n if (painOverload) {\n speedMultiplier *= this.config.painOverloadMultiplier; // -15%\n }\n\n // Clamp to minimum speed (10% of base)\n speedMultiplier = Math.max(speedMultiplier, this.config.minSpeedMultiplier);\n\n // Calculate final speed\n const finalSpeed = baseSpeed * speedMultiplier;\n\n // Determine injury state for status text based on worst leg health\n // This ensures status matches the actual movement penalty (which uses worst leg)\n // Note: isLimping = true when leg health is in range [30-70) (limping range with 0-40% penalty)\n // isSevereLimp = true when leg health is < 30 (severe/critical with 40-100% penalty)\n const worstLegHealth = Math.min(bodyPartHealth.legLeft, bodyPartHealth.legRight);\n const isLimping =\n worstLegHealth < this.config.legThresholds.normal &&\n worstLegHealth >= this.config.legThresholds.limping;\n const isSevereLimp = worstLegHealth < this.config.legThresholds.limping;\n\n // Generate status text\n const statusText = this.generateStatusText(worstLegHealth, bothLegsInjured, painOverload);\n\n return {\n finalSpeed,\n speedMultiplier,\n penalties: {\n leftLegPenalty,\n rightLegPenalty,\n torsoPenalty,\n bothLegsInjured,\n stanceModifier,\n painOverload,\n },\n isLimping,\n isSevereLimp,\n statusText,\n };\n }\n\n /**\n * Calculate movement penalty from leg injury severity.\n * \n * **Korean**: 다리 부상 페널티 계산\n * \n * Progressive penalty scaling:\n * - 100-70%: No penalty (0%)\n * - 70-30%: Linear scaling (0-40%)\n * - 30-10%: Accelerated scaling (40-80%)\n * - 10-0%: Critical scaling (80-100%)\n * \n * @param legHealth - Leg health (0-100)\n * @returns Penalty factor (0.0-1.0), clamped to valid range\n * \n * @private\n */\n private calculateLegPenalty(legHealth: number): number {\n const { normal, limping, critical } = this.config.legThresholds;\n\n // Clamp input health to valid range to prevent runaway penalties\n const clampedHealth = Math.min(Math.max(legHealth, 0), 100);\n\n let penalty: number;\n\n if (clampedHealth >= normal) {\n // 70-100%: No penalty\n penalty = 0;\n } else if (clampedHealth >= limping) {\n // 30-70%: 0-40% penalty (linear)\n const healthRange = normal - limping;\n // Guard against division by zero if thresholds are misconfigured\n const healthFactor = healthRange > 0 \n ? (normal - clampedHealth) / healthRange \n : 0;\n penalty = healthFactor * 0.4;\n } else if (clampedHealth >= critical) {\n // 10-30%: 40-80% penalty (accelerated)\n const healthRange = limping - critical;\n // Guard against division by zero if thresholds are misconfigured\n const healthFactor = healthRange > 0 \n ? (limping - clampedHealth) / healthRange \n : 0;\n penalty = 0.4 + (healthFactor * 0.4);\n } else {\n // 0-10%: 80-100% penalty (critical)\n // Guard against division by zero if critical is 0\n const healthFactor = critical > 0 \n ? (critical - clampedHealth) / critical \n : 1;\n penalty = 0.8 + (healthFactor * 0.2);\n }\n\n // Ensure returned penalty is always within [0, 1]\n return Math.min(Math.max(penalty, 0), 1);\n }\n\n /**\n * Checks if both legs are significantly injured (>30% penalty each).\n * Uses epsilon-based comparison for floating-point robustness.\n * \n * @param leftLegPenalty - Penalty for left leg (0-1)\n * @param rightLegPenalty - Penalty for right leg (0-1)\n * @returns True if both legs are significantly injured\n * @private\n */\n private areBothLegsInjured(leftLegPenalty: number, rightLegPenalty: number): boolean {\n return leftLegPenalty >= InjuryMovementModifier.BOTH_LEGS_THRESHOLD && \n rightLegPenalty >= InjuryMovementModifier.BOTH_LEGS_THRESHOLD;\n }\n\n /**\n * Get stance-based speed modifier.\n * \n * **Korean**: 자세 속도 배수 가져오기\n * \n * @param stance - Current trigram stance\n * @returns Speed multiplier (0.8-1.25), defaults to 1.0 for unknown stances\n * \n */\n public getStanceSpeedModifier(stance: TrigramStance): number {\n const modifier = STANCE_SPEED_MODIFIERS[stance];\n\n // Defensive fallback for unknown stances (e.g., test/invalid values)\n if (modifier == null) {\n // Runtime-safe environment check (works in both Node and browser/Vite environments)\n if (\n typeof process !== \"undefined\" &&\n process.env?.NODE_ENV === \"development\"\n ) {\n console.warn(\n `[InjuryMovementModifier] Unknown stance \"${String(\n stance\n )}\" received in getStanceSpeedModifier; defaulting to 1.0.`\n );\n }\n return 1.0;\n }\n\n return modifier;\n }\n\n /**\n * Generate bilingual status text based on injury state.\n * \n * **Korean**: 상태 텍스트 생성\n * \n * @param legHealthForStatus - Leg health percentage used for status determination (typically worst leg)\n * @param bothLegsInjured - Whether both legs are significantly injured\n * @param painOverload - Whether pain is over threshold\n * @returns Bilingual status text\n * \n * @private\n */\n private generateStatusText(\n legHealthForStatus: number,\n bothLegsInjured: boolean,\n painOverload: boolean\n ): { korean: string; english: string } {\n const { normal, limping, critical } = this.config.legThresholds;\n\n if (legHealthForStatus < critical) {\n const painText = painOverload ? \" | 고통 과부하\" : \"\";\n const painTextEn = painOverload ? \" | Pain Overload\" : \"\";\n const baseKorean = bothLegsInjured ? \"심각한 부상 | 양 다리\" : \"심각한 부상\";\n const baseEnglish = bothLegsInjured ? \"Critical Injury | Both Legs\" : \"Critical Injury\";\n return {\n korean: `${baseKorean}${painText}`,\n english: `${baseEnglish}${painTextEn}`,\n };\n } else if (legHealthForStatus < limping) {\n const painText = painOverload ? \" | 고통 과부하\" : \"\";\n const painTextEn = painOverload ? \" | Pain Overload\" : \"\";\n const baseKorean = bothLegsInjured ? \"중증 절름거림 | 양 다리\" : \"중증 절름거림\";\n const baseEnglish = bothLegsInjured ? \"Severe Limping | Both Legs\" : \"Severe Limping\";\n return {\n korean: `${baseKorean}${painText}`,\n english: `${baseEnglish}${painTextEn}`,\n };\n } else if (legHealthForStatus < normal) {\n const statusKrParts = [\"절름거림\"];\n const statusEnParts = [\"Limping\"];\n\n if (bothLegsInjured) {\n statusKrParts.push(\"양 다리\");\n statusEnParts.push(\"Both Legs\");\n }\n\n if (painOverload) {\n statusKrParts.push(\"고통 과부하\");\n statusEnParts.push(\"Pain Overload\");\n }\n\n return {\n korean: statusKrParts.join(\" | \"),\n english: statusEnParts.join(\" | \"),\n };\n } else if (painOverload) {\n return {\n korean: \"고통 과부하\",\n english: \"Pain Overload\",\n };\n } else {\n return {\n korean: \"정상\",\n english: \"Normal\",\n };\n }\n }\n\n /**\n * Check if player should display limping animation.\n * \n * **Korean**: 절름거림 확인\n * \n * Uses worst leg health to match movement penalty behavior.\n * \n * @param bodyPartHealth - Current body part health\n * @returns True if limping animation should play\n * \n */\n public shouldLimp(bodyPartHealth: BodyPartHealth): boolean {\n const worstLegHealth = Math.min(bodyPartHealth.legLeft, bodyPartHealth.legRight);\n return worstLegHealth < this.config.legThresholds.normal;\n }\n\n /**\n * Check if player has severe limp.\n * \n * **Korean**: 중증 절름거림 확인\n * \n * Uses worst leg health to match movement penalty behavior.\n * \n * @param bodyPartHealth - Current body part health\n * @returns True if severe limp (leg health < 30%)\n * \n */\n public hasSevereLimp(bodyPartHealth: BodyPartHealth): boolean {\n const worstLegHealth = Math.min(bodyPartHealth.legLeft, bodyPartHealth.legRight);\n return worstLegHealth < this.config.legThresholds.limping;\n }\n\n /**\n * Get current injury state description.\n * \n * **Korean**: 부상 상태 설명\n * \n * Uses worst leg health to match movement penalty behavior.\n * \n * @param bodyPartHealth - Current body part health\n * @returns Bilingual injury description\n * \n */\n public getInjuryDescription(bodyPartHealth: BodyPartHealth): {\n korean: string;\n english: string;\n } {\n const worstLegHealth = Math.min(bodyPartHealth.legLeft, bodyPartHealth.legRight);\n const leftLegPenalty = this.calculateLegPenalty(bodyPartHealth.legLeft);\n const rightLegPenalty = this.calculateLegPenalty(bodyPartHealth.legRight);\n const bothLegsInjured = this.areBothLegsInjured(leftLegPenalty, rightLegPenalty);\n\n return this.generateStatusText(worstLegHealth, bothLegsInjured, false);\n }\n}\n\n/**\n * Singleton instance for global access.\n * \n */\nexport const injuryMovementModifier = new InjuryMovementModifier();\n"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"AccelerationUpdater.js","names":[],"sources":["../../../../src/systems/movement/helpers/AccelerationUpdater.tsx"],"sourcesContent":["/**\n * AccelerationUpdater - Component that updates movement acceleration at 60fps\n *\n * Uses useFrame to track continuous movement time and calculate acceleration-based speed.\n * Throttles state updates to only call onSpeedUpdate when speed changes meaningfully.\n * This component only updates movement state and renders no visual elements.\n *\n * @module systems/movement/helpers/AccelerationUpdater\n * @category Movement\n * @korean 가속업데이터\n */\n\nimport { useFrame } from \"@react-three/fiber\";\nimport React, { useRef } from \"react\";\nimport {\n ACCELERATION_CONSTANTS,\n calculateAcceleratedSpeed,\n isDirectionConsistent,\n isSpeedChangeMeaningful,\n} from \"./accelerationUtils\";\n\n/**\n * Props for AccelerationUpdater component\n */\nexport interface AccelerationUpdaterProps {\n /** Whether player is currently moving */\n readonly isMoving: boolean;\n /** Current velocity vector */\n readonly velocity: { x: number; y: number } | undefined;\n /** Ref to track accumulated movement time */\n readonly movementTimeRef: React.MutableRefObject<number>;\n /** Ref to track last movement direction */\n readonly lastDirectionRef: React.MutableRefObject<{ x: number; y: number }>;\n /** Callback to update calculated speed - only called on meaningful changes */\n readonly onSpeedUpdate: (speed: number) => void;\n /** Walking speed in m/s (from archetype or default) */\n readonly walkSpeed?: number;\n /** Running speed in m/s (from archetype or default) */\n readonly runSpeed?: number;\n}\n\n/**\n * AccelerationUpdater Component\n *\n * Updates movement acceleration at 60fps using Three.js useFrame hook.\n * Tracks continuous movement time and calculates speed based on direction consistency.\n * Only calls onSpeedUpdate when speed changes by more than epsilon AND sufficient time\n * has passed (100ms throttle), preventing excessive React re-renders at frame rate.\n *\n * @example\n * ```tsx\n * <AccelerationUpdater\n * isMoving={isMoving}\n * velocity={velocity}\n * movementTimeRef={movementTimeRef}\n * lastDirectionRef={lastDirectionRef}\n * onSpeedUpdate={setAccelerationBasedSpeed}\n * walkSpeed={physicalAttributes.walkSpeed}\n * runSpeed={physicalAttributes.runSpeed}\n * />\n * ```\n */\nexport const AccelerationUpdater: React.FC<AccelerationUpdaterProps> = ({\n isMoving,\n velocity,\n movementTimeRef,\n lastDirectionRef,\n onSpeedUpdate,\n walkSpeed = ACCELERATION_CONSTANTS.DEFAULT_WALK_SPEED,\n runSpeed = ACCELERATION_CONSTANTS.DEFAULT_RUN_SPEED,\n}) => {\n // Track last reported speed and time to throttle updates\n // Initialize with walk speed (archetype-specific or default)\n const lastReportedSpeedRef = useRef<number>(walkSpeed);\n const lastUpdateTimeRef = useRef<number>(0);\n // Throttle interval: update at most every ~100ms (10Hz) instead of 60fps\n const UPDATE_THROTTLE_MS = 100;\n\n useFrame((_state, delta) => {\n // If not moving, reset timers and direction\n if (!isMoving || !velocity || (velocity.x === 0 && velocity.y === 0)) {\n movementTimeRef.current = 0;\n lastDirectionRef.current = { x: 0, y: 0 };\n \n // Only update if changed meaningfully\n if (isSpeedChangeMeaningful(lastReportedSpeedRef.current, walkSpeed)) {\n lastReportedSpeedRef.current = walkSpeed;\n onSpeedUpdate(walkSpeed);\n lastUpdateTimeRef.current = performance.now();\n }\n return;\n }\n\n // Check direction consistency (within 45 degrees)\n const currentDir = { x: velocity.x, y: velocity.y };\n const isSameDirection = isDirectionConsistent(currentDir, lastDirectionRef.current);\n\n // Reset accumulated movement time if direction changed too much\n if (!isSameDirection) {\n movementTimeRef.current = 0;\n } else {\n // Accumulate movement time while moving in a consistent direction\n movementTimeRef.current += delta;\n }\n\n // Update last direction for the next frame\n lastDirectionRef.current = currentDir;\n\n // Calculate new speed with archetype-specific walk/run speeds\n const newSpeed = calculateAcceleratedSpeed(movementTimeRef.current, walkSpeed, runSpeed);\n\n // Throttle updates by both time and epsilon\n // Only call onSpeedUpdate if enough time has passed AND speed changed meaningfully\n const now = performance.now();\n const timeSinceLastUpdate = now - lastUpdateTimeRef.current;\n \n if (\n timeSinceLastUpdate >= UPDATE_THROTTLE_MS &&\n isSpeedChangeMeaningful(lastReportedSpeedRef.current, newSpeed)\n ) {\n lastReportedSpeedRef.current = newSpeed;\n onSpeedUpdate(newSpeed);\n lastUpdateTimeRef.current = now;\n }\n });\n\n return null; // Component only updates movement state, renders no visual elements\n};\n\nexport default AccelerationUpdater;\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA8DA,IAAa,uBAA2D,EACtE,UACA,UACA,iBACA,kBACA,eACA,YAAY,uBAAuB,oBACnC,WAAW,uBAAuB,wBAC9B;CAGJ,MAAM,uBAAuB,OAAe,UAAU;CACtD,MAAM,oBAAoB,OAAe,EAAE;CAE3C,MAAM,qBAAqB;CAE3B,UAAU,QAAQ,UAAU;EAE1B,IAAI,CAAC,YAAY,CAAC,YAAa,SAAS,MAAM,KAAK,SAAS,MAAM,GAAI;GACpE,gBAAgB,UAAU;GAC1B,iBAAiB,UAAU;IAAE,GAAG;IAAG,GAAG;IAAG;GAGzC,IAAI,wBAAwB,qBAAqB,SAAS,UAAU,EAAE;IACpE,qBAAqB,UAAU;IAC/B,cAAc,UAAU;IACxB,kBAAkB,UAAU,YAAY,KAAK;;GAE/C;;EAIF,MAAM,aAAa;GAAE,GAAG,SAAS;GAAG,GAAG,SAAS;GAAG;EAInD,IAAI,CAHoB,sBAAsB,YAAY,iBAAiB,QAGtE,EACH,gBAAgB,UAAU;OAG1B,gBAAgB,WAAW;EAI7B,iBAAiB,UAAU;EAG3B,MAAM,WAAW,0BAA0B,gBAAgB,SAAS,WAAW,SAAS;EAIxF,MAAM,MAAM,YAAY,KAAK;EAG7B,IAF4B,MAAM,kBAAkB,WAG3B,sBACvB,wBAAwB,qBAAqB,SAAS,SAAS,EAC/D;GACA,qBAAqB,UAAU;GAC/B,cAAc,SAAS;GACvB,kBAAkB,UAAU;;GAE9B;CAEF,OAAO"}
1
+ {"version":3,"file":"AccelerationUpdater.js","names":[],"sources":["../../../../src/systems/movement/helpers/AccelerationUpdater.tsx"],"sourcesContent":["/**\n * AccelerationUpdater - Component that updates movement acceleration at 60fps\n *\n * Uses useFrame to track continuous movement time and calculate acceleration-based speed.\n * Throttles state updates to only call onSpeedUpdate when speed changes meaningfully.\n * This component only updates movement state and renders no visual elements.\n *\n * @module systems/movement/helpers/AccelerationUpdater\n * @category Movement\n * @korean 가속업데이터\n */\n\nimport { useFrame } from \"@react-three/fiber\";\nimport React, { useRef } from \"react\";\nimport {\n ACCELERATION_CONSTANTS,\n calculateAcceleratedSpeed,\n isDirectionConsistent,\n isSpeedChangeMeaningful,\n} from \"./accelerationUtils\";\n\n/**\n * Props for AccelerationUpdater component\n */\nexport interface AccelerationUpdaterProps {\n /** Whether player is currently moving */\n readonly isMoving: boolean;\n /** Current velocity vector */\n readonly velocity: { x: number; y: number } | undefined;\n /** Ref to track accumulated movement time */\n readonly movementTimeRef: React.MutableRefObject<number>;\n /** Ref to track last movement direction */\n readonly lastDirectionRef: React.MutableRefObject<{ x: number; y: number }>;\n /** Callback to update calculated speed - only called on meaningful changes */\n readonly onSpeedUpdate: (speed: number) => void;\n /** Walking speed in m/s (from archetype or default) */\n readonly walkSpeed?: number;\n /** Running speed in m/s (from archetype or default) */\n readonly runSpeed?: number;\n}\n\n/**\n * AccelerationUpdater Component\n *\n * Updates movement acceleration at 60fps using Three.js useFrame hook.\n * Tracks continuous movement time and calculates speed based on direction consistency.\n * Only calls onSpeedUpdate when speed changes by more than epsilon AND sufficient time\n * has passed (100ms throttle), preventing excessive React re-renders at frame rate.\n *\n * @example\n * ```tsx\n * <AccelerationUpdater\n * isMoving={isMoving}\n * velocity={velocity}\n * movementTimeRef={movementTimeRef}\n * lastDirectionRef={lastDirectionRef}\n * onSpeedUpdate={setAccelerationBasedSpeed}\n * walkSpeed={physicalAttributes.walkSpeed}\n * runSpeed={physicalAttributes.runSpeed}\n * />\n * ```\n */\nexport const AccelerationUpdater: React.FC<AccelerationUpdaterProps> = ({\n isMoving,\n velocity,\n movementTimeRef,\n lastDirectionRef,\n onSpeedUpdate,\n walkSpeed = ACCELERATION_CONSTANTS.DEFAULT_WALK_SPEED,\n runSpeed = ACCELERATION_CONSTANTS.DEFAULT_RUN_SPEED,\n}) => {\n // Track last reported speed and time to throttle updates\n // Initialize with walk speed (archetype-specific or default)\n const lastReportedSpeedRef = useRef<number>(walkSpeed);\n const lastUpdateTimeRef = useRef<number>(0);\n // Throttle interval: update at most every ~100ms (10Hz) instead of 60fps\n const UPDATE_THROTTLE_MS = 100;\n\n useFrame((_state, delta) => {\n // If not moving, reset timers and direction\n if (!isMoving || !velocity || (velocity.x === 0 && velocity.y === 0)) {\n movementTimeRef.current = 0;\n lastDirectionRef.current = { x: 0, y: 0 };\n \n // Only update if changed meaningfully\n if (isSpeedChangeMeaningful(lastReportedSpeedRef.current, walkSpeed)) {\n lastReportedSpeedRef.current = walkSpeed;\n onSpeedUpdate(walkSpeed);\n lastUpdateTimeRef.current = performance.now();\n }\n return;\n }\n\n // Check direction consistency (within 45 degrees)\n const currentDir = { x: velocity.x, y: velocity.y };\n const isSameDirection = isDirectionConsistent(currentDir, lastDirectionRef.current);\n\n // Reset accumulated movement time if direction changed too much\n if (!isSameDirection) {\n movementTimeRef.current = 0;\n } else {\n // Accumulate movement time while moving in a consistent direction\n movementTimeRef.current += delta;\n }\n\n // Update last direction for the next frame\n lastDirectionRef.current = currentDir;\n\n // Calculate new speed with archetype-specific walk/run speeds\n const newSpeed = calculateAcceleratedSpeed(movementTimeRef.current, walkSpeed, runSpeed);\n\n // Throttle updates by both time and epsilon\n // Only call onSpeedUpdate if enough time has passed AND speed changed meaningfully\n const now = performance.now();\n const timeSinceLastUpdate = now - lastUpdateTimeRef.current;\n \n if (\n timeSinceLastUpdate >= UPDATE_THROTTLE_MS &&\n isSpeedChangeMeaningful(lastReportedSpeedRef.current, newSpeed)\n ) {\n lastReportedSpeedRef.current = newSpeed;\n onSpeedUpdate(newSpeed);\n lastUpdateTimeRef.current = now;\n }\n });\n\n return null; // Component only updates movement state, renders no visual elements\n};\n\nexport default AccelerationUpdater;\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA8DA,IAAa,uBAA2D,EACtE,UACA,UACA,iBACA,kBACA,eACA,YAAY,uBAAuB,oBACnC,WAAW,uBAAuB,wBAC9B;CAGJ,MAAM,uBAAuB,OAAe,SAAS;CACrD,MAAM,oBAAoB,OAAe,CAAC;CAE1C,MAAM,qBAAqB;CAE3B,UAAU,QAAQ,UAAU;EAE1B,IAAI,CAAC,YAAY,CAAC,YAAa,SAAS,MAAM,KAAK,SAAS,MAAM,GAAI;GACpE,gBAAgB,UAAU;GAC1B,iBAAiB,UAAU;IAAE,GAAG;IAAG,GAAG;GAAE;GAGxC,IAAI,wBAAwB,qBAAqB,SAAS,SAAS,GAAG;IACpE,qBAAqB,UAAU;IAC/B,cAAc,SAAS;IACvB,kBAAkB,UAAU,YAAY,IAAI;GAC9C;GACA;EACF;EAGA,MAAM,aAAa;GAAE,GAAG,SAAS;GAAG,GAAG,SAAS;EAAE;EAIlD,IAAI,CAHoB,sBAAsB,YAAY,iBAAiB,OAGtE,GACH,gBAAgB,UAAU;OAG1B,gBAAgB,WAAW;EAI7B,iBAAiB,UAAU;EAG3B,MAAM,WAAW,0BAA0B,gBAAgB,SAAS,WAAW,QAAQ;EAIvF,MAAM,MAAM,YAAY,IAAI;EAG5B,IAF4B,MAAM,kBAAkB,WAG3B,sBACvB,wBAAwB,qBAAqB,SAAS,QAAQ,GAC9D;GACA,qBAAqB,UAAU;GAC/B,cAAc,QAAQ;GACtB,kBAAkB,UAAU;EAC9B;CACF,CAAC;CAED,OAAO;AACT"}