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":"EffectCalculator.js","names":[],"sources":["../../src/systems/EffectCalculator.ts"],"sourcesContent":["/**\n * Effect calculation system for vital point strikes.\n * \n * **Korean**: 효과 계산 시스템 (Effect Calculation System)\n * \n * This module provides comprehensive calculations for status effect duration,\n * intensity, and application based on vital point strikes, accuracy, severity,\n * and archetype-specific modifiers.\n * \n * ## Key Features\n * \n * - **Duration Calculation**: Based on severity, accuracy, and archetype\n * - **Intensity Scaling**: Scales with hit accuracy (0.5-1.5x)\n * - **Archetype Modifiers**: Resistance and vulnerability factors\n * - **Critical Hit Enhancement**: 2x duration for accuracy > 0.9\n * - **Effect Stacking**: Manages multiple concurrent effects\n * \n * @module systems/EffectCalculator\n * @category Combat Effects\n * @korean 효과계산기\n */\n\nimport { PlayerArchetype, VitalPointSeverity } from \"../types/common\";\nimport { EffectIntensity } from \"./effects\";\nimport { StatusEffect } from \"./types\";\nimport { VitalPointEffect } from \"./vitalpoint/types\";\n\n/**\n * Severity multipliers for effect duration calculation.\n * \n * **Korean**: 심각도 배율 (Severity Multipliers)\n */\nconst SEVERITY_MULTIPLIERS: Record<VitalPointSeverity, number> = {\n [VitalPointSeverity.MINOR]: 0.5,\n [VitalPointSeverity.MODERATE]: 1.0,\n [VitalPointSeverity.MAJOR]: 1.5,\n [VitalPointSeverity.CRITICAL]: 2.0,\n [VitalPointSeverity.LETHAL]: 3.0,\n};\n\n/**\n * Critical hit accuracy threshold.\n * Strikes with accuracy >= 0.9 are considered critical.\n * \n * **Korean**: 크리티컬 정확도 기준점\n */\nconst CRITICAL_HIT_THRESHOLD = 0.9;\n\n/**\n * Critical hit duration multiplier.\n * \n * **Korean**: 크리티컬 타격 지속시간 배율\n */\nconst CRITICAL_DURATION_MULTIPLIER = 2.0;\n\n/**\n * Maximum number of concurrent status effects allowed per player.\n * \n * **Korean**: 최대 동시 효과 개수\n */\nexport const MAX_CONCURRENT_EFFECTS = 5;\n\n/**\n * Archetype resistance modifiers.\n * Positive values = resistance (effects last shorter)\n * Negative values = vulnerability (effects last longer)\n * \n * **Korean**: 원형별 저항력 배율 (Archetype Resistance Modifiers)\n */\nconst ARCHETYPE_RESISTANCE: Record<PlayerArchetype, number> = {\n [PlayerArchetype.MUSA]: 0.2, // +20% resistance (traditional warrior discipline)\n [PlayerArchetype.AMSALJA]: -0.1, // -10% resistance (glass cannon)\n [PlayerArchetype.HACKER]: 0.0, // Neutral resistance\n [PlayerArchetype.JEONGBO_YOWON]: 0.1, // +10% resistance (mental fortitude)\n [PlayerArchetype.JOJIK_POKRYEOKBAE]: 0.15, // +15% resistance (street-hardened)\n};\n\n/**\n * Archetype offensive effect modifiers.\n * Applied when archetype deals vital point strike.\n * \n * **Korean**: 원형별 공격 효과 배율 (Archetype Offensive Modifiers)\n */\nconst ARCHETYPE_OFFENSIVE: Record<PlayerArchetype, number> = {\n [PlayerArchetype.MUSA]: 1.0, // Standard effects\n [PlayerArchetype.AMSALJA]: 1.3, // +30% effect potency (assassination mastery)\n [PlayerArchetype.HACKER]: 1.15, // +15% effect potency (precision targeting)\n [PlayerArchetype.JEONGBO_YOWON]: 1.25, // +25% effect potency (anatomical knowledge)\n [PlayerArchetype.JOJIK_POKRYEOKBAE]: 1.2, // +20% effect potency (brutal efficiency)\n};\n\n/**\n * Default effect duration in milliseconds if not specified.\n * \n * **Korean**: 기본 효과 지속시간 (Default Effect Duration)\n */\nconst DEFAULT_EFFECT_DURATION = 2000;\n\n/**\n * Calculates the duration of a status effect based on multiple factors.\n * \n * **Korean**: 효과 지속시간 계산 (Calculate Effect Duration)\n * \n * Formula:\n * ```\n * duration = baseDuration * (1 + (accuracy - 0.5) * 0.5) * severityMultiplier * archetypeModifier\n * ```\n * \n * For critical hits (accuracy >= 0.9):\n * ```\n * duration = duration * 2.0\n * ```\n * \n * @param effect - Vital point effect with base duration\n * @param accuracy - Hit accuracy (0-1)\n * @param severity - Vital point severity level\n * @param attackerArchetype - Attacking player's archetype\n * @param defenderArchetype - Defending player's archetype\n * @returns Calculated duration in milliseconds\n * \n * @example\n * ```typescript\n * const duration = calculateEffectDuration(\n * { id: \"paralysis\", duration: 2000, ... },\n * 0.95, // High accuracy\n * VitalPointSeverity.MAJOR,\n * PlayerArchetype.AMSALJA, // Assassin attacker (+30%)\n * PlayerArchetype.MUSA // Warrior defender (+20% resistance)\n * );\n * // Result: 2000 * 1.225 * 1.5 * 1.3 * 0.8 * 2.0 = ~9,568ms\n * ```\n * \n * @public\n * @korean 효과지속시간계산\n */\nexport function calculateEffectDuration(\n effect: VitalPointEffect,\n accuracy: number,\n severity: VitalPointSeverity,\n attackerArchetype: PlayerArchetype,\n defenderArchetype: PlayerArchetype\n): number {\n const baseDuration = effect.duration || DEFAULT_EFFECT_DURATION;\n\n // Accuracy bonus: 0.75x at 0 accuracy, 1.0x at 0.5, 1.25x at 1.0\n const accuracyBonus = 1 + (accuracy - 0.5) * 0.5;\n\n // Severity multiplier\n const severityMult = SEVERITY_MULTIPLIERS[severity] || 1.0;\n\n // Attacker offensive modifier\n const offensiveModifier = ARCHETYPE_OFFENSIVE[attackerArchetype] || 1.0;\n\n // Defender resistance modifier (inverted: resistance reduces duration)\n const resistanceModifier =\n 1 - (ARCHETYPE_RESISTANCE[defenderArchetype] || 0);\n\n // Calculate base duration\n let finalDuration =\n baseDuration * accuracyBonus * severityMult * offensiveModifier * resistanceModifier;\n\n // Critical hit bonus for high accuracy\n if (accuracy >= CRITICAL_HIT_THRESHOLD) {\n finalDuration *= CRITICAL_DURATION_MULTIPLIER;\n }\n\n return Math.floor(finalDuration);\n}\n\n/**\n * Calculates the intensity of a status effect based on hit accuracy.\n * \n * **Korean**: 효과 강도 계산 (Calculate Effect Intensity)\n * \n * Scales effect intensity from 0.5x (poor accuracy) to 1.5x (perfect accuracy).\n * \n * @param baseIntensity - Base effect intensity\n * @param accuracy - Hit accuracy (0-1)\n * @returns Scaled intensity value\n * \n * @example\n * ```typescript\n * const intensity = calculateEffectIntensity(\n * EffectIntensity.MEDIUM,\n * 0.9 // High accuracy\n * );\n * // Returns scaled intensity for 90% accuracy hit\n * ```\n * \n * @public\n * @korean 효과강도계산\n */\nexport function calculateEffectIntensity(\n baseIntensity: EffectIntensity,\n accuracy: number\n): EffectIntensity {\n // Map intensities to numeric scale\n const intensityMap: Record<EffectIntensity, number> = {\n [EffectIntensity.WEAK]: 1,\n [EffectIntensity.MINOR]: 2,\n [EffectIntensity.LOW]: 3,\n [EffectIntensity.MEDIUM]: 4,\n [EffectIntensity.MODERATE]: 5,\n [EffectIntensity.HIGH]: 6,\n [EffectIntensity.SEVERE]: 7,\n [EffectIntensity.CRITICAL]: 8,\n [EffectIntensity.EXTREME]: 9,\n };\n\n // Reverse map for lookup\n const reverseMap: EffectIntensity[] = [\n EffectIntensity.WEAK,\n EffectIntensity.MINOR,\n EffectIntensity.LOW,\n EffectIntensity.MEDIUM,\n EffectIntensity.MODERATE,\n EffectIntensity.HIGH,\n EffectIntensity.SEVERE,\n EffectIntensity.CRITICAL,\n EffectIntensity.EXTREME,\n ];\n\n const baseLevel = intensityMap[baseIntensity] || 4;\n\n // Accuracy modifier: 0.5x at 0 accuracy, 1.0x at 0.5, 1.5x at 1.0\n const accuracyModifier = 0.5 + accuracy;\n\n // Calculate scaled level\n const scaledLevel = Math.max(\n 1,\n Math.min(9, Math.round(baseLevel * accuracyModifier))\n );\n\n return reverseMap[scaledLevel - 1];\n}\n\n/**\n * Converts a VitalPointEffect to a StatusEffect with calculated properties.\n * \n * **Korean**: 급소 효과를 상태 효과로 변환 (Convert Vital Point Effect to Status Effect)\n * \n * @param effect - Vital point effect to convert\n * @param accuracy - Hit accuracy\n * @param severity - Vital point severity\n * @param attackerArchetype - Attacker's archetype\n * @param defenderArchetype - Defender's archetype\n * @param vitalPointId - Source vital point ID\n * @param timestamp - Current timestamp (for startTime/endTime)\n * @returns StatusEffect with calculated duration and intensity\n * \n * @public\n * @korean 상태효과변환\n */\nexport function convertToStatusEffect(\n effect: VitalPointEffect,\n accuracy: number,\n severity: VitalPointSeverity,\n attackerArchetype: PlayerArchetype,\n defenderArchetype: PlayerArchetype,\n vitalPointId: string,\n timestamp: number\n): StatusEffect {\n const duration = calculateEffectDuration(\n effect,\n accuracy,\n severity,\n attackerArchetype,\n defenderArchetype\n );\n\n const intensity = calculateEffectIntensity(effect.intensity, accuracy);\n\n return {\n id: `${effect.id}_${timestamp}`,\n type: effect.type,\n intensity,\n duration,\n description: effect.description,\n stackable: effect.stackable,\n source: vitalPointId,\n startTime: timestamp,\n endTime: timestamp + duration,\n };\n}\n\n/**\n * Applies effect stacking logic to a list of status effects.\n * \n * **Korean**: 효과 중첩 관리 (Manage Effect Stacking)\n * \n * Rules:\n * - Non-stackable effects: Keep only the most recent\n * - Stackable effects: Allow up to MAX_CONCURRENT_EFFECTS\n * - Expired effects: Remove automatically\n * \n * @param currentEffects - Existing active effects\n * @param newEffects - New effects to add\n * @param currentTime - Current timestamp\n * @returns Updated effect list with stacking applied\n * \n * @example\n * ```typescript\n * const updated = applyEffectStacking(\n * player.statusEffects,\n * newVitalPointEffects,\n * Date.now()\n * );\n * ```\n * \n * @public\n * @korean 효과중첩적용\n */\nexport function applyEffectStacking(\n currentEffects: readonly StatusEffect[],\n newEffects: readonly StatusEffect[],\n currentTime: number\n): StatusEffect[] {\n // Remove expired effects\n let activeEffects = currentEffects.filter(\n (effect) => effect.endTime > currentTime\n );\n\n // Process each new effect\n for (const newEffect of newEffects) {\n if (!newEffect.stackable) {\n // Remove existing effects of same type (non-stackable)\n activeEffects = activeEffects.filter(\n (effect) => effect.type !== newEffect.type\n );\n }\n\n // Add new effect\n activeEffects = [...activeEffects, newEffect];\n }\n\n // Limit to MAX_CONCURRENT_EFFECTS (keep most recent)\n if (activeEffects.length > MAX_CONCURRENT_EFFECTS) {\n activeEffects = activeEffects\n .sort((a, b) => b.startTime - a.startTime)\n .slice(0, MAX_CONCURRENT_EFFECTS);\n }\n\n return activeEffects;\n}\n\n/**\n * Gets the offensive effect modifier for an archetype.\n * \n * **Korean**: 원형 공격 배율 조회 (Get Archetype Offensive Modifier)\n * \n * @param archetype - Player archetype\n * @returns Offensive effect multiplier (1.0 - 1.3)\n * \n * @public\n * @korean 원형공격배율조회\n */\nexport function getArchetypeOffensiveModifier(\n archetype: PlayerArchetype\n): number {\n return ARCHETYPE_OFFENSIVE[archetype] || 1.0;\n}\n\n/**\n * Gets the defensive resistance modifier for an archetype.\n * \n * **Korean**: 원형 방어 배율 조회 (Get Archetype Defensive Modifier)\n * \n * @param archetype - Player archetype\n * @returns Resistance modifier (-0.1 to 0.2)\n * \n * @public\n * @korean 원형방어배율조회\n */\nexport function getArchetypeDefensiveModifier(\n archetype: PlayerArchetype\n): number {\n return ARCHETYPE_RESISTANCE[archetype] || 0;\n}\n\n/**\n * Checks if a hit qualifies as a critical hit based on accuracy.\n * \n * **Korean**: 크리티컬 타격 판정 (Check Critical Hit)\n * \n * @param accuracy - Hit accuracy (0-1)\n * @returns True if accuracy >= 0.9 (critical hit)\n * \n * @public\n * @korean 크리티컬판정\n */\nexport function isCriticalHit(accuracy: number): boolean {\n return accuracy >= CRITICAL_HIT_THRESHOLD;\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAgCA,IAAM,uBAA2D;EAC9D,mBAAmB,QAAQ;EAC3B,mBAAmB,WAAW;EAC9B,mBAAmB,QAAQ;EAC3B,mBAAmB,WAAW;EAC9B,mBAAmB,SAAS;CAC9B;;;;;;;AAQD,IAAM,yBAAyB;;;;;;AAO/B,IAAM,+BAA+B;;;;;;;;AAgBrC,IAAM,uBAAwD;EAC3D,gBAAgB,OAAO;EACvB,gBAAgB,UAAU;EAC1B,gBAAgB,SAAS;EACzB,gBAAgB,gBAAgB;EAChC,gBAAgB,oBAAoB;CACtC;;;;;;;AAQD,IAAM,sBAAuD;EAC1D,gBAAgB,OAAO;EACvB,gBAAgB,UAAU;EAC1B,gBAAgB,SAAS;EACzB,gBAAgB,gBAAgB;EAChC,gBAAgB,oBAAoB;CACtC;;;;;;AAOD,IAAM,0BAA0B;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAuChC,SAAgB,wBACd,QACA,UACA,UACA,mBACA,mBACQ;CACR,MAAM,eAAe,OAAO,YAAY;CAGxC,MAAM,gBAAgB,KAAK,WAAW,MAAO;CAG7C,MAAM,eAAe,qBAAqB,aAAa;CAGvD,MAAM,oBAAoB,oBAAoB,sBAAsB;CAGpE,MAAM,qBACJ,KAAK,qBAAqB,sBAAsB;CAGlD,IAAI,gBACF,eAAe,gBAAgB,eAAe,oBAAoB;AAGpE,KAAI,YAAY,uBACd,kBAAiB;AAGnB,QAAO,KAAK,MAAM,cAAc;;;;;;;;;;;;;;;;;;;;;;;;;AA0BlC,SAAgB,yBACd,eACA,UACiB;CAEjB,MAAM,eAAgD;GACnD,gBAAgB,OAAO;GACvB,gBAAgB,QAAQ;GACxB,gBAAgB,MAAM;GACtB,gBAAgB,SAAS;GACzB,gBAAgB,WAAW;GAC3B,gBAAgB,OAAO;GACvB,gBAAgB,SAAS;GACzB,gBAAgB,WAAW;GAC3B,gBAAgB,UAAU;EAC5B;CAGD,MAAM,aAAgC;EACpC,gBAAgB;EAChB,gBAAgB;EAChB,gBAAgB;EAChB,gBAAgB;EAChB,gBAAgB;EAChB,gBAAgB;EAChB,gBAAgB;EAChB,gBAAgB;EAChB,gBAAgB;EACjB;CAED,MAAM,YAAY,aAAa,kBAAkB;CAGjD,MAAM,mBAAmB,KAAM;AAQ/B,QAAO,WALa,KAAK,IACvB,GACA,KAAK,IAAI,GAAG,KAAK,MAAM,YAAY,iBAAiB,CAAC,CAGrC,GAAc;;;;;;;;;;;;;;;;;;;AAoBlC,SAAgB,sBACd,QACA,UACA,UACA,mBACA,mBACA,cACA,WACc;CACd,MAAM,WAAW,wBACf,QACA,UACA,UACA,mBACA,kBACD;CAED,MAAM,YAAY,yBAAyB,OAAO,WAAW,SAAS;AAEtE,QAAO;EACL,IAAI,GAAG,OAAO,GAAG,GAAG;EACpB,MAAM,OAAO;EACb;EACA;EACA,aAAa,OAAO;EACpB,WAAW,OAAO;EAClB,QAAQ;EACR,WAAW;EACX,SAAS,YAAY;EACtB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA8BH,SAAgB,oBACd,gBACA,YACA,aACgB;CAEhB,IAAI,gBAAgB,eAAe,QAChC,WAAW,OAAO,UAAU,YAC9B;AAGD,MAAK,MAAM,aAAa,YAAY;AAClC,MAAI,CAAC,UAAU,UAEb,iBAAgB,cAAc,QAC3B,WAAW,OAAO,SAAS,UAAU,KACvC;AAIH,kBAAgB,CAAC,GAAG,eAAe,UAAU;;AAI/C,KAAI,cAAc,SAAA,EAChB,iBAAgB,cACb,MAAM,GAAG,MAAM,EAAE,YAAY,EAAE,UAAU,CACzC,MAAM,GAAA,EAA0B;AAGrC,QAAO"}
1
+ {"version":3,"file":"EffectCalculator.js","names":[],"sources":["../../src/systems/EffectCalculator.ts"],"sourcesContent":["/**\n * Effect calculation system for vital point strikes.\n * \n * **Korean**: 효과 계산 시스템 (Effect Calculation System)\n * \n * This module provides comprehensive calculations for status effect duration,\n * intensity, and application based on vital point strikes, accuracy, severity,\n * and archetype-specific modifiers.\n * \n * ## Key Features\n * \n * - **Duration Calculation**: Based on severity, accuracy, and archetype\n * - **Intensity Scaling**: Scales with hit accuracy (0.5-1.5x)\n * - **Archetype Modifiers**: Resistance and vulnerability factors\n * - **Critical Hit Enhancement**: 2x duration for accuracy > 0.9\n * - **Effect Stacking**: Manages multiple concurrent effects\n * \n * @module systems/EffectCalculator\n * @category Combat Effects\n * @korean 효과계산기\n */\n\nimport { PlayerArchetype, VitalPointSeverity } from \"../types/common\";\nimport { EffectIntensity } from \"./effects\";\nimport { StatusEffect } from \"./types\";\nimport { VitalPointEffect } from \"./vitalpoint/types\";\n\n/**\n * Severity multipliers for effect duration calculation.\n * \n * **Korean**: 심각도 배율 (Severity Multipliers)\n */\nconst SEVERITY_MULTIPLIERS: Record<VitalPointSeverity, number> = {\n [VitalPointSeverity.MINOR]: 0.5,\n [VitalPointSeverity.MODERATE]: 1.0,\n [VitalPointSeverity.MAJOR]: 1.5,\n [VitalPointSeverity.CRITICAL]: 2.0,\n [VitalPointSeverity.LETHAL]: 3.0,\n};\n\n/**\n * Critical hit accuracy threshold.\n * Strikes with accuracy >= 0.9 are considered critical.\n * \n * **Korean**: 크리티컬 정확도 기준점\n */\nconst CRITICAL_HIT_THRESHOLD = 0.9;\n\n/**\n * Critical hit duration multiplier.\n * \n * **Korean**: 크리티컬 타격 지속시간 배율\n */\nconst CRITICAL_DURATION_MULTIPLIER = 2.0;\n\n/**\n * Maximum number of concurrent status effects allowed per player.\n * \n * **Korean**: 최대 동시 효과 개수\n */\nexport const MAX_CONCURRENT_EFFECTS = 5;\n\n/**\n * Archetype resistance modifiers.\n * Positive values = resistance (effects last shorter)\n * Negative values = vulnerability (effects last longer)\n * \n * **Korean**: 원형별 저항력 배율 (Archetype Resistance Modifiers)\n */\nconst ARCHETYPE_RESISTANCE: Record<PlayerArchetype, number> = {\n [PlayerArchetype.MUSA]: 0.2, // +20% resistance (traditional warrior discipline)\n [PlayerArchetype.AMSALJA]: -0.1, // -10% resistance (glass cannon)\n [PlayerArchetype.HACKER]: 0.0, // Neutral resistance\n [PlayerArchetype.JEONGBO_YOWON]: 0.1, // +10% resistance (mental fortitude)\n [PlayerArchetype.JOJIK_POKRYEOKBAE]: 0.15, // +15% resistance (street-hardened)\n};\n\n/**\n * Archetype offensive effect modifiers.\n * Applied when archetype deals vital point strike.\n * \n * **Korean**: 원형별 공격 효과 배율 (Archetype Offensive Modifiers)\n */\nconst ARCHETYPE_OFFENSIVE: Record<PlayerArchetype, number> = {\n [PlayerArchetype.MUSA]: 1.0, // Standard effects\n [PlayerArchetype.AMSALJA]: 1.3, // +30% effect potency (assassination mastery)\n [PlayerArchetype.HACKER]: 1.15, // +15% effect potency (precision targeting)\n [PlayerArchetype.JEONGBO_YOWON]: 1.25, // +25% effect potency (anatomical knowledge)\n [PlayerArchetype.JOJIK_POKRYEOKBAE]: 1.2, // +20% effect potency (brutal efficiency)\n};\n\n/**\n * Default effect duration in milliseconds if not specified.\n * \n * **Korean**: 기본 효과 지속시간 (Default Effect Duration)\n */\nconst DEFAULT_EFFECT_DURATION = 2000;\n\n/**\n * Calculates the duration of a status effect based on multiple factors.\n * \n * **Korean**: 효과 지속시간 계산 (Calculate Effect Duration)\n * \n * Formula:\n * ```\n * duration = baseDuration * (1 + (accuracy - 0.5) * 0.5) * severityMultiplier * archetypeModifier\n * ```\n * \n * For critical hits (accuracy >= 0.9):\n * ```\n * duration = duration * 2.0\n * ```\n * \n * @param effect - Vital point effect with base duration\n * @param accuracy - Hit accuracy (0-1)\n * @param severity - Vital point severity level\n * @param attackerArchetype - Attacking player's archetype\n * @param defenderArchetype - Defending player's archetype\n * @returns Calculated duration in milliseconds\n * \n * @example\n * ```typescript\n * const duration = calculateEffectDuration(\n * { id: \"paralysis\", duration: 2000, ... },\n * 0.95, // High accuracy\n * VitalPointSeverity.MAJOR,\n * PlayerArchetype.AMSALJA, // Assassin attacker (+30%)\n * PlayerArchetype.MUSA // Warrior defender (+20% resistance)\n * );\n * // Result: 2000 * 1.225 * 1.5 * 1.3 * 0.8 * 2.0 = ~9,568ms\n * ```\n * \n * @public\n * @korean 효과지속시간계산\n */\nexport function calculateEffectDuration(\n effect: VitalPointEffect,\n accuracy: number,\n severity: VitalPointSeverity,\n attackerArchetype: PlayerArchetype,\n defenderArchetype: PlayerArchetype\n): number {\n const baseDuration = effect.duration || DEFAULT_EFFECT_DURATION;\n\n // Accuracy bonus: 0.75x at 0 accuracy, 1.0x at 0.5, 1.25x at 1.0\n const accuracyBonus = 1 + (accuracy - 0.5) * 0.5;\n\n // Severity multiplier\n const severityMult = SEVERITY_MULTIPLIERS[severity] || 1.0;\n\n // Attacker offensive modifier\n const offensiveModifier = ARCHETYPE_OFFENSIVE[attackerArchetype] || 1.0;\n\n // Defender resistance modifier (inverted: resistance reduces duration)\n const resistanceModifier =\n 1 - (ARCHETYPE_RESISTANCE[defenderArchetype] || 0);\n\n // Calculate base duration\n let finalDuration =\n baseDuration * accuracyBonus * severityMult * offensiveModifier * resistanceModifier;\n\n // Critical hit bonus for high accuracy\n if (accuracy >= CRITICAL_HIT_THRESHOLD) {\n finalDuration *= CRITICAL_DURATION_MULTIPLIER;\n }\n\n return Math.floor(finalDuration);\n}\n\n/**\n * Calculates the intensity of a status effect based on hit accuracy.\n * \n * **Korean**: 효과 강도 계산 (Calculate Effect Intensity)\n * \n * Scales effect intensity from 0.5x (poor accuracy) to 1.5x (perfect accuracy).\n * \n * @param baseIntensity - Base effect intensity\n * @param accuracy - Hit accuracy (0-1)\n * @returns Scaled intensity value\n * \n * @example\n * ```typescript\n * const intensity = calculateEffectIntensity(\n * EffectIntensity.MEDIUM,\n * 0.9 // High accuracy\n * );\n * // Returns scaled intensity for 90% accuracy hit\n * ```\n * \n * @public\n * @korean 효과강도계산\n */\nexport function calculateEffectIntensity(\n baseIntensity: EffectIntensity,\n accuracy: number\n): EffectIntensity {\n // Map intensities to numeric scale\n const intensityMap: Record<EffectIntensity, number> = {\n [EffectIntensity.WEAK]: 1,\n [EffectIntensity.MINOR]: 2,\n [EffectIntensity.LOW]: 3,\n [EffectIntensity.MEDIUM]: 4,\n [EffectIntensity.MODERATE]: 5,\n [EffectIntensity.HIGH]: 6,\n [EffectIntensity.SEVERE]: 7,\n [EffectIntensity.CRITICAL]: 8,\n [EffectIntensity.EXTREME]: 9,\n };\n\n // Reverse map for lookup\n const reverseMap: EffectIntensity[] = [\n EffectIntensity.WEAK,\n EffectIntensity.MINOR,\n EffectIntensity.LOW,\n EffectIntensity.MEDIUM,\n EffectIntensity.MODERATE,\n EffectIntensity.HIGH,\n EffectIntensity.SEVERE,\n EffectIntensity.CRITICAL,\n EffectIntensity.EXTREME,\n ];\n\n const baseLevel = intensityMap[baseIntensity] || 4;\n\n // Accuracy modifier: 0.5x at 0 accuracy, 1.0x at 0.5, 1.5x at 1.0\n const accuracyModifier = 0.5 + accuracy;\n\n // Calculate scaled level\n const scaledLevel = Math.max(\n 1,\n Math.min(9, Math.round(baseLevel * accuracyModifier))\n );\n\n return reverseMap[scaledLevel - 1];\n}\n\n/**\n * Converts a VitalPointEffect to a StatusEffect with calculated properties.\n * \n * **Korean**: 급소 효과를 상태 효과로 변환 (Convert Vital Point Effect to Status Effect)\n * \n * @param effect - Vital point effect to convert\n * @param accuracy - Hit accuracy\n * @param severity - Vital point severity\n * @param attackerArchetype - Attacker's archetype\n * @param defenderArchetype - Defender's archetype\n * @param vitalPointId - Source vital point ID\n * @param timestamp - Current timestamp (for startTime/endTime)\n * @returns StatusEffect with calculated duration and intensity\n * \n * @public\n * @korean 상태효과변환\n */\nexport function convertToStatusEffect(\n effect: VitalPointEffect,\n accuracy: number,\n severity: VitalPointSeverity,\n attackerArchetype: PlayerArchetype,\n defenderArchetype: PlayerArchetype,\n vitalPointId: string,\n timestamp: number\n): StatusEffect {\n const duration = calculateEffectDuration(\n effect,\n accuracy,\n severity,\n attackerArchetype,\n defenderArchetype\n );\n\n const intensity = calculateEffectIntensity(effect.intensity, accuracy);\n\n return {\n id: `${effect.id}_${timestamp}`,\n type: effect.type,\n intensity,\n duration,\n description: effect.description,\n stackable: effect.stackable,\n source: vitalPointId,\n startTime: timestamp,\n endTime: timestamp + duration,\n };\n}\n\n/**\n * Applies effect stacking logic to a list of status effects.\n * \n * **Korean**: 효과 중첩 관리 (Manage Effect Stacking)\n * \n * Rules:\n * - Non-stackable effects: Keep only the most recent\n * - Stackable effects: Allow up to MAX_CONCURRENT_EFFECTS\n * - Expired effects: Remove automatically\n * \n * @param currentEffects - Existing active effects\n * @param newEffects - New effects to add\n * @param currentTime - Current timestamp\n * @returns Updated effect list with stacking applied\n * \n * @example\n * ```typescript\n * const updated = applyEffectStacking(\n * player.statusEffects,\n * newVitalPointEffects,\n * Date.now()\n * );\n * ```\n * \n * @public\n * @korean 효과중첩적용\n */\nexport function applyEffectStacking(\n currentEffects: readonly StatusEffect[],\n newEffects: readonly StatusEffect[],\n currentTime: number\n): StatusEffect[] {\n // Remove expired effects\n let activeEffects = currentEffects.filter(\n (effect) => effect.endTime > currentTime\n );\n\n // Process each new effect\n for (const newEffect of newEffects) {\n if (!newEffect.stackable) {\n // Remove existing effects of same type (non-stackable)\n activeEffects = activeEffects.filter(\n (effect) => effect.type !== newEffect.type\n );\n }\n\n // Add new effect\n activeEffects = [...activeEffects, newEffect];\n }\n\n // Limit to MAX_CONCURRENT_EFFECTS (keep most recent)\n if (activeEffects.length > MAX_CONCURRENT_EFFECTS) {\n activeEffects = activeEffects\n .sort((a, b) => b.startTime - a.startTime)\n .slice(0, MAX_CONCURRENT_EFFECTS);\n }\n\n return activeEffects;\n}\n\n/**\n * Gets the offensive effect modifier for an archetype.\n * \n * **Korean**: 원형 공격 배율 조회 (Get Archetype Offensive Modifier)\n * \n * @param archetype - Player archetype\n * @returns Offensive effect multiplier (1.0 - 1.3)\n * \n * @public\n * @korean 원형공격배율조회\n */\nexport function getArchetypeOffensiveModifier(\n archetype: PlayerArchetype\n): number {\n return ARCHETYPE_OFFENSIVE[archetype] || 1.0;\n}\n\n/**\n * Gets the defensive resistance modifier for an archetype.\n * \n * **Korean**: 원형 방어 배율 조회 (Get Archetype Defensive Modifier)\n * \n * @param archetype - Player archetype\n * @returns Resistance modifier (-0.1 to 0.2)\n * \n * @public\n * @korean 원형방어배율조회\n */\nexport function getArchetypeDefensiveModifier(\n archetype: PlayerArchetype\n): number {\n return ARCHETYPE_RESISTANCE[archetype] || 0;\n}\n\n/**\n * Checks if a hit qualifies as a critical hit based on accuracy.\n * \n * **Korean**: 크리티컬 타격 판정 (Check Critical Hit)\n * \n * @param accuracy - Hit accuracy (0-1)\n * @returns True if accuracy >= 0.9 (critical hit)\n * \n * @public\n * @korean 크리티컬판정\n */\nexport function isCriticalHit(accuracy: number): boolean {\n return accuracy >= CRITICAL_HIT_THRESHOLD;\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAgCA,IAAM,uBAA2D;EAC9D,mBAAmB,QAAQ;EAC3B,mBAAmB,WAAW;EAC9B,mBAAmB,QAAQ;EAC3B,mBAAmB,WAAW;EAC9B,mBAAmB,SAAS;CAC9B;;;;;;;AAQD,IAAM,yBAAyB;;;;;;AAO/B,IAAM,+BAA+B;;;;;;;;AAgBrC,IAAM,uBAAwD;EAC3D,gBAAgB,OAAO;EACvB,gBAAgB,UAAU;EAC1B,gBAAgB,SAAS;EACzB,gBAAgB,gBAAgB;EAChC,gBAAgB,oBAAoB;CACtC;;;;;;;AAQD,IAAM,sBAAuD;EAC1D,gBAAgB,OAAO;EACvB,gBAAgB,UAAU;EAC1B,gBAAgB,SAAS;EACzB,gBAAgB,gBAAgB;EAChC,gBAAgB,oBAAoB;CACtC;;;;;;AAOD,IAAM,0BAA0B;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAuChC,SAAgB,wBACd,QACA,UACA,UACA,mBACA,mBACQ;CACR,MAAM,eAAe,OAAO,YAAY;CAGxC,MAAM,gBAAgB,KAAK,WAAW,MAAO;CAG7C,MAAM,eAAe,qBAAqB,aAAa;CAGvD,MAAM,oBAAoB,oBAAoB,sBAAsB;CAGpE,MAAM,qBACJ,KAAK,qBAAqB,sBAAsB;CAGlD,IAAI,gBACF,eAAe,gBAAgB,eAAe,oBAAoB;CAGpE,IAAI,YAAY,wBACd,iBAAiB;CAGnB,OAAO,KAAK,MAAM,cAAc;;;;;;;;;;;;;;;;;;;;;;;;;AA0BlC,SAAgB,yBACd,eACA,UACiB;CAEjB,MAAM,eAAgD;GACnD,gBAAgB,OAAO;GACvB,gBAAgB,QAAQ;GACxB,gBAAgB,MAAM;GACtB,gBAAgB,SAAS;GACzB,gBAAgB,WAAW;GAC3B,gBAAgB,OAAO;GACvB,gBAAgB,SAAS;GACzB,gBAAgB,WAAW;GAC3B,gBAAgB,UAAU;EAC5B;CAGD,MAAM,aAAgC;EACpC,gBAAgB;EAChB,gBAAgB;EAChB,gBAAgB;EAChB,gBAAgB;EAChB,gBAAgB;EAChB,gBAAgB;EAChB,gBAAgB;EAChB,gBAAgB;EAChB,gBAAgB;EACjB;CAED,MAAM,YAAY,aAAa,kBAAkB;CAGjD,MAAM,mBAAmB,KAAM;CAQ/B,OAAO,WALa,KAAK,IACvB,GACA,KAAK,IAAI,GAAG,KAAK,MAAM,YAAY,iBAAiB,CAAC,CAGrC,GAAc;;;;;;;;;;;;;;;;;;;AAoBlC,SAAgB,sBACd,QACA,UACA,UACA,mBACA,mBACA,cACA,WACc;CACd,MAAM,WAAW,wBACf,QACA,UACA,UACA,mBACA,kBACD;CAED,MAAM,YAAY,yBAAyB,OAAO,WAAW,SAAS;CAEtE,OAAO;EACL,IAAI,GAAG,OAAO,GAAG,GAAG;EACpB,MAAM,OAAO;EACb;EACA;EACA,aAAa,OAAO;EACpB,WAAW,OAAO;EAClB,QAAQ;EACR,WAAW;EACX,SAAS,YAAY;EACtB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA8BH,SAAgB,oBACd,gBACA,YACA,aACgB;CAEhB,IAAI,gBAAgB,eAAe,QAChC,WAAW,OAAO,UAAU,YAC9B;CAGD,KAAK,MAAM,aAAa,YAAY;EAClC,IAAI,CAAC,UAAU,WAEb,gBAAgB,cAAc,QAC3B,WAAW,OAAO,SAAS,UAAU,KACvC;EAIH,gBAAgB,CAAC,GAAG,eAAe,UAAU;;CAI/C,IAAI,cAAc,SAAA,GAChB,gBAAgB,cACb,MAAM,GAAG,MAAM,EAAE,YAAY,EAAE,UAAU,CACzC,MAAM,GAAA,EAA0B;CAGrC,OAAO"}
@@ -1 +1 @@
1
- {"version":3,"file":"LayoutSystem.js","names":[],"sources":["../../src/systems/LayoutSystem.ts"],"sourcesContent":["/**\n * Unified Layout System for consistent component positioning\n *\n * Provides grid-based layout calculations, responsive positioning utilities,\n * and alignment helpers for all screens in Black Trigram (흑괘).\n *\n * Features:\n * - 12-column grid system for consistent alignment\n * - Responsive positioning that adapts to screen size\n * - Safe area handling for mobile devices with notches\n * - Z-index hierarchy management\n * - Korean-English UI alignment support\n *\n * Performance target: <1ms for layout calculations\n *\n * @module systems/LayoutSystem\n * @category Systems\n * @korean 레이아웃시스템\n */\n\nimport { Position } from \"../types/common\";\nimport {\n ContainerBounds,\n HorizontalAlignment,\n ResponsivePosition,\n SafeAreaInsets,\n ScreenSize,\n VerticalAlignment,\n} from \"../types/LayoutTypes\";\n\n/**\n * Default grid configuration\n */\nconst DEFAULT_GRID_COLUMNS = 12;\nconst DEFAULT_GUTTER_SIZE = 20;\nconst BASE_DESKTOP_WIDTH = 1200;\n\n/**\n * Base width for mobile arena scaling calculations (in pixels)\n * This represents the standard desktop arena width (80% of 1200px)\n * Used to calculate scale factor for mobile devices\n */\nconst MOBILE_ARENA_BASE_WIDTH = 960;\n\n/**\n * Default row height for grid-based vertical positioning (in pixels)\n * Used when row index is specified in grid configuration\n * Can be overridden by providing explicit y position\n * \n * @public\n */\nexport const DEFAULT_ROW_HEIGHT = 100;\n\n/**\n * Default safe area insets for mobile devices\n * Based on typical iOS device dimensions (iPhone 14 Pro as reference)\n */\nconst DEFAULT_SAFE_AREA: SafeAreaInsets = {\n top: 44, // Status bar + notch\n right: 0,\n bottom: 34, // Home indicator\n left: 0,\n};\n\n/**\n * LayoutSystem Class\n *\n * Provides unified layout calculations and positioning utilities\n * for consistent component placement across all screens.\n *\n * @example\n * ```typescript\n * const layout = new LayoutSystem();\n *\n * // Calculate grid position\n * const pos = layout.calculateGridPosition(2, 4, 1200);\n * // Returns { x: 200, width: 380 } for column 2, span 4\n *\n * // Calculate responsive position\n * const screenSize = { width: 375, height: 667, isMobile: true, isTablet: false, isDesktop: false, isLandscape: false };\n * const responsivePos = layout.calculateResponsivePosition(\n * { base: { x: 100, y: 50 } },\n * screenSize\n * );\n * ```\n */\nexport class LayoutSystem {\n private readonly gridColumns: number;\n private readonly gutterSize: number;\n private readonly safeArea: SafeAreaInsets;\n\n /**\n * Create a new LayoutSystem instance\n *\n * @param gridColumns - Number of grid columns (default: 12)\n * @param gutterSize - Gutter size between columns in pixels (default: 20)\n * @param safeArea - Safe area insets for mobile devices\n */\n constructor(\n gridColumns: number = DEFAULT_GRID_COLUMNS,\n gutterSize: number = DEFAULT_GUTTER_SIZE,\n safeArea: SafeAreaInsets = DEFAULT_SAFE_AREA\n ) {\n this.gridColumns = gridColumns;\n this.gutterSize = gutterSize;\n this.safeArea = safeArea;\n }\n\n /**\n * Calculate position and width for grid-based layout\n *\n * Uses 12-column grid system for consistent alignment.\n * Accounts for gutters between columns.\n *\n * @param column - Starting column (0-11)\n * @param span - Number of columns to span (1-12)\n * @param containerWidth - Total container width in pixels\n * @param customGutter - Custom gutter size (optional)\n * @returns Position and width for the grid cell\n * @throws Error if column or span are outside valid ranges\n *\n * @example\n * ```typescript\n * // Center element spanning 6 columns\n * const pos = layout.calculateGridPosition(3, 6, 1200);\n * // Returns { x: 300, width: 580 }\n * ```\n */\n calculateGridPosition(\n column: number,\n span: number,\n containerWidth: number,\n customGutter?: number\n ): { x: number; width: number } {\n // Validate inputs\n if (column < 0 || column >= this.gridColumns) {\n throw new Error(\n `Invalid column: ${column}. Column must be between 0 and ${this.gridColumns - 1}`\n );\n }\n if (span < 1 || span > this.gridColumns) {\n throw new Error(\n `Invalid span: ${span}. Span must be between 1 and ${this.gridColumns}`\n );\n }\n if (column + span > this.gridColumns) {\n throw new Error(\n `Invalid grid position: column ${column} + span ${span} = ${column + span} exceeds ${this.gridColumns} columns`\n );\n }\n\n const gutter = customGutter ?? this.gutterSize;\n const columnWidth = containerWidth / this.gridColumns;\n\n // Calculate x position\n const x = column * columnWidth;\n\n // Calculate width accounting for gutters\n // Width = (span * columnWidth) - gutter\n // The gutter is subtracted to create spacing between elements\n const width = span * columnWidth - gutter;\n\n return { x, width };\n }\n\n /**\n * Calculate responsive position based on screen size\n *\n * Scales position proportionally or uses specific overrides for tablet/mobile.\n *\n * @param config - Responsive position configuration\n * @param screenSize - Current screen dimensions and device type\n * @returns Calculated position for current screen size\n *\n * @example\n * ```typescript\n * const pos = layout.calculateResponsivePosition(\n * {\n * base: { x: 100, y: 50 },\n * mobile: { x: 10, y: 20 },\n * scaleProportionally: true\n * },\n * screenSize\n * );\n * ```\n */\n calculateResponsivePosition(\n config: ResponsivePosition,\n screenSize: ScreenSize\n ): Position {\n // Use specific override if available\n if (screenSize.isMobile && config.mobile) {\n return config.mobile;\n }\n if (screenSize.isTablet && config.tablet) {\n return config.tablet;\n }\n\n // Use base position for desktop\n if (screenSize.isDesktop || !config.scaleProportionally) {\n return config.base;\n }\n\n // Scale proportionally for mobile/tablet if no override\n const scale = screenSize.width / BASE_DESKTOP_WIDTH;\n return {\n x: config.base.x * scale,\n y: config.base.y * scale,\n };\n }\n\n /**\n * Calculate safe position accounting for device notches and home indicators\n *\n * Ensures UI elements don't overlap with system UI on mobile devices.\n *\n * @param position - Base position\n * @param edge - Which edge to apply safe area ('top' | 'bottom' | 'left' | 'right')\n * @returns Position adjusted for safe area\n *\n * @example\n * ```typescript\n * // Adjust top position for status bar/notch\n * const safePos = layout.calculateSafePosition({ x: 0, y: 10 }, 'top');\n * // Returns { x: 0, y: 54 } (10 + 44 for notch)\n * ```\n */\n calculateSafePosition(\n position: Position,\n edge: \"top\" | \"bottom\" | \"left\" | \"right\"\n ): Position {\n const inset = this.safeArea[edge];\n switch (edge) {\n case \"top\":\n return { ...position, y: position.y + inset };\n case \"bottom\":\n return { ...position, y: position.y - inset };\n case \"left\":\n return { ...position, x: position.x + inset };\n case \"right\":\n return { ...position, x: position.x - inset };\n }\n }\n\n /**\n * Align element horizontally within container\n *\n * @param elementWidth - Width of element to align\n * @param containerWidth - Width of container\n * @param alignment - Alignment type ('left' | 'center' | 'right')\n * @param margin - Optional margin from edges\n * @returns X position for alignment\n *\n * @example\n * ```typescript\n * const x = layout.alignHorizontal(200, 800, 'center', 10);\n * // Returns 300 (centered with margins)\n * ```\n */\n alignHorizontal(\n elementWidth: number,\n containerWidth: number,\n alignment: HorizontalAlignment,\n margin: number = 0\n ): number {\n switch (alignment) {\n case \"left\":\n return margin;\n case \"center\":\n return (containerWidth - elementWidth) / 2;\n case \"right\":\n return containerWidth - elementWidth - margin;\n }\n }\n\n /**\n * Align element vertically within container\n *\n * @param elementHeight - Height of element to align\n * @param containerHeight - Height of container\n * @param alignment - Alignment type ('top' | 'middle' | 'bottom')\n * @param margin - Optional margin from edges\n * @returns Y position for alignment\n *\n * @example\n * ```typescript\n * const y = layout.alignVertical(100, 600, 'middle', 10);\n * // Returns 250 (vertically centered)\n * ```\n */\n alignVertical(\n elementHeight: number,\n containerHeight: number,\n alignment: VerticalAlignment,\n margin: number = 0\n ): number {\n switch (alignment) {\n case \"top\":\n return margin;\n case \"middle\":\n return (containerHeight - elementHeight) / 2;\n case \"bottom\":\n return containerHeight - elementHeight - margin;\n }\n }\n\n /**\n * Create screen size information from dimensions\n *\n * Determines device type and orientation based on screen dimensions.\n *\n * @param width - Screen width in pixels\n * @param height - Screen height in pixels\n * @returns ScreenSize object with device type flags\n *\n * @example\n * ```typescript\n * const screenSize = layout.getScreenSize(375, 667);\n * // Returns { width: 375, height: 667, isMobile: true, ... }\n * ```\n */\n getScreenSize(width: number, height: number): ScreenSize {\n const isMobile = width < 768;\n const isTablet = width >= 768 && width < 1200;\n const isDesktop = width >= 1200;\n const isLandscape = width > height;\n\n return {\n width,\n height,\n isMobile,\n isTablet,\n isDesktop,\n isLandscape,\n };\n }\n\n /**\n * Calculate container bounds for arena or game area\n *\n * Accounts for HUD, controls, and safe areas to determine usable space.\n *\n * @param screenWidth - Total screen width\n * @param screenHeight - Total screen height\n * @param hudHeight - Height of top HUD\n * @param controlsHeight - Height of bottom controls\n * @param padding - Padding around content\n * @returns Container bounds for game content\n *\n * @example\n * ```typescript\n * const bounds = layout.calculateContainerBounds(1200, 800, 120, 0, 10);\n * // Returns bounds for desktop game area\n * ```\n */\n calculateContainerBounds(\n screenWidth: number,\n screenHeight: number,\n hudHeight: number = 0,\n controlsHeight: number = 0,\n padding: number = 10\n ): ContainerBounds {\n const screenSize = this.getScreenSize(screenWidth, screenHeight);\n\n // Calculate available height\n const topOffset = hudHeight + padding + (screenSize.isMobile ? this.safeArea.top : 0);\n const bottomOffset = controlsHeight + padding + (screenSize.isMobile ? this.safeArea.bottom : 0);\n const availableHeight = screenHeight - topOffset - bottomOffset;\n\n // Calculate width accounting for padding\n const availableWidth = screenWidth - padding * 2;\n\n // Calculate scale for mobile (arena should be smaller on mobile)\n const scale = screenSize.isMobile ? Math.min(availableWidth / MOBILE_ARENA_BASE_WIDTH, 1.0) : 1.0;\n\n return {\n x: padding,\n y: topOffset,\n width: availableWidth,\n height: availableHeight,\n scale,\n };\n }\n}\n\n/**\n * Default singleton instance for convenience\n *\n * @example\n * ```typescript\n * import { defaultLayoutSystem } from './systems/LayoutSystem';\n *\n * const pos = defaultLayoutSystem.alignHorizontal(200, 800, 'center');\n * ```\n */\nexport const defaultLayoutSystem = new LayoutSystem();\n\n/**\n * Helper function to create grid-based position\n *\n * Convenience wrapper around LayoutSystem.calculateGridPosition\n *\n * @param column - Starting column (0-11)\n * @param span - Number of columns to span (1-12)\n * @param containerWidth - Total container width\n * @returns Position and width for grid cell\n */\nexport function calculateGridPosition(\n column: number,\n span: number,\n containerWidth: number\n): { x: number; width: number } {\n return defaultLayoutSystem.calculateGridPosition(column, span, containerWidth);\n}\n\n/**\n * Helper function to align element horizontally\n *\n * @param elementWidth - Width of element\n * @param containerWidth - Width of container\n * @param alignment - Alignment type\n * @param margin - Optional margin\n * @returns X position\n */\nexport function alignHorizontal(\n elementWidth: number,\n containerWidth: number,\n alignment: HorizontalAlignment = \"center\",\n margin: number = 0\n): number {\n return defaultLayoutSystem.alignHorizontal(elementWidth, containerWidth, alignment, margin);\n}\n\n/**\n * Helper function to align element vertically\n *\n * @param elementHeight - Height of element\n * @param containerHeight - Height of container\n * @param alignment - Alignment type\n * @param margin - Optional margin\n * @returns Y position\n */\nexport function alignVertical(\n elementHeight: number,\n containerHeight: number,\n alignment: VerticalAlignment = \"middle\",\n margin: number = 0\n): number {\n return defaultLayoutSystem.alignVertical(elementHeight, containerHeight, alignment, margin);\n}\n\n/**\n * Helper function to center element in container\n *\n * @param elementWidth - Width of element\n * @param elementHeight - Height of element\n * @param containerWidth - Width of container\n * @param containerHeight - Height of container\n * @returns Centered position\n */\nexport function centerElement(\n elementWidth: number,\n elementHeight: number,\n containerWidth: number,\n containerHeight: number\n): Position {\n return {\n x: alignHorizontal(elementWidth, containerWidth, \"center\"),\n y: alignVertical(elementHeight, containerHeight, \"middle\"),\n };\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"LayoutSystem.js","names":[],"sources":["../../src/systems/LayoutSystem.ts"],"sourcesContent":["/**\n * Unified Layout System for consistent component positioning\n *\n * Provides grid-based layout calculations, responsive positioning utilities,\n * and alignment helpers for all screens in Black Trigram (흑괘).\n *\n * Features:\n * - 12-column grid system for consistent alignment\n * - Responsive positioning that adapts to screen size\n * - Safe area handling for mobile devices with notches\n * - Z-index hierarchy management\n * - Korean-English UI alignment support\n *\n * Performance target: <1ms for layout calculations\n *\n * @module systems/LayoutSystem\n * @category Systems\n * @korean 레이아웃시스템\n */\n\nimport { Position } from \"../types/common\";\nimport {\n ContainerBounds,\n HorizontalAlignment,\n ResponsivePosition,\n SafeAreaInsets,\n ScreenSize,\n VerticalAlignment,\n} from \"../types/LayoutTypes\";\n\n/**\n * Default grid configuration\n */\nconst DEFAULT_GRID_COLUMNS = 12;\nconst DEFAULT_GUTTER_SIZE = 20;\nconst BASE_DESKTOP_WIDTH = 1200;\n\n/**\n * Base width for mobile arena scaling calculations (in pixels)\n * This represents the standard desktop arena width (80% of 1200px)\n * Used to calculate scale factor for mobile devices\n */\nconst MOBILE_ARENA_BASE_WIDTH = 960;\n\n/**\n * Default row height for grid-based vertical positioning (in pixels)\n * Used when row index is specified in grid configuration\n * Can be overridden by providing explicit y position\n * \n * @public\n */\nexport const DEFAULT_ROW_HEIGHT = 100;\n\n/**\n * Default safe area insets for mobile devices\n * Based on typical iOS device dimensions (iPhone 14 Pro as reference)\n */\nconst DEFAULT_SAFE_AREA: SafeAreaInsets = {\n top: 44, // Status bar + notch\n right: 0,\n bottom: 34, // Home indicator\n left: 0,\n};\n\n/**\n * LayoutSystem Class\n *\n * Provides unified layout calculations and positioning utilities\n * for consistent component placement across all screens.\n *\n * @example\n * ```typescript\n * const layout = new LayoutSystem();\n *\n * // Calculate grid position\n * const pos = layout.calculateGridPosition(2, 4, 1200);\n * // Returns { x: 200, width: 380 } for column 2, span 4\n *\n * // Calculate responsive position\n * const screenSize = { width: 375, height: 667, isMobile: true, isTablet: false, isDesktop: false, isLandscape: false };\n * const responsivePos = layout.calculateResponsivePosition(\n * { base: { x: 100, y: 50 } },\n * screenSize\n * );\n * ```\n */\nexport class LayoutSystem {\n private readonly gridColumns: number;\n private readonly gutterSize: number;\n private readonly safeArea: SafeAreaInsets;\n\n /**\n * Create a new LayoutSystem instance\n *\n * @param gridColumns - Number of grid columns (default: 12)\n * @param gutterSize - Gutter size between columns in pixels (default: 20)\n * @param safeArea - Safe area insets for mobile devices\n */\n constructor(\n gridColumns: number = DEFAULT_GRID_COLUMNS,\n gutterSize: number = DEFAULT_GUTTER_SIZE,\n safeArea: SafeAreaInsets = DEFAULT_SAFE_AREA\n ) {\n this.gridColumns = gridColumns;\n this.gutterSize = gutterSize;\n this.safeArea = safeArea;\n }\n\n /**\n * Calculate position and width for grid-based layout\n *\n * Uses 12-column grid system for consistent alignment.\n * Accounts for gutters between columns.\n *\n * @param column - Starting column (0-11)\n * @param span - Number of columns to span (1-12)\n * @param containerWidth - Total container width in pixels\n * @param customGutter - Custom gutter size (optional)\n * @returns Position and width for the grid cell\n * @throws Error if column or span are outside valid ranges\n *\n * @example\n * ```typescript\n * // Center element spanning 6 columns\n * const pos = layout.calculateGridPosition(3, 6, 1200);\n * // Returns { x: 300, width: 580 }\n * ```\n */\n calculateGridPosition(\n column: number,\n span: number,\n containerWidth: number,\n customGutter?: number\n ): { x: number; width: number } {\n // Validate inputs\n if (column < 0 || column >= this.gridColumns) {\n throw new Error(\n `Invalid column: ${column}. Column must be between 0 and ${this.gridColumns - 1}`\n );\n }\n if (span < 1 || span > this.gridColumns) {\n throw new Error(\n `Invalid span: ${span}. Span must be between 1 and ${this.gridColumns}`\n );\n }\n if (column + span > this.gridColumns) {\n throw new Error(\n `Invalid grid position: column ${column} + span ${span} = ${column + span} exceeds ${this.gridColumns} columns`\n );\n }\n\n const gutter = customGutter ?? this.gutterSize;\n const columnWidth = containerWidth / this.gridColumns;\n\n // Calculate x position\n const x = column * columnWidth;\n\n // Calculate width accounting for gutters\n // Width = (span * columnWidth) - gutter\n // The gutter is subtracted to create spacing between elements\n const width = span * columnWidth - gutter;\n\n return { x, width };\n }\n\n /**\n * Calculate responsive position based on screen size\n *\n * Scales position proportionally or uses specific overrides for tablet/mobile.\n *\n * @param config - Responsive position configuration\n * @param screenSize - Current screen dimensions and device type\n * @returns Calculated position for current screen size\n *\n * @example\n * ```typescript\n * const pos = layout.calculateResponsivePosition(\n * {\n * base: { x: 100, y: 50 },\n * mobile: { x: 10, y: 20 },\n * scaleProportionally: true\n * },\n * screenSize\n * );\n * ```\n */\n calculateResponsivePosition(\n config: ResponsivePosition,\n screenSize: ScreenSize\n ): Position {\n // Use specific override if available\n if (screenSize.isMobile && config.mobile) {\n return config.mobile;\n }\n if (screenSize.isTablet && config.tablet) {\n return config.tablet;\n }\n\n // Use base position for desktop\n if (screenSize.isDesktop || !config.scaleProportionally) {\n return config.base;\n }\n\n // Scale proportionally for mobile/tablet if no override\n const scale = screenSize.width / BASE_DESKTOP_WIDTH;\n return {\n x: config.base.x * scale,\n y: config.base.y * scale,\n };\n }\n\n /**\n * Calculate safe position accounting for device notches and home indicators\n *\n * Ensures UI elements don't overlap with system UI on mobile devices.\n *\n * @param position - Base position\n * @param edge - Which edge to apply safe area ('top' | 'bottom' | 'left' | 'right')\n * @returns Position adjusted for safe area\n *\n * @example\n * ```typescript\n * // Adjust top position for status bar/notch\n * const safePos = layout.calculateSafePosition({ x: 0, y: 10 }, 'top');\n * // Returns { x: 0, y: 54 } (10 + 44 for notch)\n * ```\n */\n calculateSafePosition(\n position: Position,\n edge: \"top\" | \"bottom\" | \"left\" | \"right\"\n ): Position {\n const inset = this.safeArea[edge];\n switch (edge) {\n case \"top\":\n return { ...position, y: position.y + inset };\n case \"bottom\":\n return { ...position, y: position.y - inset };\n case \"left\":\n return { ...position, x: position.x + inset };\n case \"right\":\n return { ...position, x: position.x - inset };\n }\n }\n\n /**\n * Align element horizontally within container\n *\n * @param elementWidth - Width of element to align\n * @param containerWidth - Width of container\n * @param alignment - Alignment type ('left' | 'center' | 'right')\n * @param margin - Optional margin from edges\n * @returns X position for alignment\n *\n * @example\n * ```typescript\n * const x = layout.alignHorizontal(200, 800, 'center', 10);\n * // Returns 300 (centered with margins)\n * ```\n */\n alignHorizontal(\n elementWidth: number,\n containerWidth: number,\n alignment: HorizontalAlignment,\n margin: number = 0\n ): number {\n switch (alignment) {\n case \"left\":\n return margin;\n case \"center\":\n return (containerWidth - elementWidth) / 2;\n case \"right\":\n return containerWidth - elementWidth - margin;\n }\n }\n\n /**\n * Align element vertically within container\n *\n * @param elementHeight - Height of element to align\n * @param containerHeight - Height of container\n * @param alignment - Alignment type ('top' | 'middle' | 'bottom')\n * @param margin - Optional margin from edges\n * @returns Y position for alignment\n *\n * @example\n * ```typescript\n * const y = layout.alignVertical(100, 600, 'middle', 10);\n * // Returns 250 (vertically centered)\n * ```\n */\n alignVertical(\n elementHeight: number,\n containerHeight: number,\n alignment: VerticalAlignment,\n margin: number = 0\n ): number {\n switch (alignment) {\n case \"top\":\n return margin;\n case \"middle\":\n return (containerHeight - elementHeight) / 2;\n case \"bottom\":\n return containerHeight - elementHeight - margin;\n }\n }\n\n /**\n * Create screen size information from dimensions\n *\n * Determines device type and orientation based on screen dimensions.\n *\n * @param width - Screen width in pixels\n * @param height - Screen height in pixels\n * @returns ScreenSize object with device type flags\n *\n * @example\n * ```typescript\n * const screenSize = layout.getScreenSize(375, 667);\n * // Returns { width: 375, height: 667, isMobile: true, ... }\n * ```\n */\n getScreenSize(width: number, height: number): ScreenSize {\n const isMobile = width < 768;\n const isTablet = width >= 768 && width < 1200;\n const isDesktop = width >= 1200;\n const isLandscape = width > height;\n\n return {\n width,\n height,\n isMobile,\n isTablet,\n isDesktop,\n isLandscape,\n };\n }\n\n /**\n * Calculate container bounds for arena or game area\n *\n * Accounts for HUD, controls, and safe areas to determine usable space.\n *\n * @param screenWidth - Total screen width\n * @param screenHeight - Total screen height\n * @param hudHeight - Height of top HUD\n * @param controlsHeight - Height of bottom controls\n * @param padding - Padding around content\n * @returns Container bounds for game content\n *\n * @example\n * ```typescript\n * const bounds = layout.calculateContainerBounds(1200, 800, 120, 0, 10);\n * // Returns bounds for desktop game area\n * ```\n */\n calculateContainerBounds(\n screenWidth: number,\n screenHeight: number,\n hudHeight: number = 0,\n controlsHeight: number = 0,\n padding: number = 10\n ): ContainerBounds {\n const screenSize = this.getScreenSize(screenWidth, screenHeight);\n\n // Calculate available height\n const topOffset = hudHeight + padding + (screenSize.isMobile ? this.safeArea.top : 0);\n const bottomOffset = controlsHeight + padding + (screenSize.isMobile ? this.safeArea.bottom : 0);\n const availableHeight = screenHeight - topOffset - bottomOffset;\n\n // Calculate width accounting for padding\n const availableWidth = screenWidth - padding * 2;\n\n // Calculate scale for mobile (arena should be smaller on mobile)\n const scale = screenSize.isMobile ? Math.min(availableWidth / MOBILE_ARENA_BASE_WIDTH, 1.0) : 1.0;\n\n return {\n x: padding,\n y: topOffset,\n width: availableWidth,\n height: availableHeight,\n scale,\n };\n }\n}\n\n/**\n * Default singleton instance for convenience\n *\n * @example\n * ```typescript\n * import { defaultLayoutSystem } from './systems/LayoutSystem';\n *\n * const pos = defaultLayoutSystem.alignHorizontal(200, 800, 'center');\n * ```\n */\nexport const defaultLayoutSystem = new LayoutSystem();\n\n/**\n * Helper function to create grid-based position\n *\n * Convenience wrapper around LayoutSystem.calculateGridPosition\n *\n * @param column - Starting column (0-11)\n * @param span - Number of columns to span (1-12)\n * @param containerWidth - Total container width\n * @returns Position and width for grid cell\n */\nexport function calculateGridPosition(\n column: number,\n span: number,\n containerWidth: number\n): { x: number; width: number } {\n return defaultLayoutSystem.calculateGridPosition(column, span, containerWidth);\n}\n\n/**\n * Helper function to align element horizontally\n *\n * @param elementWidth - Width of element\n * @param containerWidth - Width of container\n * @param alignment - Alignment type\n * @param margin - Optional margin\n * @returns X position\n */\nexport function alignHorizontal(\n elementWidth: number,\n containerWidth: number,\n alignment: HorizontalAlignment = \"center\",\n margin: number = 0\n): number {\n return defaultLayoutSystem.alignHorizontal(elementWidth, containerWidth, alignment, margin);\n}\n\n/**\n * Helper function to align element vertically\n *\n * @param elementHeight - Height of element\n * @param containerHeight - Height of container\n * @param alignment - Alignment type\n * @param margin - Optional margin\n * @returns Y position\n */\nexport function alignVertical(\n elementHeight: number,\n containerHeight: number,\n alignment: VerticalAlignment = \"middle\",\n margin: number = 0\n): number {\n return defaultLayoutSystem.alignVertical(elementHeight, containerHeight, alignment, margin);\n}\n\n/**\n * Helper function to center element in container\n *\n * @param elementWidth - Width of element\n * @param elementHeight - Height of element\n * @param containerWidth - Width of container\n * @param containerHeight - Height of container\n * @returns Centered position\n */\nexport function centerElement(\n elementWidth: number,\n elementHeight: number,\n containerWidth: number,\n containerHeight: number\n): Position {\n return {\n x: alignHorizontal(elementWidth, containerWidth, \"center\"),\n y: alignVertical(elementHeight, containerHeight, \"middle\"),\n };\n}\n"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"PlayerEffectManager.js","names":[],"sources":["../../src/systems/PlayerEffectManager.ts"],"sourcesContent":["/**\n * Player Effect Manager for tracking and applying status effects.\n * \n * **Korean**: 플레이어 효과 관리자 (Player Effect Manager)\n * \n * This module provides utilities for managing status effects on player state,\n * including adding effects with stacking logic, removing expired effects,\n * and applying effect-based combat modifiers.\n * \n * ## Key Features\n * \n * - **Effect Tracking**: Add, remove, and update status effects\n * - **Stacking Logic**: Manage concurrent effects (max 5)\n * - **Expiration Handling**: Auto-remove expired effects\n * - **Combat Modifiers**: Calculate effect-based stat changes\n * \n * @module systems/PlayerEffectManager\n * @category Player Management\n * @korean 플레이어효과관리\n */\n\nimport { VitalPointEffectType } from \"../types/common\";\nimport { StatusEffect } from \"./types\";\nimport { PlayerState } from \"./player\";\nimport { applyEffectStacking, MAX_CONCURRENT_EFFECTS } from \"./EffectCalculator\";\n\n/**\n * Adds status effects to a player's active effects list.\n * \n * **Korean**: 플레이어에 효과 추가 (Add Effects to Player)\n * \n * Applies stacking logic and respects the max concurrent effects limit.\n * \n * @param player - Current player state\n * @param effects - New effects to add\n * @returns Updated player state with new effects\n * \n * @example\n * ```typescript\n * const updatedPlayer = addEffectsToPlayer(\n * player,\n * [paralyze, bleed, stun]\n * );\n * ```\n * \n * @public\n * @korean 효과추가\n */\nexport function addEffectsToPlayer(\n player: PlayerState,\n effects: readonly StatusEffect[]\n): PlayerState {\n const currentTime = Date.now();\n\n // Apply stacking logic\n const updatedEffects = applyEffectStacking(\n player.statusEffects,\n effects,\n currentTime\n );\n\n // Update activeEffects array with effect IDs\n const activeEffectIds = updatedEffects.map((effect) => effect.id);\n\n return {\n ...player,\n statusEffects: updatedEffects,\n activeEffects: activeEffectIds,\n };\n}\n\n/**\n * Removes expired status effects from player.\n * \n * **Korean**: 만료 효과 제거 (Remove Expired Effects)\n * \n * Filters out effects whose endTime has passed.\n * \n * @param player - Current player state\n * @param currentTime - Current timestamp (default: Date.now())\n * @returns Updated player state with expired effects removed\n * \n * @example\n * ```typescript\n * const updatedPlayer = removeExpiredEffects(player, Date.now());\n * ```\n * \n * @public\n * @korean 만료효과제거\n */\nexport function removeExpiredEffects(\n player: PlayerState,\n currentTime: number = Date.now()\n): PlayerState {\n const activeEffects = player.statusEffects.filter(\n (effect) => effect.endTime > currentTime\n );\n\n const activeEffectIds = activeEffects.map((effect) => effect.id);\n\n return {\n ...player,\n statusEffects: activeEffects,\n activeEffects: activeEffectIds,\n };\n}\n\n/**\n * Removes a specific status effect by ID.\n * \n * **Korean**: 특정 효과 제거 (Remove Specific Effect)\n * \n * @param player - Current player state\n * @param effectId - ID of effect to remove\n * @returns Updated player state with effect removed\n * \n * @example\n * ```typescript\n * const updatedPlayer = removeEffectById(player, \"stun_12345\");\n * ```\n * \n * @public\n * @korean 효과제거\n */\nexport function removeEffectById(\n player: PlayerState,\n effectId: string\n): PlayerState {\n const activeEffects = player.statusEffects.filter(\n (effect) => effect.id !== effectId\n );\n\n const activeEffectIds = activeEffects.map((effect) => effect.id);\n\n return {\n ...player,\n statusEffects: activeEffects,\n activeEffects: activeEffectIds,\n };\n}\n\n/**\n * Removes all status effects of a specific type.\n * \n * **Korean**: 특정 유형 효과 모두 제거 (Remove All Effects of Type)\n * \n * @param player - Current player state\n * @param effectType - Type of effects to remove\n * @returns Updated player state with effects removed\n * \n * @example\n * ```typescript\n * const updatedPlayer = removeEffectsByType(\n * player,\n * VitalPointEffectType.PARALYSIS\n * );\n * ```\n * \n * @public\n * @korean 유형별효과제거\n */\nexport function removeEffectsByType(\n player: PlayerState,\n effectType: VitalPointEffectType\n): PlayerState {\n const activeEffects = player.statusEffects.filter(\n (effect) => effect.type !== effectType\n );\n\n const activeEffectIds = activeEffects.map((effect) => effect.id);\n\n return {\n ...player,\n statusEffects: activeEffects,\n activeEffects: activeEffectIds,\n };\n}\n\n/**\n * Clears all status effects from player.\n * \n * **Korean**: 모든 효과 제거 (Clear All Effects)\n * \n * Useful for healing items, rest periods, or match resets.\n * \n * @param player - Current player state\n * @returns Updated player state with no effects\n * \n * @example\n * ```typescript\n * const clearedPlayer = clearAllEffects(player);\n * ```\n * \n * @public\n * @korean 전체효과제거\n */\nexport function clearAllEffects(player: PlayerState): PlayerState {\n return {\n ...player,\n statusEffects: [],\n activeEffects: [],\n };\n}\n\n/**\n * Calculates combat stat modifiers based on active status effects.\n * \n * **Korean**: 효과 기반 전투 배율 계산 (Calculate Effect-Based Combat Modifiers)\n * \n * Returns multipliers for various combat attributes affected by status effects.\n * \n * @param player - Current player state\n * @returns Object with stat multipliers (1.0 = no change)\n * \n * @example\n * ```typescript\n * const modifiers = getEffectModifiers(player);\n * const effectiveDamage = baseDamage * modifiers.attackPower;\n * const effectiveSpeed = baseSpeed * modifiers.speed;\n * ```\n * \n * @public\n * @korean 효과배율계산\n */\nexport function getEffectModifiers(player: PlayerState): {\n attackPower: number;\n defense: number;\n speed: number;\n technique: number;\n staminaRegen: number;\n kiRegen: number;\n} {\n let attackPower = 1.0;\n let defense = 1.0;\n let speed = 1.0;\n let technique = 1.0;\n let staminaRegen = 1.0;\n let kiRegen = 1.0;\n\n for (const effect of player.statusEffects) {\n // UNCONSCIOUSNESS takes precedence - early return with complete incapacitation\n if (effect.type === VitalPointEffectType.UNCONSCIOUSNESS) {\n return {\n attackPower: 0,\n defense: 0,\n speed: 0,\n technique: 0,\n staminaRegen: 0,\n kiRegen: 0,\n };\n }\n\n // Apply other effect modifiers multiplicatively\n switch (effect.type) {\n case VitalPointEffectType.PARALYSIS:\n // Severe movement and action impairment\n attackPower *= 0.3;\n speed *= 0.2;\n technique *= 0.4;\n break;\n\n case VitalPointEffectType.STUN:\n // Moderate impairment\n attackPower *= 0.5;\n defense *= 0.6;\n speed *= 0.5;\n technique *= 0.6;\n break;\n\n case VitalPointEffectType.PAIN:\n // Reduces effectiveness across the board\n attackPower *= 0.7;\n defense *= 0.8;\n speed *= 0.8;\n technique *= 0.7;\n break;\n\n case VitalPointEffectType.BLOOD_FLOW_RESTRICTION:\n // Gradual weakening from reduced circulation\n attackPower *= 0.85;\n staminaRegen *= 0.5;\n break;\n\n case VitalPointEffectType.BREATHLESSNESS:\n // NOTE: Stamina regen is handled by BreathingDisruptionSystem.calculateStaminaRegen()\n // which applies severity-specific multipliers (25%-75% depending on disruption level).\n // DO NOT apply a flat multiplier here as it would compound with the breathing system.\n // Only apply secondary effects here.\n speed *= 0.7; // Reduced movement speed due to breathing difficulty\n break;\n\n case VitalPointEffectType.DISORIENTATION:\n // Accuracy and technique affected\n technique *= 0.6;\n defense *= 0.7;\n break;\n\n case VitalPointEffectType.WEAKNESS:\n // Overall power reduction\n attackPower *= 0.7;\n defense *= 0.8;\n break;\n\n case VitalPointEffectType.NERVE_DISRUPTION:\n // Ki system and coordination impaired\n kiRegen *= 0.5;\n attackPower *= 0.8;\n technique *= 0.7;\n break;\n\n case VitalPointEffectType.ORGAN_DISRUPTION:\n // Internal damage affecting all systems\n attackPower *= 0.6;\n defense *= 0.7;\n staminaRegen *= 0.4;\n kiRegen *= 0.4;\n break;\n }\n }\n\n return {\n attackPower,\n defense,\n speed,\n technique,\n staminaRegen,\n kiRegen,\n };\n}\n\n/**\n * Checks if player has a specific effect type active.\n * \n * **Korean**: 효과 활성 여부 확인 (Check Effect Active)\n * \n * @param player - Current player state\n * @param effectType - Type to check for\n * @returns True if effect type is active\n * \n * @example\n * ```typescript\n * if (hasEffect(player, VitalPointEffectType.PARALYSIS)) {\n * console.log(\"Player is paralyzed!\");\n * }\n * ```\n * \n * @public\n * @korean 효과확인\n */\nexport function hasEffect(\n player: PlayerState,\n effectType: VitalPointEffectType\n): boolean {\n return player.statusEffects.some((effect) => effect.type === effectType);\n}\n\n/**\n * Gets all active effects of a specific type.\n * \n * **Korean**: 유형별 활성 효과 조회 (Get Effects by Type)\n * \n * @param player - Current player state\n * @param effectType - Type to retrieve\n * @returns Array of matching effects\n * \n * @public\n * @korean 유형별효과조회\n */\nexport function getEffectsByType(\n player: PlayerState,\n effectType: VitalPointEffectType\n): StatusEffect[] {\n return player.statusEffects.filter((effect) => effect.type === effectType);\n}\n\n/**\n * Gets the total number of active status effects.\n * \n * **Korean**: 활성 효과 개수 조회 (Get Active Effect Count)\n * \n * @param player - Current player state\n * @returns Number of active effects\n * \n * @public\n * @korean 효과개수조회\n */\nexport function getActiveEffectCount(player: PlayerState): number {\n return player.statusEffects.length;\n}\n\n/**\n * Checks if player can accept new status effects.\n * \n * **Korean**: 효과 추가 가능 여부 확인 (Can Add More Effects)\n * \n * @param player - Current player state\n * @returns True if under MAX_CONCURRENT_EFFECTS limit\n * \n * @public\n * @korean 효과추가가능확인\n */\nexport function canAddMoreEffects(player: PlayerState): boolean {\n return player.statusEffects.length < MAX_CONCURRENT_EFFECTS;\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAgDA,SAAgB,mBACd,QACA,SACa;CACb,MAAM,cAAc,KAAK,KAAK;CAG9B,MAAM,iBAAiB,oBACrB,OAAO,eACP,SACA,YACD;CAGD,MAAM,kBAAkB,eAAe,KAAK,WAAW,OAAO,GAAG;AAEjE,QAAO;EACL,GAAG;EACH,eAAe;EACf,eAAe;EAChB;;;;;;;;;;;;;;;;;;;;;AAsBH,SAAgB,qBACd,QACA,cAAsB,KAAK,KAAK,EACnB;CACb,MAAM,gBAAgB,OAAO,cAAc,QACxC,WAAW,OAAO,UAAU,YAC9B;CAED,MAAM,kBAAkB,cAAc,KAAK,WAAW,OAAO,GAAG;AAEhE,QAAO;EACL,GAAG;EACH,eAAe;EACf,eAAe;EAChB;;;;;;;;;;;;;;;;;;;;;;AAwHH,SAAgB,mBAAmB,QAOjC;CACA,IAAI,cAAc;CAClB,IAAI,UAAU;CACd,IAAI,QAAQ;CACZ,IAAI,YAAY;CAChB,IAAI,eAAe;CACnB,IAAI,UAAU;AAEd,MAAK,MAAM,UAAU,OAAO,eAAe;AAEzC,MAAI,OAAO,SAAS,qBAAqB,gBACvC,QAAO;GACL,aAAa;GACb,SAAS;GACT,OAAO;GACP,WAAW;GACX,cAAc;GACd,SAAS;GACV;AAIH,UAAQ,OAAO,MAAf;GACE,KAAK,qBAAqB;AAExB,mBAAe;AACf,aAAS;AACT,iBAAa;AACb;GAEF,KAAK,qBAAqB;AAExB,mBAAe;AACf,eAAW;AACX,aAAS;AACT,iBAAa;AACb;GAEF,KAAK,qBAAqB;AAExB,mBAAe;AACf,eAAW;AACX,aAAS;AACT,iBAAa;AACb;GAEF,KAAK,qBAAqB;AAExB,mBAAe;AACf,oBAAgB;AAChB;GAEF,KAAK,qBAAqB;AAKxB,aAAS;AACT;GAEF,KAAK,qBAAqB;AAExB,iBAAa;AACb,eAAW;AACX;GAEF,KAAK,qBAAqB;AAExB,mBAAe;AACf,eAAW;AACX;GAEF,KAAK,qBAAqB;AAExB,eAAW;AACX,mBAAe;AACf,iBAAa;AACb;GAEF,KAAK,qBAAqB;AAExB,mBAAe;AACf,eAAW;AACX,oBAAgB;AAChB,eAAW;AACX;;;AAIN,QAAO;EACL;EACA;EACA;EACA;EACA;EACA;EACD"}
1
+ {"version":3,"file":"PlayerEffectManager.js","names":[],"sources":["../../src/systems/PlayerEffectManager.ts"],"sourcesContent":["/**\n * Player Effect Manager for tracking and applying status effects.\n * \n * **Korean**: 플레이어 효과 관리자 (Player Effect Manager)\n * \n * This module provides utilities for managing status effects on player state,\n * including adding effects with stacking logic, removing expired effects,\n * and applying effect-based combat modifiers.\n * \n * ## Key Features\n * \n * - **Effect Tracking**: Add, remove, and update status effects\n * - **Stacking Logic**: Manage concurrent effects (max 5)\n * - **Expiration Handling**: Auto-remove expired effects\n * - **Combat Modifiers**: Calculate effect-based stat changes\n * \n * @module systems/PlayerEffectManager\n * @category Player Management\n * @korean 플레이어효과관리\n */\n\nimport { VitalPointEffectType } from \"../types/common\";\nimport { StatusEffect } from \"./types\";\nimport { PlayerState } from \"./player\";\nimport { applyEffectStacking, MAX_CONCURRENT_EFFECTS } from \"./EffectCalculator\";\n\n/**\n * Adds status effects to a player's active effects list.\n * \n * **Korean**: 플레이어에 효과 추가 (Add Effects to Player)\n * \n * Applies stacking logic and respects the max concurrent effects limit.\n * \n * @param player - Current player state\n * @param effects - New effects to add\n * @returns Updated player state with new effects\n * \n * @example\n * ```typescript\n * const updatedPlayer = addEffectsToPlayer(\n * player,\n * [paralyze, bleed, stun]\n * );\n * ```\n * \n * @public\n * @korean 효과추가\n */\nexport function addEffectsToPlayer(\n player: PlayerState,\n effects: readonly StatusEffect[]\n): PlayerState {\n const currentTime = Date.now();\n\n // Apply stacking logic\n const updatedEffects = applyEffectStacking(\n player.statusEffects,\n effects,\n currentTime\n );\n\n // Update activeEffects array with effect IDs\n const activeEffectIds = updatedEffects.map((effect) => effect.id);\n\n return {\n ...player,\n statusEffects: updatedEffects,\n activeEffects: activeEffectIds,\n };\n}\n\n/**\n * Removes expired status effects from player.\n * \n * **Korean**: 만료 효과 제거 (Remove Expired Effects)\n * \n * Filters out effects whose endTime has passed.\n * \n * @param player - Current player state\n * @param currentTime - Current timestamp (default: Date.now())\n * @returns Updated player state with expired effects removed\n * \n * @example\n * ```typescript\n * const updatedPlayer = removeExpiredEffects(player, Date.now());\n * ```\n * \n * @public\n * @korean 만료효과제거\n */\nexport function removeExpiredEffects(\n player: PlayerState,\n currentTime: number = Date.now()\n): PlayerState {\n const activeEffects = player.statusEffects.filter(\n (effect) => effect.endTime > currentTime\n );\n\n const activeEffectIds = activeEffects.map((effect) => effect.id);\n\n return {\n ...player,\n statusEffects: activeEffects,\n activeEffects: activeEffectIds,\n };\n}\n\n/**\n * Removes a specific status effect by ID.\n * \n * **Korean**: 특정 효과 제거 (Remove Specific Effect)\n * \n * @param player - Current player state\n * @param effectId - ID of effect to remove\n * @returns Updated player state with effect removed\n * \n * @example\n * ```typescript\n * const updatedPlayer = removeEffectById(player, \"stun_12345\");\n * ```\n * \n * @public\n * @korean 효과제거\n */\nexport function removeEffectById(\n player: PlayerState,\n effectId: string\n): PlayerState {\n const activeEffects = player.statusEffects.filter(\n (effect) => effect.id !== effectId\n );\n\n const activeEffectIds = activeEffects.map((effect) => effect.id);\n\n return {\n ...player,\n statusEffects: activeEffects,\n activeEffects: activeEffectIds,\n };\n}\n\n/**\n * Removes all status effects of a specific type.\n * \n * **Korean**: 특정 유형 효과 모두 제거 (Remove All Effects of Type)\n * \n * @param player - Current player state\n * @param effectType - Type of effects to remove\n * @returns Updated player state with effects removed\n * \n * @example\n * ```typescript\n * const updatedPlayer = removeEffectsByType(\n * player,\n * VitalPointEffectType.PARALYSIS\n * );\n * ```\n * \n * @public\n * @korean 유형별효과제거\n */\nexport function removeEffectsByType(\n player: PlayerState,\n effectType: VitalPointEffectType\n): PlayerState {\n const activeEffects = player.statusEffects.filter(\n (effect) => effect.type !== effectType\n );\n\n const activeEffectIds = activeEffects.map((effect) => effect.id);\n\n return {\n ...player,\n statusEffects: activeEffects,\n activeEffects: activeEffectIds,\n };\n}\n\n/**\n * Clears all status effects from player.\n * \n * **Korean**: 모든 효과 제거 (Clear All Effects)\n * \n * Useful for healing items, rest periods, or match resets.\n * \n * @param player - Current player state\n * @returns Updated player state with no effects\n * \n * @example\n * ```typescript\n * const clearedPlayer = clearAllEffects(player);\n * ```\n * \n * @public\n * @korean 전체효과제거\n */\nexport function clearAllEffects(player: PlayerState): PlayerState {\n return {\n ...player,\n statusEffects: [],\n activeEffects: [],\n };\n}\n\n/**\n * Calculates combat stat modifiers based on active status effects.\n * \n * **Korean**: 효과 기반 전투 배율 계산 (Calculate Effect-Based Combat Modifiers)\n * \n * Returns multipliers for various combat attributes affected by status effects.\n * \n * @param player - Current player state\n * @returns Object with stat multipliers (1.0 = no change)\n * \n * @example\n * ```typescript\n * const modifiers = getEffectModifiers(player);\n * const effectiveDamage = baseDamage * modifiers.attackPower;\n * const effectiveSpeed = baseSpeed * modifiers.speed;\n * ```\n * \n * @public\n * @korean 효과배율계산\n */\nexport function getEffectModifiers(player: PlayerState): {\n attackPower: number;\n defense: number;\n speed: number;\n technique: number;\n staminaRegen: number;\n kiRegen: number;\n} {\n let attackPower = 1.0;\n let defense = 1.0;\n let speed = 1.0;\n let technique = 1.0;\n let staminaRegen = 1.0;\n let kiRegen = 1.0;\n\n for (const effect of player.statusEffects) {\n // UNCONSCIOUSNESS takes precedence - early return with complete incapacitation\n if (effect.type === VitalPointEffectType.UNCONSCIOUSNESS) {\n return {\n attackPower: 0,\n defense: 0,\n speed: 0,\n technique: 0,\n staminaRegen: 0,\n kiRegen: 0,\n };\n }\n\n // Apply other effect modifiers multiplicatively\n switch (effect.type) {\n case VitalPointEffectType.PARALYSIS:\n // Severe movement and action impairment\n attackPower *= 0.3;\n speed *= 0.2;\n technique *= 0.4;\n break;\n\n case VitalPointEffectType.STUN:\n // Moderate impairment\n attackPower *= 0.5;\n defense *= 0.6;\n speed *= 0.5;\n technique *= 0.6;\n break;\n\n case VitalPointEffectType.PAIN:\n // Reduces effectiveness across the board\n attackPower *= 0.7;\n defense *= 0.8;\n speed *= 0.8;\n technique *= 0.7;\n break;\n\n case VitalPointEffectType.BLOOD_FLOW_RESTRICTION:\n // Gradual weakening from reduced circulation\n attackPower *= 0.85;\n staminaRegen *= 0.5;\n break;\n\n case VitalPointEffectType.BREATHLESSNESS:\n // NOTE: Stamina regen is handled by BreathingDisruptionSystem.calculateStaminaRegen()\n // which applies severity-specific multipliers (25%-75% depending on disruption level).\n // DO NOT apply a flat multiplier here as it would compound with the breathing system.\n // Only apply secondary effects here.\n speed *= 0.7; // Reduced movement speed due to breathing difficulty\n break;\n\n case VitalPointEffectType.DISORIENTATION:\n // Accuracy and technique affected\n technique *= 0.6;\n defense *= 0.7;\n break;\n\n case VitalPointEffectType.WEAKNESS:\n // Overall power reduction\n attackPower *= 0.7;\n defense *= 0.8;\n break;\n\n case VitalPointEffectType.NERVE_DISRUPTION:\n // Ki system and coordination impaired\n kiRegen *= 0.5;\n attackPower *= 0.8;\n technique *= 0.7;\n break;\n\n case VitalPointEffectType.ORGAN_DISRUPTION:\n // Internal damage affecting all systems\n attackPower *= 0.6;\n defense *= 0.7;\n staminaRegen *= 0.4;\n kiRegen *= 0.4;\n break;\n }\n }\n\n return {\n attackPower,\n defense,\n speed,\n technique,\n staminaRegen,\n kiRegen,\n };\n}\n\n/**\n * Checks if player has a specific effect type active.\n * \n * **Korean**: 효과 활성 여부 확인 (Check Effect Active)\n * \n * @param player - Current player state\n * @param effectType - Type to check for\n * @returns True if effect type is active\n * \n * @example\n * ```typescript\n * if (hasEffect(player, VitalPointEffectType.PARALYSIS)) {\n * console.log(\"Player is paralyzed!\");\n * }\n * ```\n * \n * @public\n * @korean 효과확인\n */\nexport function hasEffect(\n player: PlayerState,\n effectType: VitalPointEffectType\n): boolean {\n return player.statusEffects.some((effect) => effect.type === effectType);\n}\n\n/**\n * Gets all active effects of a specific type.\n * \n * **Korean**: 유형별 활성 효과 조회 (Get Effects by Type)\n * \n * @param player - Current player state\n * @param effectType - Type to retrieve\n * @returns Array of matching effects\n * \n * @public\n * @korean 유형별효과조회\n */\nexport function getEffectsByType(\n player: PlayerState,\n effectType: VitalPointEffectType\n): StatusEffect[] {\n return player.statusEffects.filter((effect) => effect.type === effectType);\n}\n\n/**\n * Gets the total number of active status effects.\n * \n * **Korean**: 활성 효과 개수 조회 (Get Active Effect Count)\n * \n * @param player - Current player state\n * @returns Number of active effects\n * \n * @public\n * @korean 효과개수조회\n */\nexport function getActiveEffectCount(player: PlayerState): number {\n return player.statusEffects.length;\n}\n\n/**\n * Checks if player can accept new status effects.\n * \n * **Korean**: 효과 추가 가능 여부 확인 (Can Add More Effects)\n * \n * @param player - Current player state\n * @returns True if under MAX_CONCURRENT_EFFECTS limit\n * \n * @public\n * @korean 효과추가가능확인\n */\nexport function canAddMoreEffects(player: PlayerState): boolean {\n return player.statusEffects.length < MAX_CONCURRENT_EFFECTS;\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAgDA,SAAgB,mBACd,QACA,SACa;CACb,MAAM,cAAc,KAAK,KAAK;CAG9B,MAAM,iBAAiB,oBACrB,OAAO,eACP,SACA,YACD;CAGD,MAAM,kBAAkB,eAAe,KAAK,WAAW,OAAO,GAAG;CAEjE,OAAO;EACL,GAAG;EACH,eAAe;EACf,eAAe;EAChB;;;;;;;;;;;;;;;;;;;;;AAsBH,SAAgB,qBACd,QACA,cAAsB,KAAK,KAAK,EACnB;CACb,MAAM,gBAAgB,OAAO,cAAc,QACxC,WAAW,OAAO,UAAU,YAC9B;CAED,MAAM,kBAAkB,cAAc,KAAK,WAAW,OAAO,GAAG;CAEhE,OAAO;EACL,GAAG;EACH,eAAe;EACf,eAAe;EAChB;;;;;;;;;;;;;;;;;;;;;;AAwHH,SAAgB,mBAAmB,QAOjC;CACA,IAAI,cAAc;CAClB,IAAI,UAAU;CACd,IAAI,QAAQ;CACZ,IAAI,YAAY;CAChB,IAAI,eAAe;CACnB,IAAI,UAAU;CAEd,KAAK,MAAM,UAAU,OAAO,eAAe;EAEzC,IAAI,OAAO,SAAS,qBAAqB,iBACvC,OAAO;GACL,aAAa;GACb,SAAS;GACT,OAAO;GACP,WAAW;GACX,cAAc;GACd,SAAS;GACV;EAIH,QAAQ,OAAO,MAAf;GACE,KAAK,qBAAqB;IAExB,eAAe;IACf,SAAS;IACT,aAAa;IACb;GAEF,KAAK,qBAAqB;IAExB,eAAe;IACf,WAAW;IACX,SAAS;IACT,aAAa;IACb;GAEF,KAAK,qBAAqB;IAExB,eAAe;IACf,WAAW;IACX,SAAS;IACT,aAAa;IACb;GAEF,KAAK,qBAAqB;IAExB,eAAe;IACf,gBAAgB;IAChB;GAEF,KAAK,qBAAqB;IAKxB,SAAS;IACT;GAEF,KAAK,qBAAqB;IAExB,aAAa;IACb,WAAW;IACX;GAEF,KAAK,qBAAqB;IAExB,eAAe;IACf,WAAW;IACX;GAEF,KAAK,qBAAqB;IAExB,WAAW;IACX,eAAe;IACf,aAAa;IACb;GAEF,KAAK,qBAAqB;IAExB,eAAe;IACf,WAAW;IACX,gBAAgB;IAChB,WAAW;IACX;;;CAIN,OAAO;EACL;EACA;EACA;EACA;EACA;EACA;EACD"}
@@ -1 +1 @@
1
- {"version":3,"file":"ResponsiveScaling.js","names":[],"sources":["../../src/systems/ResponsiveScaling.ts"],"sourcesContent":["/**\n * Responsive Scaling System for Black Trigram\n * \n * Centralized scaling calculations for responsive layout across all screen sizes.\n * Implements proportional font and spacing scaling with smooth transitions.\n * \n * Features:\n * - Five screen size categories (mobile, tablet, desktop, large, xlarge)\n * - Font scaling: 0.8x (mobile) to 1.4x (4K displays)\n * - Spacing scaling: 0.5x (mobile) to 1.5x (4K displays)\n * - Korean text readability: 14-24px range enforced\n * - Smooth CSS transitions for resize operations\n * - 60fps performance maintained\n * \n * @module systems/ResponsiveScaling\n * @category Responsive Layout\n * @korean 반응형스케일시스템\n */\n\nimport type {\n ScreenSize,\n ResponsiveBreakpoints,\n FontScaleMap,\n SpacingScaleMap,\n ResponsiveScaleConfig,\n ResizeTransitionConfig,\n ResponsiveValues,\n ScreenSizeTestResult,\n} from '../types/ResponsiveTypes';\n\n// Re-export types for convenience\nexport type {\n ScreenSize,\n ResponsiveBreakpoints,\n FontScaleMap,\n SpacingScaleMap,\n ResponsiveScaleConfig,\n ResizeTransitionConfig,\n ResponsiveValues,\n ScreenSizeTestResult,\n};\n\n/**\n * Standard breakpoints for responsive design\n * Maps viewport widths to device categories\n * \n * Note: XLARGE is defined as a reference value (2560px) but xlarge category\n * starts at LARGE (1920px). This allows for consistent scaling across all\n * displays from 1920px upwards, including 2K, 4K, and ultra-wide monitors.\n * \n * @constant\n * @category Responsive Layout\n */\nexport const RESPONSIVE_BREAKPOINTS: ResponsiveBreakpoints = {\n MOBILE: 768, // < 768px: Mobile devices\n TABLET: 1024, // 768-1024px: Tablets\n DESKTOP: 1440, // 1024-1440px: Standard desktop\n LARGE: 1920, // 1440-1920px: HD/2K displays\n XLARGE: 2560, // Reference for 4K displays (xlarge starts at 1920px)\n} as const;\n\n/**\n * Font scaling multipliers by screen size\n * Base size (16px) * scale = final size\n * \n * Results clamped to 14-24px for readability\n * \n * @constant\n * @category Typography\n */\nexport const FONT_SCALE_MAP: FontScaleMap = {\n mobile: 0.8, // 16px * 0.8 = 12.8px → clamped to 14px min\n tablet: 0.9, // 16px * 0.9 = 14.4px\n desktop: 1.0, // 16px * 1.0 = 16px (base)\n large: 1.2, // 16px * 1.2 = 19.2px\n xlarge: 1.4, // 16px * 1.4 = 22.4px → clamped to 24px max\n} as const;\n\n/**\n * Spacing scaling multipliers by screen size\n * Base spacing * scale = final spacing\n * \n * @constant\n * @category Layout\n */\nexport const SPACING_SCALE_MAP: SpacingScaleMap = {\n mobile: 0.5, // Compact spacing for small screens\n tablet: 0.75, // Moderate spacing for tablets\n desktop: 1.0, // Standard reference spacing\n large: 1.25, // Spacious for large displays\n xlarge: 1.5, // Maximum spacing for 4K\n} as const;\n\n/**\n * Default transition configuration for smooth resizing\n * Optimized for 60fps performance\n * \n * @constant\n * @category Animation\n */\nexport const DEFAULT_RESIZE_TRANSITION: ResizeTransitionConfig = {\n duration: '300ms',\n easing: 'ease-in-out',\n properties: ['font-size', 'padding', 'margin', 'width', 'height'] as const,\n enabled: true,\n} as const;\n\n/**\n * Font size constraints for Korean and English text\n * Ensures readability across all screen sizes\n * \n * @constant\n * @category Typography\n */\nexport const FONT_SIZE_CONSTRAINTS = {\n /** Minimum body text size for readability */\n MIN_BODY_SIZE: 14,\n /** Maximum size before text becomes too large */\n MAX_SIZE: 24,\n /** Base reference size (desktop) */\n BASE_SIZE: 16,\n} as const;\n\n/**\n * Determine screen size category from viewport width\n * \n * Categories:\n * - mobile: < 768px (phones)\n * - tablet: 768-1024px (tablets)\n * - desktop: 1024-1440px (standard monitors)\n * - large: 1440-1920px (HD/2K displays)\n * - xlarge: ≥1920px (4K/ultra-wide)\n * \n * @param width - Viewport width in pixels\n * @returns Screen size category\n * \n * @example\n * ```typescript\n * getScreenSize(375); // 'mobile'\n * getScreenSize(768); // 'tablet'\n * getScreenSize(1920); // 'xlarge'\n * ```\n * \n * @public\n * @korean 화면크기얻기\n */\nexport function getScreenSize(width: number): ScreenSize {\n if (width < RESPONSIVE_BREAKPOINTS.MOBILE) return 'mobile';\n if (width < RESPONSIVE_BREAKPOINTS.TABLET) return 'tablet';\n if (width < RESPONSIVE_BREAKPOINTS.DESKTOP) return 'desktop';\n if (width < RESPONSIVE_BREAKPOINTS.LARGE) return 'large';\n return 'xlarge';\n}\n\n/**\n * Calculate scaled font size with readability constraints\n * \n * Formula: baseSize * scale\n * Clamped: max(minSize, min(maxSize, calculated))\n * \n * Ensures Korean and English text remain readable at all sizes\n * \n * @param baseSize - Base font size in pixels (typically 16px)\n * @param screenSize - Current screen size category\n * @param minSize - Minimum allowed size (default: 14px)\n * @param maxSize - Maximum allowed size (default: 24px)\n * @returns Calculated font size in pixels\n * \n * @example\n * ```typescript\n * calculateFontSize(16, 'mobile'); // 14 (clamped from 12.8)\n * calculateFontSize(16, 'desktop'); // 16\n * calculateFontSize(16, 'xlarge'); // 22.4\n * calculateFontSize(20, 'xlarge', 14, 24); // 24 (clamped from 28)\n * ```\n * \n * @public\n * @korean 글꼴크기계산\n */\nexport function calculateFontSize(\n baseSize: number,\n screenSize: ScreenSize,\n minSize: number = FONT_SIZE_CONSTRAINTS.MIN_BODY_SIZE,\n maxSize: number = FONT_SIZE_CONSTRAINTS.MAX_SIZE\n): number {\n const scale = FONT_SCALE_MAP[screenSize];\n const scaled = baseSize * scale;\n \n // Clamp to readable range\n return Math.max(minSize, Math.min(maxSize, scaled));\n}\n\n/**\n * Calculate scaled spacing value\n * \n * Formula: baseSpacing * scale\n * Rounded to nearest integer for crisp rendering\n * \n * @param baseSpacing - Base spacing value in pixels\n * @param screenSize - Current screen size category\n * @returns Calculated spacing in pixels (rounded)\n * \n * @example\n * ```typescript\n * calculateSpacing(20, 'mobile'); // 10 (20 * 0.5)\n * calculateSpacing(20, 'desktop'); // 20\n * calculateSpacing(20, 'xlarge'); // 30 (20 * 1.5)\n * ```\n * \n * @public\n * @korean 간격계산\n */\nexport function calculateSpacing(\n baseSpacing: number,\n screenSize: ScreenSize\n): number {\n const scale = SPACING_SCALE_MAP[screenSize];\n return Math.round(baseSpacing * scale);\n}\n\n/**\n * Get font scale multiplier for screen size\n * \n * @param screenSize - Screen size category\n * @returns Scale multiplier (0.8 - 1.4)\n * \n * @example\n * ```typescript\n * getFontScale('mobile'); // 0.8\n * getFontScale('desktop'); // 1.0\n * getFontScale('xlarge'); // 1.4\n * ```\n * \n * @public\n * @korean 글꼴스케일얻기\n */\nexport function getFontScale(screenSize: ScreenSize): number {\n return FONT_SCALE_MAP[screenSize];\n}\n\n/**\n * Get spacing scale multiplier for screen size\n * \n * @param screenSize - Screen size category\n * @returns Scale multiplier (0.5 - 1.5)\n * \n * @example\n * ```typescript\n * getSpacingScale('mobile'); // 0.5\n * getSpacingScale('desktop'); // 1.0\n * getSpacingScale('xlarge'); // 1.5\n * ```\n * \n * @public\n * @korean 간격스케일얻기\n */\nexport function getSpacingScale(screenSize: ScreenSize): number {\n return SPACING_SCALE_MAP[screenSize];\n}\n\n/**\n * Create CSS transition string for smooth resizing\n * \n * @param config - Transition configuration (optional)\n * @returns CSS transition string\n * \n * @example\n * ```typescript\n * createTransitionString();\n * // 'font-size 300ms ease-in-out, padding 300ms ease-in-out, ...'\n * \n * createTransitionString({ duration: '200ms', easing: 'linear' });\n * // 'font-size 200ms linear, padding 200ms linear, ...'\n * ```\n * \n * @public\n * @korean 전환문자열생성\n */\nexport function createTransitionString(\n config: Partial<ResizeTransitionConfig> = {}\n): string {\n const {\n duration = DEFAULT_RESIZE_TRANSITION.duration,\n easing = DEFAULT_RESIZE_TRANSITION.easing,\n properties = DEFAULT_RESIZE_TRANSITION.properties,\n enabled = DEFAULT_RESIZE_TRANSITION.enabled,\n } = config;\n\n if (!enabled) {\n return 'none';\n }\n\n return properties\n .map((prop) => `${prop} ${duration} ${easing}`)\n .join(', ');\n}\n\n/**\n * Create complete responsive scale configuration\n * \n * @param width - Viewport width\n * @param height - Viewport height\n * @returns Complete responsive configuration\n * \n * @example\n * ```typescript\n * const config = createResponsiveConfig(375, 667);\n * console.log(config.screenSize); // 'mobile'\n * console.log(config.fontScale); // 0.8\n * console.log(config.spacingScale); // 0.5\n * ```\n * \n * @public\n * @korean 반응형설정생성\n */\nexport function createResponsiveConfig(\n width: number,\n height: number\n): ResponsiveScaleConfig {\n const screenSize = getScreenSize(width);\n const fontScale = getFontScale(screenSize);\n const spacingScale = getSpacingScale(screenSize);\n\n return {\n screenSize,\n fontScale,\n spacingScale,\n viewport: { width, height },\n };\n}\n\n/**\n * Calculate all responsive values for a component\n * \n * Computes font sizes, spacing, and transitions based on screen size\n * Ready-to-use values for component styling\n * \n * @param width - Viewport width\n * @param baseFontSize - Base font size (default: 16px)\n * @param baseSpacing - Base spacing unit (default: 16px)\n * @returns Complete responsive values\n * \n * @example\n * ```typescript\n * const values = calculateResponsiveValues(375);\n * \n * <div style={{\n * fontSize: values.fontSize.body,\n * padding: values.spacing.md,\n * transition: values.transition,\n * }}>\n * Responsive content\n * </div>\n * ```\n * \n * @public\n * @korean 반응형값계산\n */\nexport function calculateResponsiveValues(\n width: number,\n baseFontSize: number = FONT_SIZE_CONSTRAINTS.BASE_SIZE,\n baseSpacing: number = 16\n): ResponsiveValues {\n const screenSize = getScreenSize(width);\n \n // Calculate font sizes for all text levels\n const fontSize = {\n small: calculateFontSize(baseFontSize * 0.75, screenSize, 12, 18),\n body: calculateFontSize(baseFontSize, screenSize),\n title: calculateFontSize(baseFontSize * 1.5, screenSize, 18, 28),\n hero: calculateFontSize(baseFontSize * 2.25, screenSize, 24, 36),\n hud: calculateFontSize(baseFontSize * 1.25, screenSize, 16, 24),\n };\n\n // Calculate spacing scale\n const spacing = {\n xs: calculateSpacing(baseSpacing * 0.5, screenSize),\n sm: calculateSpacing(baseSpacing * 0.75, screenSize),\n md: calculateSpacing(baseSpacing, screenSize),\n lg: calculateSpacing(baseSpacing * 1.5, screenSize),\n xl: calculateSpacing(baseSpacing * 2, screenSize),\n };\n\n // Create transition string\n const transition = createTransitionString();\n\n return {\n fontSize,\n spacing,\n transition,\n };\n}\n\n/**\n * Test screen size determination for validation\n * \n * Useful for testing and debugging responsive breakpoints.\n * Screen size is determined by width (breakpoints), but height is included\n * in the result for testing portrait/landscape orientations.\n * \n * @param width - Viewport width to test\n * @param height - Viewport height to test\n * @returns Test result with screen size and device type flags\n * \n * @example\n * ```typescript\n * const result = testScreenSize(768, 1024);\n * console.log(result.screenSize); // 'tablet'\n * console.log(result.isTablet); // true\n * console.log(result.isMobile); // false\n * ```\n * \n * @public\n * @korean 화면크기테스트\n */\nexport function testScreenSize(\n width: number,\n height: number\n): ScreenSizeTestResult {\n const screenSize = getScreenSize(width);\n const isLandscape = width > height;\n \n return {\n width,\n height,\n screenSize,\n isMobile: screenSize === 'mobile',\n isTablet: screenSize === 'tablet',\n isDesktop: screenSize === 'desktop' || screenSize === 'large' || screenSize === 'xlarge',\n // Include landscape in test result for completeness\n isLandscape,\n };\n}\n\n/**\n * Check if screen size is mobile\n * \n * @param width - Viewport width\n * @returns True if mobile screen size\n * \n * @public\n * @korean 모바일확인\n */\nexport function isMobileSize(width: number): boolean {\n return getScreenSize(width) === 'mobile';\n}\n\n/**\n * Check if screen size is tablet\n * \n * @param width - Viewport width\n * @returns True if tablet screen size\n * \n * @public\n * @korean 태블릿확인\n */\nexport function isTabletSize(width: number): boolean {\n return getScreenSize(width) === 'tablet';\n}\n\n/**\n * Check if screen size is desktop or larger\n * \n * @param width - Viewport width\n * @returns True if desktop, large, or xlarge screen size\n * \n * @public\n * @korean 데스크톱확인\n */\nexport function isDesktopSize(width: number): boolean {\n const size = getScreenSize(width);\n return size === 'desktop' || size === 'large' || size === 'xlarge';\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"ResponsiveScaling.js","names":[],"sources":["../../src/systems/ResponsiveScaling.ts"],"sourcesContent":["/**\n * Responsive Scaling System for Black Trigram\n * \n * Centralized scaling calculations for responsive layout across all screen sizes.\n * Implements proportional font and spacing scaling with smooth transitions.\n * \n * Features:\n * - Five screen size categories (mobile, tablet, desktop, large, xlarge)\n * - Font scaling: 0.8x (mobile) to 1.4x (4K displays)\n * - Spacing scaling: 0.5x (mobile) to 1.5x (4K displays)\n * - Korean text readability: 14-24px range enforced\n * - Smooth CSS transitions for resize operations\n * - 60fps performance maintained\n * \n * @module systems/ResponsiveScaling\n * @category Responsive Layout\n * @korean 반응형스케일시스템\n */\n\nimport type {\n ScreenSize,\n ResponsiveBreakpoints,\n FontScaleMap,\n SpacingScaleMap,\n ResponsiveScaleConfig,\n ResizeTransitionConfig,\n ResponsiveValues,\n ScreenSizeTestResult,\n} from '../types/ResponsiveTypes';\n\n// Re-export types for convenience\nexport type {\n ScreenSize,\n ResponsiveBreakpoints,\n FontScaleMap,\n SpacingScaleMap,\n ResponsiveScaleConfig,\n ResizeTransitionConfig,\n ResponsiveValues,\n ScreenSizeTestResult,\n};\n\n/**\n * Standard breakpoints for responsive design\n * Maps viewport widths to device categories\n * \n * Note: XLARGE is defined as a reference value (2560px) but xlarge category\n * starts at LARGE (1920px). This allows for consistent scaling across all\n * displays from 1920px upwards, including 2K, 4K, and ultra-wide monitors.\n * \n * @constant\n * @category Responsive Layout\n */\nexport const RESPONSIVE_BREAKPOINTS: ResponsiveBreakpoints = {\n MOBILE: 768, // < 768px: Mobile devices\n TABLET: 1024, // 768-1024px: Tablets\n DESKTOP: 1440, // 1024-1440px: Standard desktop\n LARGE: 1920, // 1440-1920px: HD/2K displays\n XLARGE: 2560, // Reference for 4K displays (xlarge starts at 1920px)\n} as const;\n\n/**\n * Font scaling multipliers by screen size\n * Base size (16px) * scale = final size\n * \n * Results clamped to 14-24px for readability\n * \n * @constant\n * @category Typography\n */\nexport const FONT_SCALE_MAP: FontScaleMap = {\n mobile: 0.8, // 16px * 0.8 = 12.8px → clamped to 14px min\n tablet: 0.9, // 16px * 0.9 = 14.4px\n desktop: 1.0, // 16px * 1.0 = 16px (base)\n large: 1.2, // 16px * 1.2 = 19.2px\n xlarge: 1.4, // 16px * 1.4 = 22.4px → clamped to 24px max\n} as const;\n\n/**\n * Spacing scaling multipliers by screen size\n * Base spacing * scale = final spacing\n * \n * @constant\n * @category Layout\n */\nexport const SPACING_SCALE_MAP: SpacingScaleMap = {\n mobile: 0.5, // Compact spacing for small screens\n tablet: 0.75, // Moderate spacing for tablets\n desktop: 1.0, // Standard reference spacing\n large: 1.25, // Spacious for large displays\n xlarge: 1.5, // Maximum spacing for 4K\n} as const;\n\n/**\n * Default transition configuration for smooth resizing\n * Optimized for 60fps performance\n * \n * @constant\n * @category Animation\n */\nexport const DEFAULT_RESIZE_TRANSITION: ResizeTransitionConfig = {\n duration: '300ms',\n easing: 'ease-in-out',\n properties: ['font-size', 'padding', 'margin', 'width', 'height'] as const,\n enabled: true,\n} as const;\n\n/**\n * Font size constraints for Korean and English text\n * Ensures readability across all screen sizes\n * \n * @constant\n * @category Typography\n */\nexport const FONT_SIZE_CONSTRAINTS = {\n /** Minimum body text size for readability */\n MIN_BODY_SIZE: 14,\n /** Maximum size before text becomes too large */\n MAX_SIZE: 24,\n /** Base reference size (desktop) */\n BASE_SIZE: 16,\n} as const;\n\n/**\n * Determine screen size category from viewport width\n * \n * Categories:\n * - mobile: < 768px (phones)\n * - tablet: 768-1024px (tablets)\n * - desktop: 1024-1440px (standard monitors)\n * - large: 1440-1920px (HD/2K displays)\n * - xlarge: ≥1920px (4K/ultra-wide)\n * \n * @param width - Viewport width in pixels\n * @returns Screen size category\n * \n * @example\n * ```typescript\n * getScreenSize(375); // 'mobile'\n * getScreenSize(768); // 'tablet'\n * getScreenSize(1920); // 'xlarge'\n * ```\n * \n * @public\n * @korean 화면크기얻기\n */\nexport function getScreenSize(width: number): ScreenSize {\n if (width < RESPONSIVE_BREAKPOINTS.MOBILE) return 'mobile';\n if (width < RESPONSIVE_BREAKPOINTS.TABLET) return 'tablet';\n if (width < RESPONSIVE_BREAKPOINTS.DESKTOP) return 'desktop';\n if (width < RESPONSIVE_BREAKPOINTS.LARGE) return 'large';\n return 'xlarge';\n}\n\n/**\n * Calculate scaled font size with readability constraints\n * \n * Formula: baseSize * scale\n * Clamped: max(minSize, min(maxSize, calculated))\n * \n * Ensures Korean and English text remain readable at all sizes\n * \n * @param baseSize - Base font size in pixels (typically 16px)\n * @param screenSize - Current screen size category\n * @param minSize - Minimum allowed size (default: 14px)\n * @param maxSize - Maximum allowed size (default: 24px)\n * @returns Calculated font size in pixels\n * \n * @example\n * ```typescript\n * calculateFontSize(16, 'mobile'); // 14 (clamped from 12.8)\n * calculateFontSize(16, 'desktop'); // 16\n * calculateFontSize(16, 'xlarge'); // 22.4\n * calculateFontSize(20, 'xlarge', 14, 24); // 24 (clamped from 28)\n * ```\n * \n * @public\n * @korean 글꼴크기계산\n */\nexport function calculateFontSize(\n baseSize: number,\n screenSize: ScreenSize,\n minSize: number = FONT_SIZE_CONSTRAINTS.MIN_BODY_SIZE,\n maxSize: number = FONT_SIZE_CONSTRAINTS.MAX_SIZE\n): number {\n const scale = FONT_SCALE_MAP[screenSize];\n const scaled = baseSize * scale;\n \n // Clamp to readable range\n return Math.max(minSize, Math.min(maxSize, scaled));\n}\n\n/**\n * Calculate scaled spacing value\n * \n * Formula: baseSpacing * scale\n * Rounded to nearest integer for crisp rendering\n * \n * @param baseSpacing - Base spacing value in pixels\n * @param screenSize - Current screen size category\n * @returns Calculated spacing in pixels (rounded)\n * \n * @example\n * ```typescript\n * calculateSpacing(20, 'mobile'); // 10 (20 * 0.5)\n * calculateSpacing(20, 'desktop'); // 20\n * calculateSpacing(20, 'xlarge'); // 30 (20 * 1.5)\n * ```\n * \n * @public\n * @korean 간격계산\n */\nexport function calculateSpacing(\n baseSpacing: number,\n screenSize: ScreenSize\n): number {\n const scale = SPACING_SCALE_MAP[screenSize];\n return Math.round(baseSpacing * scale);\n}\n\n/**\n * Get font scale multiplier for screen size\n * \n * @param screenSize - Screen size category\n * @returns Scale multiplier (0.8 - 1.4)\n * \n * @example\n * ```typescript\n * getFontScale('mobile'); // 0.8\n * getFontScale('desktop'); // 1.0\n * getFontScale('xlarge'); // 1.4\n * ```\n * \n * @public\n * @korean 글꼴스케일얻기\n */\nexport function getFontScale(screenSize: ScreenSize): number {\n return FONT_SCALE_MAP[screenSize];\n}\n\n/**\n * Get spacing scale multiplier for screen size\n * \n * @param screenSize - Screen size category\n * @returns Scale multiplier (0.5 - 1.5)\n * \n * @example\n * ```typescript\n * getSpacingScale('mobile'); // 0.5\n * getSpacingScale('desktop'); // 1.0\n * getSpacingScale('xlarge'); // 1.5\n * ```\n * \n * @public\n * @korean 간격스케일얻기\n */\nexport function getSpacingScale(screenSize: ScreenSize): number {\n return SPACING_SCALE_MAP[screenSize];\n}\n\n/**\n * Create CSS transition string for smooth resizing\n * \n * @param config - Transition configuration (optional)\n * @returns CSS transition string\n * \n * @example\n * ```typescript\n * createTransitionString();\n * // 'font-size 300ms ease-in-out, padding 300ms ease-in-out, ...'\n * \n * createTransitionString({ duration: '200ms', easing: 'linear' });\n * // 'font-size 200ms linear, padding 200ms linear, ...'\n * ```\n * \n * @public\n * @korean 전환문자열생성\n */\nexport function createTransitionString(\n config: Partial<ResizeTransitionConfig> = {}\n): string {\n const {\n duration = DEFAULT_RESIZE_TRANSITION.duration,\n easing = DEFAULT_RESIZE_TRANSITION.easing,\n properties = DEFAULT_RESIZE_TRANSITION.properties,\n enabled = DEFAULT_RESIZE_TRANSITION.enabled,\n } = config;\n\n if (!enabled) {\n return 'none';\n }\n\n return properties\n .map((prop) => `${prop} ${duration} ${easing}`)\n .join(', ');\n}\n\n/**\n * Create complete responsive scale configuration\n * \n * @param width - Viewport width\n * @param height - Viewport height\n * @returns Complete responsive configuration\n * \n * @example\n * ```typescript\n * const config = createResponsiveConfig(375, 667);\n * console.log(config.screenSize); // 'mobile'\n * console.log(config.fontScale); // 0.8\n * console.log(config.spacingScale); // 0.5\n * ```\n * \n * @public\n * @korean 반응형설정생성\n */\nexport function createResponsiveConfig(\n width: number,\n height: number\n): ResponsiveScaleConfig {\n const screenSize = getScreenSize(width);\n const fontScale = getFontScale(screenSize);\n const spacingScale = getSpacingScale(screenSize);\n\n return {\n screenSize,\n fontScale,\n spacingScale,\n viewport: { width, height },\n };\n}\n\n/**\n * Calculate all responsive values for a component\n * \n * Computes font sizes, spacing, and transitions based on screen size\n * Ready-to-use values for component styling\n * \n * @param width - Viewport width\n * @param baseFontSize - Base font size (default: 16px)\n * @param baseSpacing - Base spacing unit (default: 16px)\n * @returns Complete responsive values\n * \n * @example\n * ```typescript\n * const values = calculateResponsiveValues(375);\n * \n * <div style={{\n * fontSize: values.fontSize.body,\n * padding: values.spacing.md,\n * transition: values.transition,\n * }}>\n * Responsive content\n * </div>\n * ```\n * \n * @public\n * @korean 반응형값계산\n */\nexport function calculateResponsiveValues(\n width: number,\n baseFontSize: number = FONT_SIZE_CONSTRAINTS.BASE_SIZE,\n baseSpacing: number = 16\n): ResponsiveValues {\n const screenSize = getScreenSize(width);\n \n // Calculate font sizes for all text levels\n const fontSize = {\n small: calculateFontSize(baseFontSize * 0.75, screenSize, 12, 18),\n body: calculateFontSize(baseFontSize, screenSize),\n title: calculateFontSize(baseFontSize * 1.5, screenSize, 18, 28),\n hero: calculateFontSize(baseFontSize * 2.25, screenSize, 24, 36),\n hud: calculateFontSize(baseFontSize * 1.25, screenSize, 16, 24),\n };\n\n // Calculate spacing scale\n const spacing = {\n xs: calculateSpacing(baseSpacing * 0.5, screenSize),\n sm: calculateSpacing(baseSpacing * 0.75, screenSize),\n md: calculateSpacing(baseSpacing, screenSize),\n lg: calculateSpacing(baseSpacing * 1.5, screenSize),\n xl: calculateSpacing(baseSpacing * 2, screenSize),\n };\n\n // Create transition string\n const transition = createTransitionString();\n\n return {\n fontSize,\n spacing,\n transition,\n };\n}\n\n/**\n * Test screen size determination for validation\n * \n * Useful for testing and debugging responsive breakpoints.\n * Screen size is determined by width (breakpoints), but height is included\n * in the result for testing portrait/landscape orientations.\n * \n * @param width - Viewport width to test\n * @param height - Viewport height to test\n * @returns Test result with screen size and device type flags\n * \n * @example\n * ```typescript\n * const result = testScreenSize(768, 1024);\n * console.log(result.screenSize); // 'tablet'\n * console.log(result.isTablet); // true\n * console.log(result.isMobile); // false\n * ```\n * \n * @public\n * @korean 화면크기테스트\n */\nexport function testScreenSize(\n width: number,\n height: number\n): ScreenSizeTestResult {\n const screenSize = getScreenSize(width);\n const isLandscape = width > height;\n \n return {\n width,\n height,\n screenSize,\n isMobile: screenSize === 'mobile',\n isTablet: screenSize === 'tablet',\n isDesktop: screenSize === 'desktop' || screenSize === 'large' || screenSize === 'xlarge',\n // Include landscape in test result for completeness\n isLandscape,\n };\n}\n\n/**\n * Check if screen size is mobile\n * \n * @param width - Viewport width\n * @returns True if mobile screen size\n * \n * @public\n * @korean 모바일확인\n */\nexport function isMobileSize(width: number): boolean {\n return getScreenSize(width) === 'mobile';\n}\n\n/**\n * Check if screen size is tablet\n * \n * @param width - Viewport width\n * @returns True if tablet screen size\n * \n * @public\n * @korean 태블릿확인\n */\nexport function isTabletSize(width: number): boolean {\n return getScreenSize(width) === 'tablet';\n}\n\n/**\n * Check if screen size is desktop or larger\n * \n * @param width - Viewport width\n * @returns True if desktop, large, or xlarge screen size\n * \n * @public\n * @korean 데스크톱확인\n */\nexport function isDesktopSize(width: number): boolean {\n const size = getScreenSize(width);\n return size === 'desktop' || size === 'large' || size === 'xlarge';\n}\n"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"TrigramSystem.js","names":[],"sources":["../../src/systems/TrigramSystem.ts"],"sourcesContent":["import { KoreanText, TrigramStance } from \"../types/common\";\nimport { PlayerState } from \"./player\";\n\nimport { TRIGRAM_STANCES_ORDER, TrigramTransitionCost } from \"./trigram\";\nimport { TrigramCalculator } from \"./trigram/TrigramCalculator\";\nimport { PLAYER_ARCHETYPES_DATA } from \"./types\";\n\n/**\n * Stance counter relationships based on I Ching philosophy\n * \n * Each stance has a counter stance that provides tactical advantage:\n * - **GEON (Heaven)** countered by **GAM (Water)** - Water flows around Heaven's force\n * - **TAE (Lake)** countered by **GON (Earth)** - Earth contains and grounds Lake\n * - **LI (Fire)** countered by **SON (Wind)** - Wind disperses Fire's intensity\n * - **JIN (Thunder)** countered by **GAN (Mountain)** - Mountain absorbs Thunder's impact\n * - **SON (Wind)** countered by **GEON (Heaven)** - Heaven's force overpowers Wind\n * - **GAM (Water)** countered by **TAE (Lake)** - Lake contains and channels Water\n * - **GAN (Mountain)** countered by **LI (Fire)** - Fire melts Mountain's solidity\n * - **GON (Earth)** countered by **JIN (Thunder)** - Thunder breaks Earth's stability\n * \n * Using a counter stance provides a 1.2x damage multiplier in combat.\n * \n * @korean 팔괘 상극 관계 (Eight Trigram Counter Relationships)\n */\nexport const STANCE_COUNTERS: Record<TrigramStance, TrigramStance> = {\n [TrigramStance.GEON]: TrigramStance.GAM, // Water flows around Heaven\n [TrigramStance.TAE]: TrigramStance.GON, // Earth grounds Lake\n [TrigramStance.LI]: TrigramStance.SON, // Wind disperses Fire\n [TrigramStance.JIN]: TrigramStance.GAN, // Mountain absorbs Thunder\n [TrigramStance.SON]: TrigramStance.GEON, // Heaven overpowers Wind\n [TrigramStance.GAM]: TrigramStance.TAE, // Lake contains Water\n [TrigramStance.GAN]: TrigramStance.LI, // Fire melts Mountain\n [TrigramStance.GON]: TrigramStance.JIN, // Thunder breaks Earth\n};\n\n/**\n * Counter stance damage multiplier\n * Applied when using a counter stance against opponent's stance\n * \n * @korean 상극 자세 피해 배율\n */\nexport const COUNTER_STANCE_DAMAGE_MULTIPLIER = 1.2;\n\n/**\n * Apply counter stance damage bonus when appropriate.\n *\n * This helper should be used by combat damage calculation code after it has\n * determined whether the current stance matchup is a counter stance\n * (for example, via an `isCounterStance` check elsewhere in the system).\n *\n * When `isCounterStance` is `true`, the base damage is multiplied by\n * {@link COUNTER_STANCE_DAMAGE_MULTIPLIER}. Non-positive damage values are\n * returned unchanged to avoid introducing invalid negative or zero scaling.\n *\n * @param baseDamage - The pre-modifier damage value.\n * @param isCounterStance - Whether the attacker is using a counter stance.\n * @returns The adjusted damage value with counter stance bonus applied when relevant.\n *\n * @example\n * ```ts\n * const isCounter = trigramSystem.isCounterStance(attackerStance, defenderStance);\n * const finalDamage = applyCounterStanceDamage(baseDamage, isCounter);\n * ```\n *\n * @korean\n * 반격 자세(상극 자세)일 때만 피해 배율(1.2배)을 적용합니다.\n */\nexport function applyCounterStanceDamage(\n baseDamage: number,\n isCounterStance: boolean\n): number {\n if (!isCounterStance || baseDamage <= 0) {\n return baseDamage;\n }\n\n return baseDamage * COUNTER_STANCE_DAMAGE_MULTIPLIER;\n}\n\n/**\n * System for managing Eight Trigram (팔괘) stance transitions and combat calculations.\n *\n * **Korean**: 팔괘 시스템 (Eight Trigram System)\n *\n * The TrigramSystem implements the core mechanics of the Eight Trigram martial arts system,\n * managing stance transitions, calculating effectiveness, and determining resource costs.\n * Based on I Ching (易經) philosophy adapted for tactical combat.\n *\n * ## Key Responsibilities\n *\n * - Validate stance transitions based on Ki and Stamina costs\n * - Calculate transition difficulty between stances\n * - Recommend optimal stance choices\n * - Determine stance effectiveness in combat matchups\n * - Apply archetype-specific modifiers to transitions\n *\n * @example\n * ```typescript\n * const trigramSystem = new TrigramSystem();\n *\n * // Check if transition is possible\n * const canTransition = trigramSystem.canTransitionTo(\n * TrigramStance.GEON,\n * TrigramStance.GAM,\n * playerState\n * );\n *\n * // Get recommended stance\n * const recommendedStance = trigramSystem.recommendStance(playerState);\n * ```\n *\n * @public\n * @category Trigram System\n * @korean 팔괘시스템\n */\nexport class TrigramSystem {\n private calculator: TrigramCalculator;\n\n /**\n * Creates a new TrigramSystem instance.\n *\n * Initializes the internal calculator for stance effectiveness and transition difficulty.\n */\n constructor() {\n this.calculator = new TrigramCalculator();\n }\n\n /**\n * Gets the defensive/offensive characteristic of a stance.\n *\n * **Korean**: 자세 특성 조회 (Stance Characteristic Query)\n *\n * Returns whether a stance is primarily defensive, offensive, or balanced.\n * This information is useful for UI display and tactical decision-making.\n *\n * ## Stance Classifications\n *\n * - **Defensive**: 간 (GAN/Mountain), 곤 (GON/Earth) - Protect vital areas\n * - **Offensive**: 건 (GEON/Heaven), 진 (JIN/Thunder) - Expose for power\n * - **Balanced**: 태 (TAE/Lake), 리 (LI/Fire), 손 (SON/Wind), 감 (GAM/Water)\n *\n * @param stance - Trigram stance to query\n * @returns \"defensive\", \"offensive\", or \"balanced\"\n *\n * @example\n * ```typescript\n * const characteristic = trigramSystem.getStanceCharacteristic(TrigramStance.GAN);\n * console.log(characteristic); // \"defensive\"\n *\n * const offensive = trigramSystem.getStanceCharacteristic(TrigramStance.GEON);\n * console.log(offensive); // \"offensive\"\n * ```\n *\n * @public\n * @korean 자세특성조회\n */\n getStanceCharacteristic(\n stance: TrigramStance\n ): \"defensive\" | \"offensive\" | \"balanced\" {\n switch (stance) {\n case TrigramStance.GAN: // Mountain - Immovable defense\n case TrigramStance.GON: // Earth - Grounding and stability\n return \"defensive\";\n\n case TrigramStance.GEON: // Heaven - Direct force and aggression\n case TrigramStance.JIN: // Thunder - Explosive power\n return \"offensive\";\n\n default:\n return \"balanced\";\n }\n }\n\n /**\n * Checks if a stance provides defensive advantages.\n *\n * **Korean**: 방어 자세 확인 (Check Defensive Stance)\n *\n * @param stance - Trigram stance to check\n * @returns true if stance is defensive, false otherwise\n *\n * @example\n * ```typescript\n * if (trigramSystem.isDefensiveStance(player.currentStance)) {\n * console.log(\"Player is in defensive posture\");\n * }\n * ```\n *\n * @public\n * @korean 방어자세확인\n */\n isDefensiveStance(stance: TrigramStance): boolean {\n return this.getStanceCharacteristic(stance) === \"defensive\";\n }\n\n /**\n * Checks if a stance provides offensive advantages.\n *\n * **Korean**: 공격 자세 확인 (Check Offensive Stance)\n *\n * @param stance - Trigram stance to check\n * @returns true if stance is offensive, false otherwise\n *\n * @example\n * ```typescript\n * if (trigramSystem.isOffensiveStance(player.currentStance)) {\n * console.log(\"Player is in offensive posture\");\n * }\n * ```\n *\n * @public\n * @korean 공격자세확인\n */\n isOffensiveStance(stance: TrigramStance): boolean {\n return this.getStanceCharacteristic(stance) === \"offensive\";\n }\n\n /**\n * Checks if a player can transition from one stance to another.\n *\n * Validates that the player has sufficient Ki (氣) and Stamina resources\n * to perform the stance transition. Same-stance transitions are always valid.\n *\n * @param fromStance - Current stance\n * @param toStance - Target stance\n * @param player - Player state with current Ki and Stamina\n * @returns true if transition is possible, false otherwise\n *\n * @example\n * ```typescript\n * const canChange = trigramSystem.canTransitionTo(\n * TrigramStance.GEON, // From Heaven\n * TrigramStance.GON, // To Earth\n * player\n * );\n * ```\n *\n * @public\n * @korean 자세전환가능확인\n */\n canTransitionTo(\n fromStance: TrigramStance,\n toStance: TrigramStance,\n player: PlayerState\n ): boolean {\n if (fromStance === toStance) return true;\n\n const cost = this.getTransitionCost(fromStance, toStance, player);\n\n // Check if player has sufficient resources\n const hasEnoughKi = player.ki >= cost.ki;\n const hasEnoughStamina = player.stamina >= cost.stamina;\n\n return hasEnoughKi && hasEnoughStamina;\n }\n\n /**\n * Recommends the optimal stance for current combat situation.\n *\n * Calculates the least-cost stance transition from the player's current position.\n * Uses combined cost of Ki, Stamina, and transition time to determine best option.\n *\n * **Algorithm**: Evaluates all eight stances and selects the one with minimum\n * total cost (Ki + Stamina + Time).\n *\n * @param player - Player state with current stance\n * @returns Recommended stance to transition to\n *\n * @example\n * ```typescript\n * const recommended = trigramSystem.recommendStance(player);\n * console.log(`Consider switching to ${recommended}`);\n * ```\n *\n * @public\n * @korean 최적자세추천\n */\n recommendStance(player: PlayerState): TrigramStance {\n const from = player.currentStance;\n let best = from;\n let bestScore = Infinity;\n\n for (const to of TRIGRAM_STANCES_ORDER) {\n const costObj: TrigramTransitionCost = this.getTransitionCost(from, to);\n const score = costObj.ki + costObj.stamina + costObj.timeMilliseconds;\n if (score < bestScore) {\n bestScore = score;\n best = to;\n }\n }\n\n return best;\n }\n\n /**\n * Calculates the resource cost for transitioning between stances.\n *\n * Determines Ki, Stamina, and time costs based on the I Ching philosophical\n * distance between trigrams. Applies archetype-specific modifiers for favored stances.\n *\n * ## Cost Calculation\n *\n * - **Base Cost**: 10 Ki, 15 Stamina per difficulty point\n * - **Base Time**: 500ms per difficulty point\n * - **Archetype Modifier**: 0.8x for favored stances, 1.0x otherwise\n * - **Same Stance**: Zero cost\n *\n * @param from - Starting stance\n * @param to - Target stance\n * @param player - Optional player for archetype modifiers\n * @returns Transition cost breakdown\n *\n * @example\n * ```typescript\n * const cost = trigramSystem.getTransitionCost(\n * TrigramStance.GEON,\n * TrigramStance.TAE,\n * player\n * );\n * console.log(`Cost: ${cost.ki} Ki, ${cost.stamina} Stamina`);\n * ```\n *\n * @public\n * @korean 자세전환비용\n */\n public getTransitionCost(\n from: TrigramStance,\n to: TrigramStance,\n player?: PlayerState\n ): TrigramTransitionCost {\n if (from === to) {\n return {\n ki: 0,\n stamina: 0,\n timeMilliseconds: 0, // neutral\n };\n }\n\n const difficulty = TrigramCalculator.calculateTransitionDifficulty(\n from,\n to\n );\n const baseCost = 10;\n const baseTime = 500;\n\n let ki = Math.ceil(baseCost * difficulty);\n let stamina = Math.ceil(baseCost * difficulty * 1.5);\n\n // apply archetype stance‐change cost modifier if player provided\n if (player) {\n const archData = PLAYER_ARCHETYPES_DATA[player.archetype];\n const favs = archData.favoredStances || [];\n const mod = favs.includes(to) ? 0.8 : 1.0;\n ki = Math.ceil(ki * mod);\n stamina = Math.ceil(stamina * mod);\n }\n\n return {\n ki,\n stamina,\n timeMilliseconds: Math.ceil(baseTime * difficulty),\n };\n }\n\n /**\n * Calculates stance effectiveness in combat matchup.\n *\n * Determines the multiplier advantage/disadvantage when one stance attacks another.\n * Based on I Ching elemental relationships (e.g., Water extinguishes Fire).\n *\n * @param attackerStance - Attacking player's stance\n * @param defenderStance - Defending player's stance\n * @returns Effectiveness multiplier (0.5 = disadvantage, 1.0 = neutral, 1.5 = advantage)\n *\n * @example\n * ```typescript\n * const effectiveness = trigramSystem.calculateStanceEffectiveness(\n * TrigramStance.GAM, // Water\n * TrigramStance.LI // Fire\n * ); // Returns > 1.0 (Water beats Fire)\n * ```\n *\n * @public\n * @korean 자세효과성계산\n */\n calculateStanceEffectiveness(\n attackerStance: TrigramStance,\n defenderStance: TrigramStance\n ): number {\n return this.calculator.calculateStanceEffectiveness(\n attackerStance,\n defenderStance\n );\n }\n\n /**\n * Gets bilingual name for a stance.\n *\n * Returns Korean (Hangul) and English names for display purposes.\n *\n * @param stance - Stance to get name for\n * @returns Object with korean and english name properties\n *\n * @example\n * ```typescript\n * const name = trigramSystem.getStanceName(TrigramStance.GEON);\n * console.log(`${name.korean} (${name.english})`); // \"건 (Heaven)\"\n * ```\n *\n * @public\n * @korean 자세이름조회\n */\n getStanceName(stance: TrigramStance): { korean: string; english: string } {\n const stanceNames = {\n [TrigramStance.GEON]: { korean: \"건\", english: \"Heaven\" },\n [TrigramStance.TAE]: { korean: \"태\", english: \"Lake\" },\n [TrigramStance.LI]: { korean: \"리\", english: \"Fire\" },\n [TrigramStance.JIN]: { korean: \"진\", english: \"Thunder\" },\n [TrigramStance.SON]: { korean: \"손\", english: \"Wind\" },\n [TrigramStance.GAM]: { korean: \"감\", english: \"Water\" },\n [TrigramStance.GAN]: { korean: \"간\", english: \"Mountain\" },\n [TrigramStance.GON]: { korean: \"곤\", english: \"Earth\" },\n };\n\n return stanceNames[stance] || { korean: \"Unknown\", english: \"Unknown\" };\n }\n\n /**\n * Gets the counter stance for opponent's current stance.\n * \n * Returns the stance that provides tactical advantage against the opponent's stance,\n * based on I Ching elemental relationships. Using a counter stance provides a 1.2x\n * damage multiplier in combat.\n * \n * **Korean Philosophy (상극 자세)**:\n * - Water counters Heaven (flows around force)\n * - Earth counters Lake (grounds and contains)\n * - Wind counters Fire (disperses intensity)\n * - Mountain counters Thunder (absorbs impact)\n * \n * @param opponentStance - Opponent's current stance\n * @returns Counter stance that provides advantage\n * \n * @example\n * ```typescript\n * const counterStance = trigramSystem.getCounterStance(TrigramStance.GEON);\n * console.log(counterStance); // TrigramStance.GAM (Water counters Heaven)\n * ```\n * \n * @public\n * @korean 상극자세조회\n */\n getCounterStance(opponentStance: TrigramStance): TrigramStance {\n return STANCE_COUNTERS[opponentStance];\n }\n\n /**\n * Checks if player's stance counters opponent's stance.\n * \n * Determines if the player has a tactical advantage through stance matchup.\n * When true, player should receive a 1.2x damage multiplier for attacks.\n * \n * @param myStance - Player's current stance\n * @param opponentStance - Opponent's current stance\n * @returns True if player's stance counters opponent's stance\n * \n * @example\n * ```typescript\n * const hasAdvantage = trigramSystem.isCounterStance(\n * TrigramStance.GAM, // My stance: Water\n * TrigramStance.GEON // Opponent: Heaven\n * ); // Returns true - Water counters Heaven\n * \n * if (hasAdvantage) {\n * damage *= 1.2; // Apply counter bonus\n * }\n * ```\n * \n * @public\n * @korean 상극자세확인\n */\n isCounterStance(myStance: TrigramStance, opponentStance: TrigramStance): boolean {\n return this.getCounterStance(opponentStance) === myStance;\n }\n\n /**\n * Gets complete stance data for UI display.\n *\n * Returns structured data object containing stance ID and bilingual names.\n *\n * @param stance - Stance to get data for\n * @returns Stance data object\n *\n * @public\n * @korean 자세데이터조회\n */\n getCurrentStanceData(stance: TrigramStance): {\n id: TrigramStance;\n name: KoreanText;\n korean: string;\n english: string;\n } {\n const stanceName = this.getStanceName(stance);\n return {\n id: stance,\n name: stanceName,\n korean: stanceName.korean,\n english: stanceName.english,\n };\n }\n\n /**\n * Validates a stance transition with detailed feedback.\n *\n * Checks if transition is valid and provides reason if not.\n * More detailed than {@link canTransitionTo}, includes specific failure reasons.\n *\n * @param fromStance - Current stance\n * @param toStance - Target stance\n * @param player - Player state\n * @returns Validation result with optional failure reason\n *\n * @example\n * ```typescript\n * const validation = trigramSystem.validateTransition(\n * TrigramStance.GEON,\n * TrigramStance.GON,\n * player\n * );\n * if (!validation.valid) {\n * console.error(validation.reason);\n * }\n * ```\n *\n * @public\n * @korean 자세전환검증\n */\n validateTransition(\n fromStance: TrigramStance,\n toStance: TrigramStance,\n player: PlayerState\n ): { valid: boolean; reason?: string } {\n if (fromStance === toStance) {\n return { valid: true };\n }\n\n const cost = this.getTransitionCost(fromStance, toStance, player);\n\n if (player.ki < cost.ki) {\n return {\n valid: false,\n reason: `Insufficient Ki: need ${cost.ki}, have ${player.ki}`,\n };\n }\n\n if (player.stamina < cost.stamina) {\n return {\n valid: false,\n reason: `Insufficient Stamina: need ${cost.stamina}, have ${player.stamina}`,\n };\n }\n\n return { valid: true };\n }\n}\n\nexport default TrigramSystem;\n"],"mappings":"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"}
1
+ {"version":3,"file":"TrigramSystem.js","names":[],"sources":["../../src/systems/TrigramSystem.ts"],"sourcesContent":["import { KoreanText, TrigramStance } from \"../types/common\";\nimport { PlayerState } from \"./player\";\n\nimport { TRIGRAM_STANCES_ORDER, TrigramTransitionCost } from \"./trigram\";\nimport { TrigramCalculator } from \"./trigram/TrigramCalculator\";\nimport { PLAYER_ARCHETYPES_DATA } from \"./types\";\n\n/**\n * Stance counter relationships based on I Ching philosophy\n * \n * Each stance has a counter stance that provides tactical advantage:\n * - **GEON (Heaven)** countered by **GAM (Water)** - Water flows around Heaven's force\n * - **TAE (Lake)** countered by **GON (Earth)** - Earth contains and grounds Lake\n * - **LI (Fire)** countered by **SON (Wind)** - Wind disperses Fire's intensity\n * - **JIN (Thunder)** countered by **GAN (Mountain)** - Mountain absorbs Thunder's impact\n * - **SON (Wind)** countered by **GEON (Heaven)** - Heaven's force overpowers Wind\n * - **GAM (Water)** countered by **TAE (Lake)** - Lake contains and channels Water\n * - **GAN (Mountain)** countered by **LI (Fire)** - Fire melts Mountain's solidity\n * - **GON (Earth)** countered by **JIN (Thunder)** - Thunder breaks Earth's stability\n * \n * Using a counter stance provides a 1.2x damage multiplier in combat.\n * \n * @korean 팔괘 상극 관계 (Eight Trigram Counter Relationships)\n */\nexport const STANCE_COUNTERS: Record<TrigramStance, TrigramStance> = {\n [TrigramStance.GEON]: TrigramStance.GAM, // Water flows around Heaven\n [TrigramStance.TAE]: TrigramStance.GON, // Earth grounds Lake\n [TrigramStance.LI]: TrigramStance.SON, // Wind disperses Fire\n [TrigramStance.JIN]: TrigramStance.GAN, // Mountain absorbs Thunder\n [TrigramStance.SON]: TrigramStance.GEON, // Heaven overpowers Wind\n [TrigramStance.GAM]: TrigramStance.TAE, // Lake contains Water\n [TrigramStance.GAN]: TrigramStance.LI, // Fire melts Mountain\n [TrigramStance.GON]: TrigramStance.JIN, // Thunder breaks Earth\n};\n\n/**\n * Counter stance damage multiplier\n * Applied when using a counter stance against opponent's stance\n * \n * @korean 상극 자세 피해 배율\n */\nexport const COUNTER_STANCE_DAMAGE_MULTIPLIER = 1.2;\n\n/**\n * Apply counter stance damage bonus when appropriate.\n *\n * This helper should be used by combat damage calculation code after it has\n * determined whether the current stance matchup is a counter stance\n * (for example, via an `isCounterStance` check elsewhere in the system).\n *\n * When `isCounterStance` is `true`, the base damage is multiplied by\n * {@link COUNTER_STANCE_DAMAGE_MULTIPLIER}. Non-positive damage values are\n * returned unchanged to avoid introducing invalid negative or zero scaling.\n *\n * @param baseDamage - The pre-modifier damage value.\n * @param isCounterStance - Whether the attacker is using a counter stance.\n * @returns The adjusted damage value with counter stance bonus applied when relevant.\n *\n * @example\n * ```ts\n * const isCounter = trigramSystem.isCounterStance(attackerStance, defenderStance);\n * const finalDamage = applyCounterStanceDamage(baseDamage, isCounter);\n * ```\n *\n * @korean\n * 반격 자세(상극 자세)일 때만 피해 배율(1.2배)을 적용합니다.\n */\nexport function applyCounterStanceDamage(\n baseDamage: number,\n isCounterStance: boolean\n): number {\n if (!isCounterStance || baseDamage <= 0) {\n return baseDamage;\n }\n\n return baseDamage * COUNTER_STANCE_DAMAGE_MULTIPLIER;\n}\n\n/**\n * System for managing Eight Trigram (팔괘) stance transitions and combat calculations.\n *\n * **Korean**: 팔괘 시스템 (Eight Trigram System)\n *\n * The TrigramSystem implements the core mechanics of the Eight Trigram martial arts system,\n * managing stance transitions, calculating effectiveness, and determining resource costs.\n * Based on I Ching (易經) philosophy adapted for tactical combat.\n *\n * ## Key Responsibilities\n *\n * - Validate stance transitions based on Ki and Stamina costs\n * - Calculate transition difficulty between stances\n * - Recommend optimal stance choices\n * - Determine stance effectiveness in combat matchups\n * - Apply archetype-specific modifiers to transitions\n *\n * @example\n * ```typescript\n * const trigramSystem = new TrigramSystem();\n *\n * // Check if transition is possible\n * const canTransition = trigramSystem.canTransitionTo(\n * TrigramStance.GEON,\n * TrigramStance.GAM,\n * playerState\n * );\n *\n * // Get recommended stance\n * const recommendedStance = trigramSystem.recommendStance(playerState);\n * ```\n *\n * @public\n * @category Trigram System\n * @korean 팔괘시스템\n */\nexport class TrigramSystem {\n private calculator: TrigramCalculator;\n\n /**\n * Creates a new TrigramSystem instance.\n *\n * Initializes the internal calculator for stance effectiveness and transition difficulty.\n */\n constructor() {\n this.calculator = new TrigramCalculator();\n }\n\n /**\n * Gets the defensive/offensive characteristic of a stance.\n *\n * **Korean**: 자세 특성 조회 (Stance Characteristic Query)\n *\n * Returns whether a stance is primarily defensive, offensive, or balanced.\n * This information is useful for UI display and tactical decision-making.\n *\n * ## Stance Classifications\n *\n * - **Defensive**: 간 (GAN/Mountain), 곤 (GON/Earth) - Protect vital areas\n * - **Offensive**: 건 (GEON/Heaven), 진 (JIN/Thunder) - Expose for power\n * - **Balanced**: 태 (TAE/Lake), 리 (LI/Fire), 손 (SON/Wind), 감 (GAM/Water)\n *\n * @param stance - Trigram stance to query\n * @returns \"defensive\", \"offensive\", or \"balanced\"\n *\n * @example\n * ```typescript\n * const characteristic = trigramSystem.getStanceCharacteristic(TrigramStance.GAN);\n * console.log(characteristic); // \"defensive\"\n *\n * const offensive = trigramSystem.getStanceCharacteristic(TrigramStance.GEON);\n * console.log(offensive); // \"offensive\"\n * ```\n *\n * @public\n * @korean 자세특성조회\n */\n getStanceCharacteristic(\n stance: TrigramStance\n ): \"defensive\" | \"offensive\" | \"balanced\" {\n switch (stance) {\n case TrigramStance.GAN: // Mountain - Immovable defense\n case TrigramStance.GON: // Earth - Grounding and stability\n return \"defensive\";\n\n case TrigramStance.GEON: // Heaven - Direct force and aggression\n case TrigramStance.JIN: // Thunder - Explosive power\n return \"offensive\";\n\n default:\n return \"balanced\";\n }\n }\n\n /**\n * Checks if a stance provides defensive advantages.\n *\n * **Korean**: 방어 자세 확인 (Check Defensive Stance)\n *\n * @param stance - Trigram stance to check\n * @returns true if stance is defensive, false otherwise\n *\n * @example\n * ```typescript\n * if (trigramSystem.isDefensiveStance(player.currentStance)) {\n * console.log(\"Player is in defensive posture\");\n * }\n * ```\n *\n * @public\n * @korean 방어자세확인\n */\n isDefensiveStance(stance: TrigramStance): boolean {\n return this.getStanceCharacteristic(stance) === \"defensive\";\n }\n\n /**\n * Checks if a stance provides offensive advantages.\n *\n * **Korean**: 공격 자세 확인 (Check Offensive Stance)\n *\n * @param stance - Trigram stance to check\n * @returns true if stance is offensive, false otherwise\n *\n * @example\n * ```typescript\n * if (trigramSystem.isOffensiveStance(player.currentStance)) {\n * console.log(\"Player is in offensive posture\");\n * }\n * ```\n *\n * @public\n * @korean 공격자세확인\n */\n isOffensiveStance(stance: TrigramStance): boolean {\n return this.getStanceCharacteristic(stance) === \"offensive\";\n }\n\n /**\n * Checks if a player can transition from one stance to another.\n *\n * Validates that the player has sufficient Ki (氣) and Stamina resources\n * to perform the stance transition. Same-stance transitions are always valid.\n *\n * @param fromStance - Current stance\n * @param toStance - Target stance\n * @param player - Player state with current Ki and Stamina\n * @returns true if transition is possible, false otherwise\n *\n * @example\n * ```typescript\n * const canChange = trigramSystem.canTransitionTo(\n * TrigramStance.GEON, // From Heaven\n * TrigramStance.GON, // To Earth\n * player\n * );\n * ```\n *\n * @public\n * @korean 자세전환가능확인\n */\n canTransitionTo(\n fromStance: TrigramStance,\n toStance: TrigramStance,\n player: PlayerState\n ): boolean {\n if (fromStance === toStance) return true;\n\n const cost = this.getTransitionCost(fromStance, toStance, player);\n\n // Check if player has sufficient resources\n const hasEnoughKi = player.ki >= cost.ki;\n const hasEnoughStamina = player.stamina >= cost.stamina;\n\n return hasEnoughKi && hasEnoughStamina;\n }\n\n /**\n * Recommends the optimal stance for current combat situation.\n *\n * Calculates the least-cost stance transition from the player's current position.\n * Uses combined cost of Ki, Stamina, and transition time to determine best option.\n *\n * **Algorithm**: Evaluates all eight stances and selects the one with minimum\n * total cost (Ki + Stamina + Time).\n *\n * @param player - Player state with current stance\n * @returns Recommended stance to transition to\n *\n * @example\n * ```typescript\n * const recommended = trigramSystem.recommendStance(player);\n * console.log(`Consider switching to ${recommended}`);\n * ```\n *\n * @public\n * @korean 최적자세추천\n */\n recommendStance(player: PlayerState): TrigramStance {\n const from = player.currentStance;\n let best = from;\n let bestScore = Infinity;\n\n for (const to of TRIGRAM_STANCES_ORDER) {\n const costObj: TrigramTransitionCost = this.getTransitionCost(from, to);\n const score = costObj.ki + costObj.stamina + costObj.timeMilliseconds;\n if (score < bestScore) {\n bestScore = score;\n best = to;\n }\n }\n\n return best;\n }\n\n /**\n * Calculates the resource cost for transitioning between stances.\n *\n * Determines Ki, Stamina, and time costs based on the I Ching philosophical\n * distance between trigrams. Applies archetype-specific modifiers for favored stances.\n *\n * ## Cost Calculation\n *\n * - **Base Cost**: 10 Ki, 15 Stamina per difficulty point\n * - **Base Time**: 500ms per difficulty point\n * - **Archetype Modifier**: 0.8x for favored stances, 1.0x otherwise\n * - **Same Stance**: Zero cost\n *\n * @param from - Starting stance\n * @param to - Target stance\n * @param player - Optional player for archetype modifiers\n * @returns Transition cost breakdown\n *\n * @example\n * ```typescript\n * const cost = trigramSystem.getTransitionCost(\n * TrigramStance.GEON,\n * TrigramStance.TAE,\n * player\n * );\n * console.log(`Cost: ${cost.ki} Ki, ${cost.stamina} Stamina`);\n * ```\n *\n * @public\n * @korean 자세전환비용\n */\n public getTransitionCost(\n from: TrigramStance,\n to: TrigramStance,\n player?: PlayerState\n ): TrigramTransitionCost {\n if (from === to) {\n return {\n ki: 0,\n stamina: 0,\n timeMilliseconds: 0, // neutral\n };\n }\n\n const difficulty = TrigramCalculator.calculateTransitionDifficulty(\n from,\n to\n );\n const baseCost = 10;\n const baseTime = 500;\n\n let ki = Math.ceil(baseCost * difficulty);\n let stamina = Math.ceil(baseCost * difficulty * 1.5);\n\n // apply archetype stance‐change cost modifier if player provided\n if (player) {\n const archData = PLAYER_ARCHETYPES_DATA[player.archetype];\n const favs = archData.favoredStances || [];\n const mod = favs.includes(to) ? 0.8 : 1.0;\n ki = Math.ceil(ki * mod);\n stamina = Math.ceil(stamina * mod);\n }\n\n return {\n ki,\n stamina,\n timeMilliseconds: Math.ceil(baseTime * difficulty),\n };\n }\n\n /**\n * Calculates stance effectiveness in combat matchup.\n *\n * Determines the multiplier advantage/disadvantage when one stance attacks another.\n * Based on I Ching elemental relationships (e.g., Water extinguishes Fire).\n *\n * @param attackerStance - Attacking player's stance\n * @param defenderStance - Defending player's stance\n * @returns Effectiveness multiplier (0.5 = disadvantage, 1.0 = neutral, 1.5 = advantage)\n *\n * @example\n * ```typescript\n * const effectiveness = trigramSystem.calculateStanceEffectiveness(\n * TrigramStance.GAM, // Water\n * TrigramStance.LI // Fire\n * ); // Returns > 1.0 (Water beats Fire)\n * ```\n *\n * @public\n * @korean 자세효과성계산\n */\n calculateStanceEffectiveness(\n attackerStance: TrigramStance,\n defenderStance: TrigramStance\n ): number {\n return this.calculator.calculateStanceEffectiveness(\n attackerStance,\n defenderStance\n );\n }\n\n /**\n * Gets bilingual name for a stance.\n *\n * Returns Korean (Hangul) and English names for display purposes.\n *\n * @param stance - Stance to get name for\n * @returns Object with korean and english name properties\n *\n * @example\n * ```typescript\n * const name = trigramSystem.getStanceName(TrigramStance.GEON);\n * console.log(`${name.korean} (${name.english})`); // \"건 (Heaven)\"\n * ```\n *\n * @public\n * @korean 자세이름조회\n */\n getStanceName(stance: TrigramStance): { korean: string; english: string } {\n const stanceNames = {\n [TrigramStance.GEON]: { korean: \"건\", english: \"Heaven\" },\n [TrigramStance.TAE]: { korean: \"태\", english: \"Lake\" },\n [TrigramStance.LI]: { korean: \"리\", english: \"Fire\" },\n [TrigramStance.JIN]: { korean: \"진\", english: \"Thunder\" },\n [TrigramStance.SON]: { korean: \"손\", english: \"Wind\" },\n [TrigramStance.GAM]: { korean: \"감\", english: \"Water\" },\n [TrigramStance.GAN]: { korean: \"간\", english: \"Mountain\" },\n [TrigramStance.GON]: { korean: \"곤\", english: \"Earth\" },\n };\n\n return stanceNames[stance] || { korean: \"Unknown\", english: \"Unknown\" };\n }\n\n /**\n * Gets the counter stance for opponent's current stance.\n * \n * Returns the stance that provides tactical advantage against the opponent's stance,\n * based on I Ching elemental relationships. Using a counter stance provides a 1.2x\n * damage multiplier in combat.\n * \n * **Korean Philosophy (상극 자세)**:\n * - Water counters Heaven (flows around force)\n * - Earth counters Lake (grounds and contains)\n * - Wind counters Fire (disperses intensity)\n * - Mountain counters Thunder (absorbs impact)\n * \n * @param opponentStance - Opponent's current stance\n * @returns Counter stance that provides advantage\n * \n * @example\n * ```typescript\n * const counterStance = trigramSystem.getCounterStance(TrigramStance.GEON);\n * console.log(counterStance); // TrigramStance.GAM (Water counters Heaven)\n * ```\n * \n * @public\n * @korean 상극자세조회\n */\n getCounterStance(opponentStance: TrigramStance): TrigramStance {\n return STANCE_COUNTERS[opponentStance];\n }\n\n /**\n * Checks if player's stance counters opponent's stance.\n * \n * Determines if the player has a tactical advantage through stance matchup.\n * When true, player should receive a 1.2x damage multiplier for attacks.\n * \n * @param myStance - Player's current stance\n * @param opponentStance - Opponent's current stance\n * @returns True if player's stance counters opponent's stance\n * \n * @example\n * ```typescript\n * const hasAdvantage = trigramSystem.isCounterStance(\n * TrigramStance.GAM, // My stance: Water\n * TrigramStance.GEON // Opponent: Heaven\n * ); // Returns true - Water counters Heaven\n * \n * if (hasAdvantage) {\n * damage *= 1.2; // Apply counter bonus\n * }\n * ```\n * \n * @public\n * @korean 상극자세확인\n */\n isCounterStance(myStance: TrigramStance, opponentStance: TrigramStance): boolean {\n return this.getCounterStance(opponentStance) === myStance;\n }\n\n /**\n * Gets complete stance data for UI display.\n *\n * Returns structured data object containing stance ID and bilingual names.\n *\n * @param stance - Stance to get data for\n * @returns Stance data object\n *\n * @public\n * @korean 자세데이터조회\n */\n getCurrentStanceData(stance: TrigramStance): {\n id: TrigramStance;\n name: KoreanText;\n korean: string;\n english: string;\n } {\n const stanceName = this.getStanceName(stance);\n return {\n id: stance,\n name: stanceName,\n korean: stanceName.korean,\n english: stanceName.english,\n };\n }\n\n /**\n * Validates a stance transition with detailed feedback.\n *\n * Checks if transition is valid and provides reason if not.\n * More detailed than {@link canTransitionTo}, includes specific failure reasons.\n *\n * @param fromStance - Current stance\n * @param toStance - Target stance\n * @param player - Player state\n * @returns Validation result with optional failure reason\n *\n * @example\n * ```typescript\n * const validation = trigramSystem.validateTransition(\n * TrigramStance.GEON,\n * TrigramStance.GON,\n * player\n * );\n * if (!validation.valid) {\n * console.error(validation.reason);\n * }\n * ```\n *\n * @public\n * @korean 자세전환검증\n */\n validateTransition(\n fromStance: TrigramStance,\n toStance: TrigramStance,\n player: PlayerState\n ): { valid: boolean; reason?: string } {\n if (fromStance === toStance) {\n return { valid: true };\n }\n\n const cost = this.getTransitionCost(fromStance, toStance, player);\n\n if (player.ki < cost.ki) {\n return {\n valid: false,\n reason: `Insufficient Ki: need ${cost.ki}, have ${player.ki}`,\n };\n }\n\n if (player.stamina < cost.stamina) {\n return {\n valid: false,\n reason: `Insufficient Stamina: need ${cost.stamina}, have ${player.stamina}`,\n };\n }\n\n return { valid: true };\n }\n}\n\nexport default TrigramSystem;\n"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"VitalPointSystem.js","names":[],"sources":["../../src/systems/VitalPointSystem.ts"],"sourcesContent":["/**\n * System for managing Korean martial arts vital point (급소) targeting and damage calculation.\n *\n * **Korean**: 급소 시스템 (Vital Point System)\n *\n * The VitalPointSystem implements anatomical targeting mechanics based on traditional\n * Korean martial arts knowledge of 70 vital points (급소). It handles hit detection,\n * damage calculation, and status effect application for precise strikes.\n *\n * ## Vital Point Philosophy\n *\n * Korean martial arts identify specific anatomical locations that, when struck precisely,\n * can cause disproportionate effects. The system categorizes points by:\n *\n * - **Location**: Head, neck, torso, limbs, and core\n * - **Category**: Neurological, vascular, respiratory, muscular, skeletal, etc.\n * - **Severity**: Minor, moderate, major, critical, lethal\n * - **Effects**: Unconsciousness, paralysis, pain, stunning, etc.\n *\n * ## Meridian Integration (경락 통합)\n *\n * The system integrates Traditional Korean Medicine (TKM) meridian theory:\n * - **Time-of-Day Flow**: +30% effectiveness at meridian peak hours\n * - **Meridian Disruption**: Status effects from energy flow blockage\n * - **Elemental Relationships**: 五行 (Wu Xing) elemental advantages\n *\n * ## Key Features\n *\n * - Distance-based hit detection with accuracy falloff\n * - Severity multipliers for damage calculation\n * - Targeted vs. proximity-based strikes\n * - Bilingual Korean-English vital point names\n * - Realistic anatomical positioning\n * - Meridian state tracking and flow calculation\n *\n * @example\n * ```typescript\n * const vitalPointSystem = new VitalPointSystem();\n *\n * // Process a strike at specific position with time-of-day\n * const result = vitalPointSystem.processHit(\n * { x: 100, y: 50 }, // Target position\n * { width: 10, height: 10 }, // Hit box\n * \"GB-20\", // Optional: target specific vital point\n * 14 // Optional: hour of day for meridian flow\n * );\n *\n * if (result.hit && result.vitalPointHit) {\n * console.log(`Hit ${result.vitalPointHit.names.english}!`);\n * console.log(`Damage: ${result.damage}, Severity: ${result.severity}`);\n * console.log(`Meridian bonus: ${result.meridianMultiplier}x`);\n * }\n * ```\n *\n * @public\n * @category Vital Point System\n * @korean 급소시스템\n */\nimport {\n PlayerArchetype,\n Position,\n TrigramStance,\n VitalPointSeverity,\n} from \"../types/common\";\nimport { convertToStatusEffect } from \"./EffectCalculator\";\nimport { StatusEffect } from \"./types\";\nimport {\n calculateEnhancedVulnerability,\n calculateMeridianFlow,\n generateMeridianEffects,\n} from \"./vitalpoint/KoreanAnatomy\";\nimport { getMeridiansForVitalPoint } from \"./vitalpoint/MeridianVitalPointMapping\";\nimport { VitalPoint, VitalPointHitResult } from \"./vitalpoint/types\";\nimport { VITAL_POINTS_DATA } from \"./vitalpoint/VitalPointsData\";\n\n/**\n * Amount of meridian disruption added per vital point hit (15%)\n */\nconst DISRUPTION_INCREMENT_PER_HIT = 0.15;\n\nexport class VitalPointSystem {\n private vitalPoints: VitalPoint[] = [];\n private meridianStates: Map<string, number> = new Map(); // Tracks disruption level per meridian\n private currentHour: number = 12; // Default to noon\n\n /**\n * Creates a new VitalPointSystem instance.\n *\n * Initializes the system with comprehensive Korean vital points database\n * and meridian state tracking.\n *\n * @param initialHour - Optional initial hour of day (0-23) for meridian flow calculations\n */\n constructor(initialHour: number = 12) {\n // Initialize with comprehensive Korean vital points database\n this.initializeVitalPoints();\n this.currentHour = initialHour;\n }\n\n /**\n * Sets the current hour of day for meridian flow calculations.\n *\n * **Korean**: 시간 설정\n *\n * @param hour - Hour of day (0-23)\n * @throws Error if hour is not a finite number\n *\n * @example\n * ```typescript\n * vitalPointSystem.setCurrentHour(20); // 8 PM - Pericardium peak time\n * ```\n *\n * @public\n * @korean 시간설정\n */\n setCurrentHour(hour: number): void {\n if (!Number.isFinite(hour)) {\n throw new Error(\"Hour must be a finite number\");\n }\n this.currentHour = Math.max(0, Math.min(23, Math.floor(hour)));\n }\n\n /**\n * Gets the current hour of day used for meridian calculations.\n *\n * **Korean**: 현재 시간 조회\n *\n * @returns Current hour (0-23)\n *\n * @public\n * @korean 시간조회\n */\n getCurrentHour(): number {\n return this.currentHour;\n }\n\n /**\n * Updates meridian disruption state for a specific meridian.\n *\n * **Korean**: 경락 차단 상태 업데이트\n *\n * @param meridianId - ID of the meridian\n * @param disruptionLevel - Disruption level (0-1, where 1 is fully blocked)\n *\n * @public\n * @korean 경락차단업데이트\n */\n setMeridianDisruption(meridianId: string, disruptionLevel: number): void {\n this.meridianStates.set(\n meridianId,\n Math.max(0, Math.min(1, disruptionLevel)),\n );\n }\n\n /**\n * Gets the current disruption level for a meridian.\n *\n * **Korean**: 경락 차단 수준 조회\n *\n * @param meridianId - ID of the meridian\n * @returns Disruption level (0-1)\n *\n * @public\n * @korean 경락차단조회\n */\n getMeridianDisruption(meridianId: string): number {\n return this.meridianStates.get(meridianId) ?? 0;\n }\n\n /**\n * Clears all meridian disruption states (e.g., after rest or healing).\n *\n * **Korean**: 경락 상태 초기화\n *\n * @public\n * @korean 경락초기화\n */\n clearMeridianDisruptions(): void {\n this.meridianStates.clear();\n }\n\n /**\n * Processes a hit at a specific position to determine vital point impact.\n *\n * Evaluates whether a strike lands on or near a vital point, calculating\n * damage and effects based on accuracy, severity, and meridian flow.\n * Supports both targeted strikes (specific vital point ID) and proximity-based detection.\n *\n * Enhanced with archetype-specific modifiers for realistic combat simulation.\n *\n * ## Hit Detection Algorithm\n *\n * 1. If targetedVitalPointId provided, validate that specific point\n * 2. Otherwise, find closest vital point to target position\n * 3. Calculate distance from strike to vital point\n * 4. Apply accuracy falloff based on distance (max 50px)\n * 5. Calculate meridian flow bonus based on time of day\n * 6. Apply meridian disruption if applicable\n * 7. Apply archetype offensive/defensive modifiers\n * 8. Apply enhanced vulnerability with stance and anatomical zone\n * 9. Return hit result with damage multipliers and effects\n *\n * @param targetPosition - Position where strike lands\n * @param _hitBox - Size of hit box (currently unused, reserved for future)\n * @param targetedVitalPointId - Optional specific vital point being targeted\n * @param hour - Optional hour of day (0-23) for meridian flow calculation\n * @param attackerArchetype - Optional attacker's archetype (default: MUSA)\n * @param defenderArchetype - Optional defender's archetype (default: MUSA)\n * @param defenderStance - Optional defender's trigram stance (default: GEON)\n * @returns Hit result with damage, effects, severity, and meridian multiplier\n *\n * @example\n * ```typescript\n * // Proximity-based strike\n * const result1 = vitalPointSystem.processHit(\n * { x: 100, y: 50 },\n * { width: 10, height: 10 }\n * );\n *\n * // Targeted strike with archetype modifiers and stance\n * const result2 = vitalPointSystem.processHit(\n * { x: 100, y: 50 },\n * { width: 10, height: 10 },\n * \"head_temple\",\n * 2, // Liver meridian peak hour\n * PlayerArchetype.AMSALJA, // Assassin attacker (+30% effect)\n * PlayerArchetype.MUSA, // Warrior defender (+20% resistance)\n * TrigramStance.GEON // Heaven stance (exposes head +20%)\n * );\n * ```\n *\n * @public\n * @korean 타격처리\n */\n processHit(\n targetPosition: Position,\n _hitBox: { width: number; height: number }, // Prefixed with underscore to indicate intentionally unused\n targetedVitalPointId?: string | null,\n hour?: number,\n attackerArchetype?: PlayerArchetype,\n defenderArchetype?: PlayerArchetype,\n defenderStance?: TrigramStance,\n ): VitalPointHitResult {\n // Use provided hour or current system hour\n const effectiveHour = hour ?? this.currentHour;\n const effectiveAttackerArchetype =\n attackerArchetype ?? PlayerArchetype.MUSA;\n const effectiveDefenderArchetype =\n defenderArchetype ?? PlayerArchetype.MUSA;\n const effectiveDefenderStance = defenderStance ?? TrigramStance.GEON;\n\n // If a specific vital point is targeted, check that one\n if (targetedVitalPointId) {\n const targetVitalPoint = this.getVitalPointById(targetedVitalPointId);\n if (targetVitalPoint) {\n return this.calculateVitalPointHit(\n targetVitalPoint,\n targetPosition,\n effectiveHour,\n effectiveAttackerArchetype,\n effectiveDefenderArchetype,\n effectiveDefenderStance,\n );\n }\n }\n\n // Otherwise, find the closest vital point\n const closestVitalPoint = this.findClosestVitalPoint(targetPosition);\n if (!closestVitalPoint) {\n return {\n hit: false,\n damage: 0,\n effects: [],\n severity: VitalPointSeverity.MINOR,\n };\n }\n\n // Calculate distance to determine hit accuracy\n const distance = this.calculateDistance(\n targetPosition,\n closestVitalPoint.position,\n );\n const maxHitDistance = 50; // pixels\n\n if (distance <= maxHitDistance) {\n return this.calculateVitalPointHit(\n closestVitalPoint,\n targetPosition,\n effectiveHour,\n effectiveAttackerArchetype,\n effectiveDefenderArchetype,\n effectiveDefenderStance,\n );\n }\n\n return {\n hit: false,\n damage: 0,\n effects: [],\n severity: VitalPointSeverity.MINOR,\n };\n }\n\n /**\n * Retrieves a vital point by its unique identifier.\n *\n * @param id - Vital point identifier (e.g., \"GB-20\", \"head_temple\")\n * @returns Vital point if found, null otherwise\n *\n * @example\n * ```typescript\n * const temple = vitalPointSystem.getVitalPointById(\"head_temple\");\n * if (temple) {\n * console.log(temple.names.korean); // \"태양혈\"\n * }\n * ```\n *\n * @public\n * @korean 급소조회\n */\n getVitalPointById(id: string): VitalPoint | null {\n return this.vitalPoints.find((vp) => vp.id === id) ?? null;\n }\n\n /**\n * Gets all registered vital points in the system.\n *\n * @returns Read-only array of all vital points\n *\n * @example\n * ```typescript\n * const allPoints = vitalPointSystem.getVitalPoints();\n * console.log(`${allPoints.length} vital points registered`);\n * ```\n *\n * @public\n * @korean 급소목록조회\n */\n getVitalPoints(): readonly VitalPoint[] {\n return this.vitalPoints;\n }\n\n /**\n * Calculates hit result for a technique-based attack.\n *\n * Determines whether a technique successfully lands on a vital point,\n * factoring in technique accuracy, attacker position, and randomness.\n * Used for AI and player technique execution.\n *\n * @param technique - Technique being executed with accuracy property\n * @param attackerPosition - Position where attack originates\n * @param _defenderPosition - Defender position (reserved for future use)\n * @param _defenderStance - Defender stance (reserved for future use)\n * @returns Hit result with damage and effects\n *\n * @example\n * ```typescript\n * const technique = {\n * id: \"geon-thunder-strike\",\n * accuracy: 0.85\n * };\n *\n * const result = vitalPointSystem.calculateHit(\n * technique,\n * { x: 100, y: 50 },\n * { x: 120, y: 50 },\n * TrigramStance.GEON\n * );\n * ```\n *\n * @public\n * @korean 기술타격계산\n */\n calculateHit(\n // eslint-disable-next-line @typescript-eslint/no-explicit-any -- technique type is dynamically determined by combat system\n technique: any,\n attackerPosition: Position,\n _defenderPosition: Position, // Prefixed with underscore to indicate intentionally unused\n // eslint-disable-next-line @typescript-eslint/no-explicit-any -- stance type is dynamically determined by combat system\n _defenderStance: any, // Prefixed with underscore to indicate intentionally unused\n ): VitalPointHitResult {\n // Find closest vital point to attack\n const closestVitalPoint = this.findClosestVitalPoint(attackerPosition);\n\n if (!closestVitalPoint) {\n return {\n hit: false,\n damage: 0,\n effects: [],\n severity: VitalPointSeverity.MINOR,\n };\n }\n\n // Calculate hit based on technique accuracy and distance\n const distance = this.calculateDistance(\n attackerPosition,\n closestVitalPoint.position,\n );\n const hitChance = technique.accuracy * (1 - distance / 100);\n\n if (Math.random() < hitChance) {\n return {\n hit: true,\n vitalPointHit: closestVitalPoint,\n damage: this.calculateBaseDamage(closestVitalPoint, distance),\n effects: [],\n severity: closestVitalPoint.severity,\n };\n }\n\n return {\n hit: false,\n damage: 0,\n effects: [],\n severity: VitalPointSeverity.MINOR,\n };\n }\n\n /**\n * Finds the closest vital point to a given position.\n *\n * Uses Euclidean distance to determine proximity.\n *\n * @param position - Target position\n * @returns Closest vital point or null if none registered\n *\n * @private\n * @korean 최근접급소검색\n */\n private findClosestVitalPoint(position: Position): VitalPoint | null {\n if (this.vitalPoints.length === 0) return null;\n\n let closest = this.vitalPoints[0];\n let minDistance = this.calculateDistance(position, closest.position);\n\n for (const vitalPoint of this.vitalPoints) {\n const distance = this.calculateDistance(position, vitalPoint.position);\n if (distance < minDistance) {\n minDistance = distance;\n closest = vitalPoint;\n }\n }\n\n return closest;\n }\n\n /**\n * Calculates Euclidean distance between two positions.\n *\n * @param pos1 - First position\n * @param pos2 - Second position\n * @returns Distance in pixels\n *\n * @private\n * @korean 거리계산\n */\n private calculateDistance(pos1: Position, pos2: Position): number {\n const dx = pos1.x - pos2.x;\n const dy = pos1.y - pos2.y;\n return Math.sqrt(dx * dx + dy * dy);\n }\n\n /**\n * Calculates vital point hit result with accuracy, damage, and meridian effects.\n *\n * **Korean**: 급소타격결과계산\n *\n * Enhanced with comprehensive effect calculation including:\n * - Duration based on accuracy, severity, and archetype modifiers\n * - Intensity scaling with hit accuracy\n * - Critical hit bonuses for accuracy >= 0.9\n * - Archetype offensive/defensive modifiers\n *\n * @param vitalPoint - Vital point being struck\n * @param hitPosition - Exact position of strike\n * @param hour - Hour of day for meridian flow calculation\n * @param attackerArchetype - Attacker's archetype (default: MUSA)\n * @param defenderArchetype - Defender's archetype (default: MUSA)\n * @returns Hit result with calculated damage, accuracy, and meridian bonuses\n *\n * @private\n * @korean 급소타격결과계산\n */\n private calculateVitalPointHit(\n vitalPoint: VitalPoint,\n hitPosition: Position,\n hour: number,\n attackerArchetype: PlayerArchetype = PlayerArchetype.MUSA,\n defenderArchetype: PlayerArchetype = PlayerArchetype.MUSA,\n defenderStance: TrigramStance = TrigramStance.GEON,\n ): VitalPointHitResult {\n const distance = this.calculateDistance(hitPosition, vitalPoint.position);\n const baseDamage = this.calculateBaseDamage(vitalPoint, distance);\n const now = Date.now(); // Single timestamp for all effects\n\n // Calculate hit accuracy (0-1 scale based on distance)\n const accuracy = Math.max(0, 1 - distance / 50);\n\n // Get meridians for this vital point\n const meridians = getMeridiansForVitalPoint(vitalPoint.id);\n\n // Calculate meridian flow effectiveness (highest value if multiple meridians)\n let meridianMultiplier = 1.0;\n const allMeridianEffects: StatusEffect[] = [];\n\n if (meridians.length > 0) {\n // Use the best meridian flow multiplier\n meridianMultiplier = Math.max(\n ...meridians.map((meridianId) =>\n calculateMeridianFlow(meridianId, hour),\n ),\n );\n\n // Generate meridian disruption effects for each affected meridian\n meridians.forEach((meridianId) => {\n const currentDisruption = this.getMeridianDisruption(meridianId);\n const newDisruption = Math.min(\n 1,\n currentDisruption + DISRUPTION_INCREMENT_PER_HIT,\n );\n this.setMeridianDisruption(meridianId, newDisruption);\n\n // Generate effects if disruption is significant (using shared timestamp)\n const effects = generateMeridianEffects(meridianId, newDisruption, now);\n allMeridianEffects.push(...effects);\n });\n }\n\n // Calculate enhanced vulnerability based on anatomical zone, stance, meridian flow, and time\n const meridianStates: Record<string, number> = {};\n meridians.forEach((meridianId) => {\n // Convert disruption (0=normal, 1=blocked) to flow state (1=normal, 0=blocked)\n const disruption = this.getMeridianDisruption(meridianId);\n meridianStates[meridianId] = 1.0 - disruption;\n });\n\n const vulnerabilityMultiplier = calculateEnhancedVulnerability(\n vitalPoint.position,\n hour,\n defenderStance,\n meridianStates,\n );\n\n // Apply all multipliers to damage: base × meridian flow × vulnerability\n const finalDamage = Math.floor(\n baseDamage * meridianMultiplier * vulnerabilityMultiplier,\n );\n\n // Convert VitalPointEffect to StatusEffect with comprehensive calculations\n const vitalPointStatusEffects: StatusEffect[] = vitalPoint.effects.map(\n (effect) =>\n convertToStatusEffect(\n effect,\n accuracy,\n vitalPoint.severity,\n attackerArchetype,\n defenderArchetype,\n vitalPoint.id,\n now,\n ),\n );\n\n // Combine vital point effects with meridian effects\n const combinedEffects = [...vitalPointStatusEffects, ...allMeridianEffects];\n\n return {\n hit: true,\n vitalPointHit: vitalPoint,\n damage: finalDamage,\n effects: combinedEffects,\n severity: vitalPoint.severity,\n accuracy,\n meridianMultiplier,\n meridianEffects: allMeridianEffects,\n multiplier: vulnerabilityMultiplier,\n };\n }\n\n /**\n * Calculates base damage for a vital point strike.\n *\n * Applies distance-based falloff to vital point's base damage value.\n * Closer strikes deal more damage up to maximum base damage.\n *\n * @param vitalPoint - Vital point with base damage property\n * @param distance - Distance from strike to vital point center\n * @returns Calculated damage value\n *\n * @private\n * @korean 기본피해계산\n */\n private calculateBaseDamage(\n vitalPoint: VitalPoint,\n distance: number,\n ): number {\n const baseDamage = vitalPoint.baseDamage ?? 10;\n const distanceModifier = Math.max(0.1, 1 - distance / 100);\n return Math.floor(baseDamage * distanceModifier);\n }\n\n /**\n * Initializes the system with comprehensive Korean vital points.\n *\n * Loads all 70 Korean vital points from the comprehensive database\n * covering head, torso, arms, and legs with proper categorization.\n *\n * @private\n * @korean 급소초기화\n */\n private initializeVitalPoints(): void {\n // Load all 70 vital points from the comprehensive database\n this.vitalPoints = [...VITAL_POINTS_DATA];\n }\n}\n\nexport default VitalPointSystem;\n"],"mappings":"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"}
1
+ {"version":3,"file":"VitalPointSystem.js","names":[],"sources":["../../src/systems/VitalPointSystem.ts"],"sourcesContent":["/**\n * System for managing Korean martial arts vital point (급소) targeting and damage calculation.\n *\n * **Korean**: 급소 시스템 (Vital Point System)\n *\n * The VitalPointSystem implements anatomical targeting mechanics based on traditional\n * Korean martial arts knowledge of 70 vital points (급소). It handles hit detection,\n * damage calculation, and status effect application for precise strikes.\n *\n * ## Vital Point Philosophy\n *\n * Korean martial arts identify specific anatomical locations that, when struck precisely,\n * can cause disproportionate effects. The system categorizes points by:\n *\n * - **Location**: Head, neck, torso, limbs, and core\n * - **Category**: Neurological, vascular, respiratory, muscular, skeletal, etc.\n * - **Severity**: Minor, moderate, major, critical, lethal\n * - **Effects**: Unconsciousness, paralysis, pain, stunning, etc.\n *\n * ## Meridian Integration (경락 통합)\n *\n * The system integrates Traditional Korean Medicine (TKM) meridian theory:\n * - **Time-of-Day Flow**: +30% effectiveness at meridian peak hours\n * - **Meridian Disruption**: Status effects from energy flow blockage\n * - **Elemental Relationships**: 五行 (Wu Xing) elemental advantages\n *\n * ## Key Features\n *\n * - Distance-based hit detection with accuracy falloff\n * - Severity multipliers for damage calculation\n * - Targeted vs. proximity-based strikes\n * - Bilingual Korean-English vital point names\n * - Realistic anatomical positioning\n * - Meridian state tracking and flow calculation\n *\n * @example\n * ```typescript\n * const vitalPointSystem = new VitalPointSystem();\n *\n * // Process a strike at specific position with time-of-day\n * const result = vitalPointSystem.processHit(\n * { x: 100, y: 50 }, // Target position\n * { width: 10, height: 10 }, // Hit box\n * \"GB-20\", // Optional: target specific vital point\n * 14 // Optional: hour of day for meridian flow\n * );\n *\n * if (result.hit && result.vitalPointHit) {\n * console.log(`Hit ${result.vitalPointHit.names.english}!`);\n * console.log(`Damage: ${result.damage}, Severity: ${result.severity}`);\n * console.log(`Meridian bonus: ${result.meridianMultiplier}x`);\n * }\n * ```\n *\n * @public\n * @category Vital Point System\n * @korean 급소시스템\n */\nimport {\n PlayerArchetype,\n Position,\n TrigramStance,\n VitalPointSeverity,\n} from \"../types/common\";\nimport { convertToStatusEffect } from \"./EffectCalculator\";\nimport { StatusEffect } from \"./types\";\nimport {\n calculateEnhancedVulnerability,\n calculateMeridianFlow,\n generateMeridianEffects,\n} from \"./vitalpoint/KoreanAnatomy\";\nimport { getMeridiansForVitalPoint } from \"./vitalpoint/MeridianVitalPointMapping\";\nimport { VitalPoint, VitalPointHitResult } from \"./vitalpoint/types\";\nimport { VITAL_POINTS_DATA } from \"./vitalpoint/VitalPointsData\";\n\n/**\n * Amount of meridian disruption added per vital point hit (15%)\n */\nconst DISRUPTION_INCREMENT_PER_HIT = 0.15;\n\nexport class VitalPointSystem {\n private vitalPoints: VitalPoint[] = [];\n private meridianStates: Map<string, number> = new Map(); // Tracks disruption level per meridian\n private currentHour: number = 12; // Default to noon\n\n /**\n * Creates a new VitalPointSystem instance.\n *\n * Initializes the system with comprehensive Korean vital points database\n * and meridian state tracking.\n *\n * @param initialHour - Optional initial hour of day (0-23) for meridian flow calculations\n */\n constructor(initialHour: number = 12) {\n // Initialize with comprehensive Korean vital points database\n this.initializeVitalPoints();\n this.currentHour = initialHour;\n }\n\n /**\n * Sets the current hour of day for meridian flow calculations.\n *\n * **Korean**: 시간 설정\n *\n * @param hour - Hour of day (0-23)\n * @throws Error if hour is not a finite number\n *\n * @example\n * ```typescript\n * vitalPointSystem.setCurrentHour(20); // 8 PM - Pericardium peak time\n * ```\n *\n * @public\n * @korean 시간설정\n */\n setCurrentHour(hour: number): void {\n if (!Number.isFinite(hour)) {\n throw new Error(\"Hour must be a finite number\");\n }\n this.currentHour = Math.max(0, Math.min(23, Math.floor(hour)));\n }\n\n /**\n * Gets the current hour of day used for meridian calculations.\n *\n * **Korean**: 현재 시간 조회\n *\n * @returns Current hour (0-23)\n *\n * @public\n * @korean 시간조회\n */\n getCurrentHour(): number {\n return this.currentHour;\n }\n\n /**\n * Updates meridian disruption state for a specific meridian.\n *\n * **Korean**: 경락 차단 상태 업데이트\n *\n * @param meridianId - ID of the meridian\n * @param disruptionLevel - Disruption level (0-1, where 1 is fully blocked)\n *\n * @public\n * @korean 경락차단업데이트\n */\n setMeridianDisruption(meridianId: string, disruptionLevel: number): void {\n this.meridianStates.set(\n meridianId,\n Math.max(0, Math.min(1, disruptionLevel)),\n );\n }\n\n /**\n * Gets the current disruption level for a meridian.\n *\n * **Korean**: 경락 차단 수준 조회\n *\n * @param meridianId - ID of the meridian\n * @returns Disruption level (0-1)\n *\n * @public\n * @korean 경락차단조회\n */\n getMeridianDisruption(meridianId: string): number {\n return this.meridianStates.get(meridianId) ?? 0;\n }\n\n /**\n * Clears all meridian disruption states (e.g., after rest or healing).\n *\n * **Korean**: 경락 상태 초기화\n *\n * @public\n * @korean 경락초기화\n */\n clearMeridianDisruptions(): void {\n this.meridianStates.clear();\n }\n\n /**\n * Processes a hit at a specific position to determine vital point impact.\n *\n * Evaluates whether a strike lands on or near a vital point, calculating\n * damage and effects based on accuracy, severity, and meridian flow.\n * Supports both targeted strikes (specific vital point ID) and proximity-based detection.\n *\n * Enhanced with archetype-specific modifiers for realistic combat simulation.\n *\n * ## Hit Detection Algorithm\n *\n * 1. If targetedVitalPointId provided, validate that specific point\n * 2. Otherwise, find closest vital point to target position\n * 3. Calculate distance from strike to vital point\n * 4. Apply accuracy falloff based on distance (max 50px)\n * 5. Calculate meridian flow bonus based on time of day\n * 6. Apply meridian disruption if applicable\n * 7. Apply archetype offensive/defensive modifiers\n * 8. Apply enhanced vulnerability with stance and anatomical zone\n * 9. Return hit result with damage multipliers and effects\n *\n * @param targetPosition - Position where strike lands\n * @param _hitBox - Size of hit box (currently unused, reserved for future)\n * @param targetedVitalPointId - Optional specific vital point being targeted\n * @param hour - Optional hour of day (0-23) for meridian flow calculation\n * @param attackerArchetype - Optional attacker's archetype (default: MUSA)\n * @param defenderArchetype - Optional defender's archetype (default: MUSA)\n * @param defenderStance - Optional defender's trigram stance (default: GEON)\n * @returns Hit result with damage, effects, severity, and meridian multiplier\n *\n * @example\n * ```typescript\n * // Proximity-based strike\n * const result1 = vitalPointSystem.processHit(\n * { x: 100, y: 50 },\n * { width: 10, height: 10 }\n * );\n *\n * // Targeted strike with archetype modifiers and stance\n * const result2 = vitalPointSystem.processHit(\n * { x: 100, y: 50 },\n * { width: 10, height: 10 },\n * \"head_temple\",\n * 2, // Liver meridian peak hour\n * PlayerArchetype.AMSALJA, // Assassin attacker (+30% effect)\n * PlayerArchetype.MUSA, // Warrior defender (+20% resistance)\n * TrigramStance.GEON // Heaven stance (exposes head +20%)\n * );\n * ```\n *\n * @public\n * @korean 타격처리\n */\n processHit(\n targetPosition: Position,\n _hitBox: { width: number; height: number }, // Prefixed with underscore to indicate intentionally unused\n targetedVitalPointId?: string | null,\n hour?: number,\n attackerArchetype?: PlayerArchetype,\n defenderArchetype?: PlayerArchetype,\n defenderStance?: TrigramStance,\n ): VitalPointHitResult {\n // Use provided hour or current system hour\n const effectiveHour = hour ?? this.currentHour;\n const effectiveAttackerArchetype =\n attackerArchetype ?? PlayerArchetype.MUSA;\n const effectiveDefenderArchetype =\n defenderArchetype ?? PlayerArchetype.MUSA;\n const effectiveDefenderStance = defenderStance ?? TrigramStance.GEON;\n\n // If a specific vital point is targeted, check that one\n if (targetedVitalPointId) {\n const targetVitalPoint = this.getVitalPointById(targetedVitalPointId);\n if (targetVitalPoint) {\n return this.calculateVitalPointHit(\n targetVitalPoint,\n targetPosition,\n effectiveHour,\n effectiveAttackerArchetype,\n effectiveDefenderArchetype,\n effectiveDefenderStance,\n );\n }\n }\n\n // Otherwise, find the closest vital point\n const closestVitalPoint = this.findClosestVitalPoint(targetPosition);\n if (!closestVitalPoint) {\n return {\n hit: false,\n damage: 0,\n effects: [],\n severity: VitalPointSeverity.MINOR,\n };\n }\n\n // Calculate distance to determine hit accuracy\n const distance = this.calculateDistance(\n targetPosition,\n closestVitalPoint.position,\n );\n const maxHitDistance = 50; // pixels\n\n if (distance <= maxHitDistance) {\n return this.calculateVitalPointHit(\n closestVitalPoint,\n targetPosition,\n effectiveHour,\n effectiveAttackerArchetype,\n effectiveDefenderArchetype,\n effectiveDefenderStance,\n );\n }\n\n return {\n hit: false,\n damage: 0,\n effects: [],\n severity: VitalPointSeverity.MINOR,\n };\n }\n\n /**\n * Retrieves a vital point by its unique identifier.\n *\n * @param id - Vital point identifier (e.g., \"GB-20\", \"head_temple\")\n * @returns Vital point if found, null otherwise\n *\n * @example\n * ```typescript\n * const temple = vitalPointSystem.getVitalPointById(\"head_temple\");\n * if (temple) {\n * console.log(temple.names.korean); // \"태양혈\"\n * }\n * ```\n *\n * @public\n * @korean 급소조회\n */\n getVitalPointById(id: string): VitalPoint | null {\n return this.vitalPoints.find((vp) => vp.id === id) ?? null;\n }\n\n /**\n * Gets all registered vital points in the system.\n *\n * @returns Read-only array of all vital points\n *\n * @example\n * ```typescript\n * const allPoints = vitalPointSystem.getVitalPoints();\n * console.log(`${allPoints.length} vital points registered`);\n * ```\n *\n * @public\n * @korean 급소목록조회\n */\n getVitalPoints(): readonly VitalPoint[] {\n return this.vitalPoints;\n }\n\n /**\n * Calculates hit result for a technique-based attack.\n *\n * Determines whether a technique successfully lands on a vital point,\n * factoring in technique accuracy, attacker position, and randomness.\n * Used for AI and player technique execution.\n *\n * @param technique - Technique being executed with accuracy property\n * @param attackerPosition - Position where attack originates\n * @param _defenderPosition - Defender position (reserved for future use)\n * @param _defenderStance - Defender stance (reserved for future use)\n * @returns Hit result with damage and effects\n *\n * @example\n * ```typescript\n * const technique = {\n * id: \"geon-thunder-strike\",\n * accuracy: 0.85\n * };\n *\n * const result = vitalPointSystem.calculateHit(\n * technique,\n * { x: 100, y: 50 },\n * { x: 120, y: 50 },\n * TrigramStance.GEON\n * );\n * ```\n *\n * @public\n * @korean 기술타격계산\n */\n calculateHit(\n // eslint-disable-next-line @typescript-eslint/no-explicit-any -- technique type is dynamically determined by combat system\n technique: any,\n attackerPosition: Position,\n _defenderPosition: Position, // Prefixed with underscore to indicate intentionally unused\n // eslint-disable-next-line @typescript-eslint/no-explicit-any -- stance type is dynamically determined by combat system\n _defenderStance: any, // Prefixed with underscore to indicate intentionally unused\n ): VitalPointHitResult {\n // Find closest vital point to attack\n const closestVitalPoint = this.findClosestVitalPoint(attackerPosition);\n\n if (!closestVitalPoint) {\n return {\n hit: false,\n damage: 0,\n effects: [],\n severity: VitalPointSeverity.MINOR,\n };\n }\n\n // Calculate hit based on technique accuracy and distance\n const distance = this.calculateDistance(\n attackerPosition,\n closestVitalPoint.position,\n );\n const hitChance = technique.accuracy * (1 - distance / 100);\n\n if (Math.random() < hitChance) {\n return {\n hit: true,\n vitalPointHit: closestVitalPoint,\n damage: this.calculateBaseDamage(closestVitalPoint, distance),\n effects: [],\n severity: closestVitalPoint.severity,\n };\n }\n\n return {\n hit: false,\n damage: 0,\n effects: [],\n severity: VitalPointSeverity.MINOR,\n };\n }\n\n /**\n * Finds the closest vital point to a given position.\n *\n * Uses Euclidean distance to determine proximity.\n *\n * @param position - Target position\n * @returns Closest vital point or null if none registered\n *\n * @private\n * @korean 최근접급소검색\n */\n private findClosestVitalPoint(position: Position): VitalPoint | null {\n if (this.vitalPoints.length === 0) return null;\n\n let closest = this.vitalPoints[0];\n let minDistance = this.calculateDistance(position, closest.position);\n\n for (const vitalPoint of this.vitalPoints) {\n const distance = this.calculateDistance(position, vitalPoint.position);\n if (distance < minDistance) {\n minDistance = distance;\n closest = vitalPoint;\n }\n }\n\n return closest;\n }\n\n /**\n * Calculates Euclidean distance between two positions.\n *\n * @param pos1 - First position\n * @param pos2 - Second position\n * @returns Distance in pixels\n *\n * @private\n * @korean 거리계산\n */\n private calculateDistance(pos1: Position, pos2: Position): number {\n const dx = pos1.x - pos2.x;\n const dy = pos1.y - pos2.y;\n return Math.sqrt(dx * dx + dy * dy);\n }\n\n /**\n * Calculates vital point hit result with accuracy, damage, and meridian effects.\n *\n * **Korean**: 급소타격결과계산\n *\n * Enhanced with comprehensive effect calculation including:\n * - Duration based on accuracy, severity, and archetype modifiers\n * - Intensity scaling with hit accuracy\n * - Critical hit bonuses for accuracy >= 0.9\n * - Archetype offensive/defensive modifiers\n *\n * @param vitalPoint - Vital point being struck\n * @param hitPosition - Exact position of strike\n * @param hour - Hour of day for meridian flow calculation\n * @param attackerArchetype - Attacker's archetype (default: MUSA)\n * @param defenderArchetype - Defender's archetype (default: MUSA)\n * @returns Hit result with calculated damage, accuracy, and meridian bonuses\n *\n * @private\n * @korean 급소타격결과계산\n */\n private calculateVitalPointHit(\n vitalPoint: VitalPoint,\n hitPosition: Position,\n hour: number,\n attackerArchetype: PlayerArchetype = PlayerArchetype.MUSA,\n defenderArchetype: PlayerArchetype = PlayerArchetype.MUSA,\n defenderStance: TrigramStance = TrigramStance.GEON,\n ): VitalPointHitResult {\n const distance = this.calculateDistance(hitPosition, vitalPoint.position);\n const baseDamage = this.calculateBaseDamage(vitalPoint, distance);\n const now = Date.now(); // Single timestamp for all effects\n\n // Calculate hit accuracy (0-1 scale based on distance)\n const accuracy = Math.max(0, 1 - distance / 50);\n\n // Get meridians for this vital point\n const meridians = getMeridiansForVitalPoint(vitalPoint.id);\n\n // Calculate meridian flow effectiveness (highest value if multiple meridians)\n let meridianMultiplier = 1.0;\n const allMeridianEffects: StatusEffect[] = [];\n\n if (meridians.length > 0) {\n // Use the best meridian flow multiplier\n meridianMultiplier = Math.max(\n ...meridians.map((meridianId) =>\n calculateMeridianFlow(meridianId, hour),\n ),\n );\n\n // Generate meridian disruption effects for each affected meridian\n meridians.forEach((meridianId) => {\n const currentDisruption = this.getMeridianDisruption(meridianId);\n const newDisruption = Math.min(\n 1,\n currentDisruption + DISRUPTION_INCREMENT_PER_HIT,\n );\n this.setMeridianDisruption(meridianId, newDisruption);\n\n // Generate effects if disruption is significant (using shared timestamp)\n const effects = generateMeridianEffects(meridianId, newDisruption, now);\n allMeridianEffects.push(...effects);\n });\n }\n\n // Calculate enhanced vulnerability based on anatomical zone, stance, meridian flow, and time\n const meridianStates: Record<string, number> = {};\n meridians.forEach((meridianId) => {\n // Convert disruption (0=normal, 1=blocked) to flow state (1=normal, 0=blocked)\n const disruption = this.getMeridianDisruption(meridianId);\n meridianStates[meridianId] = 1.0 - disruption;\n });\n\n const vulnerabilityMultiplier = calculateEnhancedVulnerability(\n vitalPoint.position,\n hour,\n defenderStance,\n meridianStates,\n );\n\n // Apply all multipliers to damage: base × meridian flow × vulnerability\n const finalDamage = Math.floor(\n baseDamage * meridianMultiplier * vulnerabilityMultiplier,\n );\n\n // Convert VitalPointEffect to StatusEffect with comprehensive calculations\n const vitalPointStatusEffects: StatusEffect[] = vitalPoint.effects.map(\n (effect) =>\n convertToStatusEffect(\n effect,\n accuracy,\n vitalPoint.severity,\n attackerArchetype,\n defenderArchetype,\n vitalPoint.id,\n now,\n ),\n );\n\n // Combine vital point effects with meridian effects\n const combinedEffects = [...vitalPointStatusEffects, ...allMeridianEffects];\n\n return {\n hit: true,\n vitalPointHit: vitalPoint,\n damage: finalDamage,\n effects: combinedEffects,\n severity: vitalPoint.severity,\n accuracy,\n meridianMultiplier,\n meridianEffects: allMeridianEffects,\n multiplier: vulnerabilityMultiplier,\n };\n }\n\n /**\n * Calculates base damage for a vital point strike.\n *\n * Applies distance-based falloff to vital point's base damage value.\n * Closer strikes deal more damage up to maximum base damage.\n *\n * @param vitalPoint - Vital point with base damage property\n * @param distance - Distance from strike to vital point center\n * @returns Calculated damage value\n *\n * @private\n * @korean 기본피해계산\n */\n private calculateBaseDamage(\n vitalPoint: VitalPoint,\n distance: number,\n ): number {\n const baseDamage = vitalPoint.baseDamage ?? 10;\n const distanceModifier = Math.max(0.1, 1 - distance / 100);\n return Math.floor(baseDamage * distanceModifier);\n }\n\n /**\n * Initializes the system with comprehensive Korean vital points.\n *\n * Loads all 70 Korean vital points from the comprehensive database\n * covering head, torso, arms, and legs with proper categorization.\n *\n * @private\n * @korean 급소초기화\n */\n private initializeVitalPoints(): void {\n // Load all 70 vital points from the comprehensive database\n this.vitalPoints = [...VITAL_POINTS_DATA];\n }\n}\n\nexport default VitalPointSystem;\n"],"mappings":"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"}