blacktrigram 0.7.39 → 0.7.40

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (354) hide show
  1. package/lib/App2.js.map +1 -1
  2. package/lib/audio/AudioAssetLoader.js.map +1 -1
  3. package/lib/audio/AudioAssetRegistry.js.map +1 -1
  4. package/lib/audio/AudioCache.js.map +1 -1
  5. package/lib/audio/AudioManager.js.map +1 -1
  6. package/lib/audio/AudioMonitor.js.map +1 -1
  7. package/lib/audio/AudioPool.js.map +1 -1
  8. package/lib/audio/AudioProvider.js.map +1 -1
  9. package/lib/audio/AudioUtils.js.map +1 -1
  10. package/lib/audio/BoneImpactAudioMap.js.map +1 -1
  11. package/lib/audio/VariantSelector.js.map +1 -1
  12. package/lib/audio/types.js.map +1 -1
  13. package/lib/components/screens/combat/CombatScreen3D.js.map +1 -1
  14. package/lib/components/screens/combat/components/controls/CombatButtons.js.map +1 -1
  15. package/lib/components/screens/combat/components/controls/CombatControlsPanel.js.map +1 -1
  16. package/lib/components/screens/combat/components/controls/ControlsGuide.js.map +1 -1
  17. package/lib/components/screens/combat/components/controls/KeyboardHints.js.map +1 -1
  18. package/lib/components/screens/combat/components/controls/PauseMenu.js.map +1 -1
  19. package/lib/components/screens/combat/components/controls/PauseMenuButton.js.map +1 -1
  20. package/lib/components/screens/combat/components/controls/QuickSettings.js.map +1 -1
  21. package/lib/components/screens/combat/components/effects/BloodDecals3D.js.map +1 -1
  22. package/lib/components/screens/combat/components/effects/BloodLossOverlayHtml.js.map +1 -1
  23. package/lib/components/screens/combat/components/effects/BloodParticles3D.js.map +1 -1
  24. package/lib/components/screens/combat/components/effects/BloodViscosity3D.js.map +1 -1
  25. package/lib/components/screens/combat/components/effects/CombatParticleEffects3D.js.map +1 -1
  26. package/lib/components/screens/combat/components/effects/ConsciousnessBlur.js.map +1 -1
  27. package/lib/components/screens/combat/components/effects/InternalDamage3D.js.map +1 -1
  28. package/lib/components/screens/combat/components/effects/PainVignette.js.map +1 -1
  29. package/lib/components/screens/combat/components/effects/ParticleAudio3D.js.map +1 -1
  30. package/lib/components/screens/combat/components/effects/TraumaOverlay3D.js.map +1 -1
  31. package/lib/components/screens/combat/components/feedback/MatchCountdown.js.map +1 -1
  32. package/lib/components/screens/combat/components/feedback/RoundAnnouncementOverlayHtml.js.map +1 -1
  33. package/lib/components/screens/combat/components/feedback/RoundDisplayStatus.js.map +1 -1
  34. package/lib/components/screens/combat/components/feedback/RoundStartAnnouncementOverlayHtml.js.map +1 -1
  35. package/lib/components/screens/combat/components/hud/CombatBottomHUD.js.map +1 -1
  36. package/lib/components/screens/combat/components/hud/CombatLeftHUD.js.map +1 -1
  37. package/lib/components/screens/combat/components/hud/CombatPortraitStatusStrip.js.map +1 -1
  38. package/lib/components/screens/combat/components/hud/CombatRightHUD.js.map +1 -1
  39. package/lib/components/screens/combat/components/hud/CombatTopHUD.js.map +1 -1
  40. package/lib/components/screens/combat/components/hud/DifficultyIndicator.js.map +1 -1
  41. package/lib/components/screens/combat/components/hud/FPSMonitor.js.map +1 -1
  42. package/lib/components/screens/combat/components/hud/MobileControlsWrapper.js.map +1 -1
  43. package/lib/components/screens/combat/components/hud/PlayerStateOverlayHtml.js.map +1 -1
  44. package/lib/components/screens/combat/components/indicators/BalanceIndicator.js.map +1 -1
  45. package/lib/components/screens/combat/components/indicators/InputBufferDisplay.js.map +1 -1
  46. package/lib/components/screens/combat/components/indicators/StaminaWarning.js.map +1 -1
  47. package/lib/components/screens/combat/components/indicators/TechniqueNameDisplay.js.map +1 -1
  48. package/lib/components/screens/combat/helpers/AnimationUpdater.js.map +1 -1
  49. package/lib/components/screens/combat/helpers/combatHelpers.js.map +1 -1
  50. package/lib/components/screens/combat/hooks/useAICombat.js.map +1 -1
  51. package/lib/components/screens/combat/hooks/useCombatActions.js.map +1 -1
  52. package/lib/components/screens/combat/hooks/useCombatAttackMovement.js.map +1 -1
  53. package/lib/components/screens/combat/hooks/useCombatAudio.js.map +1 -1
  54. package/lib/components/screens/combat/hooks/useCombatLayout.js.map +1 -1
  55. package/lib/components/screens/combat/hooks/useCombatState.js.map +1 -1
  56. package/lib/components/screens/controls/ControlsScreen3D.js.map +1 -1
  57. package/lib/components/screens/controls/components/ControlBindingsOverlayHtml.js.map +1 -1
  58. package/lib/components/screens/controls/components/ControlCategoryTabsOverlayHtml.js.map +1 -1
  59. package/lib/components/screens/controls/components/GamepadVisualization3D.js.map +1 -1
  60. package/lib/components/screens/controls/components/InteractiveControlDemoOverlayHtml.js.map +1 -1
  61. package/lib/components/screens/controls/components/Key3D.js.map +1 -1
  62. package/lib/components/screens/controls/components/VisualKeyboard3D.js.map +1 -1
  63. package/lib/components/screens/controls/constants/ControlsConstants.js.map +1 -1
  64. package/lib/components/screens/controls/hooks/useControlsState.js.map +1 -1
  65. package/lib/components/screens/endscreen/EndScreen3D.js.map +1 -1
  66. package/lib/components/screens/endscreen/components/DefeatAnimation3D.js.map +1 -1
  67. package/lib/components/screens/endscreen/components/MatchStatisticsDisplayOverlayHtml.js.map +1 -1
  68. package/lib/components/screens/endscreen/components/NavigationButtonsOverlayHtml.js.map +1 -1
  69. package/lib/components/screens/endscreen/components/PerformanceBreakdownOverlayHtml.js.map +1 -1
  70. package/lib/components/screens/endscreen/components/PerformanceRatingOverlayHtml.js.map +1 -1
  71. package/lib/components/screens/endscreen/components/VictoryAnimation3D.js.map +1 -1
  72. package/lib/components/screens/endscreen/components/WinnerDisplayOverlayHtml.js.map +1 -1
  73. package/lib/components/screens/intro/IntroScreen3D.js +1 -1
  74. package/lib/components/screens/intro/IntroScreen3D.js.map +1 -1
  75. package/lib/components/screens/intro/components/AbilityListOverlayHtml.js.map +1 -1
  76. package/lib/components/screens/intro/components/ArchetypeCardGridOverlayHtml.js.map +1 -1
  77. package/lib/components/screens/intro/components/ArchetypeCardOverlayHtml.js.map +1 -1
  78. package/lib/components/screens/intro/components/ArchetypeDisplayOverlayHtml.js.map +1 -1
  79. package/lib/components/screens/intro/components/EnhancedArchetypeDisplayOverlayHtml.js.map +1 -1
  80. package/lib/components/screens/intro/components/MenuButtonsOverlayHtml.js.map +1 -1
  81. package/lib/components/screens/intro/components/MenuSectionOverlayHtml.js.map +1 -1
  82. package/lib/components/screens/intro/components/StatBarOverlayHtml.js.map +1 -1
  83. package/lib/components/screens/philosophy/PhilosophyScreen3D.js.map +1 -1
  84. package/lib/components/screens/training/TrainingScreen3D.js.map +1 -1
  85. package/lib/components/screens/training/components/AnatomyControlsOverlayHtml.js.map +1 -1
  86. package/lib/components/screens/training/components/AnatomyOverlay3D.js.map +1 -1
  87. package/lib/components/screens/training/components/FootPlacementMarkers3D.js.map +1 -1
  88. package/lib/components/screens/training/components/FootworkDrillsOverlayHtml.js.map +1 -1
  89. package/lib/components/screens/training/components/HitFeedbackEffect3D.js.map +1 -1
  90. package/lib/components/screens/training/components/TrainingButtonsOverlayHtml.js.map +1 -1
  91. package/lib/components/screens/training/components/TrainingControlsOverlayHtml.js.map +1 -1
  92. package/lib/components/screens/training/components/TrainingDummy3D.js.map +1 -1
  93. package/lib/components/screens/training/components/TrainingFeedbackOverlayHtml.js.map +1 -1
  94. package/lib/components/screens/training/components/TrainingModeSelectorOverlayHtml.js.map +1 -1
  95. package/lib/components/screens/training/components/TrainingStatsOverlayHtml.js.map +1 -1
  96. package/lib/components/screens/training/components/VitalPointMarker3D.js.map +1 -1
  97. package/lib/components/screens/training/components/VitalPointTrainingOverlayHtml.js.map +1 -1
  98. package/lib/components/screens/training/components/hud/TrainingBottomHUD.js.map +1 -1
  99. package/lib/components/screens/training/components/hud/TrainingLeftHUD.js.map +1 -1
  100. package/lib/components/screens/training/components/hud/TrainingRightHUD.js.map +1 -1
  101. package/lib/components/screens/training/components/hud/TrainingTopHUD.js.map +1 -1
  102. package/lib/components/screens/training/hooks/useAttackMovement.js.map +1 -1
  103. package/lib/components/screens/training/hooks/useTrainingActions.js.map +1 -1
  104. package/lib/components/screens/training/hooks/useTrainingLayout.js.map +1 -1
  105. package/lib/components/screens/training/hooks/useTrainingState.js.map +1 -1
  106. package/lib/components/shared/base/BaseButton.js.map +1 -1
  107. package/lib/components/shared/base/BaseButtonOverlayHtml.js.map +1 -1
  108. package/lib/components/shared/base/BasePanel.js.map +1 -1
  109. package/lib/components/shared/base/BaseText.js.map +1 -1
  110. package/lib/components/shared/base/useKoreanTheme.js.map +1 -1
  111. package/lib/components/shared/debug/PerformanceDebugOverlayHtml.js.map +1 -1
  112. package/lib/components/shared/mobile/ActionButtons.js.map +1 -1
  113. package/lib/components/shared/mobile/GestureRecognizerPure.js.map +1 -1
  114. package/lib/components/shared/mobile/HapticController.js.map +1 -1
  115. package/lib/components/shared/mobile/MobileControlsPure.js.map +1 -1
  116. package/lib/components/shared/mobile/StanceWheelPure.js.map +1 -1
  117. package/lib/components/shared/mobile/TouchOptimizer.js.map +1 -1
  118. package/lib/components/shared/mobile/VirtualDPad.js.map +1 -1
  119. package/lib/components/shared/three/anatomy/BodySurface.js.map +1 -1
  120. package/lib/components/shared/three/anatomy/BoneAttachedMuscles.js.map +1 -1
  121. package/lib/components/shared/three/anatomy/BoneClothing.js.map +1 -1
  122. package/lib/components/shared/three/anatomy/BoneRenderer.js.map +1 -1
  123. package/lib/components/shared/three/anatomy/Face3D.js.map +1 -1
  124. package/lib/components/shared/three/anatomy/Foot3D.js.map +1 -1
  125. package/lib/components/shared/three/anatomy/Hand3D.js.map +1 -1
  126. package/lib/components/shared/three/effects/ActionFeedback.js.map +1 -1
  127. package/lib/components/shared/three/effects/DamageNumbers.js.map +1 -1
  128. package/lib/components/shared/three/effects/HitEffects3D.js.map +1 -1
  129. package/lib/components/shared/three/effects/PlayerStateIndicators.js.map +1 -1
  130. package/lib/components/shared/three/effects/StanceSymbol3D.js.map +1 -1
  131. package/lib/components/shared/three/effects/StanceTransitionEffect.js.map +1 -1
  132. package/lib/components/shared/three/effects/VitalPointMarkers3D.js.map +1 -1
  133. package/lib/components/shared/three/indicators/ElementalColorSystem.js.map +1 -1
  134. package/lib/components/shared/three/indicators/GuardIndicator.js.map +1 -1
  135. package/lib/components/shared/three/indicators/HapticFeedback.js.map +1 -1
  136. package/lib/components/shared/three/indicators/StanceChangeIndicator.js.map +1 -1
  137. package/lib/components/shared/three/models/Player3DWithTransitions.js.map +1 -1
  138. package/lib/components/shared/three/models/SkeletalPlayer3D.js.map +1 -1
  139. package/lib/components/shared/three/optimization/AdaptiveQuality.js.map +1 -1
  140. package/lib/components/shared/three/scene/AtmosphericParticles3D.js.map +1 -1
  141. package/lib/components/shared/three/scene/BackgroundScene3D.js.map +1 -1
  142. package/lib/components/shared/three/scene/CombatArena3D.js.map +1 -1
  143. package/lib/components/shared/three/scene/KoreanSignage3D.js.map +1 -1
  144. package/lib/components/shared/three/ui/ArchetypeCard.js.map +1 -1
  145. package/lib/components/shared/three/ui/BodyPartHealthDisplay.js.map +1 -1
  146. package/lib/components/shared/three/ui/BreathingIndicator2.js.map +1 -1
  147. package/lib/components/shared/three/ui/CombatReadinessBar.js.map +1 -1
  148. package/lib/components/shared/three/ui/ComboCounter.js.map +1 -1
  149. package/lib/components/shared/three/ui/HealthBar.js.map +1 -1
  150. package/lib/components/shared/three/ui/KoreanButton.js.map +1 -1
  151. package/lib/components/shared/three/ui/KoreanPanel.js.map +1 -1
  152. package/lib/components/shared/three/ui/KoreanText.js.map +1 -1
  153. package/lib/components/shared/three/ui/MenuList.js.map +1 -1
  154. package/lib/components/shared/three/ui/PlayerHUD.js.map +1 -1
  155. package/lib/components/shared/three/ui/ProgressBar.js.map +1 -1
  156. package/lib/components/shared/three/ui/SpeedIndicatorHUD.js.map +1 -1
  157. package/lib/components/shared/three/ui/StaminaBar.js.map +1 -1
  158. package/lib/components/shared/three/ui/TechniqueBar.js.map +1 -1
  159. package/lib/components/shared/three/ui/TechniqueCard.js.map +1 -1
  160. package/lib/components/shared/three/ui/VitalPointOverlayControlsHtml.js.map +1 -1
  161. package/lib/components/shared/ui/BackButton.js.map +1 -1
  162. package/lib/components/shared/ui/BaseHUDContainer.js.map +1 -1
  163. package/lib/components/shared/ui/CombatTimer.js.map +1 -1
  164. package/lib/components/shared/ui/ErrorModal.js.map +1 -1
  165. package/lib/components/shared/ui/LoadingState.js.map +1 -1
  166. package/lib/components/shared/ui/SplashScreen.js +2 -2
  167. package/lib/components/shared/ui/SplashScreen.js.map +1 -1
  168. package/lib/components/shared/ui/VitalPointOverlayControlsPure.js.map +1 -1
  169. package/lib/components/shared/ui/VolumeControl.js.map +1 -1
  170. package/lib/components/shared/ui/shared/ConfirmDialog.js.map +1 -1
  171. package/lib/components/ui/combat/BalanceIndicatorOverlayHtml.js.map +1 -1
  172. package/lib/constants/bodyDimensions.js.map +1 -1
  173. package/lib/constants/bodyRenderingConstants.js.map +1 -1
  174. package/lib/data/archetypeClothing.js.map +1 -1
  175. package/lib/data/archetypePhysicalAttributes.js.map +1 -1
  176. package/lib/data/techniqueMappings.js.map +1 -1
  177. package/lib/data/techniques.js.map +1 -1
  178. package/lib/hooks/useActionFeedback.js.map +1 -1
  179. package/lib/hooks/useBalanceAnimations.js.map +1 -1
  180. package/lib/hooks/useCombatTimer.js.map +1 -1
  181. package/lib/hooks/useDebounce.js.map +1 -1
  182. package/lib/hooks/useHUDLayout.js.map +1 -1
  183. package/lib/hooks/useHandPoseTransitions.js.map +1 -1
  184. package/lib/hooks/useKeyboardControls.js.map +1 -1
  185. package/lib/hooks/useMatchCountdown.js.map +1 -1
  186. package/lib/hooks/useMuscleActivation.js.map +1 -1
  187. package/lib/hooks/usePauseMenu.js.map +1 -1
  188. package/lib/hooks/usePlayerAnimation.js.map +1 -1
  189. package/lib/hooks/useResponsiveLayout.js.map +1 -1
  190. package/lib/hooks/useRoundTransition.js.map +1 -1
  191. package/lib/hooks/useSkeletalAnimation.js.map +1 -1
  192. package/lib/hooks/useTechniqueSelection.js.map +1 -1
  193. package/lib/hooks/useThrottle.js.map +1 -1
  194. package/lib/hooks/useTouchControls.js.map +1 -1
  195. package/lib/hooks/useWebGLContextLossHandler.js.map +1 -1
  196. package/lib/hooks/useWindowSize.js.map +1 -1
  197. package/lib/systems/CombatSystem.js.map +1 -1
  198. package/lib/systems/EffectCalculator.js.map +1 -1
  199. package/lib/systems/LayoutSystem.js.map +1 -1
  200. package/lib/systems/PlayerEffectManager.js.map +1 -1
  201. package/lib/systems/ResponsiveScaling.js.map +1 -1
  202. package/lib/systems/TrigramSystem.js.map +1 -1
  203. package/lib/systems/VitalPointSystem.js.map +1 -1
  204. package/lib/systems/ai/AIPersonality.js.map +1 -1
  205. package/lib/systems/ai/AdaptiveDifficulty.js.map +1 -1
  206. package/lib/systems/ai/ArchetypeEnforcer.js.map +1 -1
  207. package/lib/systems/ai/ComboSystem.js.map +1 -1
  208. package/lib/systems/ai/DecisionTree.js.map +1 -1
  209. package/lib/systems/ai/TrainingAI.js.map +1 -1
  210. package/lib/systems/ai/types.js.map +1 -1
  211. package/lib/systems/animation/builders/AnimationBuilder.js.map +1 -1
  212. package/lib/systems/animation/builders/HandPoseApplicator.js.map +1 -1
  213. package/lib/systems/animation/builders/HandPoses.js.map +1 -1
  214. package/lib/systems/animation/builders/KeyframeConfig.js.map +1 -1
  215. package/lib/systems/animation/builders/KeyframeInterpolation.js +3 -90
  216. package/lib/systems/animation/builders/KeyframeInterpolation.js.map +1 -1
  217. package/lib/systems/animation/builders/KickPhaseApplicator.js.map +1 -1
  218. package/lib/systems/animation/builders/KoreanGuardPositions.js.map +1 -1
  219. package/lib/systems/animation/builders/MartialArtsAnimationBuilder.js.map +1 -1
  220. package/lib/systems/animation/builders/MartialArtsConstants.js.map +1 -1
  221. package/lib/systems/animation/builders/MartialPoseApplicator.js.map +1 -1
  222. package/lib/systems/animation/builders/PunchPhaseApplicator.js.map +1 -1
  223. package/lib/systems/animation/builders/SkeletonRig.js.map +1 -1
  224. package/lib/systems/animation/builders/TrigramGuardApplicator.js.map +1 -1
  225. package/lib/systems/animation/catalogs/DefensiveAnimations.js.map +1 -1
  226. package/lib/systems/animation/catalogs/FootworkSkeletalAnimations.js.map +1 -1
  227. package/lib/systems/animation/catalogs/RecoveryAnimations.js.map +1 -1
  228. package/lib/systems/animation/catalogs/StanceAnimations.js.map +1 -1
  229. package/lib/systems/animation/catalogs/StanceAttackAnimations.js.map +1 -1
  230. package/lib/systems/animation/catalogs/StanceGuardPoses.js.map +1 -1
  231. package/lib/systems/animation/catalogs/StanceIdleAnimations.js.map +1 -1
  232. package/lib/systems/animation/catalogs/StanceLocomotionAnimations.js.map +1 -1
  233. package/lib/systems/animation/catalogs/StepSkeletalAnimations.js.map +1 -1
  234. package/lib/systems/animation/core/AnimationHitTiming.js.map +1 -1
  235. package/lib/systems/animation/core/AnimationOptimizations.js.map +1 -1
  236. package/lib/systems/animation/core/AnimationPriority.js.map +1 -1
  237. package/lib/systems/animation/core/AnimationRegistry.js.map +1 -1
  238. package/lib/systems/animation/core/AnimationStateMachine.js.map +1 -1
  239. package/lib/systems/animation/core/AnimationTransitions.js.map +1 -1
  240. package/lib/systems/animation/core/LateralityTransform.js.map +1 -1
  241. package/lib/systems/animation/core/RecoveryPhaseEnhancer.js.map +1 -1
  242. package/lib/systems/animation/core/TechniqueAnimationMapper.js.map +1 -1
  243. package/lib/systems/animation/core/TechniqueAnimationMapping.js.map +1 -1
  244. package/lib/systems/animation/core/types.js.map +1 -1
  245. package/lib/systems/animation/systems/AdvancedJointMovements.js.map +1 -1
  246. package/lib/systems/animation/systems/BodyFacingSystem.js.map +1 -1
  247. package/lib/systems/animation/systems/FacialExpressions.js.map +1 -1
  248. package/lib/systems/animation/systems/FallAnimations.js.map +1 -1
  249. package/lib/systems/animation/systems/MuscleActivation.js.map +1 -1
  250. package/lib/systems/bodypart/BodyPartDamageIntegration.js.map +1 -1
  251. package/lib/systems/bodypart/BodyPartHealthSystem.js.map +1 -1
  252. package/lib/systems/bodypart/BodyPartPositionMapping.js.map +1 -1
  253. package/lib/systems/bodypart/CombatInjuryIntegration.js.map +1 -1
  254. package/lib/systems/bodypart/InjuryIntegration.js.map +1 -1
  255. package/lib/systems/bodypart/InjuryTracker.js.map +1 -1
  256. package/lib/systems/bodypart/MovementPenaltySystem.js.map +1 -1
  257. package/lib/systems/bodypart/PlayerInjuryTrackingManager.js.map +1 -1
  258. package/lib/systems/bodypart/types.js.map +1 -1
  259. package/lib/systems/breathing/BreathingDisruptionSystem.js.map +1 -1
  260. package/lib/systems/breathing/feedback.js.map +1 -1
  261. package/lib/systems/breathing/integration.js.map +1 -1
  262. package/lib/systems/combat/BalanceSystem.js.map +1 -1
  263. package/lib/systems/combat/BreakingStatusEffects.js.map +1 -1
  264. package/lib/systems/combat/CombatStateSystem.js.map +1 -1
  265. package/lib/systems/combat/ConsciousnessSystem.js.map +1 -1
  266. package/lib/systems/combat/FallIntegration.js.map +1 -1
  267. package/lib/systems/combat/GrappleSystem.js.map +1 -1
  268. package/lib/systems/combat/LimbExposureSystem.js.map +1 -1
  269. package/lib/systems/combat/PainResponseSystem.js.map +1 -1
  270. package/lib/systems/combat/TrainingCombatSystem.js.map +1 -1
  271. package/lib/systems/combat/painConsciousnessUtils.js.map +1 -1
  272. package/lib/systems/combat/typeGuards.js.map +1 -1
  273. package/lib/systems/effects.js.map +1 -1
  274. package/lib/systems/game.js.map +1 -1
  275. package/lib/systems/movement/InjuryMovementModifier.js.map +1 -1
  276. package/lib/systems/movement/helpers/AccelerationUpdater.js.map +1 -1
  277. package/lib/systems/movement/helpers/accelerationUtils.js.map +1 -1
  278. package/lib/systems/movement/integration.js.map +1 -1
  279. package/lib/systems/physics/AttackMovementPhysics.js.map +1 -1
  280. package/lib/systems/physics/CollisionDetection.js.map +1 -1
  281. package/lib/systems/physics/CoordinateMapper.js.map +1 -1
  282. package/lib/systems/physics/KnockbackPhysics.js.map +1 -1
  283. package/lib/systems/physics/MovementPhysics.js.map +1 -1
  284. package/lib/systems/physics/PhysicalReachCalculator.js.map +1 -1
  285. package/lib/systems/physics/SpeedModifierSystem.js.map +1 -1
  286. package/lib/systems/trigram/KoreanCulture.js.map +1 -1
  287. package/lib/systems/trigram/KoreanTechniques.js.map +1 -1
  288. package/lib/systems/trigram/StanceManager.js.map +1 -1
  289. package/lib/systems/trigram/TransitionCalculator.js.map +1 -1
  290. package/lib/systems/trigram/TrigramCalculator.js.map +1 -1
  291. package/lib/systems/trigram/techniques/DarkOpsTechniques.js.map +1 -1
  292. package/lib/systems/trigram/techniques/GamTechniques.js.map +1 -1
  293. package/lib/systems/trigram/techniques/GanTechniques.js.map +1 -1
  294. package/lib/systems/trigram/techniques/GonTechniques.js.map +1 -1
  295. package/lib/systems/trigram/techniques/SonTechniques.js.map +1 -1
  296. package/lib/systems/trigram/techniques/TechniqueConfig.js.map +1 -1
  297. package/lib/systems/trigram/techniques/index.js.map +1 -1
  298. package/lib/systems/trigram/types/GonTechniqueExtensions.js.map +1 -1
  299. package/lib/systems/trigram/types.js.map +1 -1
  300. package/lib/systems/types.js.map +1 -1
  301. package/lib/systems/vitalpoint/DamageCalculator.js.map +1 -1
  302. package/lib/systems/vitalpoint/HitDetection.js.map +1 -1
  303. package/lib/systems/vitalpoint/KoreanAnatomy.js.map +1 -1
  304. package/lib/systems/vitalpoint/KoreanVitalPoints.js.map +1 -1
  305. package/lib/systems/vitalpoint/MeridianVitalPointMapping.js.map +1 -1
  306. package/lib/types/AccessibilityTypes.js.map +1 -1
  307. package/lib/types/PhysicsTypes.js.map +1 -1
  308. package/lib/types/common.js.map +1 -1
  309. package/lib/types/constants/colors.js.map +1 -1
  310. package/lib/types/constants/designSystem.js.map +1 -1
  311. package/lib/types/constants/layout.js.map +1 -1
  312. package/lib/types/constants/performance.js.map +1 -1
  313. package/lib/types/constants/typography.js.map +1 -1
  314. package/lib/types/facial.js.map +1 -1
  315. package/lib/types/hand-animation.js.map +1 -1
  316. package/lib/types/injury.js.map +1 -1
  317. package/lib/types/physics.js.map +1 -1
  318. package/lib/types/skeletal.js.map +1 -1
  319. package/lib/types/techniqueId.js.map +1 -1
  320. package/lib/utils/accessibility.js.map +1 -1
  321. package/lib/utils/arenaWorldDimensions.js.map +1 -1
  322. package/lib/utils/assetConfig.js.map +1 -1
  323. package/lib/utils/characterScaling.js.map +1 -1
  324. package/lib/utils/colorHelpers.js.map +1 -1
  325. package/lib/utils/colorUtils.js.map +1 -1
  326. package/lib/utils/combatReadiness.js.map +1 -1
  327. package/lib/utils/controlMapping.js.map +1 -1
  328. package/lib/utils/deviceDetection.js.map +1 -1
  329. package/lib/utils/effectUtils.js.map +1 -1
  330. package/lib/utils/fabricTextures.js.map +1 -1
  331. package/lib/utils/hapticFeedback.js.map +1 -1
  332. package/lib/utils/haptics.js.map +1 -1
  333. package/lib/utils/htmlOverlayHelpers.js.map +1 -1
  334. package/lib/utils/inputSystem.js.map +1 -1
  335. package/lib/utils/koreanThemeHelpers.js.map +1 -1
  336. package/lib/utils/math.js.map +1 -1
  337. package/lib/utils/mobileLayoutHelpers.js.map +1 -1
  338. package/lib/utils/mobileUIUtils.js.map +1 -1
  339. package/lib/utils/performance/PerformanceMonitor.js.map +1 -1
  340. package/lib/utils/performance/PerformanceOverlay3D.js.map +1 -1
  341. package/lib/utils/performance/usePerformanceMonitor.js.map +1 -1
  342. package/lib/utils/performanceOptimization.js.map +1 -1
  343. package/lib/utils/player3DHelpers.js.map +1 -1
  344. package/lib/utils/playerUtils.js.map +1 -1
  345. package/lib/utils/responsiveLayout.js.map +1 -1
  346. package/lib/utils/responsiveLayoutHelpers.js.map +1 -1
  347. package/lib/utils/responsiveOrientationConstants.js.map +1 -1
  348. package/lib/utils/safeAreaUtils.js.map +1 -1
  349. package/lib/utils/sharedPhysicsConfig.js.map +1 -1
  350. package/lib/utils/skeletonScaling.js.map +1 -1
  351. package/lib/utils/stanceHelpers.js.map +1 -1
  352. package/lib/utils/threeObjectPool.js.map +1 -1
  353. package/lib/utils/visualEffects.js.map +1 -1
  354. package/package.json +5 -5
@@ -1 +1 @@
1
- {"version":3,"file":"CoordinateMapper.js","names":[],"sources":["../../../src/systems/physics/CoordinateMapper.ts"],"sourcesContent":["/**\n * Coordinate Mapper for 2D→3D conversion in Black Trigram.\n * \n * **Korean**: 좌표 변환기 (2D → 3D)\n * \n * Maps 2D pixel coordinates from the vital points data (UI overlay) to 3D world\n * coordinates for physics-based collision detection. This allows the UI-based\n * vital point system to work seamlessly with Three.js 3D collision detection.\n * \n * ## Coordinate Systems\n * \n * - **2D Pixel Space**: Origin (0,0) at top-left, used by vital points UI overlay\n * - **3D World Space**: Origin (0,0,0) at character center, used by collision detection\n * \n * ## Character Model Assumptions\n * \n * - Character height: 1.75m (average adult)\n * - Character width: 0.5m (shoulder width)\n * - Character depth: 0.3m (chest to back)\n * - Standing position: feet at y=0, head at y=1.75\n * \n * ## Mapping Strategy\n * \n * The mapper uses anatomical regions to determine the Z-depth (front-to-back position)\n * of vital points, ensuring physically accurate 3D positions for collision detection.\n * \n * @module systems/physics/CoordinateMapper\n * @category Physics System\n * @korean 좌표변환\n */\n\nimport type { Position } from \"@/types/common\";\nimport type { Position3D, AnatomicalRegionPhysics } from \"@/types/physics\";\nimport type { VitalPoint } from \"../vitalpoint/types\";\n\n/**\n * Configuration for the character model coordinate mapping.\n * \n * **Korean**: 캐릭터 모델 설정\n */\nexport interface CharacterModelConfig {\n /** Character height in meters (default: 1.75m) */\n readonly height: number;\n \n /** Character shoulder width in meters (default: 0.5m) */\n readonly width: number;\n \n /** Character depth (chest to back) in meters (default: 0.3m) */\n readonly depth: number;\n \n /** UI overlay width in pixels (default: 200px) */\n readonly overlayWidth: number;\n \n /** UI overlay height in pixels (default: 300px) */\n readonly overlayHeight: number;\n}\n\n/**\n * Average adult standing height used for physics mapping (meters).\n * \n * This value is based on common anthropometric data and provides a\n * reasonable default for a neutral, unarmed character in Black Trigram.\n */\nconst AVERAGE_ADULT_HEIGHT_M = 1.75; // 175cm average adult height\n\n/**\n * Average shoulder width used for frontal collision and UI mapping (meters).\n * \n * This approximates the biacromial (shoulder-to-shoulder) breadth for an\n * adult, which is sufficient for our hitbox and overlay alignment needs.\n */\nconst AVERAGE_SHOULDER_WIDTH_M = 0.5; // 50cm shoulder width\n\n/**\n * Average torso depth (chest to back) used for front/back hit placement (meters).\n */\nconst AVERAGE_TORSO_DEPTH_M = 0.3; // 30cm chest depth\n\n/**\n * Default UI overlay width in pixels.\n * \n * This matches the designed vital-point sprite asset width, ensuring that\n * pixel coordinates from the overlay map consistently to the 3D character.\n */\nconst DEFAULT_OVERLAY_WIDTH_PX = 200; // Standard UI overlay width\n\n/**\n * Default UI overlay height in pixels.\n * \n * This matches the designed vital-point sprite asset height, keeping the\n * 2D→3D mapping resolution-independent but asset-consistent.\n */\nconst DEFAULT_OVERLAY_HEIGHT_PX = 300; // Standard UI overlay height\n\n/**\n * Default character model configuration based on average adult proportions.\n */\nconst DEFAULT_CHARACTER_CONFIG: CharacterModelConfig = {\n height: AVERAGE_ADULT_HEIGHT_M,\n width: AVERAGE_SHOULDER_WIDTH_M,\n depth: AVERAGE_TORSO_DEPTH_M,\n overlayWidth: DEFAULT_OVERLAY_WIDTH_PX,\n overlayHeight: DEFAULT_OVERLAY_HEIGHT_PX,\n};\n\n/**\n * Z-depth (front-to-back) offsets for different anatomical regions.\n * \n * Units are **meters in model space**, measured along the character's local Z axis,\n * and are applied relative to the character's center plane (Z = 0) and the\n * configured {@link CharacterModelConfig.depth}. With the default depth of\n * `0.3m`, an offset of `0.05` corresponds to ≈5 cm of forward displacement.\n *\n * These values were chosen as simple, stable approximations based on average\n * adult proportions and validated visually against the default rig:\n *\n * - `head: 0.05` → head center sits slightly in front of the torso plane\n * (≈1/6 of torso depth) to match typical forward head posture.\n * - `neck: 0.02` → neck base is close to the torso plane but not perfectly\n * centered, acknowledging slight anterior offset of the trachea/sternum.\n * - `torso: 0.0` → torso vital points lie on the reference center plane that\n * approximates the mid‑chest / spine midpoint.\n * - `arms: 0.05` → relaxed arms hang slightly in front of the torso plane,\n * using the same forward offset as the head for consistency.\n * - `legs: 0.0` → leg targets (front of thighs/shins) are modeled on the\n * center plane; depth variation is instead captured by vertical placement.\n *\n * If you change {@link CharacterModelConfig.depth}, you may keep these absolute\n * meter values (for physical fidelity) or re‑express them as fractions of the\n * new depth, but they should continue to represent small (≈0–5 cm) anatomical\n * offsets from the torso midline.\n */\nconst REGION_DEPTH_OFFSETS: Record<AnatomicalRegionPhysics, number> = {\n head: 0.05, // ≈5 cm forward from torso mid-plane to match cranial alignment\n neck: 0.02, // ≈2 cm forward; neck base is near, but not exactly on, center\n torso: 0.0, // Torso reference plane (mid‑chest / spine midpoint)\n arms: 0.05, // ≈5 cm forward; relaxed arms rest slightly in front of torso\n legs: 0.0, // Legs aligned to torso center plane; front/back handled elsewhere\n};\n\n/**\n * Coordinate Mapper for converting 2D pixel coordinates to 3D world coordinates.\n * \n * **Korean**: 좌표 변환기\n * \n * Provides bidirectional mapping between the 2D UI overlay coordinate system\n * and the 3D physics world coordinate system.\n * \n * @public\n * @category Physics System\n * @korean 좌표변환기\n */\nexport class CoordinateMapper {\n private readonly config: CharacterModelConfig;\n \n /**\n * Creates a new CoordinateMapper with optional custom configuration.\n * \n * @param config - Optional character model configuration\n */\n constructor(config: Partial<CharacterModelConfig> = {}) {\n this.config = { ...DEFAULT_CHARACTER_CONFIG, ...config };\n }\n \n /**\n * Converts a 2D pixel position to a 3D world position.\n * \n * **Korean**: 2D → 3D 변환\n * \n * Maps UI overlay pixel coordinates to 3D world space coordinates suitable\n * for physics collision detection.\n * \n * @param pixel2D - 2D pixel position (origin top-left)\n * @param region - Anatomical region for Z-depth calculation\n * @returns 3D world position in meters (origin at character center)\n * \n * @example\n * ```typescript\n * const mapper = new CoordinateMapper();\n * const vitalPoint = { position: { x: 100, y: 50 } };\n * const worldPos = mapper.pixel2DToWorld3D(vitalPoint.position, \"head\");\n * // worldPos: { x: 0, y: 1.45, z: 0.05 }\n * ```\n */\n pixel2DToWorld3D(pixel2D: Position, region: AnatomicalRegionPhysics): Position3D {\n // Normalize pixel coordinates to 0-1 range\n const normalizedX = pixel2D.x / this.config.overlayWidth;\n const normalizedY = pixel2D.y / this.config.overlayHeight;\n \n // Convert to world coordinates\n // X: Map from pixel space (0 = left, overlayWidth = right) to world space (-width/2 to +width/2)\n const worldX = (normalizedX - 0.5) * this.config.width;\n \n // Y: Map from pixel space (0 = top, overlayHeight = bottom) to world space (height to 0)\n // Invert Y-axis: pixel 0 = top = character head, pixel overlayHeight = bottom = character feet\n const worldY = (1 - normalizedY) * this.config.height;\n \n // Z: Use anatomical region to determine depth offset\n const worldZ = REGION_DEPTH_OFFSETS[region];\n \n return {\n x: worldX,\n y: worldY,\n z: worldZ,\n };\n }\n \n /**\n * Converts a 3D world position to a 2D pixel position.\n * \n * **Korean**: 3D → 2D 변환\n * \n * Maps 3D world space coordinates back to UI overlay pixel coordinates.\n * Useful for debugging and visual feedback.\n * \n * @param world3D - 3D world position in meters\n * @returns 2D pixel position (origin top-left)\n * \n * @example\n * ```typescript\n * const mapper = new CoordinateMapper();\n * const worldPos = { x: 0, y: 1.45, z: 0.05 };\n * const pixelPos = mapper.world3DToPixel2D(worldPos);\n * // pixelPos: { x: 100, y: 50 }\n * ```\n */\n world3DToPixel2D(world3D: Position3D): Position {\n // Convert world X to normalized 0-1 range\n const normalizedX = (world3D.x / this.config.width) + 0.5;\n \n // Convert world Y to normalized 0-1 range (inverted)\n const normalizedY = 1 - (world3D.y / this.config.height);\n \n // Convert to pixel coordinates\n return {\n x: Math.round(normalizedX * this.config.overlayWidth),\n y: Math.round(normalizedY * this.config.overlayHeight),\n };\n }\n \n /**\n * Converts a vital point's 2D position to 3D world position.\n * \n * **Korean**: 급소 위치 변환\n * \n * Convenience method that extracts position and region from a VitalPoint object.\n * \n * @param vitalPoint - Vital point with 2D position\n * @returns 3D world position\n * \n * @example\n * ```typescript\n * const mapper = new CoordinateMapper();\n * const temple = VITAL_POINTS_DATA.find(vp => vp.id === \"head_temple\");\n * const worldPos = mapper.vitalPointToWorld3D(temple);\n * ```\n */\n vitalPointToWorld3D(vitalPoint: VitalPoint): Position3D {\n // Infer region from vital point ID prefix\n const region = this.inferRegionFromId(vitalPoint.id);\n return this.pixel2DToWorld3D(vitalPoint.position, region);\n }\n \n /**\n * Infers the anatomical region from a vital point ID.\n * \n * **Korean**: 급소 ID에서 영역 추론\n * \n * @param vitalPointId - Vital point ID (e.g., \"head_temple\", \"torso_solar_plexus\")\n * @returns Anatomical region\n * \n * @private\n */\n private inferRegionFromId(vitalPointId: string): AnatomicalRegionPhysics {\n if (vitalPointId.startsWith(\"head_\")) return \"head\";\n if (vitalPointId.startsWith(\"neck_\")) return \"neck\";\n if (vitalPointId.startsWith(\"torso_\")) return \"torso\";\n if (vitalPointId.startsWith(\"arm_\")) return \"arms\";\n if (vitalPointId.startsWith(\"leg_\")) return \"legs\";\n \n // Default to torso if no clear region prefix\n return \"torso\";\n }\n \n /**\n * Calculates the distance between a 3D point and a vital point's 3D position.\n * \n * **Korean**: 거리 계산\n * \n * @param point3D - Point in 3D world space\n * @param vitalPoint - Vital point to measure distance to\n * @returns Distance in meters\n * \n * @example\n * ```typescript\n * const mapper = new CoordinateMapper();\n * const attackPoint = { x: 0, y: 1.5, z: 0.1 };\n * const temple = VITAL_POINTS_DATA.find(vp => vp.id === \"head_temple\");\n * const distance = mapper.distanceToVitalPoint(attackPoint, temple);\n * ```\n */\n distanceToVitalPoint(point3D: Position3D, vitalPoint: VitalPoint): number {\n const vitalPoint3D = this.vitalPointToWorld3D(vitalPoint);\n \n const dx = point3D.x - vitalPoint3D.x;\n const dy = point3D.y - vitalPoint3D.y;\n const dz = point3D.z - vitalPoint3D.z;\n \n return Math.sqrt(dx * dx + dy * dy + dz * dz);\n }\n \n /**\n * Finds the closest vital point to a 3D position within a given region.\n * \n * **Korean**: 가장 가까운 급소 찾기\n * \n * @param point3D - Point in 3D world space\n * @param vitalPoints - Array of vital points to search\n * @param region - Optional region filter\n * @returns Closest vital point and distance, or null if none found\n * \n * @example\n * ```typescript\n * const mapper = new CoordinateMapper();\n * const hitPoint = { x: 0, y: 1.7, z: 0.05 };\n * const result = mapper.findClosestVitalPoint(hitPoint, VITAL_POINTS_DATA, \"head\");\n * if (result) {\n * console.log(`Hit ${result.vitalPoint.names.english} at ${result.distance}m`);\n * }\n * ```\n */\n findClosestVitalPoint(\n point3D: Position3D,\n vitalPoints: readonly VitalPoint[],\n region?: AnatomicalRegionPhysics\n ): { vitalPoint: VitalPoint; distance: number } | null {\n let closestVitalPoint: VitalPoint | null = null;\n let closestDistance = Infinity;\n \n for (const vitalPoint of vitalPoints) {\n // Filter by region if specified\n if (region) {\n const vpRegion = this.inferRegionFromId(vitalPoint.id);\n if (vpRegion !== region) continue;\n }\n \n const distance = this.distanceToVitalPoint(point3D, vitalPoint);\n \n if (distance < closestDistance) {\n closestDistance = distance;\n closestVitalPoint = vitalPoint;\n }\n }\n \n if (closestVitalPoint === null) {\n return null;\n }\n \n return {\n vitalPoint: closestVitalPoint,\n distance: closestDistance,\n };\n }\n \n /**\n * Gets the current character model configuration.\n * \n * @returns Current configuration\n */\n getConfig(): CharacterModelConfig {\n return { ...this.config };\n }\n}\n\n/**\n * Default singleton instance for convenient access.\n * \n * **Korean**: 기본 인스턴스\n * \n * @example\n * ```typescript\n * import { defaultCoordinateMapper } from './CoordinateMapper';\n * \n * const worldPos = defaultCoordinateMapper.pixel2DToWorld3D({ x: 100, y: 50 }, \"head\");\n * ```\n */\nexport const defaultCoordinateMapper = new CoordinateMapper();\n"],"mappings":";;;;AAiGA,IAAM,2BAAiD;CACrD,QAAQ;CACR,OAAO;CACP,OAAO;CACP,cAAc;CACd,eAAe;CAChB;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA6BD,IAAM,uBAAgE;CACpE,MAAM;CACN,MAAM;CACN,OAAO;CACP,MAAM;CACN,MAAM;CACP;;;;;;;;;;;;;AAcD,IAAa,mBAAb,MAA8B;CAC5B;;;;;;CAOA,YAAY,SAAwC,EAAE,EAAE;AACtD,OAAK,SAAS;GAAE,GAAG;GAA0B,GAAG;GAAQ;;;;;;;;;;;;;;;;;;;;;;CAuB1D,iBAAiB,SAAmB,QAA6C;EAE/E,MAAM,cAAc,QAAQ,IAAI,KAAK,OAAO;EAC5C,MAAM,cAAc,QAAQ,IAAI,KAAK,OAAO;AAa5C,SAAO;GACL,IAVc,cAAc,MAAO,KAAK,OAAO;GAW/C,IAPc,IAAI,eAAe,KAAK,OAAO;GAQ7C,GALa,qBAAqB;GAMnC;;;;;;;;;;;;;;;;;;;;;CAsBH,iBAAiB,SAA+B;EAE9C,MAAM,cAAe,QAAQ,IAAI,KAAK,OAAO,QAAS;EAGtD,MAAM,cAAc,IAAK,QAAQ,IAAI,KAAK,OAAO;AAGjD,SAAO;GACL,GAAG,KAAK,MAAM,cAAc,KAAK,OAAO,aAAa;GACrD,GAAG,KAAK,MAAM,cAAc,KAAK,OAAO,cAAc;GACvD;;;;;;;;;;;;;;;;;;;CAoBH,oBAAoB,YAAoC;EAEtD,MAAM,SAAS,KAAK,kBAAkB,WAAW,GAAG;AACpD,SAAO,KAAK,iBAAiB,WAAW,UAAU,OAAO;;;;;;;;;;;;CAa3D,kBAA0B,cAA+C;AACvE,MAAI,aAAa,WAAW,QAAQ,CAAE,QAAO;AAC7C,MAAI,aAAa,WAAW,QAAQ,CAAE,QAAO;AAC7C,MAAI,aAAa,WAAW,SAAS,CAAE,QAAO;AAC9C,MAAI,aAAa,WAAW,OAAO,CAAE,QAAO;AAC5C,MAAI,aAAa,WAAW,OAAO,CAAE,QAAO;AAG5C,SAAO;;;;;;;;;;;;;;;;;;;CAoBT,qBAAqB,SAAqB,YAAgC;EACxE,MAAM,eAAe,KAAK,oBAAoB,WAAW;EAEzD,MAAM,KAAK,QAAQ,IAAI,aAAa;EACpC,MAAM,KAAK,QAAQ,IAAI,aAAa;EACpC,MAAM,KAAK,QAAQ,IAAI,aAAa;AAEpC,SAAO,KAAK,KAAK,KAAK,KAAK,KAAK,KAAK,KAAK,GAAG;;;;;;;;;;;;;;;;;;;;;;CAuB/C,sBACE,SACA,aACA,QACqD;EACrD,IAAI,oBAAuC;EAC3C,IAAI,kBAAkB;AAEtB,OAAK,MAAM,cAAc,aAAa;AAEpC,OAAI;QACe,KAAK,kBAAkB,WAAW,GAC/C,KAAa,OAAQ;;GAG3B,MAAM,WAAW,KAAK,qBAAqB,SAAS,WAAW;AAE/D,OAAI,WAAW,iBAAiB;AAC9B,sBAAkB;AAClB,wBAAoB;;;AAIxB,MAAI,sBAAsB,KACxB,QAAO;AAGT,SAAO;GACL,YAAY;GACZ,UAAU;GACX;;;;;;;CAQH,YAAkC;AAChC,SAAO,EAAE,GAAG,KAAK,QAAQ;;;AAgBU,IAAI,kBAAkB"}
1
+ {"version":3,"file":"CoordinateMapper.js","names":[],"sources":["../../../src/systems/physics/CoordinateMapper.ts"],"sourcesContent":["/**\n * Coordinate Mapper for 2D→3D conversion in Black Trigram.\n * \n * **Korean**: 좌표 변환기 (2D → 3D)\n * \n * Maps 2D pixel coordinates from the vital points data (UI overlay) to 3D world\n * coordinates for physics-based collision detection. This allows the UI-based\n * vital point system to work seamlessly with Three.js 3D collision detection.\n * \n * ## Coordinate Systems\n * \n * - **2D Pixel Space**: Origin (0,0) at top-left, used by vital points UI overlay\n * - **3D World Space**: Origin (0,0,0) at character center, used by collision detection\n * \n * ## Character Model Assumptions\n * \n * - Character height: 1.75m (average adult)\n * - Character width: 0.5m (shoulder width)\n * - Character depth: 0.3m (chest to back)\n * - Standing position: feet at y=0, head at y=1.75\n * \n * ## Mapping Strategy\n * \n * The mapper uses anatomical regions to determine the Z-depth (front-to-back position)\n * of vital points, ensuring physically accurate 3D positions for collision detection.\n * \n * @module systems/physics/CoordinateMapper\n * @category Physics System\n * @korean 좌표변환\n */\n\nimport type { Position } from \"@/types/common\";\nimport type { Position3D, AnatomicalRegionPhysics } from \"@/types/physics\";\nimport type { VitalPoint } from \"../vitalpoint/types\";\n\n/**\n * Configuration for the character model coordinate mapping.\n * \n * **Korean**: 캐릭터 모델 설정\n */\nexport interface CharacterModelConfig {\n /** Character height in meters (default: 1.75m) */\n readonly height: number;\n \n /** Character shoulder width in meters (default: 0.5m) */\n readonly width: number;\n \n /** Character depth (chest to back) in meters (default: 0.3m) */\n readonly depth: number;\n \n /** UI overlay width in pixels (default: 200px) */\n readonly overlayWidth: number;\n \n /** UI overlay height in pixels (default: 300px) */\n readonly overlayHeight: number;\n}\n\n/**\n * Average adult standing height used for physics mapping (meters).\n * \n * This value is based on common anthropometric data and provides a\n * reasonable default for a neutral, unarmed character in Black Trigram.\n */\nconst AVERAGE_ADULT_HEIGHT_M = 1.75; // 175cm average adult height\n\n/**\n * Average shoulder width used for frontal collision and UI mapping (meters).\n * \n * This approximates the biacromial (shoulder-to-shoulder) breadth for an\n * adult, which is sufficient for our hitbox and overlay alignment needs.\n */\nconst AVERAGE_SHOULDER_WIDTH_M = 0.5; // 50cm shoulder width\n\n/**\n * Average torso depth (chest to back) used for front/back hit placement (meters).\n */\nconst AVERAGE_TORSO_DEPTH_M = 0.3; // 30cm chest depth\n\n/**\n * Default UI overlay width in pixels.\n * \n * This matches the designed vital-point sprite asset width, ensuring that\n * pixel coordinates from the overlay map consistently to the 3D character.\n */\nconst DEFAULT_OVERLAY_WIDTH_PX = 200; // Standard UI overlay width\n\n/**\n * Default UI overlay height in pixels.\n * \n * This matches the designed vital-point sprite asset height, keeping the\n * 2D→3D mapping resolution-independent but asset-consistent.\n */\nconst DEFAULT_OVERLAY_HEIGHT_PX = 300; // Standard UI overlay height\n\n/**\n * Default character model configuration based on average adult proportions.\n */\nconst DEFAULT_CHARACTER_CONFIG: CharacterModelConfig = {\n height: AVERAGE_ADULT_HEIGHT_M,\n width: AVERAGE_SHOULDER_WIDTH_M,\n depth: AVERAGE_TORSO_DEPTH_M,\n overlayWidth: DEFAULT_OVERLAY_WIDTH_PX,\n overlayHeight: DEFAULT_OVERLAY_HEIGHT_PX,\n};\n\n/**\n * Z-depth (front-to-back) offsets for different anatomical regions.\n * \n * Units are **meters in model space**, measured along the character's local Z axis,\n * and are applied relative to the character's center plane (Z = 0) and the\n * configured {@link CharacterModelConfig.depth}. With the default depth of\n * `0.3m`, an offset of `0.05` corresponds to ≈5 cm of forward displacement.\n *\n * These values were chosen as simple, stable approximations based on average\n * adult proportions and validated visually against the default rig:\n *\n * - `head: 0.05` → head center sits slightly in front of the torso plane\n * (≈1/6 of torso depth) to match typical forward head posture.\n * - `neck: 0.02` → neck base is close to the torso plane but not perfectly\n * centered, acknowledging slight anterior offset of the trachea/sternum.\n * - `torso: 0.0` → torso vital points lie on the reference center plane that\n * approximates the mid‑chest / spine midpoint.\n * - `arms: 0.05` → relaxed arms hang slightly in front of the torso plane,\n * using the same forward offset as the head for consistency.\n * - `legs: 0.0` → leg targets (front of thighs/shins) are modeled on the\n * center plane; depth variation is instead captured by vertical placement.\n *\n * If you change {@link CharacterModelConfig.depth}, you may keep these absolute\n * meter values (for physical fidelity) or re‑express them as fractions of the\n * new depth, but they should continue to represent small (≈0–5 cm) anatomical\n * offsets from the torso midline.\n */\nconst REGION_DEPTH_OFFSETS: Record<AnatomicalRegionPhysics, number> = {\n head: 0.05, // ≈5 cm forward from torso mid-plane to match cranial alignment\n neck: 0.02, // ≈2 cm forward; neck base is near, but not exactly on, center\n torso: 0.0, // Torso reference plane (mid‑chest / spine midpoint)\n arms: 0.05, // ≈5 cm forward; relaxed arms rest slightly in front of torso\n legs: 0.0, // Legs aligned to torso center plane; front/back handled elsewhere\n};\n\n/**\n * Coordinate Mapper for converting 2D pixel coordinates to 3D world coordinates.\n * \n * **Korean**: 좌표 변환기\n * \n * Provides bidirectional mapping between the 2D UI overlay coordinate system\n * and the 3D physics world coordinate system.\n * \n * @public\n * @category Physics System\n * @korean 좌표변환기\n */\nexport class CoordinateMapper {\n private readonly config: CharacterModelConfig;\n \n /**\n * Creates a new CoordinateMapper with optional custom configuration.\n * \n * @param config - Optional character model configuration\n */\n constructor(config: Partial<CharacterModelConfig> = {}) {\n this.config = { ...DEFAULT_CHARACTER_CONFIG, ...config };\n }\n \n /**\n * Converts a 2D pixel position to a 3D world position.\n * \n * **Korean**: 2D → 3D 변환\n * \n * Maps UI overlay pixel coordinates to 3D world space coordinates suitable\n * for physics collision detection.\n * \n * @param pixel2D - 2D pixel position (origin top-left)\n * @param region - Anatomical region for Z-depth calculation\n * @returns 3D world position in meters (origin at character center)\n * \n * @example\n * ```typescript\n * const mapper = new CoordinateMapper();\n * const vitalPoint = { position: { x: 100, y: 50 } };\n * const worldPos = mapper.pixel2DToWorld3D(vitalPoint.position, \"head\");\n * // worldPos: { x: 0, y: 1.45, z: 0.05 }\n * ```\n */\n pixel2DToWorld3D(pixel2D: Position, region: AnatomicalRegionPhysics): Position3D {\n // Normalize pixel coordinates to 0-1 range\n const normalizedX = pixel2D.x / this.config.overlayWidth;\n const normalizedY = pixel2D.y / this.config.overlayHeight;\n \n // Convert to world coordinates\n // X: Map from pixel space (0 = left, overlayWidth = right) to world space (-width/2 to +width/2)\n const worldX = (normalizedX - 0.5) * this.config.width;\n \n // Y: Map from pixel space (0 = top, overlayHeight = bottom) to world space (height to 0)\n // Invert Y-axis: pixel 0 = top = character head, pixel overlayHeight = bottom = character feet\n const worldY = (1 - normalizedY) * this.config.height;\n \n // Z: Use anatomical region to determine depth offset\n const worldZ = REGION_DEPTH_OFFSETS[region];\n \n return {\n x: worldX,\n y: worldY,\n z: worldZ,\n };\n }\n \n /**\n * Converts a 3D world position to a 2D pixel position.\n * \n * **Korean**: 3D → 2D 변환\n * \n * Maps 3D world space coordinates back to UI overlay pixel coordinates.\n * Useful for debugging and visual feedback.\n * \n * @param world3D - 3D world position in meters\n * @returns 2D pixel position (origin top-left)\n * \n * @example\n * ```typescript\n * const mapper = new CoordinateMapper();\n * const worldPos = { x: 0, y: 1.45, z: 0.05 };\n * const pixelPos = mapper.world3DToPixel2D(worldPos);\n * // pixelPos: { x: 100, y: 50 }\n * ```\n */\n world3DToPixel2D(world3D: Position3D): Position {\n // Convert world X to normalized 0-1 range\n const normalizedX = (world3D.x / this.config.width) + 0.5;\n \n // Convert world Y to normalized 0-1 range (inverted)\n const normalizedY = 1 - (world3D.y / this.config.height);\n \n // Convert to pixel coordinates\n return {\n x: Math.round(normalizedX * this.config.overlayWidth),\n y: Math.round(normalizedY * this.config.overlayHeight),\n };\n }\n \n /**\n * Converts a vital point's 2D position to 3D world position.\n * \n * **Korean**: 급소 위치 변환\n * \n * Convenience method that extracts position and region from a VitalPoint object.\n * \n * @param vitalPoint - Vital point with 2D position\n * @returns 3D world position\n * \n * @example\n * ```typescript\n * const mapper = new CoordinateMapper();\n * const temple = VITAL_POINTS_DATA.find(vp => vp.id === \"head_temple\");\n * const worldPos = mapper.vitalPointToWorld3D(temple);\n * ```\n */\n vitalPointToWorld3D(vitalPoint: VitalPoint): Position3D {\n // Infer region from vital point ID prefix\n const region = this.inferRegionFromId(vitalPoint.id);\n return this.pixel2DToWorld3D(vitalPoint.position, region);\n }\n \n /**\n * Infers the anatomical region from a vital point ID.\n * \n * **Korean**: 급소 ID에서 영역 추론\n * \n * @param vitalPointId - Vital point ID (e.g., \"head_temple\", \"torso_solar_plexus\")\n * @returns Anatomical region\n * \n * @private\n */\n private inferRegionFromId(vitalPointId: string): AnatomicalRegionPhysics {\n if (vitalPointId.startsWith(\"head_\")) return \"head\";\n if (vitalPointId.startsWith(\"neck_\")) return \"neck\";\n if (vitalPointId.startsWith(\"torso_\")) return \"torso\";\n if (vitalPointId.startsWith(\"arm_\")) return \"arms\";\n if (vitalPointId.startsWith(\"leg_\")) return \"legs\";\n \n // Default to torso if no clear region prefix\n return \"torso\";\n }\n \n /**\n * Calculates the distance between a 3D point and a vital point's 3D position.\n * \n * **Korean**: 거리 계산\n * \n * @param point3D - Point in 3D world space\n * @param vitalPoint - Vital point to measure distance to\n * @returns Distance in meters\n * \n * @example\n * ```typescript\n * const mapper = new CoordinateMapper();\n * const attackPoint = { x: 0, y: 1.5, z: 0.1 };\n * const temple = VITAL_POINTS_DATA.find(vp => vp.id === \"head_temple\");\n * const distance = mapper.distanceToVitalPoint(attackPoint, temple);\n * ```\n */\n distanceToVitalPoint(point3D: Position3D, vitalPoint: VitalPoint): number {\n const vitalPoint3D = this.vitalPointToWorld3D(vitalPoint);\n \n const dx = point3D.x - vitalPoint3D.x;\n const dy = point3D.y - vitalPoint3D.y;\n const dz = point3D.z - vitalPoint3D.z;\n \n return Math.sqrt(dx * dx + dy * dy + dz * dz);\n }\n \n /**\n * Finds the closest vital point to a 3D position within a given region.\n * \n * **Korean**: 가장 가까운 급소 찾기\n * \n * @param point3D - Point in 3D world space\n * @param vitalPoints - Array of vital points to search\n * @param region - Optional region filter\n * @returns Closest vital point and distance, or null if none found\n * \n * @example\n * ```typescript\n * const mapper = new CoordinateMapper();\n * const hitPoint = { x: 0, y: 1.7, z: 0.05 };\n * const result = mapper.findClosestVitalPoint(hitPoint, VITAL_POINTS_DATA, \"head\");\n * if (result) {\n * console.log(`Hit ${result.vitalPoint.names.english} at ${result.distance}m`);\n * }\n * ```\n */\n findClosestVitalPoint(\n point3D: Position3D,\n vitalPoints: readonly VitalPoint[],\n region?: AnatomicalRegionPhysics\n ): { vitalPoint: VitalPoint; distance: number } | null {\n let closestVitalPoint: VitalPoint | null = null;\n let closestDistance = Infinity;\n \n for (const vitalPoint of vitalPoints) {\n // Filter by region if specified\n if (region) {\n const vpRegion = this.inferRegionFromId(vitalPoint.id);\n if (vpRegion !== region) continue;\n }\n \n const distance = this.distanceToVitalPoint(point3D, vitalPoint);\n \n if (distance < closestDistance) {\n closestDistance = distance;\n closestVitalPoint = vitalPoint;\n }\n }\n \n if (closestVitalPoint === null) {\n return null;\n }\n \n return {\n vitalPoint: closestVitalPoint,\n distance: closestDistance,\n };\n }\n \n /**\n * Gets the current character model configuration.\n * \n * @returns Current configuration\n */\n getConfig(): CharacterModelConfig {\n return { ...this.config };\n }\n}\n\n/**\n * Default singleton instance for convenient access.\n * \n * **Korean**: 기본 인스턴스\n * \n * @example\n * ```typescript\n * import { defaultCoordinateMapper } from './CoordinateMapper';\n * \n * const worldPos = defaultCoordinateMapper.pixel2DToWorld3D({ x: 100, y: 50 }, \"head\");\n * ```\n */\nexport const defaultCoordinateMapper = new CoordinateMapper();\n"],"mappings":";;;;AAiGA,IAAM,2BAAiD;CACrD,QAAQ;CACR,OAAO;CACP,OAAO;CACP,cAAc;CACd,eAAe;CAChB;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA6BD,IAAM,uBAAgE;CACpE,MAAM;CACN,MAAM;CACN,OAAO;CACP,MAAM;CACN,MAAM;CACP;;;;;;;;;;;;;AAcD,IAAa,mBAAb,MAA8B;CAC5B;;;;;;CAOA,YAAY,SAAwC,EAAE,EAAE;EACtD,KAAK,SAAS;GAAE,GAAG;GAA0B,GAAG;GAAQ;;;;;;;;;;;;;;;;;;;;;;CAuB1D,iBAAiB,SAAmB,QAA6C;EAE/E,MAAM,cAAc,QAAQ,IAAI,KAAK,OAAO;EAC5C,MAAM,cAAc,QAAQ,IAAI,KAAK,OAAO;EAa5C,OAAO;GACL,IAVc,cAAc,MAAO,KAAK,OAAO;GAW/C,IAPc,IAAI,eAAe,KAAK,OAAO;GAQ7C,GALa,qBAAqB;GAMnC;;;;;;;;;;;;;;;;;;;;;CAsBH,iBAAiB,SAA+B;EAE9C,MAAM,cAAe,QAAQ,IAAI,KAAK,OAAO,QAAS;EAGtD,MAAM,cAAc,IAAK,QAAQ,IAAI,KAAK,OAAO;EAGjD,OAAO;GACL,GAAG,KAAK,MAAM,cAAc,KAAK,OAAO,aAAa;GACrD,GAAG,KAAK,MAAM,cAAc,KAAK,OAAO,cAAc;GACvD;;;;;;;;;;;;;;;;;;;CAoBH,oBAAoB,YAAoC;EAEtD,MAAM,SAAS,KAAK,kBAAkB,WAAW,GAAG;EACpD,OAAO,KAAK,iBAAiB,WAAW,UAAU,OAAO;;;;;;;;;;;;CAa3D,kBAA0B,cAA+C;EACvE,IAAI,aAAa,WAAW,QAAQ,EAAE,OAAO;EAC7C,IAAI,aAAa,WAAW,QAAQ,EAAE,OAAO;EAC7C,IAAI,aAAa,WAAW,SAAS,EAAE,OAAO;EAC9C,IAAI,aAAa,WAAW,OAAO,EAAE,OAAO;EAC5C,IAAI,aAAa,WAAW,OAAO,EAAE,OAAO;EAG5C,OAAO;;;;;;;;;;;;;;;;;;;CAoBT,qBAAqB,SAAqB,YAAgC;EACxE,MAAM,eAAe,KAAK,oBAAoB,WAAW;EAEzD,MAAM,KAAK,QAAQ,IAAI,aAAa;EACpC,MAAM,KAAK,QAAQ,IAAI,aAAa;EACpC,MAAM,KAAK,QAAQ,IAAI,aAAa;EAEpC,OAAO,KAAK,KAAK,KAAK,KAAK,KAAK,KAAK,KAAK,GAAG;;;;;;;;;;;;;;;;;;;;;;CAuB/C,sBACE,SACA,aACA,QACqD;EACrD,IAAI,oBAAuC;EAC3C,IAAI,kBAAkB;EAEtB,KAAK,MAAM,cAAc,aAAa;GAEpC,IAAI;QACe,KAAK,kBAAkB,WAAW,GAC/C,KAAa,QAAQ;;GAG3B,MAAM,WAAW,KAAK,qBAAqB,SAAS,WAAW;GAE/D,IAAI,WAAW,iBAAiB;IAC9B,kBAAkB;IAClB,oBAAoB;;;EAIxB,IAAI,sBAAsB,MACxB,OAAO;EAGT,OAAO;GACL,YAAY;GACZ,UAAU;GACX;;;;;;;CAQH,YAAkC;EAChC,OAAO,EAAE,GAAG,KAAK,QAAQ;;;AAgBU,IAAI,kBAAkB"}
@@ -1 +1 @@
1
- {"version":3,"file":"KnockbackPhysics.js","names":[],"sources":["../../../src/systems/physics/KnockbackPhysics.ts"],"sourcesContent":["/**\n * Knockback Physics System for realistic force-based displacement.\n * \n * **Korean**: 밀침 물리 시스템 (Knockback Physics System)\n * \n * Implements realistic combat knockback mechanics that calculate force-based\n * displacement when players are hit. The system integrates with BalanceSystem\n * for authentic stumbling/falling reactions.\n * \n * ## Knockback Mechanics\n * \n * Knockback force is determined by:\n * - Attack damage and technique type\n * - Impact angle and direction\n * - Current balance state\n * - Stance resistance modifiers (8 trigram stances)\n * \n * ## Balance Integration\n * \n * - High balance (>70%): Reduced knockback by 30%\n * - Medium balance (40-70%): Normal knockback\n * - Low balance (<40%): Increased knockback by 50%, stumbling\n * - Critical balance (<20%): Guaranteed fall, 2s recovery\n * \n * ## Performance\n * \n * Optimized for 60fps with efficient vector calculations and smooth\n * easing curves for realistic knockback feel.\n * \n * @module systems/physics/KnockbackPhysics\n * @category Physics System\n * @korean 밀침물리\n */\n\nimport * as THREE from 'three';\nimport { TrigramStance } from '@/types/common';\n\n/**\n * Balance state information for knockback calculations.\n * \n * **Korean**: 균형 상태 (Balance State)\n * \n * @public\n * @korean 균형상태\n */\nexport interface BalanceState {\n /** Current balance value (0-100) */\n readonly current: number;\n /** Maximum balance value (typically 100) */\n readonly max: number;\n}\n\n/**\n * Configuration for knockback force calculation.\n * \n * **Korean**: 밀침 설정 (Knockback Configuration)\n * \n * @public\n * @korean 밀침설정\n */\nexport interface KnockbackConfig {\n /** Force magnitude in Newtons */\n readonly force: number;\n /** Normalized attack direction vector (attacker → defender) */\n readonly direction: THREE.Vector3;\n /** Duration of knockback effect in seconds */\n readonly duration: number;\n /** Current balance state */\n readonly balanceState: BalanceState;\n /** Current trigram stance */\n readonly currentStance: TrigramStance;\n}\n\n/**\n * Result of knockback calculation.\n * \n * **Korean**: 밀침 결과 (Knockback Result)\n * \n * Contains displacement vector, animation timing, and fall determination.\n * \n * @public\n * @korean 밀침결과\n */\nexport interface KnockbackResult {\n /** Total knockback displacement vector in world space */\n readonly displacement: THREE.Vector3;\n /** Duration of knockback animation in seconds */\n readonly duration: number;\n /** Vulnerable state duration after knockback ends in seconds */\n readonly recoveryWindow: number;\n /** Whether fall animation should trigger */\n readonly shouldFall: boolean;\n}\n\n/**\n * Knockback Physics Engine.\n * \n * **Korean**: 밀침 물리 엔진\n * \n * Calculates realistic knockback displacement based on attack force,\n * stance resistance, and balance state. Integrates with BalanceSystem\n * for stumbling and falling mechanics.\n * \n * @example\n * ```typescript\n * const physics = new KnockbackPhysics();\n * \n * // Calculate knockback from heavy strike\n * const config: KnockbackConfig = {\n * force: 800, // 80 damage\n * direction: new THREE.Vector3(1, 0, 0).normalize(),\n * duration: 0.8,\n * balanceState: { current: 35, max: 100 }, // Low balance\n * currentStance: TrigramStance.LI, // Fire stance (low resistance)\n * };\n * \n * const result = physics.calculateKnockback(config, 80);\n * // Result: 2.5m base * 1.3 (Fire penalty) * 1.5 (low balance) = ~4.9m\n * // shouldFall: false (balance > 20%)\n * // recoveryWindow: 1.05s (0.7s * 1.5 for low balance)\n * ```\n * \n * @public\n * @korean 밀침물리\n */\nexport class KnockbackPhysics {\n /**\n * Calculates knockback displacement and effects.\n * \n * **Korean**: 밀침 계산 (Calculate Knockback)\n * \n * Determines knockback distance, duration, and fall state based on:\n * 1. Base knockback from damage amount\n * 2. Stance resistance modifier (8 trigram effects)\n * 3. Balance state modifier (stumbling/falling)\n * 4. Recovery window (vulnerable state after)\n * \n * @param config - Knockback configuration with force and state\n * @param attackDamage - Damage dealt by attack (0-100+)\n * @returns Knockback result with displacement and timing\n * \n * @example\n * ```typescript\n * // Light strike on stable player\n * const light = physics.calculateKnockback({\n * force: 300,\n * direction: attackVector,\n * duration: 0.3,\n * balanceState: { current: 85, max: 100 },\n * currentStance: TrigramStance.GEON,\n * }, 30);\n * // Result: ~0.3m knockback (0.5m * 0.9 Geon * 0.7 high balance)\n * \n * // Critical strike on low-balance player\n * const critical = physics.calculateKnockback({\n * force: 1200,\n * direction: attackVector,\n * duration: 1.2,\n * balanceState: { current: 15, max: 100 },\n * currentStance: TrigramStance.SON,\n * }, 110);\n * // Result: ~9.6m knockback, shouldFall: true, 3.0s recovery\n * ```\n * \n * @public\n * @korean 밀침계산\n */\n calculateKnockback(config: KnockbackConfig, attackDamage: number): KnockbackResult {\n // 1. Base knockback distance from damage\n let baseDistance = this.getBaseKnockbackDistance(attackDamage);\n \n // 2. Apply stance resistance modifier\n const stanceResistance = this.getStanceResistanceModifier(config.currentStance);\n baseDistance *= (1 - stanceResistance);\n \n // 3. Apply balance modifier (stumbling/falling)\n const balanceModifier = this.getBalanceModifier(config.balanceState);\n const finalDistance = baseDistance * balanceModifier;\n \n // 4. Calculate displacement vector\n const displacement = config.direction.clone().multiplyScalar(finalDistance);\n \n // 5. Determine duration based on distance\n const duration = this.calculateKnockbackDuration(finalDistance);\n \n // 6. Calculate recovery window (vulnerable state)\n const recoveryWindow = this.calculateRecoveryWindow(attackDamage, config.balanceState);\n \n // 7. Check if balance is critical (should fall)\n const shouldFall = config.balanceState.current < 20;\n \n return {\n displacement,\n duration,\n recoveryWindow,\n shouldFall,\n };\n }\n\n /**\n * Gets base knockback distance from damage amount.\n * \n * **Korean**: 기본 밀침 거리 (Base Knockback Distance)\n * \n * Damage thresholds:\n * - Light (20-40): 0.5m knockback, 0.3s duration\n * - Medium (40-70): 1.2m knockback, 0.5s duration\n * - Heavy (70-100): 2.5m knockback, 0.8s duration\n * - Critical (100+): 4.0m knockback, 1.2s duration + fall\n * \n * @param damage - Attack damage amount\n * @returns Base knockback distance in meters\n * \n * @private\n * @korean 기본밀침거리\n */\n private getBaseKnockbackDistance(damage: number): number {\n if (damage < 40) return 0.5; // Light strike\n if (damage < 70) return 1.2; // Medium strike\n if (damage < 100) return 2.5; // Heavy strike\n return 4.0; // Critical strike\n }\n\n /**\n * Gets stance resistance modifier for knockback.\n * \n * **Korean**: 자세 저항 배율 (Stance Resistance Modifier)\n * \n * Trigram stance resistance modifiers:\n * - ☶ 간 (Gan/Mountain): +40% resistance (defensive mastery)\n * - ☷ 곤 (Gon/Earth): +30% resistance (grounded techniques)\n * - ☰ 건 (Geon/Heaven): +10% resistance (balanced force)\n * - ☵ 감 (Gam/Water): 0% (neutral, adaptive)\n * - ☱ 태 (Tae/Lake): 0% (neutral, fluid)\n * - ☳ 진 (Jin/Thunder): -10% resistance (explosive power)\n * - ☴ 손 (Son/Wind): -20% resistance (fluid, mobile)\n * - ☲ 리 (Li/Fire): -30% resistance (aggressive, exposed)\n * \n * @param stance - Current trigram stance\n * @returns Resistance modifier (-0.3 to +0.4)\n * \n * @private\n * @korean 자세저항배율\n */\n private getStanceResistanceModifier(stance: TrigramStance): number {\n const resistances: Record<TrigramStance, number> = {\n [TrigramStance.GAN]: 0.40, // Mountain: +40% resistance\n [TrigramStance.GON]: 0.30, // Earth: +30% resistance\n [TrigramStance.GEON]: 0.10, // Heaven: +10% resistance\n [TrigramStance.GAM]: 0.00, // Water: neutral\n [TrigramStance.TAE]: 0.00, // Lake: neutral\n [TrigramStance.JIN]: -0.10, // Thunder: -10% resistance\n [TrigramStance.SON]: -0.20, // Wind: -20% resistance (fluid)\n [TrigramStance.LI]: -0.30, // Fire: -30% resistance (aggressive)\n };\n return resistances[stance];\n }\n\n /**\n * Gets balance modifier for knockback distance.\n * \n * **Korean**: 균형 배율 (Balance Modifier)\n * \n * Balance thresholds:\n * - High balance (>70%): 0.70x (30% reduction)\n * - Medium balance (40-70%): 1.00x (normal)\n * - Low balance (20-40%): 1.50x (50% increase, stumbling)\n * - Critical balance (<20%): 2.00x (100% increase, falling)\n * \n * @param balanceState - Current balance state\n * @returns Balance multiplier (0.7 to 2.0)\n * \n * @private\n * @korean 균형배율\n */\n private getBalanceModifier(balanceState: BalanceState): number {\n const balancePercent = (balanceState.current / balanceState.max) * 100;\n \n if (balancePercent > 70) return 0.70; // High balance: -30% knockback\n if (balancePercent > 40) return 1.00; // Normal knockback\n if (balancePercent > 20) return 1.50; // Low balance: +50% knockback\n return 2.00; // Critical balance: +100% knockback\n }\n\n /**\n * Calculates knockback animation duration.\n * \n * **Korean**: 밀침 지속시간 계산 (Calculate Knockback Duration)\n * \n * Linear relationship between distance and duration:\n * - 0.5m = 0.3s (light)\n * - 1.2m = 0.48s\n * - 2.5m = 0.86s\n * - 4.0m = 1.2s (critical)\n * \n * @param distance - Knockback distance in meters\n * @returns Duration in seconds\n * \n * @private\n * @korean 밀침지속시간계산\n */\n private calculateKnockbackDuration(distance: number): number {\n // Linear relationship: 0.5m = 0.3s, 4.0m = 1.2s\n return 0.3 + (distance / 4.0) * 0.9;\n }\n\n /**\n * Calculates recovery window (vulnerability duration).\n * \n * **Korean**: 회복 대기시간 계산 (Calculate Recovery Window)\n * \n * Recovery windows by damage:\n * - Light (<40): 0.2s vulnerability\n * - Medium (40-70): 0.4s vulnerability\n * - Heavy (70-100): 0.7s vulnerability\n * - Critical (100+): 1.5s vulnerability + grounded\n * \n * Low balance increases vulnerability by 50%.\n * \n * @param damage - Attack damage amount\n * @param balanceState - Current balance state\n * @returns Vulnerability duration in seconds\n * \n * @private\n * @korean 회복대기시간계산\n */\n private calculateRecoveryWindow(damage: number, balanceState: BalanceState): number {\n // Base recovery time from damage\n const baseRecoveryTime = damage < 40 ? 0.2 : damage < 70 ? 0.4 : damage < 100 ? 0.7 : 1.5;\n \n // Increase recovery time if balance is low (more vulnerable)\n const balancePercent = (balanceState.current / balanceState.max) * 100;\n const balanceModifier = balancePercent < 40 ? 1.5 : 1.0;\n \n return baseRecoveryTime * balanceModifier;\n }\n\n /**\n * Applies knockback force to player position over time.\n * \n * **Korean**: 밀침 힘 적용 (Apply Knockback Force)\n * \n * Uses smooth ease-out cubic curve for realistic knockback feel:\n * - Fast initial displacement (impact)\n * - Gradual deceleration (friction/resistance)\n * - Smooth stop at final position\n * \n * @param playerPosition - Current player position\n * @param result - Knockback result with displacement\n * @param deltaTime - Time elapsed since knockback started\n * @param progress - Knockback progress (0 to 1)\n * @returns New player position\n * \n * @example\n * ```typescript\n * // In game loop (60fps)\n * const newPosition = physics.applyKnockbackForce(\n * playerPosition,\n * knockbackResult,\n * deltaTime,\n * elapsedTime / knockbackResult.duration\n * );\n * \n * // Progress 0.0: At original position\n * // Progress 0.5: ~88% of total displacement (ease-out)\n * // Progress 1.0: At final position\n * ```\n * \n * @public\n * @korean 밀침힘적용\n */\n applyKnockbackForce(\n playerPosition: THREE.Vector3,\n result: KnockbackResult,\n _deltaTime: number,\n progress: number\n ): THREE.Vector3 {\n // Clamp progress to [0, 1]\n const clampedProgress = Math.max(0, Math.min(1, progress));\n \n // Smooth knockback curve (ease-out cubic)\n // Fast initial impact, gradual deceleration\n const easedProgress = 1 - Math.pow(1 - clampedProgress, 3);\n \n // Calculate position along knockback path\n const currentDisplacement = result.displacement.clone().multiplyScalar(easedProgress);\n \n return playerPosition.clone().add(currentDisplacement);\n }\n\n /**\n * Checks if player is currently in knockback state.\n * \n * **Korean**: 밀침 상태 확인 (Check Knockback State)\n * \n * @param elapsedTime - Time elapsed since knockback started\n * @param duration - Total knockback duration\n * @returns True if still in knockback animation\n * \n * @public\n * @korean 밀침상태확인\n */\n isInKnockback(elapsedTime: number, duration: number): boolean {\n return elapsedTime < duration;\n }\n\n /**\n * Checks if player is in recovery window (vulnerable state).\n * \n * **Korean**: 회복 대기 확인 (Check Recovery Window)\n * \n * @param elapsedTime - Time elapsed since knockback ended\n * @param recoveryWindow - Total recovery window duration\n * @returns True if in vulnerable recovery state\n * \n * @public\n * @korean 회복대기확인\n */\n isInRecoveryWindow(elapsedTime: number, recoveryWindow: number): boolean {\n return elapsedTime < recoveryWindow;\n }\n\n /**\n * Gets bilingual Korean-English name for knockback state.\n * \n * **Korean**: 밀침 상태 이름 (Knockback State Name)\n * \n * @param shouldFall - Whether knockback causes fall\n * @returns Korean and English state names\n * \n * @public\n * @korean 밀침상태이름\n */\n static getKnockbackStateName(shouldFall: boolean): { korean: string; english: string } {\n if (shouldFall) {\n return {\n korean: \"넘어짐\",\n english: \"Falling\",\n };\n }\n \n return {\n korean: \"밀침\",\n english: \"Knockback\",\n };\n }\n\n /**\n * Gets bilingual Korean-English name for recovery state.\n * \n * **Korean**: 회복 상태 이름 (Recovery State Name)\n * \n * @returns Korean and English state names\n * \n * @public\n * @korean 회복상태이름\n */\n getRecoveryStateName(): { korean: string; english: string } {\n return {\n korean: \"회복\",\n english: \"Recovery\",\n };\n }\n\n /**\n * Gets bilingual Korean-English name for stumbling state.\n * \n * **Korean**: 휘청거림 상태 이름 (Stumbling State Name)\n * \n * @returns Korean and English state names\n * \n * @public\n * @korean 휘청거림상태이름\n */\n getStumblingStateName(): { korean: string; english: string } {\n return {\n korean: \"휘청거림\",\n english: \"Stumbling\",\n };\n }\n}\n\nexport default KnockbackPhysics;\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA6HA,IAAa,mBAAb,MAA8B;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA0C5B,mBAAmB,QAAyB,cAAuC;EAEjF,IAAI,eAAe,KAAK,yBAAyB,aAAa;EAG9D,MAAM,mBAAmB,KAAK,4BAA4B,OAAO,cAAc;AAC/E,kBAAiB,IAAI;EAGrB,MAAM,kBAAkB,KAAK,mBAAmB,OAAO,aAAa;EACpE,MAAM,gBAAgB,eAAe;AAcrC,SAAO;GACL,cAZmB,OAAO,UAAU,OAAO,CAAC,eAAe,cAY3D;GACA,UAVe,KAAK,2BAA2B,cAU/C;GACA,gBARqB,KAAK,wBAAwB,cAAc,OAAO,aAQvE;GACA,YANiB,OAAO,aAAa,UAAU;GAOhD;;;;;;;;;;;;;;;;;;;CAoBH,yBAAiC,QAAwB;AACvD,MAAI,SAAS,GAAI,QAAO;AACxB,MAAI,SAAS,GAAI,QAAO;AACxB,MAAI,SAAS,IAAK,QAAO;AACzB,SAAO;;;;;;;;;;;;;;;;;;;;;;;CAwBT,4BAAoC,QAA+B;AAWjE,SAAO;IATJ,cAAc,MAAM;IACpB,cAAc,MAAM;IACpB,cAAc,OAAO;IACrB,cAAc,MAAM;IACpB,cAAc,MAAM;IACpB,cAAc,MAAM;IACpB,cAAc,MAAM;IACpB,cAAc,KAAK;GAEf,CAAY;;;;;;;;;;;;;;;;;;;CAoBrB,mBAA2B,cAAoC;EAC7D,MAAM,iBAAkB,aAAa,UAAU,aAAa,MAAO;AAEnE,MAAI,iBAAiB,GAAI,QAAO;AAChC,MAAI,iBAAiB,GAAI,QAAO;AAChC,MAAI,iBAAiB,GAAI,QAAO;AAChC,SAAO;;;;;;;;;;;;;;;;;;;CAoBT,2BAAmC,UAA0B;AAE3D,SAAO,KAAO,WAAW,IAAO;;;;;;;;;;;;;;;;;;;;;;CAuBlC,wBAAgC,QAAgB,cAAoC;AAQlF,UANyB,SAAS,KAAK,KAAM,SAAS,KAAK,KAAM,SAAS,MAAM,KAAM,QAG9D,aAAa,UAAU,aAAa,MAAO,MAC1B,KAAK,MAAM;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAuCtD,oBACE,gBACA,QACA,YACA,UACe;EAMf,MAAM,gBAAgB,IAAI,KAAK,IAAI,IAJX,KAAK,IAAI,GAAG,KAAK,IAAI,GAAG,SAAS,CAIlB,EAAiB,EAAE;EAG1D,MAAM,sBAAsB,OAAO,aAAa,OAAO,CAAC,eAAe,cAAc;AAErF,SAAO,eAAe,OAAO,CAAC,IAAI,oBAAoB;;;;;;;;;;;;;;CAexD,cAAc,aAAqB,UAA2B;AAC5D,SAAO,cAAc;;;;;;;;;;;;;;CAevB,mBAAmB,aAAqB,gBAAiC;AACvE,SAAO,cAAc;;;;;;;;;;;;;CAcvB,OAAO,sBAAsB,YAA0D;AACrF,MAAI,WACF,QAAO;GACL,QAAQ;GACR,SAAS;GACV;AAGH,SAAO;GACL,QAAQ;GACR,SAAS;GACV;;;;;;;;;;;;CAaH,uBAA4D;AAC1D,SAAO;GACL,QAAQ;GACR,SAAS;GACV;;;;;;;;;;;;CAaH,wBAA6D;AAC3D,SAAO;GACL,QAAQ;GACR,SAAS;GACV"}
1
+ {"version":3,"file":"KnockbackPhysics.js","names":[],"sources":["../../../src/systems/physics/KnockbackPhysics.ts"],"sourcesContent":["/**\n * Knockback Physics System for realistic force-based displacement.\n * \n * **Korean**: 밀침 물리 시스템 (Knockback Physics System)\n * \n * Implements realistic combat knockback mechanics that calculate force-based\n * displacement when players are hit. The system integrates with BalanceSystem\n * for authentic stumbling/falling reactions.\n * \n * ## Knockback Mechanics\n * \n * Knockback force is determined by:\n * - Attack damage and technique type\n * - Impact angle and direction\n * - Current balance state\n * - Stance resistance modifiers (8 trigram stances)\n * \n * ## Balance Integration\n * \n * - High balance (>70%): Reduced knockback by 30%\n * - Medium balance (40-70%): Normal knockback\n * - Low balance (<40%): Increased knockback by 50%, stumbling\n * - Critical balance (<20%): Guaranteed fall, 2s recovery\n * \n * ## Performance\n * \n * Optimized for 60fps with efficient vector calculations and smooth\n * easing curves for realistic knockback feel.\n * \n * @module systems/physics/KnockbackPhysics\n * @category Physics System\n * @korean 밀침물리\n */\n\nimport * as THREE from 'three';\nimport { TrigramStance } from '@/types/common';\n\n/**\n * Balance state information for knockback calculations.\n * \n * **Korean**: 균형 상태 (Balance State)\n * \n * @public\n * @korean 균형상태\n */\nexport interface BalanceState {\n /** Current balance value (0-100) */\n readonly current: number;\n /** Maximum balance value (typically 100) */\n readonly max: number;\n}\n\n/**\n * Configuration for knockback force calculation.\n * \n * **Korean**: 밀침 설정 (Knockback Configuration)\n * \n * @public\n * @korean 밀침설정\n */\nexport interface KnockbackConfig {\n /** Force magnitude in Newtons */\n readonly force: number;\n /** Normalized attack direction vector (attacker → defender) */\n readonly direction: THREE.Vector3;\n /** Duration of knockback effect in seconds */\n readonly duration: number;\n /** Current balance state */\n readonly balanceState: BalanceState;\n /** Current trigram stance */\n readonly currentStance: TrigramStance;\n}\n\n/**\n * Result of knockback calculation.\n * \n * **Korean**: 밀침 결과 (Knockback Result)\n * \n * Contains displacement vector, animation timing, and fall determination.\n * \n * @public\n * @korean 밀침결과\n */\nexport interface KnockbackResult {\n /** Total knockback displacement vector in world space */\n readonly displacement: THREE.Vector3;\n /** Duration of knockback animation in seconds */\n readonly duration: number;\n /** Vulnerable state duration after knockback ends in seconds */\n readonly recoveryWindow: number;\n /** Whether fall animation should trigger */\n readonly shouldFall: boolean;\n}\n\n/**\n * Knockback Physics Engine.\n * \n * **Korean**: 밀침 물리 엔진\n * \n * Calculates realistic knockback displacement based on attack force,\n * stance resistance, and balance state. Integrates with BalanceSystem\n * for stumbling and falling mechanics.\n * \n * @example\n * ```typescript\n * const physics = new KnockbackPhysics();\n * \n * // Calculate knockback from heavy strike\n * const config: KnockbackConfig = {\n * force: 800, // 80 damage\n * direction: new THREE.Vector3(1, 0, 0).normalize(),\n * duration: 0.8,\n * balanceState: { current: 35, max: 100 }, // Low balance\n * currentStance: TrigramStance.LI, // Fire stance (low resistance)\n * };\n * \n * const result = physics.calculateKnockback(config, 80);\n * // Result: 2.5m base * 1.3 (Fire penalty) * 1.5 (low balance) = ~4.9m\n * // shouldFall: false (balance > 20%)\n * // recoveryWindow: 1.05s (0.7s * 1.5 for low balance)\n * ```\n * \n * @public\n * @korean 밀침물리\n */\nexport class KnockbackPhysics {\n /**\n * Calculates knockback displacement and effects.\n * \n * **Korean**: 밀침 계산 (Calculate Knockback)\n * \n * Determines knockback distance, duration, and fall state based on:\n * 1. Base knockback from damage amount\n * 2. Stance resistance modifier (8 trigram effects)\n * 3. Balance state modifier (stumbling/falling)\n * 4. Recovery window (vulnerable state after)\n * \n * @param config - Knockback configuration with force and state\n * @param attackDamage - Damage dealt by attack (0-100+)\n * @returns Knockback result with displacement and timing\n * \n * @example\n * ```typescript\n * // Light strike on stable player\n * const light = physics.calculateKnockback({\n * force: 300,\n * direction: attackVector,\n * duration: 0.3,\n * balanceState: { current: 85, max: 100 },\n * currentStance: TrigramStance.GEON,\n * }, 30);\n * // Result: ~0.3m knockback (0.5m * 0.9 Geon * 0.7 high balance)\n * \n * // Critical strike on low-balance player\n * const critical = physics.calculateKnockback({\n * force: 1200,\n * direction: attackVector,\n * duration: 1.2,\n * balanceState: { current: 15, max: 100 },\n * currentStance: TrigramStance.SON,\n * }, 110);\n * // Result: ~9.6m knockback, shouldFall: true, 3.0s recovery\n * ```\n * \n * @public\n * @korean 밀침계산\n */\n calculateKnockback(config: KnockbackConfig, attackDamage: number): KnockbackResult {\n // 1. Base knockback distance from damage\n let baseDistance = this.getBaseKnockbackDistance(attackDamage);\n \n // 2. Apply stance resistance modifier\n const stanceResistance = this.getStanceResistanceModifier(config.currentStance);\n baseDistance *= (1 - stanceResistance);\n \n // 3. Apply balance modifier (stumbling/falling)\n const balanceModifier = this.getBalanceModifier(config.balanceState);\n const finalDistance = baseDistance * balanceModifier;\n \n // 4. Calculate displacement vector\n const displacement = config.direction.clone().multiplyScalar(finalDistance);\n \n // 5. Determine duration based on distance\n const duration = this.calculateKnockbackDuration(finalDistance);\n \n // 6. Calculate recovery window (vulnerable state)\n const recoveryWindow = this.calculateRecoveryWindow(attackDamage, config.balanceState);\n \n // 7. Check if balance is critical (should fall)\n const shouldFall = config.balanceState.current < 20;\n \n return {\n displacement,\n duration,\n recoveryWindow,\n shouldFall,\n };\n }\n\n /**\n * Gets base knockback distance from damage amount.\n * \n * **Korean**: 기본 밀침 거리 (Base Knockback Distance)\n * \n * Damage thresholds:\n * - Light (20-40): 0.5m knockback, 0.3s duration\n * - Medium (40-70): 1.2m knockback, 0.5s duration\n * - Heavy (70-100): 2.5m knockback, 0.8s duration\n * - Critical (100+): 4.0m knockback, 1.2s duration + fall\n * \n * @param damage - Attack damage amount\n * @returns Base knockback distance in meters\n * \n * @private\n * @korean 기본밀침거리\n */\n private getBaseKnockbackDistance(damage: number): number {\n if (damage < 40) return 0.5; // Light strike\n if (damage < 70) return 1.2; // Medium strike\n if (damage < 100) return 2.5; // Heavy strike\n return 4.0; // Critical strike\n }\n\n /**\n * Gets stance resistance modifier for knockback.\n * \n * **Korean**: 자세 저항 배율 (Stance Resistance Modifier)\n * \n * Trigram stance resistance modifiers:\n * - ☶ 간 (Gan/Mountain): +40% resistance (defensive mastery)\n * - ☷ 곤 (Gon/Earth): +30% resistance (grounded techniques)\n * - ☰ 건 (Geon/Heaven): +10% resistance (balanced force)\n * - ☵ 감 (Gam/Water): 0% (neutral, adaptive)\n * - ☱ 태 (Tae/Lake): 0% (neutral, fluid)\n * - ☳ 진 (Jin/Thunder): -10% resistance (explosive power)\n * - ☴ 손 (Son/Wind): -20% resistance (fluid, mobile)\n * - ☲ 리 (Li/Fire): -30% resistance (aggressive, exposed)\n * \n * @param stance - Current trigram stance\n * @returns Resistance modifier (-0.3 to +0.4)\n * \n * @private\n * @korean 자세저항배율\n */\n private getStanceResistanceModifier(stance: TrigramStance): number {\n const resistances: Record<TrigramStance, number> = {\n [TrigramStance.GAN]: 0.40, // Mountain: +40% resistance\n [TrigramStance.GON]: 0.30, // Earth: +30% resistance\n [TrigramStance.GEON]: 0.10, // Heaven: +10% resistance\n [TrigramStance.GAM]: 0.00, // Water: neutral\n [TrigramStance.TAE]: 0.00, // Lake: neutral\n [TrigramStance.JIN]: -0.10, // Thunder: -10% resistance\n [TrigramStance.SON]: -0.20, // Wind: -20% resistance (fluid)\n [TrigramStance.LI]: -0.30, // Fire: -30% resistance (aggressive)\n };\n return resistances[stance];\n }\n\n /**\n * Gets balance modifier for knockback distance.\n * \n * **Korean**: 균형 배율 (Balance Modifier)\n * \n * Balance thresholds:\n * - High balance (>70%): 0.70x (30% reduction)\n * - Medium balance (40-70%): 1.00x (normal)\n * - Low balance (20-40%): 1.50x (50% increase, stumbling)\n * - Critical balance (<20%): 2.00x (100% increase, falling)\n * \n * @param balanceState - Current balance state\n * @returns Balance multiplier (0.7 to 2.0)\n * \n * @private\n * @korean 균형배율\n */\n private getBalanceModifier(balanceState: BalanceState): number {\n const balancePercent = (balanceState.current / balanceState.max) * 100;\n \n if (balancePercent > 70) return 0.70; // High balance: -30% knockback\n if (balancePercent > 40) return 1.00; // Normal knockback\n if (balancePercent > 20) return 1.50; // Low balance: +50% knockback\n return 2.00; // Critical balance: +100% knockback\n }\n\n /**\n * Calculates knockback animation duration.\n * \n * **Korean**: 밀침 지속시간 계산 (Calculate Knockback Duration)\n * \n * Linear relationship between distance and duration:\n * - 0.5m = 0.3s (light)\n * - 1.2m = 0.48s\n * - 2.5m = 0.86s\n * - 4.0m = 1.2s (critical)\n * \n * @param distance - Knockback distance in meters\n * @returns Duration in seconds\n * \n * @private\n * @korean 밀침지속시간계산\n */\n private calculateKnockbackDuration(distance: number): number {\n // Linear relationship: 0.5m = 0.3s, 4.0m = 1.2s\n return 0.3 + (distance / 4.0) * 0.9;\n }\n\n /**\n * Calculates recovery window (vulnerability duration).\n * \n * **Korean**: 회복 대기시간 계산 (Calculate Recovery Window)\n * \n * Recovery windows by damage:\n * - Light (<40): 0.2s vulnerability\n * - Medium (40-70): 0.4s vulnerability\n * - Heavy (70-100): 0.7s vulnerability\n * - Critical (100+): 1.5s vulnerability + grounded\n * \n * Low balance increases vulnerability by 50%.\n * \n * @param damage - Attack damage amount\n * @param balanceState - Current balance state\n * @returns Vulnerability duration in seconds\n * \n * @private\n * @korean 회복대기시간계산\n */\n private calculateRecoveryWindow(damage: number, balanceState: BalanceState): number {\n // Base recovery time from damage\n const baseRecoveryTime = damage < 40 ? 0.2 : damage < 70 ? 0.4 : damage < 100 ? 0.7 : 1.5;\n \n // Increase recovery time if balance is low (more vulnerable)\n const balancePercent = (balanceState.current / balanceState.max) * 100;\n const balanceModifier = balancePercent < 40 ? 1.5 : 1.0;\n \n return baseRecoveryTime * balanceModifier;\n }\n\n /**\n * Applies knockback force to player position over time.\n * \n * **Korean**: 밀침 힘 적용 (Apply Knockback Force)\n * \n * Uses smooth ease-out cubic curve for realistic knockback feel:\n * - Fast initial displacement (impact)\n * - Gradual deceleration (friction/resistance)\n * - Smooth stop at final position\n * \n * @param playerPosition - Current player position\n * @param result - Knockback result with displacement\n * @param deltaTime - Time elapsed since knockback started\n * @param progress - Knockback progress (0 to 1)\n * @returns New player position\n * \n * @example\n * ```typescript\n * // In game loop (60fps)\n * const newPosition = physics.applyKnockbackForce(\n * playerPosition,\n * knockbackResult,\n * deltaTime,\n * elapsedTime / knockbackResult.duration\n * );\n * \n * // Progress 0.0: At original position\n * // Progress 0.5: ~88% of total displacement (ease-out)\n * // Progress 1.0: At final position\n * ```\n * \n * @public\n * @korean 밀침힘적용\n */\n applyKnockbackForce(\n playerPosition: THREE.Vector3,\n result: KnockbackResult,\n _deltaTime: number,\n progress: number\n ): THREE.Vector3 {\n // Clamp progress to [0, 1]\n const clampedProgress = Math.max(0, Math.min(1, progress));\n \n // Smooth knockback curve (ease-out cubic)\n // Fast initial impact, gradual deceleration\n const easedProgress = 1 - Math.pow(1 - clampedProgress, 3);\n \n // Calculate position along knockback path\n const currentDisplacement = result.displacement.clone().multiplyScalar(easedProgress);\n \n return playerPosition.clone().add(currentDisplacement);\n }\n\n /**\n * Checks if player is currently in knockback state.\n * \n * **Korean**: 밀침 상태 확인 (Check Knockback State)\n * \n * @param elapsedTime - Time elapsed since knockback started\n * @param duration - Total knockback duration\n * @returns True if still in knockback animation\n * \n * @public\n * @korean 밀침상태확인\n */\n isInKnockback(elapsedTime: number, duration: number): boolean {\n return elapsedTime < duration;\n }\n\n /**\n * Checks if player is in recovery window (vulnerable state).\n * \n * **Korean**: 회복 대기 확인 (Check Recovery Window)\n * \n * @param elapsedTime - Time elapsed since knockback ended\n * @param recoveryWindow - Total recovery window duration\n * @returns True if in vulnerable recovery state\n * \n * @public\n * @korean 회복대기확인\n */\n isInRecoveryWindow(elapsedTime: number, recoveryWindow: number): boolean {\n return elapsedTime < recoveryWindow;\n }\n\n /**\n * Gets bilingual Korean-English name for knockback state.\n * \n * **Korean**: 밀침 상태 이름 (Knockback State Name)\n * \n * @param shouldFall - Whether knockback causes fall\n * @returns Korean and English state names\n * \n * @public\n * @korean 밀침상태이름\n */\n static getKnockbackStateName(shouldFall: boolean): { korean: string; english: string } {\n if (shouldFall) {\n return {\n korean: \"넘어짐\",\n english: \"Falling\",\n };\n }\n \n return {\n korean: \"밀침\",\n english: \"Knockback\",\n };\n }\n\n /**\n * Gets bilingual Korean-English name for recovery state.\n * \n * **Korean**: 회복 상태 이름 (Recovery State Name)\n * \n * @returns Korean and English state names\n * \n * @public\n * @korean 회복상태이름\n */\n getRecoveryStateName(): { korean: string; english: string } {\n return {\n korean: \"회복\",\n english: \"Recovery\",\n };\n }\n\n /**\n * Gets bilingual Korean-English name for stumbling state.\n * \n * **Korean**: 휘청거림 상태 이름 (Stumbling State Name)\n * \n * @returns Korean and English state names\n * \n * @public\n * @korean 휘청거림상태이름\n */\n getStumblingStateName(): { korean: string; english: string } {\n return {\n korean: \"휘청거림\",\n english: \"Stumbling\",\n };\n }\n}\n\nexport default KnockbackPhysics;\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA6HA,IAAa,mBAAb,MAA8B;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA0C5B,mBAAmB,QAAyB,cAAuC;EAEjF,IAAI,eAAe,KAAK,yBAAyB,aAAa;EAG9D,MAAM,mBAAmB,KAAK,4BAA4B,OAAO,cAAc;EAC/E,gBAAiB,IAAI;EAGrB,MAAM,kBAAkB,KAAK,mBAAmB,OAAO,aAAa;EACpE,MAAM,gBAAgB,eAAe;EAcrC,OAAO;GACL,cAZmB,OAAO,UAAU,OAAO,CAAC,eAAe,cAY3D;GACA,UAVe,KAAK,2BAA2B,cAU/C;GACA,gBARqB,KAAK,wBAAwB,cAAc,OAAO,aAQvE;GACA,YANiB,OAAO,aAAa,UAAU;GAOhD;;;;;;;;;;;;;;;;;;;CAoBH,yBAAiC,QAAwB;EACvD,IAAI,SAAS,IAAI,OAAO;EACxB,IAAI,SAAS,IAAI,OAAO;EACxB,IAAI,SAAS,KAAK,OAAO;EACzB,OAAO;;;;;;;;;;;;;;;;;;;;;;;CAwBT,4BAAoC,QAA+B;EAWjE,OAAO;IATJ,cAAc,MAAM;IACpB,cAAc,MAAM;IACpB,cAAc,OAAO;IACrB,cAAc,MAAM;IACpB,cAAc,MAAM;IACpB,cAAc,MAAM;IACpB,cAAc,MAAM;IACpB,cAAc,KAAK;GAEf,CAAY;;;;;;;;;;;;;;;;;;;CAoBrB,mBAA2B,cAAoC;EAC7D,MAAM,iBAAkB,aAAa,UAAU,aAAa,MAAO;EAEnE,IAAI,iBAAiB,IAAI,OAAO;EAChC,IAAI,iBAAiB,IAAI,OAAO;EAChC,IAAI,iBAAiB,IAAI,OAAO;EAChC,OAAO;;;;;;;;;;;;;;;;;;;CAoBT,2BAAmC,UAA0B;EAE3D,OAAO,KAAO,WAAW,IAAO;;;;;;;;;;;;;;;;;;;;;;CAuBlC,wBAAgC,QAAgB,cAAoC;EAQlF,QANyB,SAAS,KAAK,KAAM,SAAS,KAAK,KAAM,SAAS,MAAM,KAAM,QAG9D,aAAa,UAAU,aAAa,MAAO,MAC1B,KAAK,MAAM;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAuCtD,oBACE,gBACA,QACA,YACA,UACe;EAMf,MAAM,gBAAgB,IAAI,KAAK,IAAI,IAJX,KAAK,IAAI,GAAG,KAAK,IAAI,GAAG,SAAS,CAIlB,EAAiB,EAAE;EAG1D,MAAM,sBAAsB,OAAO,aAAa,OAAO,CAAC,eAAe,cAAc;EAErF,OAAO,eAAe,OAAO,CAAC,IAAI,oBAAoB;;;;;;;;;;;;;;CAexD,cAAc,aAAqB,UAA2B;EAC5D,OAAO,cAAc;;;;;;;;;;;;;;CAevB,mBAAmB,aAAqB,gBAAiC;EACvE,OAAO,cAAc;;;;;;;;;;;;;CAcvB,OAAO,sBAAsB,YAA0D;EACrF,IAAI,YACF,OAAO;GACL,QAAQ;GACR,SAAS;GACV;EAGH,OAAO;GACL,QAAQ;GACR,SAAS;GACV;;;;;;;;;;;;CAaH,uBAA4D;EAC1D,OAAO;GACL,QAAQ;GACR,SAAS;GACV;;;;;;;;;;;;CAaH,wBAA6D;EAC3D,OAAO;GACL,QAAQ;GACR,SAAS;GACV"}
@@ -1 +1 @@
1
- {"version":3,"file":"MovementPhysics.js","names":[],"sources":["../../../src/systems/physics/MovementPhysics.ts"],"sourcesContent":["/**\n * Physics-based movement system for realistic combat movement.\n *\n * **Korean**: 이동 물리 시스템 (Movement Physics System)\n *\n * This module implements realistic physics-based player movement with proper\n * acceleration/deceleration, foot-wide step precision, and stance-based speed\n * modifiers for authentic Korean martial arts combat feel.\n *\n * ## Features\n *\n * - **Realistic Acceleration**: 0 to 2m/s in 0.5 seconds (4.0 m/s²)\n * - **Realistic Deceleration**: 2m/s to 0 in 0.3 seconds (6.67 m/s²)\n * - **Foot-wide Steps**: Discrete 30cm movement increments for tactical positioning\n * - **Stance Modifiers**: 8 trigram stances with different speed characteristics\n * - **Injury Integration**: Movement speed reduced by leg damage (10-50%)\n *\n * @module systems/physics/MovementPhysics\n * @category Physics System\n * @korean 이동물리\n */\n\nimport { TrigramStance } from \"@/types/common\";\nimport { BASE_MOVEMENT_ACCELERATION } from \"@/types/physicsConstants\";\nimport type { MovementArenaBounds } from \"@/types/PhysicsTypes\";\nimport * as THREE from \"three\";\n\n/**\n * Movement input from keyboard/gamepad controls.\n *\n * **Korean**: 이동 입력 (Movement Input)\n *\n * @public\n * @category Physics System\n * @korean 이동입력\n */\nexport interface MovementInput {\n /** Forward/backward input (-1 to 1, where 1 is forward) */\n readonly forward: number;\n /** Lateral left/right input (-1 to 1, where 1 is right) */\n readonly lateral: number;\n /** Whether sprint/run key is held */\n readonly isRunning: boolean;\n /** Whether any movement input is active */\n readonly isMoving: boolean;\n /** Whether to use tactical step mode (30cm grid) */\n readonly useTacticalSteps: boolean;\n}\n\n/**\n * Complete movement state for physics calculations.\n *\n * **Korean**: 이동 상태 (Movement State)\n *\n * Contains position, velocity, and current movement parameters.\n * All vectors are mutable for performance (updated in-place during physics loop).\n *\n * @public\n * @category Physics System\n * @korean 이동상태\n */\nexport interface MovementState {\n /**\n * Current position in 3D space (meters).\n *\n * NOTE: This is a readonly reference to a mutable THREE.Vector3.\n * The physics engine intentionally mutates the vector in-place\n * (e.g. via position.add(...)) for performance. The readonly\n * modifier prevents reassignment of the Vector3 instance, not\n * mutation of its components.\n */\n readonly position: THREE.Vector3;\n /**\n * Current velocity vector (m/s).\n *\n * NOTE: This is a readonly reference to a mutable THREE.Vector3.\n * The physics engine updates this vector in-place each frame.\n * Callers must not reassign the velocity reference, but may pass\n * it to APIs that read or modify its components.\n */\n readonly velocity: THREE.Vector3;\n /** Current acceleration magnitude (m/s²) */\n acceleration: number;\n /** Maximum speed for current state (m/s) */\n maxSpeed: number;\n /** Current Eight Trigram stance */\n readonly currentStance: TrigramStance;\n /** Leg injury percentage (0-1, where 1 is fully injured) */\n legInjuryFactor: number;\n}\n\n/**\n * Stance-based speed modifiers for Eight Trigram system.\n *\n * **Korean**: 팔괘 속도 배수 (Eight Trigram Speed Multipliers)\n *\n * Each trigram stance has unique movement characteristics based on\n * traditional Korean martial arts philosophy:\n *\n * - ☰ 건 (Geon/Heaven): 100% - Balanced, standard speed\n * - ☱ 태 (Tae/Lake): 110% - Fluid movement, flowing techniques\n * - ☲ 리 (Li/Fire): 120% - Aggressive, fast attacks\n * - ☳ 진 (Jin/Thunder): 115% - Explosive power\n * - ☴ 손 (Son/Wind): 125% - Continuous motion, fastest stance\n * - ☵ 감 (Gam/Water): 105% - Adaptive, slightly faster than neutral\n * - ☶ 간 (Gan/Mountain): 80% - Solid defense, slower movement\n * - ☷ 곤 (Gon/Earth): 85% - Grounded, stable but slower\n *\n * @korean 자세속도배수\n */\nexport const STANCE_SPEED_MODIFIERS: Record<TrigramStance, number> = {\n [TrigramStance.GEON]: 1.0, // Heaven: balanced\n [TrigramStance.TAE]: 1.1, // Lake: fluid\n [TrigramStance.LI]: 1.2, // Fire: aggressive\n [TrigramStance.JIN]: 1.15, // Thunder: explosive\n [TrigramStance.SON]: 1.25, // Wind: fastest\n [TrigramStance.GAM]: 1.05, // Water: adaptive\n [TrigramStance.GAN]: 0.8, // Mountain: defensive\n [TrigramStance.GON]: 0.85, // Earth: grounded\n};\n\n/**\n * Physics-based movement engine for combat.\n *\n * **Korean**: 이동 물리 엔진 (Movement Physics Engine)\n *\n * Implements realistic acceleration, deceleration, and stance-based movement\n * for authentic Korean martial arts combat. Supports both continuous movement\n * and tactical foot-wide steps for precise positioning.\n *\n * @example\n * ```typescript\n * const physics = new MovementPhysics();\n *\n * const state: MovementState = {\n * position: new THREE.Vector3(0, 0, 0),\n * velocity: new THREE.Vector3(0, 0, 0),\n * acceleration: 0,\n * maxSpeed: 2.0,\n * currentStance: TrigramStance.GEON,\n * legInjuryFactor: 0,\n * };\n *\n * const input: MovementInput = {\n * forward: 1.0,\n * lateral: 0,\n * isRunning: false,\n * isMoving: true,\n * useTacticalSteps: false,\n * };\n *\n * // In game loop at 60fps\n * physics.updateMovement(state, input, deltaTime);\n * ```\n *\n * @public\n * @category Physics System\n * @korean 이동물리엔진\n */\nexport class MovementPhysics {\n /**\n * Base acceleration rate (m/s²)\n * Achieves 0 to 6m/s in 0.2 seconds (instant-response combat movement)\n * Increased from 12.0 to 30.0 for arcade-style responsiveness\n *\n * Imported from physicsConstants.ts to maintain consistency across systems.\n *\n * **Korean**: 기본 가속도 (Base Acceleration)\n */\n private readonly BASE_ACCELERATION = BASE_MOVEMENT_ACCELERATION;\n\n /**\n * Base deceleration rate (m/s²)\n * Achieves 6m/s to 0 in 0.3 seconds (responsive combat stopping)\n *\n * **Korean**: 기본 감속도 (Base Deceleration)\n */\n private readonly BASE_DECELERATION = 20.0;\n\n /**\n * Foot-wide step size (meters)\n * Standard Korean martial arts step is approximately 30cm\n *\n * **Korean**: 보법 거리 (Step Distance)\n */\n private readonly STEP_SIZE = 0.3;\n\n /**\n * Base walking speed (m/s)\n * Optimized for responsive combat movement - crosses 14m arena in ~2.3s\n *\n * **Korean**: 기본 걷기 속도 (Base Walking Speed)\n */\n private readonly BASE_WALK_SPEED = 6.0;\n\n /**\n * Base running speed (m/s)\n * Sprint speed for rapid repositioning - crosses 14m arena in ~1.4s\n *\n * **Korean**: 기본 달리기 속도 (Base Running Speed)\n */\n private readonly BASE_RUN_SPEED = 10.0;\n\n /**\n * Reference arena size for speed calibration (meters).\n * All speeds are calibrated for a 10m arena.\n *\n * **Korean**: 기준 경기장 크기 (Reference Arena Size)\n */\n private readonly REFERENCE_ARENA_SIZE = 10.0;\n\n /**\n * Current arena width in meters for arena-aware speed scaling.\n *\n * **Korean**: 현재 경기장 너비 (Current Arena Width)\n */\n private _arenaWidthMeters: number = 10.0;\n\n // LATERAL_SPEED removed - now using state.maxSpeed for all directions\n // This ensures speed override applies to lateral movement too\n\n /**\n * Override for max speed from external speed modifier systems.\n *\n * **Korean**: 최대속도 재정의 (Max Speed Override)\n */\n private _overrideMaxSpeed: number | null = null;\n\n /**\n * Override for acceleration from external speed modifier systems.\n *\n * **Korean**: 가속도 재정의 (Acceleration Override)\n */\n private _overrideAcceleration: number | null = null;\n\n /**\n * Cached arena speed scale to avoid repeated calculations.\n *\n * **Korean**: 캐시된 경기장 속도 배수 (Cached Arena Speed Scale)\n */\n private _cachedArenaSpeedScale: number = 1.0;\n\n // Temporary vectors to avoid allocations in update loop\n private readonly tempTargetVelocity = new THREE.Vector3();\n private readonly tempMovement = new THREE.Vector3();\n private readonly tempDirection = new THREE.Vector3();\n private readonly tempTargetDirection = new THREE.Vector3();\n\n /**\n * Create a new MovementPhysics instance.\n *\n * **Korean**: 이동 물리 생성 (Create Movement Physics)\n *\n * @param arenaWidthMeters - Width of the arena in meters (default: 10m, min: 1m)\n * @throws {Error} If arenaWidthMeters is not a positive number\n *\n * @example\n * ```typescript\n * // Default 10m arena (1.0x speed scale)\n * const physics = new MovementPhysics();\n *\n * // Small 6m arena (0.7x speed scale)\n * const smallPhysics = new MovementPhysics(6.0);\n *\n * // Large 14m arena (1.3x speed scale)\n * const largePhysics = new MovementPhysics(14.0);\n * ```\n *\n * @public\n */\n constructor(arenaWidthMeters: number = 10.0) {\n if (arenaWidthMeters <= 0 || !Number.isFinite(arenaWidthMeters)) {\n throw new Error(\n `Arena width must be a positive finite number, got: ${arenaWidthMeters}`,\n );\n }\n this._arenaWidthMeters = arenaWidthMeters;\n this._cachedArenaSpeedScale = this.calculateArenaSpeedScale();\n }\n\n /**\n * Calculate arena-aware speed scaling factor.\n *\n * **Korean**: 경기장 크기 기반 속도 배수 (Arena-Based Speed Multiplier)\n *\n * Scales movement speed proportionally to arena size to maintain consistent\n * gameplay feel across different screen resolutions. Smaller arenas get\n * slightly slower speeds, larger arenas get slightly faster speeds.\n *\n * Formula: scaleFactor = arenaWidth / referenceArenaSize\n * Clamped to [0.7, 1.3] range for balanced gameplay\n *\n * Examples:\n * - 6m arena: 0.7x speed (70% of base)\n * - 10m arena: 1.0x speed (baseline)\n * - 14m arena: 1.3x speed (130% of base)\n *\n * @returns Speed multiplier (0.7 to 1.3)\n *\n * @korean 경기장속도배수\n */\n private calculateArenaSpeedScale(): number {\n const rawScale = this._arenaWidthMeters / this.REFERENCE_ARENA_SIZE;\n // Clamp to reasonable range to maintain gameplay balance\n return Math.max(0.7, Math.min(1.3, rawScale));\n }\n\n /**\n * Update player movement based on input and physics.\n *\n * **Korean**: 이동 업데이트 (Update Movement)\n *\n * Called every frame (60fps) to update player position based on\n * current velocity, acceleration, and input. Applies stance modifiers\n * and injury penalties automatically.\n *\n * @param state - Current movement state (modified in-place)\n * @param input - Current movement input from controls\n * @param deltaTime - Time since last update (seconds)\n * @param bounds - Optional arena bounds for clamping position (meters)\n *\n * @korean 이동업데이트\n */\n public updateMovement(\n state: MovementState,\n input: MovementInput,\n deltaTime: number,\n bounds?: MovementArenaBounds,\n ): void {\n // Use cached arena-aware speed scaling\n const arenaSpeedScale = this._cachedArenaSpeedScale;\n\n // Calculate stance speed modifier\n const stanceModifier = this.getStanceSpeedModifier(state.currentStance);\n\n // Calculate injury penalty (0-50% speed reduction)\n const injuryPenalty = 1.0 - state.legInjuryFactor * 0.5;\n\n // Calculate base target speed (walking or running)\n const baseSpeed = input.isRunning\n ? this.BASE_RUN_SPEED\n : this.BASE_WALK_SPEED;\n\n // Apply all modifiers including arena scaling to get final max speed (or use override)\n state.maxSpeed =\n this._overrideMaxSpeed ??\n baseSpeed * arenaSpeedScale * stanceModifier * injuryPenalty;\n\n // Use override acceleration if set, otherwise use base\n const currentAcceleration =\n this._overrideAcceleration ?? this.BASE_ACCELERATION;\n\n // Calculate target velocity based on input direction\n // forward > 0 = moving in positive Z direction (toward bottom of screen)\n // forward < 0 = moving in negative Z direction (toward top of screen)\n // ✅ REMOVED backward multiplier: All directions use full speed for responsive gameplay\n // The backward penalty should be applied contextually by the combat system\n // based on player facing direction vs movement direction\n // ✅ FIX: Both lateral and forward now use state.maxSpeed (which includes all modifiers)\n // This ensures consistent speed in all movement directions and includes arena scaling\n this.tempTargetVelocity.set(\n input.lateral * state.maxSpeed,\n 0,\n input.forward * state.maxSpeed,\n );\n\n // Apply acceleration or deceleration\n if (input.isMoving) {\n // Accelerate toward target velocity with realistic direction changes\n const currentSpeed = state.velocity.length();\n const targetSpeed = this.tempTargetVelocity.length();\n\n if (currentSpeed < targetSpeed) {\n // Check if direction change is needed\n if (currentSpeed > 0.001 && targetSpeed > 0.001) {\n // Current movement direction\n this.tempDirection.copy(state.velocity).normalize();\n // Desired movement direction (reuse temp vector to avoid allocation)\n this.tempTargetDirection.copy(this.tempTargetVelocity).normalize();\n const directionDot = this.tempDirection.dot(this.tempTargetDirection);\n\n if (directionDot < 0) {\n // Moving in opposite direction: decelerate first before reversing\n const velocityDelta = this.BASE_DECELERATION * deltaTime;\n const newSpeed = Math.max(currentSpeed - velocityDelta, 0);\n\n if (newSpeed > 0.001) {\n state.velocity.copy(this.tempDirection.multiplyScalar(newSpeed));\n } else {\n // Fully stopped; can now start accelerating in new direction\n state.velocity.set(0, 0, 0);\n }\n state.acceleration = -this.BASE_DECELERATION;\n } else if (directionDot < 0.7) {\n // Perpendicular direction change (e.g., forward to strafe): moderate deceleration\n const blendedAccel = currentAcceleration * 0.6; // Reduced acceleration for sharp turns\n const velocityDelta = blendedAccel * deltaTime;\n const newSpeed = Math.min(\n currentSpeed + velocityDelta,\n targetSpeed,\n );\n this.tempDirection.copy(this.tempTargetVelocity).normalize();\n state.velocity.copy(this.tempDirection.multiplyScalar(newSpeed));\n state.acceleration = blendedAccel;\n } else {\n // Same or similar direction: full acceleration\n this.tempDirection.copy(this.tempTargetVelocity).normalize();\n const velocityDelta = currentAcceleration * deltaTime;\n const newSpeed = Math.min(\n currentSpeed + velocityDelta,\n targetSpeed,\n );\n state.velocity.copy(this.tempDirection.multiplyScalar(newSpeed));\n state.acceleration = currentAcceleration;\n }\n } else {\n // Very low speed: safe to accelerate directly toward target\n this.tempDirection.copy(this.tempTargetVelocity).normalize();\n const velocityDelta = currentAcceleration * deltaTime;\n const newSpeed = Math.min(currentSpeed + velocityDelta, targetSpeed);\n state.velocity.copy(this.tempDirection.multiplyScalar(newSpeed));\n state.acceleration = currentAcceleration;\n }\n } else {\n // Already at or above target speed: snap to target velocity\n state.velocity.copy(this.tempTargetVelocity);\n state.acceleration = 0;\n }\n } else {\n // Decelerate to stop\n const currentSpeed = state.velocity.length();\n if (currentSpeed > 0.01) {\n this.tempDirection.copy(state.velocity).normalize();\n const velocityDelta = this.BASE_DECELERATION * deltaTime;\n const newSpeed = Math.max(currentSpeed - velocityDelta, 0);\n state.velocity.copy(this.tempDirection.multiplyScalar(newSpeed));\n } else {\n state.velocity.set(0, 0, 0);\n }\n state.acceleration = -this.BASE_DECELERATION;\n }\n\n // Calculate movement delta for this frame\n this.tempMovement.copy(state.velocity).multiplyScalar(deltaTime);\n\n // Apply tactical step quantization if enabled\n if (input.useTacticalSteps) {\n // Quantize to 30cm grid steps\n this.tempMovement.x =\n Math.round(this.tempMovement.x / this.STEP_SIZE) * this.STEP_SIZE;\n this.tempMovement.z =\n Math.round(this.tempMovement.z / this.STEP_SIZE) * this.STEP_SIZE;\n }\n\n // Update position\n state.position.add(this.tempMovement);\n\n // Apply arena bounds clamping if bounds provided\n if (bounds) {\n // Check if position exceeded boundaries\n const exceededMinX = state.position.x < bounds.minX;\n const exceededMaxX = state.position.x > bounds.maxX;\n const exceededMinZ = state.position.z < bounds.minZ;\n const exceededMaxZ = state.position.z > bounds.maxZ;\n\n // Clamp position to arena boundaries\n state.position.x = Math.max(bounds.minX, Math.min(bounds.maxX, state.position.x));\n state.position.z = Math.max(bounds.minZ, Math.min(bounds.maxZ, state.position.z));\n\n // Zero velocity component if exceeded boundary (smooth stopping)\n if (exceededMinX || exceededMaxX) {\n state.velocity.x = 0;\n }\n if (exceededMinZ || exceededMaxZ) {\n state.velocity.z = 0;\n }\n }\n }\n\n /**\n * Get speed modifier for a specific trigram stance.\n *\n * **Korean**: 자세 속도 배수 가져오기 (Get Stance Speed Modifier)\n *\n * @param stance - Eight Trigram stance\n * @returns Speed multiplier (0.8 to 1.25)\n *\n * @korean 자세속도배수\n */\n public getStanceSpeedModifier(stance: TrigramStance): number {\n return STANCE_SPEED_MODIFIERS[stance];\n }\n\n /**\n * Calculate movement penalty from leg injury.\n *\n * **Korean**: 부상 이동 페널티 (Injury Movement Penalty)\n *\n * Leg damage reduces movement speed by 10-50% based on injury severity.\n *\n * @param legHealthPercentage - Remaining leg health (0-1)\n * @returns Injury factor (0 = no injury, 1 = maximum injury)\n *\n * @korean 부상페널티\n */\n public calculateInjuryPenalty(legHealthPercentage: number): number {\n // Injury penalty scales from 0% (healthy) to 50% (critical)\n // Clamp between 0 and 1\n const healthFactor = Math.max(0, Math.min(1, legHealthPercentage));\n return 1.0 - healthFactor;\n }\n\n /**\n * Get maximum speed for current state configuration.\n *\n * **Korean**: 최대 속도 계산 (Calculate Maximum Speed)\n *\n * @param isRunning - Whether running (vs walking)\n * @param stance - Current trigram stance\n * @param legInjuryFactor - Leg injury severity (0-1)\n * @returns Maximum speed in m/s (includes arena scaling)\n *\n * @korean 최대속도\n */\n public getMaxSpeed(\n isRunning: boolean,\n stance: TrigramStance,\n legInjuryFactor: number,\n ): number {\n const arenaSpeedScale = this._cachedArenaSpeedScale;\n const baseSpeed = isRunning ? this.BASE_RUN_SPEED : this.BASE_WALK_SPEED;\n const stanceModifier = this.getStanceSpeedModifier(stance);\n const injuryPenalty = 1.0 - legInjuryFactor * 0.5;\n return baseSpeed * arenaSpeedScale * stanceModifier * injuryPenalty;\n }\n\n /**\n * Calculate time required to reach target speed from current velocity.\n *\n * **Korean**: 가속 시간 (Acceleration Time)\n *\n * @param currentSpeed - Current speed magnitude (m/s)\n * @param targetSpeed - Desired speed magnitude (m/s)\n * @returns Time in seconds to reach target speed\n *\n * @korean 가속시간\n */\n public getAccelerationTime(\n currentSpeed: number,\n targetSpeed: number,\n ): number {\n const speedDiff = Math.abs(targetSpeed - currentSpeed);\n return speedDiff / this.BASE_ACCELERATION;\n }\n\n /**\n * Calculate stopping distance from current velocity.\n *\n * **Korean**: 제동 거리 (Braking Distance)\n *\n * @param currentSpeed - Current speed magnitude (m/s)\n * @returns Distance in meters required to stop\n *\n * @korean 제동거리\n */\n public getStoppingDistance(currentSpeed: number): number {\n // Using kinematic equation: d = v² / (2a)\n return (currentSpeed * currentSpeed) / (2 * this.BASE_DECELERATION);\n }\n\n /**\n * Get tactical step size.\n *\n * **Korean**: 보법 거리 (Step Distance)\n *\n * @returns Step size in meters (0.3m = 30cm)\n *\n * @korean 보법거리\n */\n public getStepSize(): number {\n return this.STEP_SIZE;\n }\n\n /**\n * Override maximum speed for external speed modifier systems.\n *\n * **Korean**: 최대 속도 설정 (Set Maximum Speed)\n *\n * Allows external systems (like SpeedModifierSystem) to override\n * the calculated maximum speed. This is applied in the next\n * updateMovement call.\n *\n * @param speed - Maximum speed in m/s\n *\n * @public\n */\n public setMaxSpeed(speed: number): void {\n this._overrideMaxSpeed = speed;\n }\n\n /**\n * Override acceleration for external speed modifier systems.\n *\n * **Korean**: 가속도 설정 (Set Acceleration)\n *\n * Allows external systems (like SpeedModifierSystem) to override\n * the base acceleration rate. This is applied in the next\n * updateMovement call.\n *\n * @param acceleration - Acceleration in m/s²\n *\n * @public\n */\n public setAcceleration(acceleration: number): void {\n this._overrideAcceleration = acceleration;\n }\n\n /**\n * Clear speed and acceleration overrides.\n *\n * **Korean**: 속도 재정의 해제 (Clear Speed Overrides)\n *\n * Resets movement to use default calculations without external\n * override values.\n *\n * @public\n */\n public clearOverrides(): void {\n this._overrideMaxSpeed = null;\n this._overrideAcceleration = null;\n }\n\n /**\n * Set arena width for arena-aware speed scaling.\n *\n * **Korean**: 경기장 너비 설정 (Set Arena Width)\n *\n * Updates the arena width used for speed scaling calculations.\n * Call this when the arena size changes (e.g., screen resize).\n * Recalculates and caches the arena speed scale.\n *\n * @param widthMeters - Arena width in meters (must be positive)\n * @throws {Error} If widthMeters is not a positive number\n *\n * @public\n */\n public setArenaWidth(widthMeters: number): void {\n if (widthMeters <= 0 || !Number.isFinite(widthMeters)) {\n throw new Error(\n `Arena width must be a positive finite number, got: ${widthMeters}`,\n );\n }\n this._arenaWidthMeters = widthMeters;\n this._cachedArenaSpeedScale = this.calculateArenaSpeedScale();\n }\n\n /**\n * Get current arena width.\n *\n * **Korean**: 경기장 너비 가져오기 (Get Arena Width)\n *\n * @returns Arena width in meters\n *\n * @public\n */\n public getArenaWidth(): number {\n return this._arenaWidthMeters;\n }\n\n /**\n * Get current arena speed scale factor.\n *\n * **Korean**: 경기장 속도 배수 가져오기 (Get Arena Speed Scale)\n *\n * Returns the cached arena speed scale value.\n *\n * @returns Arena-based speed multiplier (0.7 to 1.3)\n *\n * @public\n */\n public getArenaSpeedScale(): number {\n return this._cachedArenaSpeedScale;\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"MovementPhysics.js","names":[],"sources":["../../../src/systems/physics/MovementPhysics.ts"],"sourcesContent":["/**\n * Physics-based movement system for realistic combat movement.\n *\n * **Korean**: 이동 물리 시스템 (Movement Physics System)\n *\n * This module implements realistic physics-based player movement with proper\n * acceleration/deceleration, foot-wide step precision, and stance-based speed\n * modifiers for authentic Korean martial arts combat feel.\n *\n * ## Features\n *\n * - **Realistic Acceleration**: 0 to 2m/s in 0.5 seconds (4.0 m/s²)\n * - **Realistic Deceleration**: 2m/s to 0 in 0.3 seconds (6.67 m/s²)\n * - **Foot-wide Steps**: Discrete 30cm movement increments for tactical positioning\n * - **Stance Modifiers**: 8 trigram stances with different speed characteristics\n * - **Injury Integration**: Movement speed reduced by leg damage (10-50%)\n *\n * @module systems/physics/MovementPhysics\n * @category Physics System\n * @korean 이동물리\n */\n\nimport { TrigramStance } from \"@/types/common\";\nimport { BASE_MOVEMENT_ACCELERATION } from \"@/types/physicsConstants\";\nimport type { MovementArenaBounds } from \"@/types/PhysicsTypes\";\nimport * as THREE from \"three\";\n\n/**\n * Movement input from keyboard/gamepad controls.\n *\n * **Korean**: 이동 입력 (Movement Input)\n *\n * @public\n * @category Physics System\n * @korean 이동입력\n */\nexport interface MovementInput {\n /** Forward/backward input (-1 to 1, where 1 is forward) */\n readonly forward: number;\n /** Lateral left/right input (-1 to 1, where 1 is right) */\n readonly lateral: number;\n /** Whether sprint/run key is held */\n readonly isRunning: boolean;\n /** Whether any movement input is active */\n readonly isMoving: boolean;\n /** Whether to use tactical step mode (30cm grid) */\n readonly useTacticalSteps: boolean;\n}\n\n/**\n * Complete movement state for physics calculations.\n *\n * **Korean**: 이동 상태 (Movement State)\n *\n * Contains position, velocity, and current movement parameters.\n * All vectors are mutable for performance (updated in-place during physics loop).\n *\n * @public\n * @category Physics System\n * @korean 이동상태\n */\nexport interface MovementState {\n /**\n * Current position in 3D space (meters).\n *\n * NOTE: This is a readonly reference to a mutable THREE.Vector3.\n * The physics engine intentionally mutates the vector in-place\n * (e.g. via position.add(...)) for performance. The readonly\n * modifier prevents reassignment of the Vector3 instance, not\n * mutation of its components.\n */\n readonly position: THREE.Vector3;\n /**\n * Current velocity vector (m/s).\n *\n * NOTE: This is a readonly reference to a mutable THREE.Vector3.\n * The physics engine updates this vector in-place each frame.\n * Callers must not reassign the velocity reference, but may pass\n * it to APIs that read or modify its components.\n */\n readonly velocity: THREE.Vector3;\n /** Current acceleration magnitude (m/s²) */\n acceleration: number;\n /** Maximum speed for current state (m/s) */\n maxSpeed: number;\n /** Current Eight Trigram stance */\n readonly currentStance: TrigramStance;\n /** Leg injury percentage (0-1, where 1 is fully injured) */\n legInjuryFactor: number;\n}\n\n/**\n * Stance-based speed modifiers for Eight Trigram system.\n *\n * **Korean**: 팔괘 속도 배수 (Eight Trigram Speed Multipliers)\n *\n * Each trigram stance has unique movement characteristics based on\n * traditional Korean martial arts philosophy:\n *\n * - ☰ 건 (Geon/Heaven): 100% - Balanced, standard speed\n * - ☱ 태 (Tae/Lake): 110% - Fluid movement, flowing techniques\n * - ☲ 리 (Li/Fire): 120% - Aggressive, fast attacks\n * - ☳ 진 (Jin/Thunder): 115% - Explosive power\n * - ☴ 손 (Son/Wind): 125% - Continuous motion, fastest stance\n * - ☵ 감 (Gam/Water): 105% - Adaptive, slightly faster than neutral\n * - ☶ 간 (Gan/Mountain): 80% - Solid defense, slower movement\n * - ☷ 곤 (Gon/Earth): 85% - Grounded, stable but slower\n *\n * @korean 자세속도배수\n */\nexport const STANCE_SPEED_MODIFIERS: Record<TrigramStance, number> = {\n [TrigramStance.GEON]: 1.0, // Heaven: balanced\n [TrigramStance.TAE]: 1.1, // Lake: fluid\n [TrigramStance.LI]: 1.2, // Fire: aggressive\n [TrigramStance.JIN]: 1.15, // Thunder: explosive\n [TrigramStance.SON]: 1.25, // Wind: fastest\n [TrigramStance.GAM]: 1.05, // Water: adaptive\n [TrigramStance.GAN]: 0.8, // Mountain: defensive\n [TrigramStance.GON]: 0.85, // Earth: grounded\n};\n\n/**\n * Physics-based movement engine for combat.\n *\n * **Korean**: 이동 물리 엔진 (Movement Physics Engine)\n *\n * Implements realistic acceleration, deceleration, and stance-based movement\n * for authentic Korean martial arts combat. Supports both continuous movement\n * and tactical foot-wide steps for precise positioning.\n *\n * @example\n * ```typescript\n * const physics = new MovementPhysics();\n *\n * const state: MovementState = {\n * position: new THREE.Vector3(0, 0, 0),\n * velocity: new THREE.Vector3(0, 0, 0),\n * acceleration: 0,\n * maxSpeed: 2.0,\n * currentStance: TrigramStance.GEON,\n * legInjuryFactor: 0,\n * };\n *\n * const input: MovementInput = {\n * forward: 1.0,\n * lateral: 0,\n * isRunning: false,\n * isMoving: true,\n * useTacticalSteps: false,\n * };\n *\n * // In game loop at 60fps\n * physics.updateMovement(state, input, deltaTime);\n * ```\n *\n * @public\n * @category Physics System\n * @korean 이동물리엔진\n */\nexport class MovementPhysics {\n /**\n * Base acceleration rate (m/s²)\n * Achieves 0 to 6m/s in 0.2 seconds (instant-response combat movement)\n * Increased from 12.0 to 30.0 for arcade-style responsiveness\n *\n * Imported from physicsConstants.ts to maintain consistency across systems.\n *\n * **Korean**: 기본 가속도 (Base Acceleration)\n */\n private readonly BASE_ACCELERATION = BASE_MOVEMENT_ACCELERATION;\n\n /**\n * Base deceleration rate (m/s²)\n * Achieves 6m/s to 0 in 0.3 seconds (responsive combat stopping)\n *\n * **Korean**: 기본 감속도 (Base Deceleration)\n */\n private readonly BASE_DECELERATION = 20.0;\n\n /**\n * Foot-wide step size (meters)\n * Standard Korean martial arts step is approximately 30cm\n *\n * **Korean**: 보법 거리 (Step Distance)\n */\n private readonly STEP_SIZE = 0.3;\n\n /**\n * Base walking speed (m/s)\n * Optimized for responsive combat movement - crosses 14m arena in ~2.3s\n *\n * **Korean**: 기본 걷기 속도 (Base Walking Speed)\n */\n private readonly BASE_WALK_SPEED = 6.0;\n\n /**\n * Base running speed (m/s)\n * Sprint speed for rapid repositioning - crosses 14m arena in ~1.4s\n *\n * **Korean**: 기본 달리기 속도 (Base Running Speed)\n */\n private readonly BASE_RUN_SPEED = 10.0;\n\n /**\n * Reference arena size for speed calibration (meters).\n * All speeds are calibrated for a 10m arena.\n *\n * **Korean**: 기준 경기장 크기 (Reference Arena Size)\n */\n private readonly REFERENCE_ARENA_SIZE = 10.0;\n\n /**\n * Current arena width in meters for arena-aware speed scaling.\n *\n * **Korean**: 현재 경기장 너비 (Current Arena Width)\n */\n private _arenaWidthMeters: number = 10.0;\n\n // LATERAL_SPEED removed - now using state.maxSpeed for all directions\n // This ensures speed override applies to lateral movement too\n\n /**\n * Override for max speed from external speed modifier systems.\n *\n * **Korean**: 최대속도 재정의 (Max Speed Override)\n */\n private _overrideMaxSpeed: number | null = null;\n\n /**\n * Override for acceleration from external speed modifier systems.\n *\n * **Korean**: 가속도 재정의 (Acceleration Override)\n */\n private _overrideAcceleration: number | null = null;\n\n /**\n * Cached arena speed scale to avoid repeated calculations.\n *\n * **Korean**: 캐시된 경기장 속도 배수 (Cached Arena Speed Scale)\n */\n private _cachedArenaSpeedScale: number = 1.0;\n\n // Temporary vectors to avoid allocations in update loop\n private readonly tempTargetVelocity = new THREE.Vector3();\n private readonly tempMovement = new THREE.Vector3();\n private readonly tempDirection = new THREE.Vector3();\n private readonly tempTargetDirection = new THREE.Vector3();\n\n /**\n * Create a new MovementPhysics instance.\n *\n * **Korean**: 이동 물리 생성 (Create Movement Physics)\n *\n * @param arenaWidthMeters - Width of the arena in meters (default: 10m, min: 1m)\n * @throws {Error} If arenaWidthMeters is not a positive number\n *\n * @example\n * ```typescript\n * // Default 10m arena (1.0x speed scale)\n * const physics = new MovementPhysics();\n *\n * // Small 6m arena (0.7x speed scale)\n * const smallPhysics = new MovementPhysics(6.0);\n *\n * // Large 14m arena (1.3x speed scale)\n * const largePhysics = new MovementPhysics(14.0);\n * ```\n *\n * @public\n */\n constructor(arenaWidthMeters: number = 10.0) {\n if (arenaWidthMeters <= 0 || !Number.isFinite(arenaWidthMeters)) {\n throw new Error(\n `Arena width must be a positive finite number, got: ${arenaWidthMeters}`,\n );\n }\n this._arenaWidthMeters = arenaWidthMeters;\n this._cachedArenaSpeedScale = this.calculateArenaSpeedScale();\n }\n\n /**\n * Calculate arena-aware speed scaling factor.\n *\n * **Korean**: 경기장 크기 기반 속도 배수 (Arena-Based Speed Multiplier)\n *\n * Scales movement speed proportionally to arena size to maintain consistent\n * gameplay feel across different screen resolutions. Smaller arenas get\n * slightly slower speeds, larger arenas get slightly faster speeds.\n *\n * Formula: scaleFactor = arenaWidth / referenceArenaSize\n * Clamped to [0.7, 1.3] range for balanced gameplay\n *\n * Examples:\n * - 6m arena: 0.7x speed (70% of base)\n * - 10m arena: 1.0x speed (baseline)\n * - 14m arena: 1.3x speed (130% of base)\n *\n * @returns Speed multiplier (0.7 to 1.3)\n *\n * @korean 경기장속도배수\n */\n private calculateArenaSpeedScale(): number {\n const rawScale = this._arenaWidthMeters / this.REFERENCE_ARENA_SIZE;\n // Clamp to reasonable range to maintain gameplay balance\n return Math.max(0.7, Math.min(1.3, rawScale));\n }\n\n /**\n * Update player movement based on input and physics.\n *\n * **Korean**: 이동 업데이트 (Update Movement)\n *\n * Called every frame (60fps) to update player position based on\n * current velocity, acceleration, and input. Applies stance modifiers\n * and injury penalties automatically.\n *\n * @param state - Current movement state (modified in-place)\n * @param input - Current movement input from controls\n * @param deltaTime - Time since last update (seconds)\n * @param bounds - Optional arena bounds for clamping position (meters)\n *\n * @korean 이동업데이트\n */\n public updateMovement(\n state: MovementState,\n input: MovementInput,\n deltaTime: number,\n bounds?: MovementArenaBounds,\n ): void {\n // Use cached arena-aware speed scaling\n const arenaSpeedScale = this._cachedArenaSpeedScale;\n\n // Calculate stance speed modifier\n const stanceModifier = this.getStanceSpeedModifier(state.currentStance);\n\n // Calculate injury penalty (0-50% speed reduction)\n const injuryPenalty = 1.0 - state.legInjuryFactor * 0.5;\n\n // Calculate base target speed (walking or running)\n const baseSpeed = input.isRunning\n ? this.BASE_RUN_SPEED\n : this.BASE_WALK_SPEED;\n\n // Apply all modifiers including arena scaling to get final max speed (or use override)\n state.maxSpeed =\n this._overrideMaxSpeed ??\n baseSpeed * arenaSpeedScale * stanceModifier * injuryPenalty;\n\n // Use override acceleration if set, otherwise use base\n const currentAcceleration =\n this._overrideAcceleration ?? this.BASE_ACCELERATION;\n\n // Calculate target velocity based on input direction\n // forward > 0 = moving in positive Z direction (toward bottom of screen)\n // forward < 0 = moving in negative Z direction (toward top of screen)\n // ✅ REMOVED backward multiplier: All directions use full speed for responsive gameplay\n // The backward penalty should be applied contextually by the combat system\n // based on player facing direction vs movement direction\n // ✅ FIX: Both lateral and forward now use state.maxSpeed (which includes all modifiers)\n // This ensures consistent speed in all movement directions and includes arena scaling\n this.tempTargetVelocity.set(\n input.lateral * state.maxSpeed,\n 0,\n input.forward * state.maxSpeed,\n );\n\n // Apply acceleration or deceleration\n if (input.isMoving) {\n // Accelerate toward target velocity with realistic direction changes\n const currentSpeed = state.velocity.length();\n const targetSpeed = this.tempTargetVelocity.length();\n\n if (currentSpeed < targetSpeed) {\n // Check if direction change is needed\n if (currentSpeed > 0.001 && targetSpeed > 0.001) {\n // Current movement direction\n this.tempDirection.copy(state.velocity).normalize();\n // Desired movement direction (reuse temp vector to avoid allocation)\n this.tempTargetDirection.copy(this.tempTargetVelocity).normalize();\n const directionDot = this.tempDirection.dot(this.tempTargetDirection);\n\n if (directionDot < 0) {\n // Moving in opposite direction: decelerate first before reversing\n const velocityDelta = this.BASE_DECELERATION * deltaTime;\n const newSpeed = Math.max(currentSpeed - velocityDelta, 0);\n\n if (newSpeed > 0.001) {\n state.velocity.copy(this.tempDirection.multiplyScalar(newSpeed));\n } else {\n // Fully stopped; can now start accelerating in new direction\n state.velocity.set(0, 0, 0);\n }\n state.acceleration = -this.BASE_DECELERATION;\n } else if (directionDot < 0.7) {\n // Perpendicular direction change (e.g., forward to strafe): moderate deceleration\n const blendedAccel = currentAcceleration * 0.6; // Reduced acceleration for sharp turns\n const velocityDelta = blendedAccel * deltaTime;\n const newSpeed = Math.min(\n currentSpeed + velocityDelta,\n targetSpeed,\n );\n this.tempDirection.copy(this.tempTargetVelocity).normalize();\n state.velocity.copy(this.tempDirection.multiplyScalar(newSpeed));\n state.acceleration = blendedAccel;\n } else {\n // Same or similar direction: full acceleration\n this.tempDirection.copy(this.tempTargetVelocity).normalize();\n const velocityDelta = currentAcceleration * deltaTime;\n const newSpeed = Math.min(\n currentSpeed + velocityDelta,\n targetSpeed,\n );\n state.velocity.copy(this.tempDirection.multiplyScalar(newSpeed));\n state.acceleration = currentAcceleration;\n }\n } else {\n // Very low speed: safe to accelerate directly toward target\n this.tempDirection.copy(this.tempTargetVelocity).normalize();\n const velocityDelta = currentAcceleration * deltaTime;\n const newSpeed = Math.min(currentSpeed + velocityDelta, targetSpeed);\n state.velocity.copy(this.tempDirection.multiplyScalar(newSpeed));\n state.acceleration = currentAcceleration;\n }\n } else {\n // Already at or above target speed: snap to target velocity\n state.velocity.copy(this.tempTargetVelocity);\n state.acceleration = 0;\n }\n } else {\n // Decelerate to stop\n const currentSpeed = state.velocity.length();\n if (currentSpeed > 0.01) {\n this.tempDirection.copy(state.velocity).normalize();\n const velocityDelta = this.BASE_DECELERATION * deltaTime;\n const newSpeed = Math.max(currentSpeed - velocityDelta, 0);\n state.velocity.copy(this.tempDirection.multiplyScalar(newSpeed));\n } else {\n state.velocity.set(0, 0, 0);\n }\n state.acceleration = -this.BASE_DECELERATION;\n }\n\n // Calculate movement delta for this frame\n this.tempMovement.copy(state.velocity).multiplyScalar(deltaTime);\n\n // Apply tactical step quantization if enabled\n if (input.useTacticalSteps) {\n // Quantize to 30cm grid steps\n this.tempMovement.x =\n Math.round(this.tempMovement.x / this.STEP_SIZE) * this.STEP_SIZE;\n this.tempMovement.z =\n Math.round(this.tempMovement.z / this.STEP_SIZE) * this.STEP_SIZE;\n }\n\n // Update position\n state.position.add(this.tempMovement);\n\n // Apply arena bounds clamping if bounds provided\n if (bounds) {\n // Check if position exceeded boundaries\n const exceededMinX = state.position.x < bounds.minX;\n const exceededMaxX = state.position.x > bounds.maxX;\n const exceededMinZ = state.position.z < bounds.minZ;\n const exceededMaxZ = state.position.z > bounds.maxZ;\n\n // Clamp position to arena boundaries\n state.position.x = Math.max(bounds.minX, Math.min(bounds.maxX, state.position.x));\n state.position.z = Math.max(bounds.minZ, Math.min(bounds.maxZ, state.position.z));\n\n // Zero velocity component if exceeded boundary (smooth stopping)\n if (exceededMinX || exceededMaxX) {\n state.velocity.x = 0;\n }\n if (exceededMinZ || exceededMaxZ) {\n state.velocity.z = 0;\n }\n }\n }\n\n /**\n * Get speed modifier for a specific trigram stance.\n *\n * **Korean**: 자세 속도 배수 가져오기 (Get Stance Speed Modifier)\n *\n * @param stance - Eight Trigram stance\n * @returns Speed multiplier (0.8 to 1.25)\n *\n * @korean 자세속도배수\n */\n public getStanceSpeedModifier(stance: TrigramStance): number {\n return STANCE_SPEED_MODIFIERS[stance];\n }\n\n /**\n * Calculate movement penalty from leg injury.\n *\n * **Korean**: 부상 이동 페널티 (Injury Movement Penalty)\n *\n * Leg damage reduces movement speed by 10-50% based on injury severity.\n *\n * @param legHealthPercentage - Remaining leg health (0-1)\n * @returns Injury factor (0 = no injury, 1 = maximum injury)\n *\n * @korean 부상페널티\n */\n public calculateInjuryPenalty(legHealthPercentage: number): number {\n // Injury penalty scales from 0% (healthy) to 50% (critical)\n // Clamp between 0 and 1\n const healthFactor = Math.max(0, Math.min(1, legHealthPercentage));\n return 1.0 - healthFactor;\n }\n\n /**\n * Get maximum speed for current state configuration.\n *\n * **Korean**: 최대 속도 계산 (Calculate Maximum Speed)\n *\n * @param isRunning - Whether running (vs walking)\n * @param stance - Current trigram stance\n * @param legInjuryFactor - Leg injury severity (0-1)\n * @returns Maximum speed in m/s (includes arena scaling)\n *\n * @korean 최대속도\n */\n public getMaxSpeed(\n isRunning: boolean,\n stance: TrigramStance,\n legInjuryFactor: number,\n ): number {\n const arenaSpeedScale = this._cachedArenaSpeedScale;\n const baseSpeed = isRunning ? this.BASE_RUN_SPEED : this.BASE_WALK_SPEED;\n const stanceModifier = this.getStanceSpeedModifier(stance);\n const injuryPenalty = 1.0 - legInjuryFactor * 0.5;\n return baseSpeed * arenaSpeedScale * stanceModifier * injuryPenalty;\n }\n\n /**\n * Calculate time required to reach target speed from current velocity.\n *\n * **Korean**: 가속 시간 (Acceleration Time)\n *\n * @param currentSpeed - Current speed magnitude (m/s)\n * @param targetSpeed - Desired speed magnitude (m/s)\n * @returns Time in seconds to reach target speed\n *\n * @korean 가속시간\n */\n public getAccelerationTime(\n currentSpeed: number,\n targetSpeed: number,\n ): number {\n const speedDiff = Math.abs(targetSpeed - currentSpeed);\n return speedDiff / this.BASE_ACCELERATION;\n }\n\n /**\n * Calculate stopping distance from current velocity.\n *\n * **Korean**: 제동 거리 (Braking Distance)\n *\n * @param currentSpeed - Current speed magnitude (m/s)\n * @returns Distance in meters required to stop\n *\n * @korean 제동거리\n */\n public getStoppingDistance(currentSpeed: number): number {\n // Using kinematic equation: d = v² / (2a)\n return (currentSpeed * currentSpeed) / (2 * this.BASE_DECELERATION);\n }\n\n /**\n * Get tactical step size.\n *\n * **Korean**: 보법 거리 (Step Distance)\n *\n * @returns Step size in meters (0.3m = 30cm)\n *\n * @korean 보법거리\n */\n public getStepSize(): number {\n return this.STEP_SIZE;\n }\n\n /**\n * Override maximum speed for external speed modifier systems.\n *\n * **Korean**: 최대 속도 설정 (Set Maximum Speed)\n *\n * Allows external systems (like SpeedModifierSystem) to override\n * the calculated maximum speed. This is applied in the next\n * updateMovement call.\n *\n * @param speed - Maximum speed in m/s\n *\n * @public\n */\n public setMaxSpeed(speed: number): void {\n this._overrideMaxSpeed = speed;\n }\n\n /**\n * Override acceleration for external speed modifier systems.\n *\n * **Korean**: 가속도 설정 (Set Acceleration)\n *\n * Allows external systems (like SpeedModifierSystem) to override\n * the base acceleration rate. This is applied in the next\n * updateMovement call.\n *\n * @param acceleration - Acceleration in m/s²\n *\n * @public\n */\n public setAcceleration(acceleration: number): void {\n this._overrideAcceleration = acceleration;\n }\n\n /**\n * Clear speed and acceleration overrides.\n *\n * **Korean**: 속도 재정의 해제 (Clear Speed Overrides)\n *\n * Resets movement to use default calculations without external\n * override values.\n *\n * @public\n */\n public clearOverrides(): void {\n this._overrideMaxSpeed = null;\n this._overrideAcceleration = null;\n }\n\n /**\n * Set arena width for arena-aware speed scaling.\n *\n * **Korean**: 경기장 너비 설정 (Set Arena Width)\n *\n * Updates the arena width used for speed scaling calculations.\n * Call this when the arena size changes (e.g., screen resize).\n * Recalculates and caches the arena speed scale.\n *\n * @param widthMeters - Arena width in meters (must be positive)\n * @throws {Error} If widthMeters is not a positive number\n *\n * @public\n */\n public setArenaWidth(widthMeters: number): void {\n if (widthMeters <= 0 || !Number.isFinite(widthMeters)) {\n throw new Error(\n `Arena width must be a positive finite number, got: ${widthMeters}`,\n );\n }\n this._arenaWidthMeters = widthMeters;\n this._cachedArenaSpeedScale = this.calculateArenaSpeedScale();\n }\n\n /**\n * Get current arena width.\n *\n * **Korean**: 경기장 너비 가져오기 (Get Arena Width)\n *\n * @returns Arena width in meters\n *\n * @public\n */\n public getArenaWidth(): number {\n return this._arenaWidthMeters;\n }\n\n /**\n * Get current arena speed scale factor.\n *\n * **Korean**: 경기장 속도 배수 가져오기 (Get Arena Speed Scale)\n *\n * Returns the cached arena speed scale value.\n *\n * @returns Arena-based speed multiplier (0.7 to 1.3)\n *\n * @public\n */\n public getArenaSpeedScale(): number {\n return this._cachedArenaSpeedScale;\n }\n}\n"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"PhysicalReachCalculator.js","names":[],"sources":["../../../src/systems/physics/PhysicalReachCalculator.ts"],"sourcesContent":["/**\n * Physical Reach Calculator\n *\n * **Korean**: 물리적 도달 거리 계산기\n *\n * Calculates actual combat reach based on physical attributes and animation timing.\n * Integrates archetype-specific limb lengths with animation hit windows for\n * reality-based hit detection.\n *\n * ## Philosophy\n *\n * Black Trigram emphasizes realistic combat. Hit detection must account for:\n * - Physical differences between archetypes (arm/leg length)\n * - Animation phase (chamber vs extension vs retraction)\n * - Technique type (punch vs kick vs elbow)\n * - Stance modifiers from Eight Trigrams\n *\n * A small Hacker (73cm arms) cannot reach as far as a large Jojik (84cm arms),\n * and this difference must be reflected in combat.\n *\n * @module systems/physics/PhysicalReachCalculator\n * @category Combat Physics\n * @korean 물리적도달계산기\n */\n\nimport { PhysicalAttributes } from \"@/types\";\nimport { TrigramStance } from \"../../types/common\";\nimport { \n STANCE_REACH_MODIFIERS, \n TechniqueType,\n PhysicalReachConfig,\n} from \"../../types/physics\";\nimport {\n AnimationType,\n getAnimationHitTiming,\n getCurrentReachMultiplier,\n isWithinHitWindow,\n} from \"../animation\";\n\n/**\n * Physical reach calculation result.\n *\n * **Korean**: 물리적 도달 계산 결과\n *\n * @public\n * @korean 물리적도달결과\n */\nexport interface PhysicalReachResult {\n /**\n * Base limb length in meters.\n * Archetype-specific arm or leg length from physical attributes.\n * @korean 기본팔다리길이\n */\n readonly baseLimbLength: number;\n\n /**\n * Body pivot contribution in meters (kicks only).\n * Accounts for hip rotation and torso lean during kicks (~0.25m).\n * @korean 몸통회전기여도\n */\n readonly bodyPivotContribution: number;\n\n /**\n * Technique type used.\n * @korean 기술유형\n */\n readonly techniqueType: TechniqueType;\n\n /**\n * Current animation time in seconds.\n * @korean 현재애니메이션시간\n */\n readonly animationTime: number;\n\n /**\n * Animation reach multiplier at current time (0.0 - 1.5).\n * @korean 애니메이션도달배수\n */\n readonly animationReachMultiplier: number;\n\n /**\n * Base extension from technique reachConfig (0.0 - 1.5).\n * Designer-specified reach multiplier from technique definition.\n * @korean 기본확장배수\n */\n readonly baseExtension?: number;\n\n /**\n * Final extension multiplier used in reach calculation.\n * When reachConfig is provided, applies curve factor to hybrid peak:\n * `(animationReachMultiplier / peakMultiplier) * max(baseExtension, peakMultiplier)`\n * Otherwise uses time-varying animationReachMultiplier directly.\n * @korean 최종확장배수\n */\n readonly finalExtensionMultiplier: number;\n\n /**\n * Stance reach modifier (0.9 - 1.2).\n * @korean 자세도달수정자\n */\n readonly stanceModifier: number;\n\n /**\n * Final effective reach in meters.\n * (baseLimbLength + bodyPivotContribution) × finalExtensionMultiplier × stanceModifier\n * @korean 최종유효도달\n */\n readonly effectiveReach: number;\n\n /**\n * Whether currently within hit window.\n * @korean 타격창내여부\n */\n readonly canHit: boolean;\n}\n\n/**\n * Physical Reach Calculator.\n *\n * **Korean**: 물리적 도달 계산기\n *\n * Calculates reality-based reach using archetype physical attributes\n * and animation timing.\n *\n * @public\n * @korean 물리적도달계산기\n */\nexport class PhysicalReachCalculator {\n /**\n * Calculate effective reach for a technique at a specific animation time.\n *\n * **Korean**: 특정 애니메이션 시간의 유효 도달 거리 계산\n *\n * This is the core method that integrates:\n * 1. Physical attributes (archetype-specific limb length)\n * 2. Animation timing (hit window and extension phase)\n * 3. Technique baseExtension (designer-specified reach)\n * 4. Stance modifiers (Eight Trigrams reach bonuses)\n * 5. Body pivot contribution (hip rotation and torso lean for kicks)\n *\n * **Hybrid Reach System**: Uses the maximum of:\n * - `reachConfig.baseExtension` (designer-specified reach)\n * - `maxReachMultiplier` (animation-driven reach)\n *\n * This ensures techniques get at least their designed reach while allowing\n * animations to extend beyond the base if needed.\n *\n * **Body Pivot Mechanics for Kicks**:\n * Kicks benefit from whole-body rotation that punches don't utilize:\n * - Hip rotation adds ~0.15m (pelvis width + pivot)\n * - Torso lean adds ~0.1m (forward lean during kick)\n * - Total body pivot: ~0.25m additional reach\n *\n * This accounts for the biomechanics of kicks where the fighter rotates\n * their entire body to extend reach, unlike punches which rely primarily\n * on arm extension.\n *\n * @param physicalAttributes - Fighter's physical attributes\n * @param animationType - Animation being executed\n * @param animationTime - Current time in animation (seconds)\n * @param stance - Current trigram stance\n * @param reachConfig - Optional technique reach configuration with baseExtension\n * @returns Physical reach calculation result\n *\n * @example\n * ```typescript\n * const calculator = new PhysicalReachCalculator();\n *\n * // With reachConfig (uses max of baseExtension and animation multiplier)\n * const frontKick = calculator.calculateReach(\n * MUSA_PHYSICAL,\n * AnimationType.FRONT_KICK,\n * 0.27, // Peak time\n * TrigramStance.GEON,\n * { bodyPart: \"leg\", techniqueType: \"kick\", baseExtension: 1.05 }\n * );\n * // Uses max(1.05, 1.0) = 1.05 for proper designed reach\n *\n * // Without reachConfig (uses only animation multiplier - backward compatible)\n * const legacyKick = calculator.calculateReach(\n * MUSA_PHYSICAL,\n * AnimationType.FRONT_KICK,\n * 0.27,\n * TrigramStance.GEON\n * );\n * // Uses animation multiplier (1.0) only\n * ```\n *\n * @public\n * @korean 도달계산\n */\n calculateReach(\n physicalAttributes: PhysicalAttributes,\n animationType: AnimationType,\n animationTime: number,\n stance: TrigramStance,\n reachConfig?: PhysicalReachConfig,\n ): PhysicalReachResult {\n // Determine technique type from animation\n const techniqueType = this.getTechniqueTypeFromAnimation(animationType);\n\n // Get base limb length for technique type\n const baseLimbLength = this.getLimbLength(\n physicalAttributes,\n techniqueType,\n );\n\n // Get animation hit timing\n const canHit = isWithinHitWindow(animationType, animationTime);\n const animationReachMultiplier = getCurrentReachMultiplier(\n animationType,\n animationTime,\n );\n\n // **Hybrid Reach System with Curve Factor**:\n // Apply baseExtension at the peak reach level, then scale by the time-varying\n // curve factor so reach still ramps up and down with the animation.\n // This prevents phantom hits at the start/end of the hit window.\n const baseExtension = reachConfig?.baseExtension;\n\n // Retrieve peak (max) reach multiplier for this animation's hit window\n const hitTiming = getAnimationHitTiming(animationType);\n const rawPeakMultiplier = hitTiming?.hitWindow.maxReachMultiplier;\n\n // Determine whether we have valid timing data. When timing is missing or\n // the configured peak multiplier is non-positive, fall back to a neutral\n // curve so reach/damage checks remain possible for those techniques.\n const hasValidTiming =\n rawPeakMultiplier !== undefined && rawPeakMultiplier > 0;\n\n const fallbackBase = baseExtension ?? 1;\n\n const peakMultiplier = hasValidTiming ? rawPeakMultiplier : fallbackBase;\n\n const effectiveAnimationReachMultiplier = hasValidTiming\n ? animationReachMultiplier\n : fallbackBase;\n\n // Normalized curve factor in [0, 1] that represents where we are on\n // the reach curve. When peakMultiplier is 0, we treat reach as 0.\n const curveFactor =\n peakMultiplier > 0 ? effectiveAnimationReachMultiplier / peakMultiplier : 0;\n\n // Apply the hybrid \"max\" at the peak level, then reapply the curve\n const peakExtension =\n baseExtension !== undefined\n ? Math.max(baseExtension, peakMultiplier)\n : peakMultiplier;\n\n const finalExtensionMultiplier = curveFactor * peakExtension;\n\n // Get stance modifier\n const stanceModifier = STANCE_REACH_MODIFIERS[stance];\n\n // Calculate final effective reach\n // Convert cm to meters for consistency with physics system\n const baseLimbLengthMeters = baseLimbLength / 100;\n\n // Add body pivot/offset contribution based on technique type\n // This accounts for the fact that limbs extend from the body surface,\n // not the body center, plus rotational contributions\n let bodyPivotContribution: number;\n\n if (techniqueType === \"kick\" || techniqueType === \"knee\") {\n // Kicks benefit from hip rotation and torso lean which add 0.25m\n // This accounts for:\n // - Hip width/rotation (0.15m)\n // - Torso lean during kick (0.1m)\n // Total body pivot contribution: 0.25m for kicks\n bodyPivotContribution = 0.25;\n } else if (\n techniqueType === \"punch\" ||\n techniqueType === \"pressure_point\"\n ) {\n // Punches extend from the shoulder, which is offset from body center\n // Shoulder offset = shoulderWidth / 2 (converted to meters)\n // Plus torso rotation contribution for cross/hooks (~0.1m)\n // Average shoulder width ~45cm → offset ~0.225m, plus rotation ~0.1m\n // Total: ~0.30m for arm techniques\n const shoulderOffset = physicalAttributes.shoulderWidth / 2 / 100; // Convert cm to m\n const torsoRotation = 0.1; // 10cm from torso rotation during punches\n bodyPivotContribution = shoulderOffset + torsoRotation;\n } else if (techniqueType === \"elbow\") {\n // Elbows are close range but still extend from shoulder\n // Less torso rotation contribution\n const shoulderOffset = physicalAttributes.shoulderWidth / 2 / 100;\n bodyPivotContribution = shoulderOffset;\n } else {\n bodyPivotContribution = 0;\n }\n\n const effectiveReach =\n (baseLimbLengthMeters + bodyPivotContribution) *\n finalExtensionMultiplier *\n stanceModifier;\n\n return {\n baseLimbLength: baseLimbLengthMeters,\n bodyPivotContribution,\n techniqueType,\n animationTime,\n animationReachMultiplier,\n baseExtension,\n finalExtensionMultiplier,\n stanceModifier,\n effectiveReach,\n canHit,\n };\n }\n\n /**\n * Calculate maximum possible reach for a technique.\n *\n * **Korean**: 기술의 최대 가능 도달 거리\n *\n * Calculates reach at peak animation time (maximum extension).\n * Uses hybrid reach system with reachConfig if provided.\n *\n * @param physicalAttributes - Fighter's physical attributes\n * @param animationType - Animation type\n * @param stance - Current trigram stance\n * @param reachConfig - Optional technique reach configuration with baseExtension\n * @returns Maximum effective reach in meters\n *\n * @example\n * ```typescript\n * const calculator = new PhysicalReachCalculator();\n *\n * // With reachConfig for accurate designed reach\n * const maxReachWithConfig = calculator.calculateMaxReach(\n * MUSA_PHYSICAL,\n * AnimationType.FRONT_KICK,\n * TrigramStance.GEON,\n * { bodyPart: \"leg\", techniqueType: \"kick\", baseExtension: 1.05 }\n * );\n * // Uses max(1.05, 1.0) = 1.05\n *\n * // Without reachConfig (backward compatible)\n * const maxReachLegacy = calculator.calculateMaxReach(\n * MUSA_PHYSICAL,\n * AnimationType.FRONT_KICK,\n * TrigramStance.GEON\n * );\n * // Uses animation multiplier only (1.0)\n * ```\n *\n * @public\n * @korean 최대도달계산\n */\n calculateMaxReach(\n physicalAttributes: PhysicalAttributes,\n animationType: AnimationType,\n stance: TrigramStance,\n reachConfig?: PhysicalReachConfig,\n ): number {\n const hitTiming = getAnimationHitTiming(animationType);\n \n // Handle missing AnimationHitTiming entries\n // When timing data is unavailable, use baseExtension if provided,\n // otherwise use a neutral 1.0 multiplier to keep reach calculations functional.\n // This ensures techniques like GEON_ROUNDHOUSE, WATER_COUNTER, IRON_BLOCK\n // (which have technique definitions but missing animation timing entries)\n // can still perform reach/damage checks.\n if (!hitTiming) {\n // Use fallback approach: calculate reach with neutral timing\n const fallbackTime = 0.5; // Midpoint of typical animation\n const result = this.calculateReach(\n physicalAttributes,\n animationType,\n fallbackTime,\n stance,\n reachConfig,\n );\n return result.effectiveReach;\n }\n \n const peakTime = hitTiming.hitWindow.peakTime;\n\n const result = this.calculateReach(\n physicalAttributes,\n animationType,\n peakTime,\n stance,\n reachConfig,\n );\n\n return result.effectiveReach;\n }\n\n /**\n * Get limb length for a technique type.\n *\n * **Korean**: 기술 유형에 대한 팔다리 길이 가져오기\n *\n * @param physicalAttributes - Fighter's physical attributes\n * @param techniqueType - Type of technique\n * @returns Limb length in centimeters\n *\n * @private\n * @korean 팔다리길이가져오기\n */\n private getLimbLength(\n physicalAttributes: PhysicalAttributes,\n techniqueType: TechniqueType,\n ): number {\n switch (techniqueType) {\n case \"punch\":\n case \"elbow\":\n case \"pressure_point\":\n // Use arm length for hand-based techniques\n return physicalAttributes.armLength;\n\n case \"kick\":\n case \"knee\":\n // Use leg length for leg-based techniques\n return physicalAttributes.legLength;\n\n default:\n // Fallback to arm length\n return physicalAttributes.armLength;\n }\n }\n\n /**\n * Determine technique type from animation type.\n *\n * Public method exposed to avoid duplication across codebase.\n *\n * **Korean**: 애니메이션 타입에서 기술 유형 결정\n *\n * @param animationType - Animation type\n * @returns Technique type\n *\n * @public\n * @korean 기술유형결정\n */\n public getTechniqueTypeFromAnimation(\n animationType: AnimationType,\n ): TechniqueType {\n // Punch techniques\n if (\n animationType === AnimationType.JAB ||\n animationType === AnimationType.CROSS ||\n animationType === AnimationType.HOOK ||\n animationType === AnimationType.UPPERCUT ||\n animationType === AnimationType.OVERHAND ||\n animationType === AnimationType.BACKFIST ||\n animationType === AnimationType.HAMMER_FIST ||\n animationType === AnimationType.PALM_STRIKE ||\n animationType === AnimationType.SPEAR_HAND_STRIKE ||\n animationType === AnimationType.HEAVEN_STRIKE ||\n animationType === AnimationType.FLOWING_CROSS ||\n animationType === AnimationType.SOLAR_PLEXUS_STRIKE ||\n animationType === AnimationType.FLOWING_PUSH ||\n animationType === AnimationType.LIVER_DISRUPTION ||\n animationType === AnimationType.EAR_STRIKE\n ) {\n return \"punch\";\n }\n\n // Kick techniques\n if (\n animationType === AnimationType.FRONT_KICK ||\n animationType === AnimationType.ROUNDHOUSE_KICK ||\n animationType === AnimationType.SIDE_KICK ||\n animationType === AnimationType.BACK_KICK ||\n animationType === AnimationType.AXE_KICK ||\n animationType === AnimationType.CRESCENT_KICK ||\n animationType === AnimationType.LOW_KICK ||\n animationType === AnimationType.PUSH_KICK ||\n animationType === AnimationType.JUMPING_KICK ||\n animationType === AnimationType.SPINNING_HEEL_KICK ||\n animationType === AnimationType.TORNADO_KICK\n ) {\n return \"kick\";\n }\n\n // Elbow techniques\n if (\n animationType === AnimationType.ELBOW_STRIKE ||\n animationType === AnimationType.ELBOW_UPPERCUT ||\n animationType === AnimationType.SPINNING_ELBOW ||\n animationType === AnimationType.TEMPLE_ELBOW ||\n animationType === AnimationType.SPINNING_BACK_ELBOW ||\n animationType === AnimationType.SPINAL_ELBOW ||\n animationType === AnimationType.BRACHIAL_ELBOW\n ) {\n return \"elbow\";\n }\n\n // Knee techniques\n if (\n animationType === AnimationType.KNEE_STRIKE ||\n animationType === AnimationType.FLYING_KNEE ||\n animationType === AnimationType.KIDNEY_KNEE ||\n animationType === AnimationType.FEMORAL_KNEE\n ) {\n return \"knee\";\n }\n\n // Pressure point techniques\n if (\n animationType === AnimationType.NERVE_STRIKE ||\n animationType === AnimationType.PRESSURE_POINT_STRIKE ||\n animationType === AnimationType.NERVE_PARALYSIS ||\n animationType === AnimationType.THROAT_STRIKE ||\n animationType === AnimationType.EYE_GOUGE\n ) {\n return \"pressure_point\";\n }\n\n // Default to \"punch\" for any techniques not explicitly mapped above, including\n // complex grappling or hybrid animations. For *reach calculation* purposes we\n // approximate these using primary arm/forearm extension, since initial contact\n // is typically established with the upper limbs before the torso closes distance.\n // If a dedicated grappling TechniqueType and reach model are introduced later,\n // update this fallback to return that specific type instead of \"punch\".\n return \"punch\";\n }\n}\n\n/**\n * Singleton instance for convenient access.\n *\n * **Korean**: 싱글톤 인스턴스\n *\n * @example\n * ```typescript\n * import { physicalReachCalculator } from '@/systems/physics/PhysicalReachCalculator';\n *\n * const reach = physicalReachCalculator.calculateReach(\n * playerPhysical,\n * AnimationType.ROUNDHOUSE_KICK,\n * 0.32, // Peak time\n * TrigramStance.GEON\n * );\n * ```\n *\n * @public\n * @korean 싱글톤인스턴스\n */\nexport const physicalReachCalculator = new PhysicalReachCalculator();\n"],"mappings":"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"}
1
+ {"version":3,"file":"PhysicalReachCalculator.js","names":[],"sources":["../../../src/systems/physics/PhysicalReachCalculator.ts"],"sourcesContent":["/**\n * Physical Reach Calculator\n *\n * **Korean**: 물리적 도달 거리 계산기\n *\n * Calculates actual combat reach based on physical attributes and animation timing.\n * Integrates archetype-specific limb lengths with animation hit windows for\n * reality-based hit detection.\n *\n * ## Philosophy\n *\n * Black Trigram emphasizes realistic combat. Hit detection must account for:\n * - Physical differences between archetypes (arm/leg length)\n * - Animation phase (chamber vs extension vs retraction)\n * - Technique type (punch vs kick vs elbow)\n * - Stance modifiers from Eight Trigrams\n *\n * A small Hacker (73cm arms) cannot reach as far as a large Jojik (84cm arms),\n * and this difference must be reflected in combat.\n *\n * @module systems/physics/PhysicalReachCalculator\n * @category Combat Physics\n * @korean 물리적도달계산기\n */\n\nimport { PhysicalAttributes } from \"@/types\";\nimport { TrigramStance } from \"../../types/common\";\nimport { \n STANCE_REACH_MODIFIERS, \n TechniqueType,\n PhysicalReachConfig,\n} from \"../../types/physics\";\nimport {\n AnimationType,\n getAnimationHitTiming,\n getCurrentReachMultiplier,\n isWithinHitWindow,\n} from \"../animation\";\n\n/**\n * Physical reach calculation result.\n *\n * **Korean**: 물리적 도달 계산 결과\n *\n * @public\n * @korean 물리적도달결과\n */\nexport interface PhysicalReachResult {\n /**\n * Base limb length in meters.\n * Archetype-specific arm or leg length from physical attributes.\n * @korean 기본팔다리길이\n */\n readonly baseLimbLength: number;\n\n /**\n * Body pivot contribution in meters (kicks only).\n * Accounts for hip rotation and torso lean during kicks (~0.25m).\n * @korean 몸통회전기여도\n */\n readonly bodyPivotContribution: number;\n\n /**\n * Technique type used.\n * @korean 기술유형\n */\n readonly techniqueType: TechniqueType;\n\n /**\n * Current animation time in seconds.\n * @korean 현재애니메이션시간\n */\n readonly animationTime: number;\n\n /**\n * Animation reach multiplier at current time (0.0 - 1.5).\n * @korean 애니메이션도달배수\n */\n readonly animationReachMultiplier: number;\n\n /**\n * Base extension from technique reachConfig (0.0 - 1.5).\n * Designer-specified reach multiplier from technique definition.\n * @korean 기본확장배수\n */\n readonly baseExtension?: number;\n\n /**\n * Final extension multiplier used in reach calculation.\n * When reachConfig is provided, applies curve factor to hybrid peak:\n * `(animationReachMultiplier / peakMultiplier) * max(baseExtension, peakMultiplier)`\n * Otherwise uses time-varying animationReachMultiplier directly.\n * @korean 최종확장배수\n */\n readonly finalExtensionMultiplier: number;\n\n /**\n * Stance reach modifier (0.9 - 1.2).\n * @korean 자세도달수정자\n */\n readonly stanceModifier: number;\n\n /**\n * Final effective reach in meters.\n * (baseLimbLength + bodyPivotContribution) × finalExtensionMultiplier × stanceModifier\n * @korean 최종유효도달\n */\n readonly effectiveReach: number;\n\n /**\n * Whether currently within hit window.\n * @korean 타격창내여부\n */\n readonly canHit: boolean;\n}\n\n/**\n * Physical Reach Calculator.\n *\n * **Korean**: 물리적 도달 계산기\n *\n * Calculates reality-based reach using archetype physical attributes\n * and animation timing.\n *\n * @public\n * @korean 물리적도달계산기\n */\nexport class PhysicalReachCalculator {\n /**\n * Calculate effective reach for a technique at a specific animation time.\n *\n * **Korean**: 특정 애니메이션 시간의 유효 도달 거리 계산\n *\n * This is the core method that integrates:\n * 1. Physical attributes (archetype-specific limb length)\n * 2. Animation timing (hit window and extension phase)\n * 3. Technique baseExtension (designer-specified reach)\n * 4. Stance modifiers (Eight Trigrams reach bonuses)\n * 5. Body pivot contribution (hip rotation and torso lean for kicks)\n *\n * **Hybrid Reach System**: Uses the maximum of:\n * - `reachConfig.baseExtension` (designer-specified reach)\n * - `maxReachMultiplier` (animation-driven reach)\n *\n * This ensures techniques get at least their designed reach while allowing\n * animations to extend beyond the base if needed.\n *\n * **Body Pivot Mechanics for Kicks**:\n * Kicks benefit from whole-body rotation that punches don't utilize:\n * - Hip rotation adds ~0.15m (pelvis width + pivot)\n * - Torso lean adds ~0.1m (forward lean during kick)\n * - Total body pivot: ~0.25m additional reach\n *\n * This accounts for the biomechanics of kicks where the fighter rotates\n * their entire body to extend reach, unlike punches which rely primarily\n * on arm extension.\n *\n * @param physicalAttributes - Fighter's physical attributes\n * @param animationType - Animation being executed\n * @param animationTime - Current time in animation (seconds)\n * @param stance - Current trigram stance\n * @param reachConfig - Optional technique reach configuration with baseExtension\n * @returns Physical reach calculation result\n *\n * @example\n * ```typescript\n * const calculator = new PhysicalReachCalculator();\n *\n * // With reachConfig (uses max of baseExtension and animation multiplier)\n * const frontKick = calculator.calculateReach(\n * MUSA_PHYSICAL,\n * AnimationType.FRONT_KICK,\n * 0.27, // Peak time\n * TrigramStance.GEON,\n * { bodyPart: \"leg\", techniqueType: \"kick\", baseExtension: 1.05 }\n * );\n * // Uses max(1.05, 1.0) = 1.05 for proper designed reach\n *\n * // Without reachConfig (uses only animation multiplier - backward compatible)\n * const legacyKick = calculator.calculateReach(\n * MUSA_PHYSICAL,\n * AnimationType.FRONT_KICK,\n * 0.27,\n * TrigramStance.GEON\n * );\n * // Uses animation multiplier (1.0) only\n * ```\n *\n * @public\n * @korean 도달계산\n */\n calculateReach(\n physicalAttributes: PhysicalAttributes,\n animationType: AnimationType,\n animationTime: number,\n stance: TrigramStance,\n reachConfig?: PhysicalReachConfig,\n ): PhysicalReachResult {\n // Determine technique type from animation\n const techniqueType = this.getTechniqueTypeFromAnimation(animationType);\n\n // Get base limb length for technique type\n const baseLimbLength = this.getLimbLength(\n physicalAttributes,\n techniqueType,\n );\n\n // Get animation hit timing\n const canHit = isWithinHitWindow(animationType, animationTime);\n const animationReachMultiplier = getCurrentReachMultiplier(\n animationType,\n animationTime,\n );\n\n // **Hybrid Reach System with Curve Factor**:\n // Apply baseExtension at the peak reach level, then scale by the time-varying\n // curve factor so reach still ramps up and down with the animation.\n // This prevents phantom hits at the start/end of the hit window.\n const baseExtension = reachConfig?.baseExtension;\n\n // Retrieve peak (max) reach multiplier for this animation's hit window\n const hitTiming = getAnimationHitTiming(animationType);\n const rawPeakMultiplier = hitTiming?.hitWindow.maxReachMultiplier;\n\n // Determine whether we have valid timing data. When timing is missing or\n // the configured peak multiplier is non-positive, fall back to a neutral\n // curve so reach/damage checks remain possible for those techniques.\n const hasValidTiming =\n rawPeakMultiplier !== undefined && rawPeakMultiplier > 0;\n\n const fallbackBase = baseExtension ?? 1;\n\n const peakMultiplier = hasValidTiming ? rawPeakMultiplier : fallbackBase;\n\n const effectiveAnimationReachMultiplier = hasValidTiming\n ? animationReachMultiplier\n : fallbackBase;\n\n // Normalized curve factor in [0, 1] that represents where we are on\n // the reach curve. When peakMultiplier is 0, we treat reach as 0.\n const curveFactor =\n peakMultiplier > 0 ? effectiveAnimationReachMultiplier / peakMultiplier : 0;\n\n // Apply the hybrid \"max\" at the peak level, then reapply the curve\n const peakExtension =\n baseExtension !== undefined\n ? Math.max(baseExtension, peakMultiplier)\n : peakMultiplier;\n\n const finalExtensionMultiplier = curveFactor * peakExtension;\n\n // Get stance modifier\n const stanceModifier = STANCE_REACH_MODIFIERS[stance];\n\n // Calculate final effective reach\n // Convert cm to meters for consistency with physics system\n const baseLimbLengthMeters = baseLimbLength / 100;\n\n // Add body pivot/offset contribution based on technique type\n // This accounts for the fact that limbs extend from the body surface,\n // not the body center, plus rotational contributions\n let bodyPivotContribution: number;\n\n if (techniqueType === \"kick\" || techniqueType === \"knee\") {\n // Kicks benefit from hip rotation and torso lean which add 0.25m\n // This accounts for:\n // - Hip width/rotation (0.15m)\n // - Torso lean during kick (0.1m)\n // Total body pivot contribution: 0.25m for kicks\n bodyPivotContribution = 0.25;\n } else if (\n techniqueType === \"punch\" ||\n techniqueType === \"pressure_point\"\n ) {\n // Punches extend from the shoulder, which is offset from body center\n // Shoulder offset = shoulderWidth / 2 (converted to meters)\n // Plus torso rotation contribution for cross/hooks (~0.1m)\n // Average shoulder width ~45cm → offset ~0.225m, plus rotation ~0.1m\n // Total: ~0.30m for arm techniques\n const shoulderOffset = physicalAttributes.shoulderWidth / 2 / 100; // Convert cm to m\n const torsoRotation = 0.1; // 10cm from torso rotation during punches\n bodyPivotContribution = shoulderOffset + torsoRotation;\n } else if (techniqueType === \"elbow\") {\n // Elbows are close range but still extend from shoulder\n // Less torso rotation contribution\n const shoulderOffset = physicalAttributes.shoulderWidth / 2 / 100;\n bodyPivotContribution = shoulderOffset;\n } else {\n bodyPivotContribution = 0;\n }\n\n const effectiveReach =\n (baseLimbLengthMeters + bodyPivotContribution) *\n finalExtensionMultiplier *\n stanceModifier;\n\n return {\n baseLimbLength: baseLimbLengthMeters,\n bodyPivotContribution,\n techniqueType,\n animationTime,\n animationReachMultiplier,\n baseExtension,\n finalExtensionMultiplier,\n stanceModifier,\n effectiveReach,\n canHit,\n };\n }\n\n /**\n * Calculate maximum possible reach for a technique.\n *\n * **Korean**: 기술의 최대 가능 도달 거리\n *\n * Calculates reach at peak animation time (maximum extension).\n * Uses hybrid reach system with reachConfig if provided.\n *\n * @param physicalAttributes - Fighter's physical attributes\n * @param animationType - Animation type\n * @param stance - Current trigram stance\n * @param reachConfig - Optional technique reach configuration with baseExtension\n * @returns Maximum effective reach in meters\n *\n * @example\n * ```typescript\n * const calculator = new PhysicalReachCalculator();\n *\n * // With reachConfig for accurate designed reach\n * const maxReachWithConfig = calculator.calculateMaxReach(\n * MUSA_PHYSICAL,\n * AnimationType.FRONT_KICK,\n * TrigramStance.GEON,\n * { bodyPart: \"leg\", techniqueType: \"kick\", baseExtension: 1.05 }\n * );\n * // Uses max(1.05, 1.0) = 1.05\n *\n * // Without reachConfig (backward compatible)\n * const maxReachLegacy = calculator.calculateMaxReach(\n * MUSA_PHYSICAL,\n * AnimationType.FRONT_KICK,\n * TrigramStance.GEON\n * );\n * // Uses animation multiplier only (1.0)\n * ```\n *\n * @public\n * @korean 최대도달계산\n */\n calculateMaxReach(\n physicalAttributes: PhysicalAttributes,\n animationType: AnimationType,\n stance: TrigramStance,\n reachConfig?: PhysicalReachConfig,\n ): number {\n const hitTiming = getAnimationHitTiming(animationType);\n \n // Handle missing AnimationHitTiming entries\n // When timing data is unavailable, use baseExtension if provided,\n // otherwise use a neutral 1.0 multiplier to keep reach calculations functional.\n // This ensures techniques like GEON_ROUNDHOUSE, WATER_COUNTER, IRON_BLOCK\n // (which have technique definitions but missing animation timing entries)\n // can still perform reach/damage checks.\n if (!hitTiming) {\n // Use fallback approach: calculate reach with neutral timing\n const fallbackTime = 0.5; // Midpoint of typical animation\n const result = this.calculateReach(\n physicalAttributes,\n animationType,\n fallbackTime,\n stance,\n reachConfig,\n );\n return result.effectiveReach;\n }\n \n const peakTime = hitTiming.hitWindow.peakTime;\n\n const result = this.calculateReach(\n physicalAttributes,\n animationType,\n peakTime,\n stance,\n reachConfig,\n );\n\n return result.effectiveReach;\n }\n\n /**\n * Get limb length for a technique type.\n *\n * **Korean**: 기술 유형에 대한 팔다리 길이 가져오기\n *\n * @param physicalAttributes - Fighter's physical attributes\n * @param techniqueType - Type of technique\n * @returns Limb length in centimeters\n *\n * @private\n * @korean 팔다리길이가져오기\n */\n private getLimbLength(\n physicalAttributes: PhysicalAttributes,\n techniqueType: TechniqueType,\n ): number {\n switch (techniqueType) {\n case \"punch\":\n case \"elbow\":\n case \"pressure_point\":\n // Use arm length for hand-based techniques\n return physicalAttributes.armLength;\n\n case \"kick\":\n case \"knee\":\n // Use leg length for leg-based techniques\n return physicalAttributes.legLength;\n\n default:\n // Fallback to arm length\n return physicalAttributes.armLength;\n }\n }\n\n /**\n * Determine technique type from animation type.\n *\n * Public method exposed to avoid duplication across codebase.\n *\n * **Korean**: 애니메이션 타입에서 기술 유형 결정\n *\n * @param animationType - Animation type\n * @returns Technique type\n *\n * @public\n * @korean 기술유형결정\n */\n public getTechniqueTypeFromAnimation(\n animationType: AnimationType,\n ): TechniqueType {\n // Punch techniques\n if (\n animationType === AnimationType.JAB ||\n animationType === AnimationType.CROSS ||\n animationType === AnimationType.HOOK ||\n animationType === AnimationType.UPPERCUT ||\n animationType === AnimationType.OVERHAND ||\n animationType === AnimationType.BACKFIST ||\n animationType === AnimationType.HAMMER_FIST ||\n animationType === AnimationType.PALM_STRIKE ||\n animationType === AnimationType.SPEAR_HAND_STRIKE ||\n animationType === AnimationType.HEAVEN_STRIKE ||\n animationType === AnimationType.FLOWING_CROSS ||\n animationType === AnimationType.SOLAR_PLEXUS_STRIKE ||\n animationType === AnimationType.FLOWING_PUSH ||\n animationType === AnimationType.LIVER_DISRUPTION ||\n animationType === AnimationType.EAR_STRIKE\n ) {\n return \"punch\";\n }\n\n // Kick techniques\n if (\n animationType === AnimationType.FRONT_KICK ||\n animationType === AnimationType.ROUNDHOUSE_KICK ||\n animationType === AnimationType.SIDE_KICK ||\n animationType === AnimationType.BACK_KICK ||\n animationType === AnimationType.AXE_KICK ||\n animationType === AnimationType.CRESCENT_KICK ||\n animationType === AnimationType.LOW_KICK ||\n animationType === AnimationType.PUSH_KICK ||\n animationType === AnimationType.JUMPING_KICK ||\n animationType === AnimationType.SPINNING_HEEL_KICK ||\n animationType === AnimationType.TORNADO_KICK\n ) {\n return \"kick\";\n }\n\n // Elbow techniques\n if (\n animationType === AnimationType.ELBOW_STRIKE ||\n animationType === AnimationType.ELBOW_UPPERCUT ||\n animationType === AnimationType.SPINNING_ELBOW ||\n animationType === AnimationType.TEMPLE_ELBOW ||\n animationType === AnimationType.SPINNING_BACK_ELBOW ||\n animationType === AnimationType.SPINAL_ELBOW ||\n animationType === AnimationType.BRACHIAL_ELBOW\n ) {\n return \"elbow\";\n }\n\n // Knee techniques\n if (\n animationType === AnimationType.KNEE_STRIKE ||\n animationType === AnimationType.FLYING_KNEE ||\n animationType === AnimationType.KIDNEY_KNEE ||\n animationType === AnimationType.FEMORAL_KNEE\n ) {\n return \"knee\";\n }\n\n // Pressure point techniques\n if (\n animationType === AnimationType.NERVE_STRIKE ||\n animationType === AnimationType.PRESSURE_POINT_STRIKE ||\n animationType === AnimationType.NERVE_PARALYSIS ||\n animationType === AnimationType.THROAT_STRIKE ||\n animationType === AnimationType.EYE_GOUGE\n ) {\n return \"pressure_point\";\n }\n\n // Default to \"punch\" for any techniques not explicitly mapped above, including\n // complex grappling or hybrid animations. For *reach calculation* purposes we\n // approximate these using primary arm/forearm extension, since initial contact\n // is typically established with the upper limbs before the torso closes distance.\n // If a dedicated grappling TechniqueType and reach model are introduced later,\n // update this fallback to return that specific type instead of \"punch\".\n return \"punch\";\n }\n}\n\n/**\n * Singleton instance for convenient access.\n *\n * **Korean**: 싱글톤 인스턴스\n *\n * @example\n * ```typescript\n * import { physicalReachCalculator } from '@/systems/physics/PhysicalReachCalculator';\n *\n * const reach = physicalReachCalculator.calculateReach(\n * playerPhysical,\n * AnimationType.ROUNDHOUSE_KICK,\n * 0.32, // Peak time\n * TrigramStance.GEON\n * );\n * ```\n *\n * @public\n * @korean 싱글톤인스턴스\n */\nexport const physicalReachCalculator = new PhysicalReachCalculator();\n"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"SpeedModifierSystem.js","names":[],"sources":["../../../src/systems/physics/SpeedModifierSystem.ts"],"sourcesContent":["/**\n * Speed Modifier System\n *\n * **Korean**: 속도 변경 시스템 (Speed Modifier System)\n *\n * Implements comprehensive movement speed modifier system that dynamically adjusts\n * player movement speed based on multiple factors: combat stance, leg injuries,\n * stamina depletion, and combat state. Integrates with physics-based movement\n * system for authentic Korean martial arts combat feel.\n *\n * ## System Features\n *\n * - Base movement speeds for walking, running, backward, lateral, and crouching\n * - Eight Trigram stance-based speed modifiers (80% to 125%)\n * - Injury-based speed reduction from leg damage (0% to 100% penalty)\n * - Stamina-based acceleration penalty (0% to 75% penalty)\n * - Combat state speed adjustments (0% to 100% penalty)\n * - Multiplicative and additive modifier stacking\n *\n * ## Speed Calculation Formula\n *\n * ```\n * Final Speed = Base Speed\n * × Stance Modifier\n * × (1 - Injury Penalty from Legs, Torso & Pain)\n * × (1 - Combat State Penalty)\n *\n * Final Acceleration = Base Acceleration\n * × (1 - Stamina Penalty)\n * ```\n *\n * @module systems/physics/SpeedModifierSystem\n * @category Physics System\n * @korean 속도변경시스템\n */\n\nimport type {\n BodyPartHealth,\n BodyPartMaxHealth,\n} from \"@/systems/bodypart/types\";\nimport type { PlayerState } from \"@/systems/player\";\nimport { CombatState, TrigramStance } from \"@/types/common\";\nimport { BASE_MOVEMENT_ACCELERATION } from \"@/types/physicsConstants\";\nimport { STANCE_SPEED_MODIFIERS } from \"./MovementPhysics\";\nimport { injuryMovementModifier } from \"@/systems/movement/InjuryMovementModifier\";\n\n/**\n * Complete speed modifier state for physics calculations.\n *\n * **Korean**: 속도 변경 상태 (Speed Modifier State)\n *\n * Contains all calculated speed modifiers and final values for application\n * to the physics-based movement system.\n *\n * @public\n * @category Physics System\n * @korean 속도변경상태\n */\nexport interface SpeedModifierState {\n /** Base movement speed before modifiers (m/s) */\n readonly baseSpeed: number;\n /** Stance-based speed multiplier (0.80 to 1.25) */\n readonly stanceModifier: number;\n /** Injury-based speed reduction (0.0 to 0.75, percentage reduction) - Korean: 부상감소 */\n readonly injuryPenalty: number;\n /** Combat state speed reduction (0.0 to 1.0, percentage reduction) */\n readonly combatStatePenalty: number;\n /** Stamina-based acceleration reduction (0.0 to 0.75, percentage reduction) - Korean: 스태미나효과 */\n readonly staminaPenalty: number;\n /** Final calculated movement speed (m/s) */\n readonly finalSpeed: number;\n /** Final calculated acceleration (m/s²) */\n readonly finalAcceleration: number;\n /** Whether running is allowed (false when stamina < 10%) */\n readonly canRun: boolean;\n}\n\n/**\n * Movement type enumeration.\n *\n * **Korean**: 이동 유형 (Movement Type)\n *\n * @public\n * @category Physics System\n */\nexport enum MovementType {\n /** Walking forward - Korean: 걷기 */\n WALKING = \"walking\",\n /** Running/sprinting forward - Korean: 달리기 */\n RUNNING = \"running\",\n /** Moving backward - Korean: 후퇴 */\n BACKWARD = \"backward\",\n /** Side-stepping left/right - Korean: 측면이동 */\n LATERAL = \"lateral\",\n /** Crouching movement - Korean: 웅크리기 */\n CROUCHING = \"crouching\",\n}\n\n/**\n * Speed Modifier System class.\n *\n * **Korean**: 속도 변경 시스템 클래스\n *\n * Calculates comprehensive speed modifiers from multiple factors including\n * stance, injuries, stamina, and combat state. Integrates with existing\n * body part and movement systems.\n *\n * @example\n * ```typescript\n * const system = new SpeedModifierSystem();\n *\n * const modifiers = system.calculateSpeedModifiers(\n * playerState,\n * MovementType.RUNNING,\n * true // isCrouching\n * );\n *\n * // Apply to movement physics\n * movementPhysics.setMaxSpeed(modifiers.finalSpeed);\n * movementPhysics.setAcceleration(modifiers.finalAcceleration);\n * ```\n *\n * @public\n * @category Physics System\n */\nexport class SpeedModifierSystem {\n /**\n * Base walking speed (m/s)\n * Standard walking pace for combat movement - crosses 14m arena in ~2.3s\n *\n * **Korean**: 기본 걷기 속도 (Base Walking Speed)\n */\n private readonly BASE_WALKING_SPEED = 6.0;\n\n /**\n * Base running speed (m/s)\n * Sprint speed for rapid repositioning - crosses 14m arena in ~1.4s\n *\n * **Korean**: 기본 달리기 속도 (Base Running Speed)\n */\n private readonly BASE_RUNNING_SPEED = 10.0;\n\n /**\n * Lateral movement speed (m/s)\n * Fast side-stepping for combat positioning\n *\n * **Korean**: 측면 이동 속도 (Lateral Movement Speed)\n */\n private readonly LATERAL_SPEED = 5.0;\n\n /**\n * Crouching movement speed (m/s)\n * Defensive low movement for cautious approach\n *\n * **Korean**: 웅크림 이동 속도 (Crouching Movement Speed)\n */\n private readonly CROUCHING_SPEED = 3.0;\n\n /**\n * Backward speed multiplier (75% of forward speed)\n *\n * **Korean**: 후퇴 속도 배수 (Backward Speed Multiplier)\n */\n private readonly BACKWARD_SPEED_MULTIPLIER = 0.75;\n\n /**\n * Base acceleration rate (m/s²)\n * Instant-response acceleration for combat movement\n * (e.g., reaches 6 m/s walking speed in 0.2s; 10 m/s sprint speed in 0.33s)\n * Increased from 12.0 to 30.0 for responsive, arcade-style combat feel\n *\n * Imported from physicsConstants.ts to maintain consistency across systems.\n *\n * **Korean**: 기본 가속도 (Base Acceleration)\n */\n private readonly BASE_ACCELERATION = BASE_MOVEMENT_ACCELERATION;\n\n /**\n * Combat state speed penalty multipliers\n *\n * **Korean**: 전투 상태 속도 패널티 (Combat State Speed Penalties)\n */\n private readonly COMBAT_STATE_PENALTIES: Record<CombatState, number> = {\n [CombatState.IDLE]: 0.0, // No penalty when idle\n [CombatState.ATTACKING]: 0.3, // -30% during attack commitment\n [CombatState.DEFENDING]: 0.2, // -20% while guarding\n [CombatState.STUNNED]: 1.0, // Cannot move when stunned\n [CombatState.RECOVERING]: 0.4, // -40% during recovery frames\n [CombatState.COUNTERING]: 0.25, // -25% during counter execution\n [CombatState.TRANSITIONING]: 0.15, // -15% during stance transitions\n [CombatState.GRAPPLING]: 0.8, // -80% while controlling opponent\n [CombatState.GRAPPLED]: 1.0, // Cannot move while being controlled\n };\n\n // Constructor removed - no longer need MovementPenaltySystem instance\n // Using injuryMovementModifier singleton instead\n\n /**\n * Calculate comprehensive speed modifiers for player movement.\n *\n * **Korean**: 속도 변경 계산 (Calculate Speed Modifiers)\n *\n * Analyzes player state and returns complete speed modifier information\n * including all individual factors and final calculated values.\n *\n * @param playerState - Current player state with stance, health, stamina\n * @param movementType - Type of movement being performed\n * @param isCrouching - Whether player is in crouching stance\n * @returns Complete speed modifier state with all factors\n *\n * @public\n */\n public calculateSpeedModifiers(\n playerState: PlayerState,\n movementType: MovementType = MovementType.WALKING,\n isCrouching: boolean = false,\n ): SpeedModifierState {\n // Get base speed based on movement type and archetype-specific speeds\n const baseSpeed = this.getBaseSpeed(\n movementType,\n isCrouching,\n playerState.physicalAttributes,\n );\n\n // Calculate injury penalty from leg damage using NEW InjuryMovementModifier system\n // This replaces the old MovementPenaltySystem with comprehensive injury calculations\n const injuryPenalty = this.calculateInjuryPenalty(\n playerState.bodyPartHealth,\n playerState.bodyPartMaxHealth,\n playerState.pain,\n );\n\n // Calculate stance modifier separately.\n // NOTE: InjuryMovementModifier is called with neutral GEON stance inside\n // calculateInjuryPenalty() so that injury effects are isolated from stance.\n // The actual current stance effect must be applied here via stanceModifier.\n const stanceModifier = this.getStanceSpeedModifier(\n playerState.currentStance,\n );\n\n // Calculate combat state penalty\n const combatStatePenalty = this.getCombatStatePenalty(\n playerState.combatState,\n );\n\n // Calculate stamina penalty (affects acceleration only)\n const staminaPenalty = this.calculateStaminaPenalty(\n playerState.stamina,\n playerState.maxStamina,\n );\n\n // Check if running is allowed\n const canRun = this.canPlayerRun(\n playerState.stamina,\n playerState.maxStamina,\n );\n\n // Calculate final speed with all modifiers applied multiplicatively\n // Formula: Base × Stance × (1 - Injury) × (1 - CombatState)\n // Note: Injury penalty now excludes stance (calculated with GEON), so we apply stance here\n const finalSpeed =\n baseSpeed *\n stanceModifier *\n (1.0 - injuryPenalty) *\n (1.0 - combatStatePenalty);\n\n // Calculate final acceleration using archetype-specific base and stamina penalty\n // Formula: ArchetypeAcceleration × (1 - StaminaPenalty)\n const archetypeAcceleration =\n playerState.physicalAttributes?.acceleration ?? this.BASE_ACCELERATION;\n const finalAcceleration = archetypeAcceleration * (1.0 - staminaPenalty);\n\n return {\n baseSpeed,\n stanceModifier,\n injuryPenalty,\n combatStatePenalty,\n staminaPenalty,\n finalSpeed,\n finalAcceleration,\n canRun,\n };\n }\n\n /**\n * Get base speed for movement type.\n *\n * **Korean**: 기본 속도 계산 (Get Base Speed)\n *\n * Uses archetype-specific walk/run speeds when available,\n * falls back to default values for backwards compatibility.\n *\n * @param movementType - Type of movement\n * @param isCrouching - Whether player is crouching\n * @param physicalAttributes - Player's physical attributes (optional)\n * @returns Base speed in m/s\n *\n * @private\n */\n private getBaseSpeed(\n movementType: MovementType,\n isCrouching: boolean,\n physicalAttributes?: import(\"@/types\").PhysicalAttributes,\n ): number {\n if (isCrouching) {\n return this.CROUCHING_SPEED;\n }\n\n // Use archetype-specific speeds if available\n const archetypeWalkSpeed =\n physicalAttributes?.walkSpeed ?? this.BASE_WALKING_SPEED;\n const archetypeRunSpeed =\n physicalAttributes?.runSpeed ?? this.BASE_RUNNING_SPEED;\n\n switch (movementType) {\n case MovementType.WALKING:\n return archetypeWalkSpeed;\n case MovementType.RUNNING:\n return archetypeRunSpeed;\n case MovementType.BACKWARD:\n return archetypeWalkSpeed * this.BACKWARD_SPEED_MULTIPLIER;\n case MovementType.LATERAL:\n // Lateral speed scales proportionally to archetype walk speed\n return (\n archetypeWalkSpeed * (this.LATERAL_SPEED / this.BASE_WALKING_SPEED)\n );\n case MovementType.CROUCHING:\n return this.CROUCHING_SPEED;\n default:\n return archetypeWalkSpeed;\n }\n }\n\n /**\n * Get stance-based speed modifier.\n *\n * **Korean**: 자세 속도 배수 계산 (Get Stance Speed Modifier)\n *\n * @param stance - Current Eight Trigram stance\n * @returns Speed multiplier (0.80 to 1.25)\n *\n * @private\n */\n private getStanceSpeedModifier(stance: TrigramStance): number {\n return STANCE_SPEED_MODIFIERS[stance] ?? 1.0;\n }\n\n /**\n * Calculate injury-based speed penalty from leg damage.\n *\n * **Korean**: 부상 속도 패널티 계산 (Calculate Injury Penalty)\n *\n * Uses NEW InjuryMovementModifier to determine speed reduction based on\n * leg damage severity, torso damage, and pain levels.\n * NOTE: Stance modifier is calculated separately to maintain compatibility\n * with existing SpeedModifierState interface.\n * \n * Penalties are progressive and realistic:\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 * - Pain overload: -15% when pain ≥ 80\n *\n * @param bodyPartHealth - Current body part health\n * @param bodyPartMaxHealth - Maximum body part health\n * @param painLevel - Current pain level (0-100)\n * @returns Speed penalty as percentage (0.0 to 0.9 max)\n *\n * @private\n */\n private calculateInjuryPenalty(\n bodyPartHealth: BodyPartHealth | undefined,\n bodyPartMaxHealth: BodyPartMaxHealth | undefined,\n painLevel: number,\n ): number {\n if (!bodyPartHealth || !bodyPartMaxHealth) {\n return 0.0; // No penalty if body part health not tracked\n }\n\n // Use NEW InjuryMovementModifier system for comprehensive injury calculation\n // Use NEUTRAL stance (GEON = 1.0x) to get pure injury penalty without stance influence\n // Stance is applied separately in calculateSpeedModifiers for compatibility\n const result = injuryMovementModifier.calculateMovementSpeed(\n 1.0, // Base speed of 1.0 to get pure multiplier\n bodyPartHealth,\n TrigramStance.GEON, // Neutral stance to isolate injury penalty\n painLevel,\n );\n\n // Convert speedMultiplier to penalty percentage\n // speedMultiplier of 1.0 = no penalty (0.0)\n // speedMultiplier of 0.5 = 50% penalty (0.5)\n // speedMultiplier of 0.1 = 90% penalty (0.9)\n const penalty = 1.0 - result.speedMultiplier;\n\n // Return penalty (already clamped by InjuryMovementModifier to min 0.1 speed)\n return Math.min(penalty, 0.9);\n }\n\n /**\n * Calculate combat state speed penalty.\n *\n * **Korean**: 전투 상태 속도 패널티 계산 (Get Combat State Penalty)\n *\n * Different combat states restrict movement:\n * - IDLE: 0% penalty (full movement)\n * - ATTACKING: -30% penalty (committed to technique)\n * - DEFENDING: -20% penalty (guard up, limited mobility)\n * - STUNNED: -100% penalty (cannot move)\n * - RECOVERING: -40% penalty (vulnerable, slow movement)\n * - COUNTERING: -25% penalty (counter execution)\n * - TRANSITIONING: -15% penalty (stance change)\n *\n * @param combatState - Current combat state\n * @returns Speed penalty as percentage (0.0 to 1.0)\n *\n * @private\n */\n private getCombatStatePenalty(combatState: CombatState): number {\n return this.COMBAT_STATE_PENALTIES[combatState] ?? 0.0;\n }\n\n /**\n * Calculate stamina-based acceleration penalty.\n *\n * **Korean**: 스태미나 가속 패널티 계산 (Calculate Stamina Penalty)\n *\n * Stamina affects acceleration rate, making it harder to change direction\n * or speed up when fatigued:\n * - High stamina (70-100%): 0% penalty (normal acceleration)\n * - Medium stamina (40-70%): -20% penalty\n * - Low stamina (10-40%): -50% penalty\n * - Depleted stamina (<10%): -75% penalty, cannot run\n *\n * @param currentStamina - Current stamina points\n * @param maxStamina - Maximum stamina capacity\n * @returns Acceleration penalty as percentage (0.0 to 0.75)\n *\n * @private\n */\n private calculateStaminaPenalty(\n currentStamina: number,\n maxStamina: number,\n ): number {\n if (maxStamina <= 0) {\n return 0.0; // No penalty if stamina not tracked\n }\n\n const staminaPercent = (currentStamina / maxStamina) * 100;\n\n if (staminaPercent >= 70) {\n return 0.0; // High stamina: no penalty\n } else if (staminaPercent >= 40) {\n return 0.2; // Medium stamina: -20% acceleration\n } else if (staminaPercent >= 10) {\n return 0.5; // Low stamina: -50% acceleration\n } else {\n return 0.75; // Depleted stamina: -75% acceleration\n }\n }\n\n /**\n * Check if player can run based on stamina level.\n *\n * **Korean**: 달리기 가능 확인 (Can Player Run)\n *\n * @param currentStamina - Current stamina points\n * @param maxStamina - Maximum stamina capacity\n * @returns True if player has enough stamina to run\n *\n * @private\n */\n private canPlayerRun(currentStamina: number, maxStamina: number): boolean {\n if (maxStamina <= 0) {\n return true; // Allow running if stamina not tracked\n }\n\n const staminaPercent = (currentStamina / maxStamina) * 100;\n return staminaPercent >= 10; // Cannot run when stamina < 10%\n }\n\n /**\n * Apply speed modifiers to movement physics system.\n *\n * **Korean**: 속도 변경 적용 (Apply Speed Modifiers)\n *\n * Convenience method to directly apply calculated modifiers to a\n * movement physics instance.\n *\n * @param movementPhysics - Movement physics system to update\n * @param modifiers - Calculated speed modifier state\n *\n * @public\n */\n public applySpeedModifiers(\n movementPhysics: {\n setMaxSpeed: (speed: number) => void;\n setAcceleration: (accel: number) => void;\n },\n modifiers: SpeedModifierState,\n ): void {\n movementPhysics.setMaxSpeed(modifiers.finalSpeed);\n movementPhysics.setAcceleration(modifiers.finalAcceleration);\n }\n}\n"],"mappings":";;;;;;;;;;;;;AAqFA,IAAY,eAAL,yBAAA,cAAA;;AAEL,cAAA,aAAU;;AAEV,cAAA,aAAU;;AAEV,cAAA,cAAW;;AAEX,cAAA,aAAU;;AAEV,cAAA,eAAY;;KACb;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA6BD,IAAa,sBAAb,MAAiC;;;;;;;CAO/B,qBAAsC;;;;;;;CAQtC,qBAAsC;;;;;;;CAQtC,gBAAiC;;;;;;;CAQjC,kBAAmC;;;;;;CAOnC,4BAA6C;;;;;;;;;;;CAY7C,oBAAA;;;;;;CAOA,yBAAuE;GACpE,YAAY,OAAO;GACnB,YAAY,YAAY;GACxB,YAAY,YAAY;GACxB,YAAY,UAAU;GACtB,YAAY,aAAa;GACzB,YAAY,aAAa;GACzB,YAAY,gBAAgB;GAC5B,YAAY,YAAY;GACxB,YAAY,WAAW;EACzB;;;;;;;;;;;;;;;;CAoBD,wBACE,aACA,eAA6B,aAAa,SAC1C,cAAuB,OACH;EAEpB,MAAM,YAAY,KAAK,aACrB,cACA,aACA,YAAY,mBACb;EAID,MAAM,gBAAgB,KAAK,uBACzB,YAAY,gBACZ,YAAY,mBACZ,YAAY,KACb;EAMD,MAAM,iBAAiB,KAAK,uBAC1B,YAAY,cACb;EAGD,MAAM,qBAAqB,KAAK,sBAC9B,YAAY,YACb;EAGD,MAAM,iBAAiB,KAAK,wBAC1B,YAAY,SACZ,YAAY,WACb;EAGD,MAAM,SAAS,KAAK,aAClB,YAAY,SACZ,YAAY,WACb;AAiBD,SAAO;GACL;GACA;GACA;GACA;GACA;GACA,YAjBA,YACA,kBACC,IAAM,kBACN,IAAM;GAeP,oBAVA,YAAY,oBAAoB,gBAAgB,KAAK,sBACJ,IAAM;GAUvD;GACD;;;;;;;;;;;;;;;;;CAkBH,aACE,cACA,aACA,oBACQ;AACR,MAAI,YACF,QAAO,KAAK;EAId,MAAM,qBACJ,oBAAoB,aAAa,KAAK;EACxC,MAAM,oBACJ,oBAAoB,YAAY,KAAK;AAEvC,UAAQ,cAAR;GACE,KAAK,aAAa,QAChB,QAAO;GACT,KAAK,aAAa,QAChB,QAAO;GACT,KAAK,aAAa,SAChB,QAAO,qBAAqB,KAAK;GACnC,KAAK,aAAa,QAEhB,QACE,sBAAsB,KAAK,gBAAgB,KAAK;GAEpD,KAAK,aAAa,UAChB,QAAO,KAAK;GACd,QACE,QAAO;;;;;;;;;;;;;CAcb,uBAA+B,QAA+B;AAC5D,SAAO,uBAAuB,WAAW;;;;;;;;;;;;;;;;;;;;;;;;;CA0B3C,uBACE,gBACA,mBACA,WACQ;AACR,MAAI,CAAC,kBAAkB,CAAC,kBACtB,QAAO;EAiBT,MAAM,UAAU,IAXD,uBAAuB,uBACpC,GACA,gBACA,cAAc,MACd,UAOoB,CAAO;AAG7B,SAAO,KAAK,IAAI,SAAS,GAAI;;;;;;;;;;;;;;;;;;;;;CAsB/B,sBAA8B,aAAkC;AAC9D,SAAO,KAAK,uBAAuB,gBAAgB;;;;;;;;;;;;;;;;;;;;CAqBrD,wBACE,gBACA,YACQ;AACR,MAAI,cAAc,EAChB,QAAO;EAGT,MAAM,iBAAkB,iBAAiB,aAAc;AAEvD,MAAI,kBAAkB,GACpB,QAAO;WACE,kBAAkB,GAC3B,QAAO;WACE,kBAAkB,GAC3B,QAAO;MAEP,QAAO;;;;;;;;;;;;;CAeX,aAAqB,gBAAwB,YAA6B;AACxE,MAAI,cAAc,EAChB,QAAO;AAIT,SADwB,iBAAiB,aAAc,OAC9B;;;;;;;;;;;;;;;CAgB3B,oBACE,iBAIA,WACM;AACN,kBAAgB,YAAY,UAAU,WAAW;AACjD,kBAAgB,gBAAgB,UAAU,kBAAkB"}
1
+ {"version":3,"file":"SpeedModifierSystem.js","names":[],"sources":["../../../src/systems/physics/SpeedModifierSystem.ts"],"sourcesContent":["/**\n * Speed Modifier System\n *\n * **Korean**: 속도 변경 시스템 (Speed Modifier System)\n *\n * Implements comprehensive movement speed modifier system that dynamically adjusts\n * player movement speed based on multiple factors: combat stance, leg injuries,\n * stamina depletion, and combat state. Integrates with physics-based movement\n * system for authentic Korean martial arts combat feel.\n *\n * ## System Features\n *\n * - Base movement speeds for walking, running, backward, lateral, and crouching\n * - Eight Trigram stance-based speed modifiers (80% to 125%)\n * - Injury-based speed reduction from leg damage (0% to 100% penalty)\n * - Stamina-based acceleration penalty (0% to 75% penalty)\n * - Combat state speed adjustments (0% to 100% penalty)\n * - Multiplicative and additive modifier stacking\n *\n * ## Speed Calculation Formula\n *\n * ```\n * Final Speed = Base Speed\n * × Stance Modifier\n * × (1 - Injury Penalty from Legs, Torso & Pain)\n * × (1 - Combat State Penalty)\n *\n * Final Acceleration = Base Acceleration\n * × (1 - Stamina Penalty)\n * ```\n *\n * @module systems/physics/SpeedModifierSystem\n * @category Physics System\n * @korean 속도변경시스템\n */\n\nimport type {\n BodyPartHealth,\n BodyPartMaxHealth,\n} from \"@/systems/bodypart/types\";\nimport type { PlayerState } from \"@/systems/player\";\nimport { CombatState, TrigramStance } from \"@/types/common\";\nimport { BASE_MOVEMENT_ACCELERATION } from \"@/types/physicsConstants\";\nimport { STANCE_SPEED_MODIFIERS } from \"./MovementPhysics\";\nimport { injuryMovementModifier } from \"@/systems/movement/InjuryMovementModifier\";\n\n/**\n * Complete speed modifier state for physics calculations.\n *\n * **Korean**: 속도 변경 상태 (Speed Modifier State)\n *\n * Contains all calculated speed modifiers and final values for application\n * to the physics-based movement system.\n *\n * @public\n * @category Physics System\n * @korean 속도변경상태\n */\nexport interface SpeedModifierState {\n /** Base movement speed before modifiers (m/s) */\n readonly baseSpeed: number;\n /** Stance-based speed multiplier (0.80 to 1.25) */\n readonly stanceModifier: number;\n /** Injury-based speed reduction (0.0 to 0.75, percentage reduction) - Korean: 부상감소 */\n readonly injuryPenalty: number;\n /** Combat state speed reduction (0.0 to 1.0, percentage reduction) */\n readonly combatStatePenalty: number;\n /** Stamina-based acceleration reduction (0.0 to 0.75, percentage reduction) - Korean: 스태미나효과 */\n readonly staminaPenalty: number;\n /** Final calculated movement speed (m/s) */\n readonly finalSpeed: number;\n /** Final calculated acceleration (m/s²) */\n readonly finalAcceleration: number;\n /** Whether running is allowed (false when stamina < 10%) */\n readonly canRun: boolean;\n}\n\n/**\n * Movement type enumeration.\n *\n * **Korean**: 이동 유형 (Movement Type)\n *\n * @public\n * @category Physics System\n */\nexport enum MovementType {\n /** Walking forward - Korean: 걷기 */\n WALKING = \"walking\",\n /** Running/sprinting forward - Korean: 달리기 */\n RUNNING = \"running\",\n /** Moving backward - Korean: 후퇴 */\n BACKWARD = \"backward\",\n /** Side-stepping left/right - Korean: 측면이동 */\n LATERAL = \"lateral\",\n /** Crouching movement - Korean: 웅크리기 */\n CROUCHING = \"crouching\",\n}\n\n/**\n * Speed Modifier System class.\n *\n * **Korean**: 속도 변경 시스템 클래스\n *\n * Calculates comprehensive speed modifiers from multiple factors including\n * stance, injuries, stamina, and combat state. Integrates with existing\n * body part and movement systems.\n *\n * @example\n * ```typescript\n * const system = new SpeedModifierSystem();\n *\n * const modifiers = system.calculateSpeedModifiers(\n * playerState,\n * MovementType.RUNNING,\n * true // isCrouching\n * );\n *\n * // Apply to movement physics\n * movementPhysics.setMaxSpeed(modifiers.finalSpeed);\n * movementPhysics.setAcceleration(modifiers.finalAcceleration);\n * ```\n *\n * @public\n * @category Physics System\n */\nexport class SpeedModifierSystem {\n /**\n * Base walking speed (m/s)\n * Standard walking pace for combat movement - crosses 14m arena in ~2.3s\n *\n * **Korean**: 기본 걷기 속도 (Base Walking Speed)\n */\n private readonly BASE_WALKING_SPEED = 6.0;\n\n /**\n * Base running speed (m/s)\n * Sprint speed for rapid repositioning - crosses 14m arena in ~1.4s\n *\n * **Korean**: 기본 달리기 속도 (Base Running Speed)\n */\n private readonly BASE_RUNNING_SPEED = 10.0;\n\n /**\n * Lateral movement speed (m/s)\n * Fast side-stepping for combat positioning\n *\n * **Korean**: 측면 이동 속도 (Lateral Movement Speed)\n */\n private readonly LATERAL_SPEED = 5.0;\n\n /**\n * Crouching movement speed (m/s)\n * Defensive low movement for cautious approach\n *\n * **Korean**: 웅크림 이동 속도 (Crouching Movement Speed)\n */\n private readonly CROUCHING_SPEED = 3.0;\n\n /**\n * Backward speed multiplier (75% of forward speed)\n *\n * **Korean**: 후퇴 속도 배수 (Backward Speed Multiplier)\n */\n private readonly BACKWARD_SPEED_MULTIPLIER = 0.75;\n\n /**\n * Base acceleration rate (m/s²)\n * Instant-response acceleration for combat movement\n * (e.g., reaches 6 m/s walking speed in 0.2s; 10 m/s sprint speed in 0.33s)\n * Increased from 12.0 to 30.0 for responsive, arcade-style combat feel\n *\n * Imported from physicsConstants.ts to maintain consistency across systems.\n *\n * **Korean**: 기본 가속도 (Base Acceleration)\n */\n private readonly BASE_ACCELERATION = BASE_MOVEMENT_ACCELERATION;\n\n /**\n * Combat state speed penalty multipliers\n *\n * **Korean**: 전투 상태 속도 패널티 (Combat State Speed Penalties)\n */\n private readonly COMBAT_STATE_PENALTIES: Record<CombatState, number> = {\n [CombatState.IDLE]: 0.0, // No penalty when idle\n [CombatState.ATTACKING]: 0.3, // -30% during attack commitment\n [CombatState.DEFENDING]: 0.2, // -20% while guarding\n [CombatState.STUNNED]: 1.0, // Cannot move when stunned\n [CombatState.RECOVERING]: 0.4, // -40% during recovery frames\n [CombatState.COUNTERING]: 0.25, // -25% during counter execution\n [CombatState.TRANSITIONING]: 0.15, // -15% during stance transitions\n [CombatState.GRAPPLING]: 0.8, // -80% while controlling opponent\n [CombatState.GRAPPLED]: 1.0, // Cannot move while being controlled\n };\n\n // Constructor removed - no longer need MovementPenaltySystem instance\n // Using injuryMovementModifier singleton instead\n\n /**\n * Calculate comprehensive speed modifiers for player movement.\n *\n * **Korean**: 속도 변경 계산 (Calculate Speed Modifiers)\n *\n * Analyzes player state and returns complete speed modifier information\n * including all individual factors and final calculated values.\n *\n * @param playerState - Current player state with stance, health, stamina\n * @param movementType - Type of movement being performed\n * @param isCrouching - Whether player is in crouching stance\n * @returns Complete speed modifier state with all factors\n *\n * @public\n */\n public calculateSpeedModifiers(\n playerState: PlayerState,\n movementType: MovementType = MovementType.WALKING,\n isCrouching: boolean = false,\n ): SpeedModifierState {\n // Get base speed based on movement type and archetype-specific speeds\n const baseSpeed = this.getBaseSpeed(\n movementType,\n isCrouching,\n playerState.physicalAttributes,\n );\n\n // Calculate injury penalty from leg damage using NEW InjuryMovementModifier system\n // This replaces the old MovementPenaltySystem with comprehensive injury calculations\n const injuryPenalty = this.calculateInjuryPenalty(\n playerState.bodyPartHealth,\n playerState.bodyPartMaxHealth,\n playerState.pain,\n );\n\n // Calculate stance modifier separately.\n // NOTE: InjuryMovementModifier is called with neutral GEON stance inside\n // calculateInjuryPenalty() so that injury effects are isolated from stance.\n // The actual current stance effect must be applied here via stanceModifier.\n const stanceModifier = this.getStanceSpeedModifier(\n playerState.currentStance,\n );\n\n // Calculate combat state penalty\n const combatStatePenalty = this.getCombatStatePenalty(\n playerState.combatState,\n );\n\n // Calculate stamina penalty (affects acceleration only)\n const staminaPenalty = this.calculateStaminaPenalty(\n playerState.stamina,\n playerState.maxStamina,\n );\n\n // Check if running is allowed\n const canRun = this.canPlayerRun(\n playerState.stamina,\n playerState.maxStamina,\n );\n\n // Calculate final speed with all modifiers applied multiplicatively\n // Formula: Base × Stance × (1 - Injury) × (1 - CombatState)\n // Note: Injury penalty now excludes stance (calculated with GEON), so we apply stance here\n const finalSpeed =\n baseSpeed *\n stanceModifier *\n (1.0 - injuryPenalty) *\n (1.0 - combatStatePenalty);\n\n // Calculate final acceleration using archetype-specific base and stamina penalty\n // Formula: ArchetypeAcceleration × (1 - StaminaPenalty)\n const archetypeAcceleration =\n playerState.physicalAttributes?.acceleration ?? this.BASE_ACCELERATION;\n const finalAcceleration = archetypeAcceleration * (1.0 - staminaPenalty);\n\n return {\n baseSpeed,\n stanceModifier,\n injuryPenalty,\n combatStatePenalty,\n staminaPenalty,\n finalSpeed,\n finalAcceleration,\n canRun,\n };\n }\n\n /**\n * Get base speed for movement type.\n *\n * **Korean**: 기본 속도 계산 (Get Base Speed)\n *\n * Uses archetype-specific walk/run speeds when available,\n * falls back to default values for backwards compatibility.\n *\n * @param movementType - Type of movement\n * @param isCrouching - Whether player is crouching\n * @param physicalAttributes - Player's physical attributes (optional)\n * @returns Base speed in m/s\n *\n * @private\n */\n private getBaseSpeed(\n movementType: MovementType,\n isCrouching: boolean,\n physicalAttributes?: import(\"@/types\").PhysicalAttributes,\n ): number {\n if (isCrouching) {\n return this.CROUCHING_SPEED;\n }\n\n // Use archetype-specific speeds if available\n const archetypeWalkSpeed =\n physicalAttributes?.walkSpeed ?? this.BASE_WALKING_SPEED;\n const archetypeRunSpeed =\n physicalAttributes?.runSpeed ?? this.BASE_RUNNING_SPEED;\n\n switch (movementType) {\n case MovementType.WALKING:\n return archetypeWalkSpeed;\n case MovementType.RUNNING:\n return archetypeRunSpeed;\n case MovementType.BACKWARD:\n return archetypeWalkSpeed * this.BACKWARD_SPEED_MULTIPLIER;\n case MovementType.LATERAL:\n // Lateral speed scales proportionally to archetype walk speed\n return (\n archetypeWalkSpeed * (this.LATERAL_SPEED / this.BASE_WALKING_SPEED)\n );\n case MovementType.CROUCHING:\n return this.CROUCHING_SPEED;\n default:\n return archetypeWalkSpeed;\n }\n }\n\n /**\n * Get stance-based speed modifier.\n *\n * **Korean**: 자세 속도 배수 계산 (Get Stance Speed Modifier)\n *\n * @param stance - Current Eight Trigram stance\n * @returns Speed multiplier (0.80 to 1.25)\n *\n * @private\n */\n private getStanceSpeedModifier(stance: TrigramStance): number {\n return STANCE_SPEED_MODIFIERS[stance] ?? 1.0;\n }\n\n /**\n * Calculate injury-based speed penalty from leg damage.\n *\n * **Korean**: 부상 속도 패널티 계산 (Calculate Injury Penalty)\n *\n * Uses NEW InjuryMovementModifier to determine speed reduction based on\n * leg damage severity, torso damage, and pain levels.\n * NOTE: Stance modifier is calculated separately to maintain compatibility\n * with existing SpeedModifierState interface.\n * \n * Penalties are progressive and realistic:\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 * - Pain overload: -15% when pain ≥ 80\n *\n * @param bodyPartHealth - Current body part health\n * @param bodyPartMaxHealth - Maximum body part health\n * @param painLevel - Current pain level (0-100)\n * @returns Speed penalty as percentage (0.0 to 0.9 max)\n *\n * @private\n */\n private calculateInjuryPenalty(\n bodyPartHealth: BodyPartHealth | undefined,\n bodyPartMaxHealth: BodyPartMaxHealth | undefined,\n painLevel: number,\n ): number {\n if (!bodyPartHealth || !bodyPartMaxHealth) {\n return 0.0; // No penalty if body part health not tracked\n }\n\n // Use NEW InjuryMovementModifier system for comprehensive injury calculation\n // Use NEUTRAL stance (GEON = 1.0x) to get pure injury penalty without stance influence\n // Stance is applied separately in calculateSpeedModifiers for compatibility\n const result = injuryMovementModifier.calculateMovementSpeed(\n 1.0, // Base speed of 1.0 to get pure multiplier\n bodyPartHealth,\n TrigramStance.GEON, // Neutral stance to isolate injury penalty\n painLevel,\n );\n\n // Convert speedMultiplier to penalty percentage\n // speedMultiplier of 1.0 = no penalty (0.0)\n // speedMultiplier of 0.5 = 50% penalty (0.5)\n // speedMultiplier of 0.1 = 90% penalty (0.9)\n const penalty = 1.0 - result.speedMultiplier;\n\n // Return penalty (already clamped by InjuryMovementModifier to min 0.1 speed)\n return Math.min(penalty, 0.9);\n }\n\n /**\n * Calculate combat state speed penalty.\n *\n * **Korean**: 전투 상태 속도 패널티 계산 (Get Combat State Penalty)\n *\n * Different combat states restrict movement:\n * - IDLE: 0% penalty (full movement)\n * - ATTACKING: -30% penalty (committed to technique)\n * - DEFENDING: -20% penalty (guard up, limited mobility)\n * - STUNNED: -100% penalty (cannot move)\n * - RECOVERING: -40% penalty (vulnerable, slow movement)\n * - COUNTERING: -25% penalty (counter execution)\n * - TRANSITIONING: -15% penalty (stance change)\n *\n * @param combatState - Current combat state\n * @returns Speed penalty as percentage (0.0 to 1.0)\n *\n * @private\n */\n private getCombatStatePenalty(combatState: CombatState): number {\n return this.COMBAT_STATE_PENALTIES[combatState] ?? 0.0;\n }\n\n /**\n * Calculate stamina-based acceleration penalty.\n *\n * **Korean**: 스태미나 가속 패널티 계산 (Calculate Stamina Penalty)\n *\n * Stamina affects acceleration rate, making it harder to change direction\n * or speed up when fatigued:\n * - High stamina (70-100%): 0% penalty (normal acceleration)\n * - Medium stamina (40-70%): -20% penalty\n * - Low stamina (10-40%): -50% penalty\n * - Depleted stamina (<10%): -75% penalty, cannot run\n *\n * @param currentStamina - Current stamina points\n * @param maxStamina - Maximum stamina capacity\n * @returns Acceleration penalty as percentage (0.0 to 0.75)\n *\n * @private\n */\n private calculateStaminaPenalty(\n currentStamina: number,\n maxStamina: number,\n ): number {\n if (maxStamina <= 0) {\n return 0.0; // No penalty if stamina not tracked\n }\n\n const staminaPercent = (currentStamina / maxStamina) * 100;\n\n if (staminaPercent >= 70) {\n return 0.0; // High stamina: no penalty\n } else if (staminaPercent >= 40) {\n return 0.2; // Medium stamina: -20% acceleration\n } else if (staminaPercent >= 10) {\n return 0.5; // Low stamina: -50% acceleration\n } else {\n return 0.75; // Depleted stamina: -75% acceleration\n }\n }\n\n /**\n * Check if player can run based on stamina level.\n *\n * **Korean**: 달리기 가능 확인 (Can Player Run)\n *\n * @param currentStamina - Current stamina points\n * @param maxStamina - Maximum stamina capacity\n * @returns True if player has enough stamina to run\n *\n * @private\n */\n private canPlayerRun(currentStamina: number, maxStamina: number): boolean {\n if (maxStamina <= 0) {\n return true; // Allow running if stamina not tracked\n }\n\n const staminaPercent = (currentStamina / maxStamina) * 100;\n return staminaPercent >= 10; // Cannot run when stamina < 10%\n }\n\n /**\n * Apply speed modifiers to movement physics system.\n *\n * **Korean**: 속도 변경 적용 (Apply Speed Modifiers)\n *\n * Convenience method to directly apply calculated modifiers to a\n * movement physics instance.\n *\n * @param movementPhysics - Movement physics system to update\n * @param modifiers - Calculated speed modifier state\n *\n * @public\n */\n public applySpeedModifiers(\n movementPhysics: {\n setMaxSpeed: (speed: number) => void;\n setAcceleration: (accel: number) => void;\n },\n modifiers: SpeedModifierState,\n ): void {\n movementPhysics.setMaxSpeed(modifiers.finalSpeed);\n movementPhysics.setAcceleration(modifiers.finalAcceleration);\n }\n}\n"],"mappings":";;;;;;;;;;;;;AAqFA,IAAY,eAAL,yBAAA,cAAA;;CAEL,aAAA,aAAU;;CAEV,aAAA,aAAU;;CAEV,aAAA,cAAW;;CAEX,aAAA,aAAU;;CAEV,aAAA,eAAY;;KACb;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA6BD,IAAa,sBAAb,MAAiC;;;;;;;CAO/B,qBAAsC;;;;;;;CAQtC,qBAAsC;;;;;;;CAQtC,gBAAiC;;;;;;;CAQjC,kBAAmC;;;;;;CAOnC,4BAA6C;;;;;;;;;;;CAY7C,oBAAA;;;;;;CAOA,yBAAuE;GACpE,YAAY,OAAO;GACnB,YAAY,YAAY;GACxB,YAAY,YAAY;GACxB,YAAY,UAAU;GACtB,YAAY,aAAa;GACzB,YAAY,aAAa;GACzB,YAAY,gBAAgB;GAC5B,YAAY,YAAY;GACxB,YAAY,WAAW;EACzB;;;;;;;;;;;;;;;;CAoBD,wBACE,aACA,eAA6B,aAAa,SAC1C,cAAuB,OACH;EAEpB,MAAM,YAAY,KAAK,aACrB,cACA,aACA,YAAY,mBACb;EAID,MAAM,gBAAgB,KAAK,uBACzB,YAAY,gBACZ,YAAY,mBACZ,YAAY,KACb;EAMD,MAAM,iBAAiB,KAAK,uBAC1B,YAAY,cACb;EAGD,MAAM,qBAAqB,KAAK,sBAC9B,YAAY,YACb;EAGD,MAAM,iBAAiB,KAAK,wBAC1B,YAAY,SACZ,YAAY,WACb;EAGD,MAAM,SAAS,KAAK,aAClB,YAAY,SACZ,YAAY,WACb;EAiBD,OAAO;GACL;GACA;GACA;GACA;GACA;GACA,YAjBA,YACA,kBACC,IAAM,kBACN,IAAM;GAeP,oBAVA,YAAY,oBAAoB,gBAAgB,KAAK,sBACJ,IAAM;GAUvD;GACD;;;;;;;;;;;;;;;;;CAkBH,aACE,cACA,aACA,oBACQ;EACR,IAAI,aACF,OAAO,KAAK;EAId,MAAM,qBACJ,oBAAoB,aAAa,KAAK;EACxC,MAAM,oBACJ,oBAAoB,YAAY,KAAK;EAEvC,QAAQ,cAAR;GACE,KAAK,aAAa,SAChB,OAAO;GACT,KAAK,aAAa,SAChB,OAAO;GACT,KAAK,aAAa,UAChB,OAAO,qBAAqB,KAAK;GACnC,KAAK,aAAa,SAEhB,OACE,sBAAsB,KAAK,gBAAgB,KAAK;GAEpD,KAAK,aAAa,WAChB,OAAO,KAAK;GACd,SACE,OAAO;;;;;;;;;;;;;CAcb,uBAA+B,QAA+B;EAC5D,OAAO,uBAAuB,WAAW;;;;;;;;;;;;;;;;;;;;;;;;;CA0B3C,uBACE,gBACA,mBACA,WACQ;EACR,IAAI,CAAC,kBAAkB,CAAC,mBACtB,OAAO;EAiBT,MAAM,UAAU,IAXD,uBAAuB,uBACpC,GACA,gBACA,cAAc,MACd,UAOoB,CAAO;EAG7B,OAAO,KAAK,IAAI,SAAS,GAAI;;;;;;;;;;;;;;;;;;;;;CAsB/B,sBAA8B,aAAkC;EAC9D,OAAO,KAAK,uBAAuB,gBAAgB;;;;;;;;;;;;;;;;;;;;CAqBrD,wBACE,gBACA,YACQ;EACR,IAAI,cAAc,GAChB,OAAO;EAGT,MAAM,iBAAkB,iBAAiB,aAAc;EAEvD,IAAI,kBAAkB,IACpB,OAAO;OACF,IAAI,kBAAkB,IAC3B,OAAO;OACF,IAAI,kBAAkB,IAC3B,OAAO;OAEP,OAAO;;;;;;;;;;;;;CAeX,aAAqB,gBAAwB,YAA6B;EACxE,IAAI,cAAc,GAChB,OAAO;EAIT,OADwB,iBAAiB,aAAc,OAC9B;;;;;;;;;;;;;;;CAgB3B,oBACE,iBAIA,WACM;EACN,gBAAgB,YAAY,UAAU,WAAW;EACjD,gBAAgB,gBAAgB,UAAU,kBAAkB"}
@@ -1 +1 @@
1
- {"version":3,"file":"KoreanCulture.js","names":[],"sources":["../../../src/systems/trigram/KoreanCulture.ts"],"sourcesContent":["import type { KoreanText, TrigramStance } from \"../../types\";\n\n/**\n * Korean cultural elements and philosophy for martial arts\n */\nexport class KoreanCulture {\n /**\n * Traditional Korean martial arts values\n */\n static readonly MARTIAL_VALUES = {\n honor: { korean: \"명예\", english: \"Honor\" },\n respect: { korean: \"존경\", english: \"Respect\" },\n discipline: { korean: \"절제\", english: \"Discipline\" },\n perseverance: { korean: \"인내\", english: \"Perseverance\" },\n loyalty: { korean: \"충성\", english: \"Loyalty\" },\n righteousness: { korean: \"의\", english: \"Righteousness\" },\n } as const;\n\n /**\n * Trigram philosophical meanings\n */\n static readonly TRIGRAM_PHILOSOPHY: Record<TrigramStance, KoreanText> = {\n geon: {\n korean: \"하늘의 창조력과 무한한 가능성\",\n english: \"Heaven's creative force and infinite possibilities\",\n },\n tae: {\n korean: \"호수의 평온함과 유연한 적응\",\n english: \"Lake's tranquility and flexible adaptation\",\n },\n li: {\n korean: \"불의 밝음과 정확한 판단\",\n english: \"Fire's brightness and precise judgment\",\n },\n jin: {\n korean: \"천둥의 역동성과 순간적 행동\",\n english: \"Thunder's dynamism and instantaneous action\",\n },\n son: {\n korean: \"바람의 지속성과 점진적 변화\",\n english: \"Wind's persistence and gradual change\",\n },\n gam: {\n korean: \"물의 깊이와 흐르는 지혜\",\n english: \"Water's depth and flowing wisdom\",\n },\n gan: {\n korean: \"산의 견고함과 정적인 힘\",\n english: \"Mountain's solidity and static strength\",\n },\n gon: {\n korean: \"땅의 포용과 양육하는 힘\",\n english: \"Earth's embrace and nurturing power\",\n },\n };\n\n /**\n * Korean martial arts terminology\n */\n static readonly MARTIAL_TERMS = {\n techniques: {\n strike: { korean: \"타격\", english: \"Strike\" },\n block: { korean: \"막기\", english: \"Block\" },\n throw: { korean: \"던지기\", english: \"Throw\" },\n lock: { korean: \"꺾기\", english: \"Lock\" },\n pressure: { korean: \"압박\", english: \"Pressure\" },\n },\n body_parts: {\n head: { korean: \"머리\", english: \"Head\" },\n neck: { korean: \"목\", english: \"Neck\" },\n torso: { korean: \"몸통\", english: \"Torso\" },\n arm: { korean: \"팔\", english: \"Arm\" },\n leg: { korean: \"다리\", english: \"Leg\" },\n },\n concepts: {\n ki: { korean: \"기\", english: \"Ki/Energy\" },\n balance: { korean: \"균형\", english: \"Balance\" },\n harmony: { korean: \"조화\", english: \"Harmony\" },\n flow: { korean: \"흐름\", english: \"Flow\" },\n center: { korean: \"중심\", english: \"Center\" },\n },\n } as const;\n\n /**\n * Get cultural wisdom for a stance\n */\n static getStanceWisdom(stance: TrigramStance): KoreanText {\n return this.TRIGRAM_PHILOSOPHY[stance];\n }\n\n /**\n * Get martial arts greeting\n */\n static getMartialGreeting(): KoreanText {\n return {\n korean: \"태권도!\",\n english: \"Taekwondo!\",\n };\n }\n\n /**\n * Get respect bow phrase\n */\n static getRespectPhrase(): KoreanText {\n return {\n korean: \"경례!\",\n english: \"Bow with respect!\",\n };\n }\n\n /**\n * Get training motivation phrases\n */\n static getMotivationPhrases(): readonly KoreanText[] {\n return [\n {\n korean: \"마음을 비우고 수련하라\",\n english: \"Empty your mind and train\",\n },\n { korean: \"강한 의지로 극복하라\", english: \"Overcome with strong will\" },\n {\n korean: \"겸손함이 진정한 힘이다\",\n english: \"Humility is true strength\",\n },\n {\n korean: \"끊임없는 노력이 완성을 만든다\",\n english: \"Continuous effort creates perfection\",\n },\n ];\n }\n}\n"],"mappings":";;;;AAKA,IAAa,gBAAb,MAA2B;;;;CAIzB,OAAgB,iBAAiB;EAC/B,OAAO;GAAE,QAAQ;GAAM,SAAS;GAAS;EACzC,SAAS;GAAE,QAAQ;GAAM,SAAS;GAAW;EAC7C,YAAY;GAAE,QAAQ;GAAM,SAAS;GAAc;EACnD,cAAc;GAAE,QAAQ;GAAM,SAAS;GAAgB;EACvD,SAAS;GAAE,QAAQ;GAAM,SAAS;GAAW;EAC7C,eAAe;GAAE,QAAQ;GAAK,SAAS;GAAiB;EACzD;;;;CAKD,OAAgB,qBAAwD;EACtE,MAAM;GACJ,QAAQ;GACR,SAAS;GACV;EACD,KAAK;GACH,QAAQ;GACR,SAAS;GACV;EACD,IAAI;GACF,QAAQ;GACR,SAAS;GACV;EACD,KAAK;GACH,QAAQ;GACR,SAAS;GACV;EACD,KAAK;GACH,QAAQ;GACR,SAAS;GACV;EACD,KAAK;GACH,QAAQ;GACR,SAAS;GACV;EACD,KAAK;GACH,QAAQ;GACR,SAAS;GACV;EACD,KAAK;GACH,QAAQ;GACR,SAAS;GACV;EACF;;;;CAKD,OAAgB,gBAAgB;EAC9B,YAAY;GACV,QAAQ;IAAE,QAAQ;IAAM,SAAS;IAAU;GAC3C,OAAO;IAAE,QAAQ;IAAM,SAAS;IAAS;GACzC,OAAO;IAAE,QAAQ;IAAO,SAAS;IAAS;GAC1C,MAAM;IAAE,QAAQ;IAAM,SAAS;IAAQ;GACvC,UAAU;IAAE,QAAQ;IAAM,SAAS;IAAY;GAChD;EACD,YAAY;GACV,MAAM;IAAE,QAAQ;IAAM,SAAS;IAAQ;GACvC,MAAM;IAAE,QAAQ;IAAK,SAAS;IAAQ;GACtC,OAAO;IAAE,QAAQ;IAAM,SAAS;IAAS;GACzC,KAAK;IAAE,QAAQ;IAAK,SAAS;IAAO;GACpC,KAAK;IAAE,QAAQ;IAAM,SAAS;IAAO;GACtC;EACD,UAAU;GACR,IAAI;IAAE,QAAQ;IAAK,SAAS;IAAa;GACzC,SAAS;IAAE,QAAQ;IAAM,SAAS;IAAW;GAC7C,SAAS;IAAE,QAAQ;IAAM,SAAS;IAAW;GAC7C,MAAM;IAAE,QAAQ;IAAM,SAAS;IAAQ;GACvC,QAAQ;IAAE,QAAQ;IAAM,SAAS;IAAU;GAC5C;EACF;;;;CAKD,OAAO,gBAAgB,QAAmC;AACxD,SAAO,KAAK,mBAAmB;;;;;CAMjC,OAAO,qBAAiC;AACtC,SAAO;GACL,QAAQ;GACR,SAAS;GACV;;;;;CAMH,OAAO,mBAA+B;AACpC,SAAO;GACL,QAAQ;GACR,SAAS;GACV;;;;;CAMH,OAAO,uBAA8C;AACnD,SAAO;GACL;IACE,QAAQ;IACR,SAAS;IACV;GACD;IAAE,QAAQ;IAAe,SAAS;IAA6B;GAC/D;IACE,QAAQ;IACR,SAAS;IACV;GACD;IACE,QAAQ;IACR,SAAS;IACV;GACF"}
1
+ {"version":3,"file":"KoreanCulture.js","names":[],"sources":["../../../src/systems/trigram/KoreanCulture.ts"],"sourcesContent":["import type { KoreanText, TrigramStance } from \"../../types\";\n\n/**\n * Korean cultural elements and philosophy for martial arts\n */\nexport class KoreanCulture {\n /**\n * Traditional Korean martial arts values\n */\n static readonly MARTIAL_VALUES = {\n honor: { korean: \"명예\", english: \"Honor\" },\n respect: { korean: \"존경\", english: \"Respect\" },\n discipline: { korean: \"절제\", english: \"Discipline\" },\n perseverance: { korean: \"인내\", english: \"Perseverance\" },\n loyalty: { korean: \"충성\", english: \"Loyalty\" },\n righteousness: { korean: \"의\", english: \"Righteousness\" },\n } as const;\n\n /**\n * Trigram philosophical meanings\n */\n static readonly TRIGRAM_PHILOSOPHY: Record<TrigramStance, KoreanText> = {\n geon: {\n korean: \"하늘의 창조력과 무한한 가능성\",\n english: \"Heaven's creative force and infinite possibilities\",\n },\n tae: {\n korean: \"호수의 평온함과 유연한 적응\",\n english: \"Lake's tranquility and flexible adaptation\",\n },\n li: {\n korean: \"불의 밝음과 정확한 판단\",\n english: \"Fire's brightness and precise judgment\",\n },\n jin: {\n korean: \"천둥의 역동성과 순간적 행동\",\n english: \"Thunder's dynamism and instantaneous action\",\n },\n son: {\n korean: \"바람의 지속성과 점진적 변화\",\n english: \"Wind's persistence and gradual change\",\n },\n gam: {\n korean: \"물의 깊이와 흐르는 지혜\",\n english: \"Water's depth and flowing wisdom\",\n },\n gan: {\n korean: \"산의 견고함과 정적인 힘\",\n english: \"Mountain's solidity and static strength\",\n },\n gon: {\n korean: \"땅의 포용과 양육하는 힘\",\n english: \"Earth's embrace and nurturing power\",\n },\n };\n\n /**\n * Korean martial arts terminology\n */\n static readonly MARTIAL_TERMS = {\n techniques: {\n strike: { korean: \"타격\", english: \"Strike\" },\n block: { korean: \"막기\", english: \"Block\" },\n throw: { korean: \"던지기\", english: \"Throw\" },\n lock: { korean: \"꺾기\", english: \"Lock\" },\n pressure: { korean: \"압박\", english: \"Pressure\" },\n },\n body_parts: {\n head: { korean: \"머리\", english: \"Head\" },\n neck: { korean: \"목\", english: \"Neck\" },\n torso: { korean: \"몸통\", english: \"Torso\" },\n arm: { korean: \"팔\", english: \"Arm\" },\n leg: { korean: \"다리\", english: \"Leg\" },\n },\n concepts: {\n ki: { korean: \"기\", english: \"Ki/Energy\" },\n balance: { korean: \"균형\", english: \"Balance\" },\n harmony: { korean: \"조화\", english: \"Harmony\" },\n flow: { korean: \"흐름\", english: \"Flow\" },\n center: { korean: \"중심\", english: \"Center\" },\n },\n } as const;\n\n /**\n * Get cultural wisdom for a stance\n */\n static getStanceWisdom(stance: TrigramStance): KoreanText {\n return this.TRIGRAM_PHILOSOPHY[stance];\n }\n\n /**\n * Get martial arts greeting\n */\n static getMartialGreeting(): KoreanText {\n return {\n korean: \"태권도!\",\n english: \"Taekwondo!\",\n };\n }\n\n /**\n * Get respect bow phrase\n */\n static getRespectPhrase(): KoreanText {\n return {\n korean: \"경례!\",\n english: \"Bow with respect!\",\n };\n }\n\n /**\n * Get training motivation phrases\n */\n static getMotivationPhrases(): readonly KoreanText[] {\n return [\n {\n korean: \"마음을 비우고 수련하라\",\n english: \"Empty your mind and train\",\n },\n { korean: \"강한 의지로 극복하라\", english: \"Overcome with strong will\" },\n {\n korean: \"겸손함이 진정한 힘이다\",\n english: \"Humility is true strength\",\n },\n {\n korean: \"끊임없는 노력이 완성을 만든다\",\n english: \"Continuous effort creates perfection\",\n },\n ];\n }\n}\n"],"mappings":";;;;AAKA,IAAa,gBAAb,MAA2B;;;;CAIzB,OAAgB,iBAAiB;EAC/B,OAAO;GAAE,QAAQ;GAAM,SAAS;GAAS;EACzC,SAAS;GAAE,QAAQ;GAAM,SAAS;GAAW;EAC7C,YAAY;GAAE,QAAQ;GAAM,SAAS;GAAc;EACnD,cAAc;GAAE,QAAQ;GAAM,SAAS;GAAgB;EACvD,SAAS;GAAE,QAAQ;GAAM,SAAS;GAAW;EAC7C,eAAe;GAAE,QAAQ;GAAK,SAAS;GAAiB;EACzD;;;;CAKD,OAAgB,qBAAwD;EACtE,MAAM;GACJ,QAAQ;GACR,SAAS;GACV;EACD,KAAK;GACH,QAAQ;GACR,SAAS;GACV;EACD,IAAI;GACF,QAAQ;GACR,SAAS;GACV;EACD,KAAK;GACH,QAAQ;GACR,SAAS;GACV;EACD,KAAK;GACH,QAAQ;GACR,SAAS;GACV;EACD,KAAK;GACH,QAAQ;GACR,SAAS;GACV;EACD,KAAK;GACH,QAAQ;GACR,SAAS;GACV;EACD,KAAK;GACH,QAAQ;GACR,SAAS;GACV;EACF;;;;CAKD,OAAgB,gBAAgB;EAC9B,YAAY;GACV,QAAQ;IAAE,QAAQ;IAAM,SAAS;IAAU;GAC3C,OAAO;IAAE,QAAQ;IAAM,SAAS;IAAS;GACzC,OAAO;IAAE,QAAQ;IAAO,SAAS;IAAS;GAC1C,MAAM;IAAE,QAAQ;IAAM,SAAS;IAAQ;GACvC,UAAU;IAAE,QAAQ;IAAM,SAAS;IAAY;GAChD;EACD,YAAY;GACV,MAAM;IAAE,QAAQ;IAAM,SAAS;IAAQ;GACvC,MAAM;IAAE,QAAQ;IAAK,SAAS;IAAQ;GACtC,OAAO;IAAE,QAAQ;IAAM,SAAS;IAAS;GACzC,KAAK;IAAE,QAAQ;IAAK,SAAS;IAAO;GACpC,KAAK;IAAE,QAAQ;IAAM,SAAS;IAAO;GACtC;EACD,UAAU;GACR,IAAI;IAAE,QAAQ;IAAK,SAAS;IAAa;GACzC,SAAS;IAAE,QAAQ;IAAM,SAAS;IAAW;GAC7C,SAAS;IAAE,QAAQ;IAAM,SAAS;IAAW;GAC7C,MAAM;IAAE,QAAQ;IAAM,SAAS;IAAQ;GACvC,QAAQ;IAAE,QAAQ;IAAM,SAAS;IAAU;GAC5C;EACF;;;;CAKD,OAAO,gBAAgB,QAAmC;EACxD,OAAO,KAAK,mBAAmB;;;;;CAMjC,OAAO,qBAAiC;EACtC,OAAO;GACL,QAAQ;GACR,SAAS;GACV;;;;;CAMH,OAAO,mBAA+B;EACpC,OAAO;GACL,QAAQ;GACR,SAAS;GACV;;;;;CAMH,OAAO,uBAA8C;EACnD,OAAO;GACL;IACE,QAAQ;IACR,SAAS;IACV;GACD;IAAE,QAAQ;IAAe,SAAS;IAA6B;GAC/D;IACE,QAAQ;IACR,SAAS;IACV;GACD;IACE,QAAQ;IACR,SAAS;IACV;GACF"}