blacktrigram 0.7.67 → 0.7.69

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 (219) hide show
  1. package/lib/App2.js.map +1 -1
  2. package/lib/assets/index.css +987 -987
  3. package/lib/audio/AudioAssetLoader.js.map +1 -1
  4. package/lib/audio/AudioAssetRegistry.js +2 -2
  5. package/lib/audio/AudioAssetRegistry.js.map +1 -1
  6. package/lib/audio/AudioCache.js.map +1 -1
  7. package/lib/audio/AudioManager.js +1 -1
  8. package/lib/audio/AudioManager.js.map +1 -1
  9. package/lib/audio/AudioMonitor.js.map +1 -1
  10. package/lib/audio/AudioPool.js.map +1 -1
  11. package/lib/audio/AudioProvider.js.map +1 -1
  12. package/lib/audio/AudioUtils.js.map +1 -1
  13. package/lib/audio/BoneImpactAudioMap.js.map +1 -1
  14. package/lib/components/screens/combat/CombatScreen3D.js +3 -3
  15. package/lib/components/screens/combat/CombatScreen3D.js.map +1 -1
  16. package/lib/components/screens/combat/components/controls/CombatControlsPanel.js.map +1 -1
  17. package/lib/components/screens/combat/components/controls/PauseMenu.js.map +1 -1
  18. package/lib/components/screens/combat/components/effects/BloodDecals3D.js.map +1 -1
  19. package/lib/components/screens/combat/components/effects/BloodParticles3D.js.map +1 -1
  20. package/lib/components/screens/combat/components/effects/BloodViscosity3D.js.map +1 -1
  21. package/lib/components/screens/combat/components/effects/CombatParticleEffects3D.js.map +1 -1
  22. package/lib/components/screens/combat/components/effects/InternalDamage3D.js.map +1 -1
  23. package/lib/components/screens/combat/components/effects/ParticleAudio3D.js.map +1 -1
  24. package/lib/components/screens/combat/components/hud/FPSMonitor.js.map +1 -1
  25. package/lib/components/screens/combat/components/indicators/BalanceIndicator.js.map +1 -1
  26. package/lib/components/screens/combat/hooks/useAICombat.js.map +1 -1
  27. package/lib/components/screens/combat/hooks/useCombatActions.js.map +1 -1
  28. package/lib/components/screens/combat/hooks/useCombatAttackMovement.js.map +1 -1
  29. package/lib/components/screens/controls/ControlsScreen3D.js.map +1 -1
  30. package/lib/components/screens/controls/components/ControlCategoryTabsOverlayHtml.js.map +1 -1
  31. package/lib/components/screens/endscreen/components/MatchStatisticsDisplayOverlayHtml.js.map +1 -1
  32. package/lib/components/screens/endscreen/components/PerformanceBreakdownOverlayHtml.js.map +1 -1
  33. package/lib/components/screens/endscreen/components/VictoryAnimation3D.js.map +1 -1
  34. package/lib/components/screens/intro/IntroScreen3D.js +1 -1
  35. package/lib/components/screens/intro/IntroScreen3D.js.map +1 -1
  36. package/lib/components/screens/intro/components/AbilityListOverlayHtml.js.map +1 -1
  37. package/lib/components/screens/intro/components/ArchetypeCardGridOverlayHtml.js.map +1 -1
  38. package/lib/components/screens/intro/components/ArchetypeDisplayOverlayHtml.js.map +1 -1
  39. package/lib/components/screens/intro/components/EnhancedArchetypeDisplayOverlayHtml.js.map +1 -1
  40. package/lib/components/screens/intro/components/MenuButtonsOverlayHtml.js.map +1 -1
  41. package/lib/components/screens/intro/components/MenuSectionOverlayHtml.js.map +1 -1
  42. package/lib/components/screens/intro/components/StatBarOverlayHtml.js.map +1 -1
  43. package/lib/components/screens/philosophy/PhilosophyScreen3D.js.map +1 -1
  44. package/lib/components/screens/training/TrainingScreen3D.js +1 -1
  45. package/lib/components/screens/training/TrainingScreen3D.js.map +1 -1
  46. package/lib/components/screens/training/components/AnatomyOverlay3D.js.map +1 -1
  47. package/lib/components/screens/training/components/FootworkDrillsOverlayHtml.js.map +1 -1
  48. package/lib/components/screens/training/components/HitFeedbackEffect3D.js.map +1 -1
  49. package/lib/components/screens/training/components/TrainingButtonsOverlayHtml.js.map +1 -1
  50. package/lib/components/screens/training/components/TrainingDummy3D.js.map +1 -1
  51. package/lib/components/screens/training/components/TrainingModeSelectorOverlayHtml.js.map +1 -1
  52. package/lib/components/screens/training/components/TrainingStatsOverlayHtml.js.map +1 -1
  53. package/lib/components/screens/training/components/VitalPointMarker3D.js.map +1 -1
  54. package/lib/components/screens/training/hooks/useAttackMovement.js.map +1 -1
  55. package/lib/components/screens/training/hooks/useTrainingActions.js.map +1 -1
  56. package/lib/components/shared/mobile/ActionButtons.js.map +1 -1
  57. package/lib/components/shared/mobile/GestureRecognizerPure.js.map +1 -1
  58. package/lib/components/shared/mobile/HapticController.js.map +1 -1
  59. package/lib/components/shared/mobile/StanceWheelPure.js.map +1 -1
  60. package/lib/components/shared/mobile/VirtualDPad.js.map +1 -1
  61. package/lib/components/shared/three/anatomy/BoneAttachedMuscles.js.map +1 -1
  62. package/lib/components/shared/three/anatomy/BoneClothing.js.map +1 -1
  63. package/lib/components/shared/three/anatomy/BoneRenderer.js.map +1 -1
  64. package/lib/components/shared/three/anatomy/Face3D.js.map +1 -1
  65. package/lib/components/shared/three/effects/ActionFeedback.js.map +1 -1
  66. package/lib/components/shared/three/effects/DamageNumbers.js.map +1 -1
  67. package/lib/components/shared/three/effects/HitEffects3D.js.map +1 -1
  68. package/lib/components/shared/three/effects/StanceTransitionEffect.js.map +1 -1
  69. package/lib/components/shared/three/effects/VitalPointMarkers3D.js.map +1 -1
  70. package/lib/components/shared/three/indicators/ElementalColorSystem.js.map +1 -1
  71. package/lib/components/shared/three/indicators/GuardIndicator.js.map +1 -1
  72. package/lib/components/shared/three/scene/CombatArena3D.js.map +1 -1
  73. package/lib/components/shared/three/ui/BodyPartHealthDisplay.js.map +1 -1
  74. package/lib/components/shared/three/ui/CombatReadinessBar.js.map +1 -1
  75. package/lib/components/shared/three/ui/HealthBar.js.map +1 -1
  76. package/lib/components/shared/three/ui/PlayerHUD.js.map +1 -1
  77. package/lib/components/shared/three/ui/SpeedIndicatorHUD.js.map +1 -1
  78. package/lib/components/shared/three/ui/StaminaBar.js.map +1 -1
  79. package/lib/components/shared/three/ui/TechniqueCard.js.map +1 -1
  80. package/lib/components/shared/three/ui/VitalPointOverlayControlsHtml.js.map +1 -1
  81. package/lib/components/shared/ui/SplashScreen.js +2 -2
  82. package/lib/components/shared/ui/VitalPointOverlayControlsPure.js.map +1 -1
  83. package/lib/components/ui/combat/BalanceIndicatorOverlayHtml.js.map +1 -1
  84. package/lib/data/techniques.js.map +1 -1
  85. package/lib/hooks/useCombatTimer.js.map +1 -1
  86. package/lib/hooks/useKeyboardControls.js.map +1 -1
  87. package/lib/hooks/useSkeletalAnimation.js +2 -2
  88. package/lib/hooks/useSkeletalAnimation.js.map +1 -1
  89. package/lib/hooks/useTechniqueSelection.js.map +1 -1
  90. package/lib/hooks/useWebGLContextLossHandler.js.map +1 -1
  91. package/lib/index.js +2 -2
  92. package/lib/systems/CombatSystem.js.map +1 -1
  93. package/lib/systems/EffectCalculator.js.map +1 -1
  94. package/lib/systems/ResponsiveScaling.js.map +1 -1
  95. package/lib/systems/TrigramSystem.js.map +1 -1
  96. package/lib/systems/ai/AIPersonality.js.map +1 -1
  97. package/lib/systems/ai/ArchetypeEnforcer.js.map +1 -1
  98. package/lib/systems/ai/DecisionTree.js.map +1 -1
  99. package/lib/systems/animation/builders/KeyframeConfig.js.map +1 -1
  100. package/lib/systems/animation/builders/KeyframeInterpolation.js.map +1 -1
  101. package/lib/systems/animation/builders/MartialArtsAnimationBuilder.js.map +1 -1
  102. package/lib/systems/animation/builders/SkeletonRig.js +1 -1
  103. package/lib/systems/animation/builders/SkeletonRig.js.map +1 -1
  104. package/lib/systems/animation/catalogs/AttackAnimations.js +1 -1
  105. package/lib/systems/animation/catalogs/AttackAnimations.js.map +1 -1
  106. package/lib/systems/animation/catalogs/BasicAnimations.js +1 -1
  107. package/lib/systems/animation/catalogs/BasicAnimations.js.map +1 -1
  108. package/lib/systems/animation/catalogs/ComboAnimations.js +1 -1
  109. package/lib/systems/animation/catalogs/ComboAnimations.js.map +1 -1
  110. package/lib/systems/animation/catalogs/DarkOpsAnimations.js +1 -1
  111. package/lib/systems/animation/catalogs/DarkOpsAnimations.js.map +1 -1
  112. package/lib/systems/animation/catalogs/DefensiveAnimations.js +2 -11
  113. package/lib/systems/animation/catalogs/DefensiveAnimations.js.map +1 -1
  114. package/lib/systems/animation/catalogs/ElbowKneeAnimations.js +1 -1
  115. package/lib/systems/animation/catalogs/ElbowKneeAnimations.js.map +1 -1
  116. package/lib/systems/animation/catalogs/FootworkSkeletalAnimations.js +1 -1
  117. package/lib/systems/animation/catalogs/FootworkSkeletalAnimations.js.map +1 -1
  118. package/lib/systems/animation/catalogs/GamRedirectionAnimations.js.map +1 -1
  119. package/lib/systems/animation/catalogs/GamStanceAnimations.js.map +1 -1
  120. package/lib/systems/animation/catalogs/GamTechniqueAnimations.js.map +1 -1
  121. package/lib/systems/animation/catalogs/GanStanceAnimations.js +1 -1
  122. package/lib/systems/animation/catalogs/GanStanceAnimations.js.map +1 -1
  123. package/lib/systems/animation/catalogs/GanTechniqueAnimations.js +1 -1
  124. package/lib/systems/animation/catalogs/GanTechniqueAnimations.js.map +1 -1
  125. package/lib/systems/animation/catalogs/GeonStanceAnimations.js +1 -1
  126. package/lib/systems/animation/catalogs/GeonStanceAnimations.js.map +1 -1
  127. package/lib/systems/animation/catalogs/GonTechniqueAnimations.js +1 -1
  128. package/lib/systems/animation/catalogs/GonTechniqueAnimations.js.map +1 -1
  129. package/lib/systems/animation/catalogs/GrapplingAnimations.js +1 -1
  130. package/lib/systems/animation/catalogs/GrapplingAnimations.js.map +1 -1
  131. package/lib/systems/animation/catalogs/JinStanceAnimations.js +1 -1
  132. package/lib/systems/animation/catalogs/JinStanceAnimations.js.map +1 -1
  133. package/lib/systems/animation/catalogs/JinTechniqueAnimations.js +1 -1
  134. package/lib/systems/animation/catalogs/JinTechniqueAnimations.js.map +1 -1
  135. package/lib/systems/animation/catalogs/KickAnimations.js +1 -1
  136. package/lib/systems/animation/catalogs/KickAnimations.js.map +1 -1
  137. package/lib/systems/animation/catalogs/LiStanceAnimations.js +1 -1
  138. package/lib/systems/animation/catalogs/LiStanceAnimations.js.map +1 -1
  139. package/lib/systems/animation/catalogs/LiTechniqueAnimations.js.map +1 -1
  140. package/lib/systems/animation/catalogs/MovementAnimations.js +1 -1
  141. package/lib/systems/animation/catalogs/MovementAnimations.js.map +1 -1
  142. package/lib/systems/animation/catalogs/PunchAnimations.js +1 -1
  143. package/lib/systems/animation/catalogs/PunchAnimations.js.map +1 -1
  144. package/lib/systems/animation/catalogs/SonStanceAnimations.js +1 -1
  145. package/lib/systems/animation/catalogs/SonStanceAnimations.js.map +1 -1
  146. package/lib/systems/animation/catalogs/SonTechniqueAnimations.js +1 -1
  147. package/lib/systems/animation/catalogs/SonTechniqueAnimations.js.map +1 -1
  148. package/lib/systems/animation/catalogs/SpecializedPunchAnimations.js +1 -1
  149. package/lib/systems/animation/catalogs/SpecializedPunchAnimations.js.map +1 -1
  150. package/lib/systems/animation/catalogs/StanceAnimations.js +1 -1
  151. package/lib/systems/animation/catalogs/StanceAnimations.js.map +1 -1
  152. package/lib/systems/animation/catalogs/StanceAttackAnimations.js +215 -272
  153. package/lib/systems/animation/catalogs/StanceAttackAnimations.js.map +1 -1
  154. package/lib/systems/animation/catalogs/StanceIdleAnimations.js +2 -11
  155. package/lib/systems/animation/catalogs/StanceIdleAnimations.js.map +1 -1
  156. package/lib/systems/animation/catalogs/StanceLocomotionAnimations.js +2 -2
  157. package/lib/systems/animation/catalogs/StanceLocomotionAnimations.js.map +1 -1
  158. package/lib/systems/animation/catalogs/StepSkeletalAnimations.js +1 -1
  159. package/lib/systems/animation/catalogs/StepSkeletalAnimations.js.map +1 -1
  160. package/lib/systems/animation/catalogs/TaeJointLockAnimations.js.map +1 -1
  161. package/lib/systems/animation/catalogs/TaeStanceAnimations.js +1 -1
  162. package/lib/systems/animation/catalogs/TaeStanceAnimations.js.map +1 -1
  163. package/lib/systems/animation/core/AnimationOptimizations.js +1 -1
  164. package/lib/systems/animation/core/AnimationOptimizations.js.map +1 -1
  165. package/lib/systems/animation/core/AnimationPriority.js.map +1 -1
  166. package/lib/systems/animation/core/AnimationRegistry.js +11 -209
  167. package/lib/systems/animation/core/AnimationRegistry.js.map +1 -1
  168. package/lib/systems/animation/core/AnimationStateMachine.js +2 -2
  169. package/lib/systems/animation/core/AnimationStateMachine.js.map +1 -1
  170. package/lib/systems/animation/core/TechniqueAnimationMapper.js +1 -1
  171. package/lib/systems/animation/core/TechniqueAnimationMapper.js.map +1 -1
  172. package/lib/systems/animation/core/TechniqueAnimationMapping.js +1 -1
  173. package/lib/systems/animation/core/TechniqueAnimationMapping.js.map +1 -1
  174. package/lib/systems/animation/systems/MuscleActivation.js.map +1 -1
  175. package/lib/systems/bodypart/BodyPartDamageIntegration.js.map +1 -1
  176. package/lib/systems/bodypart/BodyPartHealthSystem.js.map +1 -1
  177. package/lib/systems/bodypart/BodyPartPositionMapping.js.map +1 -1
  178. package/lib/systems/bodypart/InjuryTracker.js.map +1 -1
  179. package/lib/systems/breathing/BreathingDisruptionSystem.js.map +1 -1
  180. package/lib/systems/combat/BalanceSystem.js.map +1 -1
  181. package/lib/systems/combat/BreakingStatusEffects.js.map +1 -1
  182. package/lib/systems/combat/CombatStateSystem.js.map +1 -1
  183. package/lib/systems/combat/GrappleSystem.js.map +1 -1
  184. package/lib/systems/combat/painConsciousnessUtils.js.map +1 -1
  185. package/lib/systems/combat/typeGuards.js.map +1 -1
  186. package/lib/systems/index.js +2 -2
  187. package/lib/systems/movement/integration.js.map +1 -1
  188. package/lib/systems/physics/AttackMovementPhysics.js.map +1 -1
  189. package/lib/systems/physics/CollisionDetection.js +1 -1
  190. package/lib/systems/physics/CollisionDetection.js.map +1 -1
  191. package/lib/systems/physics/KnockbackPhysics.js.map +1 -1
  192. package/lib/systems/physics/MovementPhysics.js.map +1 -1
  193. package/lib/systems/physics/PhysicalReachCalculator.js.map +1 -1
  194. package/lib/systems/physics/SpeedModifierSystem.js.map +1 -1
  195. package/lib/systems/trigram/KoreanTechniques.js.map +1 -1
  196. package/lib/systems/trigram/StanceManager.js.map +1 -1
  197. package/lib/systems/trigram/TransitionCalculator.js.map +1 -1
  198. package/lib/systems/trigram/TrigramCalculator.js.map +1 -1
  199. package/lib/systems/trigram/techniques/TechniqueConfig.js.map +1 -1
  200. package/lib/systems/trigram/techniques/index.js.map +1 -1
  201. package/lib/systems/trigram/types.js.map +1 -1
  202. package/lib/systems/vitalpoint/DamageCalculator.js.map +1 -1
  203. package/lib/systems/vitalpoint/KoreanAnatomy.js.map +1 -1
  204. package/lib/systems/vitalpoint/KoreanVitalPoints.js.map +1 -1
  205. package/lib/systems/vitalpoint/MeridianVitalPointMapping.js.map +1 -1
  206. package/lib/types/constants/designSystem.js.map +1 -1
  207. package/lib/utils/colorHelpers.js.map +1 -1
  208. package/lib/utils/colorUtils.js.map +1 -1
  209. package/lib/utils/controlMapping.js.map +1 -1
  210. package/lib/utils/deviceDetection.js.map +1 -1
  211. package/lib/utils/effectUtils.js.map +1 -1
  212. package/lib/utils/fabricTextures.js.map +1 -1
  213. package/lib/utils/htmlOverlayHelpers.js.map +1 -1
  214. package/lib/utils/performance/PerformanceMonitor.js.map +1 -1
  215. package/lib/utils/performance/PerformanceOverlay3D.js.map +1 -1
  216. package/lib/utils/safeAreaUtils.js.map +1 -1
  217. package/lib/utils/stanceHelpers.js.map +1 -1
  218. package/lib/utils/visualEffects.js.map +1 -1
  219. package/package.json +9 -9
@@ -1 +1 @@
1
- {"version":3,"file":"CombatSystem.js","names":[],"sources":["../../src/systems/CombatSystem.ts"],"sourcesContent":["import * as THREE from \"three\";\nimport { getArchetypePhysicalAttributes } from \"../data/archetypePhysicalAttributes\";\nimport { getTechniqueById } from \"../data/techniques\";\nimport { BodyRegion, CombatAttackType, CombatState, DamageType, GrappleTarget } from \"../types\";\nimport { VitalPointCategory, VitalPointSeverity } from \"../types/common\";\nimport { BASE_STAMINA_REGEN_RATE } from \"../types/physicsConstants\";\nimport { Technique } from \"../types/technique\";\nimport { calculateDistance3D } from \"../utils/math\";\nimport { calculateBodyRadius } from \"../utils/skeletonScaling\";\nimport type { DefensiveAnimationType } from \"./animation\";\nimport {\n AnimationType,\n calculateSpeedModifierForDamage,\n determineAnimationTypeForTechnique,\n getAdjustedAnimationDuration,\n getAnimationNameForType,\n isWithinHitWindow,\n} from \"./animation\";\nimport { applyDamageToBodyParts } from \"./bodypart/BodyPartDamageIntegration\";\nimport { playerInjuryManager } from \"./bodypart\";\nimport {\n applyBreathingDisruptionFromVitalPoint,\n applyBreathingDisruptionFromTorsoDamage,\n BreathingDisruptionSystem,\n causesBreathingDisruption,\n updateBreathingDisruption,\n} from \"./breathing\";\nimport BalanceSystem from \"./combat/BalanceSystem\";\nimport ConsciousnessSystem from \"./combat/ConsciousnessSystem\";\nimport {\n extractVitalPointCategory,\n isHeadTraumaHit,\n} from \"./combat/painConsciousnessUtils\";\nimport PainResponseSystem, {\n ShockPainEffect,\n} from \"./combat/PainResponseSystem\";\nimport { CombatResult, CombatSystemInterface } from \"./combat/types\";\nimport {\n CollisionDetection,\n KnockbackPhysics,\n physicalReachCalculator,\n type KnockbackConfig,\n} from \"./physics\";\nimport { PlayerState } from \"./player\";\nimport {\n addEffectsToPlayer,\n getEffectModifiers,\n removeExpiredEffects,\n} from \"./PlayerEffectManager\";\nimport { TRIGRAM_TECHNIQUES } from \"./trigram\";\nimport { TrigramSystem } from \"./TrigramSystem\";\nimport { StatusEffect } from \"./types\";\nimport { KoreanTechnique, VitalPointHitResult } from \"./vitalpoint/types\";\nimport { VitalPointSystem } from \"./VitalPointSystem\";\nimport { GrappleSystem } from \"./combat/GrappleSystem\";\n\n/**\n * Enhanced Combat System with Pain Response and Consciousness integration.\n *\n * Integrates realistic pain accumulation and consciousness tracking for\n * progressive combat impairment.\n */\nexport class CombatSystem implements CombatSystemInterface {\n private vitalPointSystem: VitalPointSystem;\n protected trigramSystem: TrigramSystem;\n private painSystem: PainResponseSystem;\n private consciousnessSystem: ConsciousnessSystem;\n private balanceSystem: BalanceSystem;\n private knockbackPhysics: KnockbackPhysics;\n private collisionDetection: CollisionDetection;\n private grappleSystem: GrappleSystem;\n\n // Track shock pain effects per player\n private shockPainEffects: Map<string, ShockPainEffect>;\n // Track last head trauma time per player for consciousness recovery\n private lastHeadTraumaTime: Map<string, number>;\n\n // Vital point severity thresholds\n private readonly SEVERITY_MAJOR_THRESHOLD = 30;\n private readonly SEVERITY_MODERATE_THRESHOLD = 20;\n\n constructor() {\n this.vitalPointSystem = new VitalPointSystem();\n this.trigramSystem = new TrigramSystem();\n this.painSystem = new PainResponseSystem();\n this.consciousnessSystem = new ConsciousnessSystem();\n this.balanceSystem = new BalanceSystem();\n this.knockbackPhysics = new KnockbackPhysics();\n this.collisionDetection = new CollisionDetection();\n this.grappleSystem = new GrappleSystem();\n this.shockPainEffects = new Map();\n this.lastHeadTraumaTime = new Map();\n }\n\n /**\n * Cleanup per-player combat state.\n *\n * Call this when a player permanently leaves the match or when\n * match-level cleanup is performed to avoid unbounded Map growth.\n *\n * @param playerId - ID of the player to cleanup\n *\n * @korean 플레이어데이터정리\n */\n public cleanupPlayerData(playerId: string): void {\n this.shockPainEffects.delete(playerId);\n this.lastHeadTraumaTime.delete(playerId);\n }\n\n /**\n * Dispose of all combat system resources.\n *\n * **Korean**: 전투 시스템 자원 정리\n *\n * Cleans up Three.js resources (geometries, raycaster) used by the collision\n * detection system to prevent memory leaks. Should be called when the\n * CombatSystem is destroyed or reinitialized.\n *\n * @korean 전투시스템자원정리\n */\n public dispose(): void {\n // Dispose collision detection resources (cached geometries, raycaster)\n this.collisionDetection.dispose();\n }\n\n /**\n * Get the balance system instance for fall checking.\n *\n * @returns BalanceSystem instance\n * @korean 균형시스템가져오기\n */\n public getBalanceSystem(): BalanceSystem {\n return this.balanceSystem;\n }\n\n /**\n * Get the consciousness system instance for fall checking.\n *\n * @returns ConsciousnessSystem instance\n * @korean 의식시스템가져오기\n */\n public getConsciousnessSystem(): ConsciousnessSystem {\n return this.consciousnessSystem;\n }\n\n /**\n * Get the collision detection system instance.\n *\n * @returns CollisionDetection instance\n * @korean 충돌감지시스템가져오기\n */\n public getCollisionDetection(): CollisionDetection {\n return this.collisionDetection;\n }\n\n /**\n * Calculate knockback physics for combat hit.\n *\n * **Korean**: 밀침 계산 (Calculate Knockback)\n *\n * Determines knockback displacement, duration, and fall state based on:\n * - Attack damage amount\n * - Defender's balance state\n * - Defender's stance resistance\n * - Attack direction vector\n *\n * @param attacker - Attacking player state\n * @param defender - Defending player state\n * @param damage - Total damage dealt\n * @returns Knockback information or undefined if no knockback\n *\n * @example\n * ```typescript\n * const knockback = this.calculateKnockback(attacker, defender, 80);\n * // Returns: { displacement: {x:2.5,y:0,z:0}, duration:0.8, recoveryWindow:0.7, shouldFall:false }\n * ```\n *\n * @private\n * @korean 밀침계산\n */\n private calculateKnockback(\n attacker: PlayerState,\n defender: PlayerState,\n damage: number,\n ): CombatResult[\"knockback\"] {\n const attackDirection = new THREE.Vector3(\n defender.position.x - attacker.position.x,\n 0, // Keep knockback on horizontal plane\n defender.position.y - attacker.position.y,\n );\n\n // If attacker and defender are at the exact same position, skip knockback to avoid NaN direction\n if (attackDirection.lengthSq() === 0) {\n return undefined;\n }\n\n attackDirection.normalize();\n\n const config: KnockbackConfig = {\n force: damage * 10, // Convert damage to force (arbitrary scaling)\n direction: attackDirection,\n duration: 0, // Will be calculated by physics engine\n balanceState: {\n current: defender.balance,\n max: 100, // Assuming max balance is always 100\n },\n currentStance: defender.currentStance,\n };\n\n const result = this.knockbackPhysics.calculateKnockback(config, damage);\n\n return {\n displacement: {\n x: result.displacement.x,\n y: result.displacement.y,\n z: result.displacement.z,\n },\n duration: result.duration,\n recoveryWindow: result.recoveryWindow,\n shouldFall: result.shouldFall,\n };\n }\n\n /**\n * Fix: Update resolveAttack to match interface signature and add animation-aware hit detection\n *\n * **Korean**: 공격 해결 (애니메이션 인식)\n *\n * Integrates animation timing and physical reach calculation for reality-based hit detection.\n * Hits only register when:\n * 1. Animation is in hit window (extension phase)\n * 2. Attacker is within effective reach based on limb length and animation\n * 3. Existing accuracy and stance checks pass\n *\n * @param attacker - Attacking player state\n * @param defender - Defending player state\n * @param technique - Technique being executed\n * @param targetedVitalPointId - Optional specific vital point target\n * @param animationContext - Optional animation timing context for reality-based hit detection\n * @returns Combat result with hit/miss and damage information\n */\n resolveAttack(\n attacker: PlayerState,\n defender: PlayerState,\n technique: KoreanTechnique,\n targetedVitalPointId?: string,\n animationContext?: {\n animationType: AnimationType;\n currentTime: number;\n },\n ): CombatResult {\n const timestamp = Date.now();\n\n if (!this.canExecuteTechnique(attacker, technique)) {\n return {\n hit: false,\n damage: 0,\n criticalHit: false,\n vitalPointHit: false,\n effects: [],\n timestamp,\n technique,\n attacker,\n defender,\n success: false,\n isCritical: false,\n isBlocked: false,\n };\n }\n\n if (attacker.combatState === CombatState.GRAPPLED) {\n return {\n hit: false,\n damage: 0,\n criticalHit: false,\n vitalPointHit: false,\n effects: [],\n timestamp,\n technique,\n attacker,\n defender,\n success: false,\n isCritical: false,\n isBlocked: false,\n };\n }\n\n if (animationContext) {\n const { animationType, currentTime } = animationContext;\n\n if (!isWithinHitWindow(animationType, currentTime)) {\n return {\n hit: false,\n damage: 0,\n criticalHit: false,\n vitalPointHit: false,\n effects: [],\n timestamp,\n technique,\n attacker,\n defender,\n success: false,\n isCritical: false,\n isBlocked: false,\n };\n }\n\n const attackerPhysical = getArchetypePhysicalAttributes(\n attacker.archetype,\n );\n const reachResult = physicalReachCalculator.calculateReach(\n attackerPhysical,\n animationType,\n currentTime,\n attacker.currentStance,\n technique.reachConfig, // Pass reachConfig for hybrid reach calculation\n );\n\n // Physics-first: Position type is 2D in METERS\n // No conversion needed - positions are already in meters\n const centerToCenterDistance = calculateDistance3D(\n [attacker.position.x, attacker.position.y, 0],\n [defender.position.x, defender.position.y, 0],\n );\n\n // Note: PhysicalReachCalculator already includes attacker body pivot/offset in reach calculation\n // (shoulder offset for punches, hip rotation for kicks), so we only subtract defender radius\n // to avoid double-counting the attacker's body dimension.\n // 타격 거리 계산: 수비자 몸체 반경만 제외 (공격자 몸체 오프셋은 이미 도달 거리에 포함됨)\n const defenderPhysical = getArchetypePhysicalAttributes(\n defender.archetype,\n );\n const defenderBodyRadius = calculateBodyRadius(defenderPhysical);\n\n // Reach is calculated from attacker center to striking limb surface (includes body pivot),\n // so we measure from attacker center to defender surface (subtracting defender radius only).\n // 유효 거리 = 중심간 거리 - 수비자 몸체 반경\n const distance = Math.max(0, centerToCenterDistance - defenderBodyRadius);\n\n if (distance > reachResult.effectiveReach) {\n return {\n hit: false,\n damage: 0,\n criticalHit: false,\n vitalPointHit: false,\n effects: [],\n timestamp,\n technique,\n attacker,\n defender,\n success: false,\n isCritical: false,\n isBlocked: false,\n };\n }\n }\n\n // Fix: Use correct method signature\n const stanceEffectiveness = this.trigramSystem.calculateStanceEffectiveness(\n attacker.currentStance,\n defender.currentStance,\n );\n\n // Calculate base hit chance\n const baseAccuracy = technique.accuracy * stanceEffectiveness;\n const hitRoll = Math.random();\n const hit = hitRoll <= baseAccuracy;\n\n if (!hit) {\n return {\n hit: false,\n damage: 0,\n criticalHit: false,\n vitalPointHit: false,\n effects: [],\n timestamp,\n technique,\n attacker,\n defender,\n success: false,\n isCritical: false,\n isBlocked: false,\n };\n }\n\n // Special handling for grapple techniques\n if (technique.type === CombatAttackType.GRAPPLE) {\n const grappleResult = this.handleGrappleTechnique(\n attacker,\n defender,\n technique,\n timestamp\n );\n\n // Grapples don't deal immediate damage, they establish control\n // Small damage on successful grapple to represent the initial impact\n const grappleDamage = grappleResult.grappleSuccess\n ? technique.damage * 0.3\n : 0;\n\n return {\n hit: grappleResult.grappleSuccess,\n damage: grappleDamage,\n criticalHit: false,\n vitalPointHit: false,\n effects: [],\n timestamp,\n technique,\n attacker: grappleResult.updatedAttacker,\n defender: grappleResult.updatedDefender,\n success: grappleResult.grappleSuccess,\n isCritical: false,\n isBlocked: false,\n animation: this.getAnimationInfoForTechnique(technique),\n };\n }\n\n // Process vital point hit if targeted (with archetype parameters)\n let vitalPointResult: VitalPointHitResult | null = null;\n if (targetedVitalPointId) {\n vitalPointResult = this.processVitalPointHit(\n targetedVitalPointId,\n technique.damage ?? 15,\n attacker,\n defender,\n );\n }\n\n const damageResult = this.calculateDamage(\n technique,\n attacker,\n defender,\n vitalPointResult ?? {\n hit: false,\n damage: 0,\n effects: [],\n severity: VitalPointSeverity.MINOR,\n },\n );\n\n const critRoll = Math.random();\n const isCritical = critRoll <= (technique.critChance ?? 0.1);\n\n const animationInfo = this.getAnimationInfoForTechnique(technique);\n\n // Calculate knockback physics (밀침 물리)\n const knockbackInfo = this.calculateKnockback(\n attacker,\n defender,\n damageResult.totalDamage,\n );\n\n return {\n hit: true,\n damage: damageResult.totalDamage,\n criticalHit: isCritical,\n vitalPointHit: vitalPointResult?.hit ?? false,\n effects: damageResult.effectsApplied,\n timestamp,\n technique,\n attacker,\n defender,\n success: true,\n isCritical: vitalPointResult?.hit ?? false,\n isBlocked: false,\n targetedVitalPointId, // Pass through the targeted vital point ID\n animation: animationInfo, // Add animation information\n knockback: knockbackInfo, // Add knockback information\n };\n }\n\n /**\n * Get animation information for a technique\n *\n * Determines the skeletal animation to play, duration, and speed modifier\n * based on technique configuration or automatic determination.\n *\n * @param technique - Korean technique to execute\n * @returns Animation information or undefined\n *\n * @private\n * @korean 기술애니메이션정보가져오기\n */\n private getAnimationInfoForTechnique(\n technique: KoreanTechnique,\n ): CombatResult[\"animation\"] {\n // Check if technique has explicit animation config (from Technique interface)\n // KoreanTechnique may not have animation field, so check the technique data\n const techniqueData = this.getTechniqueData(technique);\n\n let animationType;\n let speedModifier;\n\n if (techniqueData?.animation) {\n animationType = techniqueData.animation.type;\n speedModifier = techniqueData.animation.speedModifier;\n } else {\n const techniqueName =\n technique.name?.english || technique.englishName || \"\";\n const techniqueId = technique.id || \"\";\n const damageType = technique.damageType || \"\";\n\n animationType = determineAnimationTypeForTechnique(\n techniqueName,\n techniqueId,\n damageType,\n );\n\n speedModifier = calculateSpeedModifierForDamage(technique.damage || 15);\n }\n\n const animationName = getAnimationNameForType(animationType);\n const duration = getAdjustedAnimationDuration(animationName, speedModifier);\n const techniqueDisplayName =\n technique.name?.korean || technique.koreanName || technique.id;\n\n return {\n animationName,\n duration,\n speedModifier,\n techniqueDisplayName,\n };\n }\n\n /**\n * Get Technique data if this KoreanTechnique has an associated Technique definition\n *\n * @param technique - Korean technique\n * @returns Technique data or null\n *\n * @private\n * @korean 기술데이터가져오기\n */\n private getTechniqueData(technique: KoreanTechnique): Technique | null {\n return getTechniqueById(technique.id) ?? null;\n }\n\n /**\n * Handle grapple technique execution.\n *\n * **Korean**: 잡기 기술 처리 (Handle Grapple Technique)\n *\n * Initiates or transitions grapple control based on technique.\n *\n * @param attacker - Player executing grapple\n * @param defender - Target player\n * @param technique - Grapple technique being used\n * @param currentTime - Current game time in milliseconds\n * @returns Updated player states with grapple control\n */\n handleGrappleTechnique(\n attacker: PlayerState,\n defender: PlayerState,\n technique: KoreanTechnique,\n currentTime: number\n ): {\n updatedAttacker: PlayerState;\n updatedDefender: PlayerState;\n grappleSuccess: boolean;\n } {\n // Determine grapple target from technique\n const target = this.getGrappleTargetFromTechnique(technique);\n\n // Attempt grapple\n const result = this.grappleSystem.attemptGrapple(\n attacker,\n defender,\n target,\n currentTime\n );\n\n if (result.success && result.grappleControl) {\n // Grapple succeeded - update both players\n return {\n updatedAttacker: {\n ...attacker,\n combatState: CombatState.GRAPPLING,\n grappleControl: result.grappleControl,\n stamina: Math.max(0, attacker.stamina - result.staminaCost),\n },\n updatedDefender: {\n ...defender,\n combatState: CombatState.GRAPPLED,\n grappleControl: result.grappleControl,\n },\n grappleSuccess: true,\n };\n }\n\n // Grapple failed\n return {\n updatedAttacker: {\n ...attacker,\n stamina: Math.max(0, attacker.stamina - result.staminaCost),\n },\n updatedDefender: defender,\n grappleSuccess: false,\n };\n }\n\n /**\n * Determine grapple target from technique characteristics.\n *\n * **Korean**: 기술에서 잡기 목표 결정 (Determine Grapple Target from Technique)\n *\n * @private\n */\n private getGrappleTargetFromTechnique(\n technique: KoreanTechnique\n ): GrappleTarget {\n // Check technique name/ID for hints (both English and Korean)\n const techName = (\n technique.name?.english ||\n technique.englishName ||\n \"\"\n ).toLowerCase();\n const techNameKorean = (\n technique.name?.korean ||\n technique.koreanName ||\n \"\"\n );\n const techId = technique.id.toLowerCase();\n\n // Check for wrist/hand - 손목 (sonmok)\n if (\n techName.includes(\"wrist\") ||\n techId.includes(\"wrist\") ||\n techNameKorean.includes(\"손목\")\n ) {\n return GrappleTarget.HAND;\n }\n\n // Check for arm - 팔 (pal)\n if (\n techName.includes(\"arm\") ||\n techId.includes(\"arm\") ||\n techNameKorean.includes(\"팔\")\n ) {\n return GrappleTarget.ARM;\n }\n\n // Check for leg - 다리 (dari)\n if (\n techName.includes(\"leg\") ||\n techId.includes(\"leg\") ||\n techNameKorean.includes(\"다리\")\n ) {\n return GrappleTarget.LEG;\n }\n\n // Check for neck - 목 (mok)\n if (\n techName.includes(\"neck\") ||\n techId.includes(\"neck\") ||\n techNameKorean.includes(\"목\")\n ) {\n return GrappleTarget.NECK;\n }\n\n // Check for both arms - 양팔 (yangpal)\n if (\n techName.includes(\"both\") ||\n techName.includes(\"double\") ||\n techId.includes(\"both\") ||\n techNameKorean.includes(\"양팔\") ||\n techNameKorean.includes(\"쌍\")\n ) {\n return GrappleTarget.BOTH_ARMS;\n }\n\n // Check for torso/body/hip - 몸통 (momtong), 허리 (heori), 몸 (mom)\n if (\n techName.includes(\"torso\") ||\n techName.includes(\"body\") ||\n techName.includes(\"hip\") ||\n techId.includes(\"torso\") ||\n techNameKorean.includes(\"몸통\") ||\n techNameKorean.includes(\"허리\") ||\n techNameKorean.includes(\"몸\")\n ) {\n return GrappleTarget.TORSO;\n }\n\n // Default to arm for unspecified grapples\n return GrappleTarget.ARM;\n }\n\n /**\n * Update grapple state for players over time.\n *\n * **Korean**: 잡기 상태 업데이트 (Update Grapple State)\n *\n * @param controller - Player maintaining control\n * @param target - Player being controlled\n * @param deltaTime - Time elapsed in seconds\n * @param currentTime - Current game time in milliseconds\n * @returns Updated player states\n */\n updateGrappleState(\n controller: PlayerState,\n target: PlayerState,\n deltaTime: number,\n currentTime: number\n ): {\n updatedController: PlayerState;\n updatedTarget: PlayerState;\n } {\n if (!controller.grappleControl) {\n // No active grapple\n return {\n updatedController: controller,\n updatedTarget: target,\n };\n }\n\n // Validate grapple control consistency\n if (\n controller.grappleControl.controllerId !== controller.id ||\n controller.grappleControl.targetId !== target.id\n ) {\n // Inconsistent state - break grapple\n return {\n updatedController: {\n ...controller,\n combatState: CombatState.IDLE,\n grappleControl: null,\n },\n updatedTarget: {\n ...target,\n combatState: CombatState.IDLE,\n grappleControl: null,\n },\n };\n }\n\n // Update grapple control\n const updatedControl = this.grappleSystem.updateGrapple(\n controller.grappleControl,\n controller,\n target,\n deltaTime,\n currentTime\n );\n\n if (!updatedControl) {\n // Grapple broken - reset both players\n return {\n updatedController: {\n ...controller,\n combatState: CombatState.IDLE,\n grappleControl: null,\n },\n updatedTarget: {\n ...target,\n combatState: CombatState.IDLE,\n grappleControl: null,\n },\n };\n }\n\n // Deduct stamina from controller\n const staminaCost = updatedControl.staminaCostPerSecond * deltaTime;\n\n return {\n updatedController: {\n ...controller,\n grappleControl: updatedControl,\n stamina: Math.max(0, controller.stamina - staminaCost),\n },\n updatedTarget: {\n ...target,\n grappleControl: updatedControl,\n },\n };\n }\n\n /**\n * Fix: Make applyCombatResult non-static instance method with effect application\n * Enhanced with Pain Response and Consciousness System integration\n */\n applyCombatResult(\n result: CombatResult,\n attacker: PlayerState,\n defender: PlayerState,\n ): { updatedAttacker: PlayerState; updatedDefender: PlayerState } {\n // Start with base result\n const { updatedAttacker, updatedDefender: initialDefender } =\n CombatSystem.applyCombatResult(result, attacker, defender);\n let updatedDefender = initialDefender;\n\n if (result.hit && result.damage > 0) {\n // Determine vital point category and severity from hit result\n const category = this.getVitalPointCategory(result);\n const severity = this.getVitalPointSeverity(result);\n\n // Apply pain from damage\n const { player: defenderWithPain, shockEffect: newShockEffect } =\n this.painSystem.applyPain(\n updatedDefender,\n result.damage,\n severity,\n category,\n );\n updatedDefender = defenderWithPain;\n\n // Store shock pain effect if triggered\n if (newShockEffect) {\n this.shockPainEffects.set(updatedDefender.id, newShockEffect);\n }\n\n // Check for pain overload stun\n if (this.painSystem.shouldTriggerStun(updatedDefender)) {\n updatedDefender = {\n ...updatedDefender,\n isStunned: true,\n };\n }\n\n // Apply consciousness damage for head/neurological hits\n if (this.isHeadTrauma(result, category)) {\n updatedDefender = this.consciousnessSystem.applyDamage(\n updatedDefender,\n result.damage,\n category,\n );\n\n // Track head trauma time for recovery gating\n this.lastHeadTraumaTime.set(updatedDefender.id, Date.now());\n\n // Check incapacitation threshold\n if (\n this.consciousnessSystem.isAtIncapacitationThreshold(updatedDefender)\n ) {\n updatedDefender = {\n ...updatedDefender,\n isStunned: true,\n // Could add helpless duration tracking here if needed\n };\n }\n }\n\n // Apply pain and consciousness effects to stats\n const currentShockEffect = this.shockPainEffects.get(updatedDefender.id);\n updatedDefender = this.painSystem.applyEffects(\n updatedDefender,\n currentShockEffect,\n );\n updatedDefender = this.consciousnessSystem.applyEffects(updatedDefender);\n\n // Apply balance disruption from the hit\n // Determine body region from vital point or use default\n const bodyRegion = this.getBodyRegionFromResult(result);\n updatedDefender = this.balanceSystem.disruptBalance(\n updatedDefender,\n result.damage,\n bodyRegion,\n );\n\n // Apply breathing disruption for torso strikes\n if (result.vitalPointHit && result.targetedVitalPointId) {\n // Vital point strike to torso\n const vitalPoint = this.vitalPointSystem.getVitalPointById(\n result.targetedVitalPointId,\n );\n if (vitalPoint && causesBreathingDisruption(vitalPoint.id)) {\n updatedDefender = applyBreathingDisruptionFromVitalPoint(\n updatedDefender,\n vitalPoint,\n Date.now(),\n );\n }\n } else if (bodyRegion === BodyRegion.TORSO && result.damage >= 10) {\n // General torso damage (non-vital point hits)\n // Apply breathing disruption if damage is significant enough\n // Note: Solar plexus detection is best-effort; vital point system is primary mechanism\n const techniqueId = (result.technique?.id ?? \"\").toLowerCase();\n const isSolarPlexusArea = \n techniqueId.includes(\"solar\") ||\n techniqueId.includes(\"myeongchi\");\n updatedDefender = applyBreathingDisruptionFromTorsoDamage(\n updatedDefender,\n result.damage,\n isSolarPlexusArea,\n Date.now(),\n );\n }\n }\n\n return { updatedAttacker, updatedDefender };\n }\n\n /**\n * Determines if a hit caused head trauma (affects consciousness).\n *\n * @param result - Combat result\n * @param category - Vital point category\n * @returns True if hit should affect consciousness\n */\n private isHeadTrauma(\n result: CombatResult,\n category?: VitalPointCategory,\n ): boolean {\n return isHeadTraumaHit(result, category);\n }\n\n /**\n * Extracts vital point category from combat result.\n *\n * @param result - Combat result\n * @returns Vital point category if available\n */\n private getVitalPointCategory(\n result: CombatResult,\n ): VitalPointCategory | undefined {\n return extractVitalPointCategory(result);\n }\n\n /**\n * Extracts vital point severity from combat result.\n *\n * @param result - Combat result\n * @returns Vital point severity if critical hit\n */\n private getVitalPointSeverity(\n result: CombatResult,\n ): VitalPointSeverity | undefined {\n if (result.vitalPointHit) {\n if (result.isCritical) {\n return VitalPointSeverity.CRITICAL;\n }\n if (result.damage > this.SEVERITY_MAJOR_THRESHOLD) {\n return VitalPointSeverity.MAJOR;\n }\n if (result.damage > this.SEVERITY_MODERATE_THRESHOLD) {\n return VitalPointSeverity.MODERATE;\n }\n return VitalPointSeverity.MINOR;\n }\n return undefined;\n }\n\n /**\n * Determines body region from combat result for balance disruption.\n *\n * Maps vital points to body regions for balance system integration.\n * Uses string matching on vital point IDs as a pragmatic heuristic since\n * VitalPoint interface doesn't currently include a bodyRegion property.\n *\n * Future improvement: Add bodyRegion: BodyRegion to VitalPoint interface\n * for more robust region mapping without string pattern matching.\n *\n * @param result - Combat result\n * @returns Body region that was struck\n * @private\n * @korean 신체부위결정\n */\n private getBodyRegionFromResult(result: CombatResult): BodyRegion {\n // If we have a targeted vital point, try to determine region\n if (result.targetedVitalPointId) {\n const vitalPoint = this.vitalPointSystem.getVitalPointById(\n result.targetedVitalPointId,\n );\n\n if (vitalPoint) {\n const pointId = vitalPoint.id.toLowerCase();\n\n // Check for leg/lower body targets\n if (\n pointId.includes(\"leg\") ||\n pointId.includes(\"knee\") ||\n pointId.includes(\"ankle\") ||\n pointId.includes(\"thigh\")\n ) {\n return BodyRegion.LEFT_LEG; // Generic leg for balance disruption\n }\n\n // Check for head targets\n if (\n pointId.includes(\"head\") ||\n pointId.includes(\"temple\") ||\n pointId.includes(\"jaw\") ||\n pointId.includes(\"nose\")\n ) {\n return BodyRegion.HEAD;\n }\n\n // Check for arm targets\n if (\n pointId.includes(\"arm\") ||\n pointId.includes(\"elbow\") ||\n pointId.includes(\"wrist\") ||\n pointId.includes(\"shoulder\")\n ) {\n return BodyRegion.LEFT_ARM;\n }\n }\n }\n\n // Default to torso for general strikes\n return BodyRegion.TORSO;\n }\n\n /**\n * Updates player states for recovery (pain dissipation, consciousness recovery, balance recovery).\n * Call this regularly in game loop.\n *\n * @param player - Player to update\n * @param deltaTime - Time elapsed since last update (ms)\n * @returns Updated player state\n */\n applyRecovery(player: PlayerState, deltaTime: number): PlayerState {\n let updatedPlayer = player;\n\n // Apply pain recovery\n updatedPlayer = this.painSystem.applyDissipation(updatedPlayer, deltaTime);\n\n // Apply consciousness recovery (only if enough time since head trauma)\n const lastTrauma = this.lastHeadTraumaTime.get(player.id);\n updatedPlayer = this.consciousnessSystem.applyRecovery(\n updatedPlayer,\n deltaTime,\n lastTrauma,\n );\n\n // Apply balance recovery\n updatedPlayer = this.balanceSystem.applyRecovery(updatedPlayer, deltaTime);\n\n // Clean up expired shock pain effects\n const shockEffect = this.shockPainEffects.get(player.id);\n if (shockEffect) {\n const elapsed = Date.now() - shockEffect.startTime;\n if (elapsed >= shockEffect.duration) {\n this.shockPainEffects.delete(player.id);\n }\n }\n\n return updatedPlayer;\n }\n\n /**\n * Static version for backwards compatibility with comprehensive effect application\n * Enhanced with Pain Response and Consciousness System integration\n * Updated to apply damage to body parts for 8-body-part health visualization\n */\n static applyCombatResult(\n result: CombatResult,\n attacker: PlayerState,\n defender: PlayerState,\n ): { updatedAttacker: PlayerState; updatedDefender: PlayerState } {\n let updatedDefender = defender;\n let updatedAttacker = attacker;\n\n if (result.hit) {\n // Determine body region from technique or use random distribution\n const bodyRegion = CombatSystem.getBodyRegionFromTechnique(\n result.technique,\n );\n\n // Apply damage to body parts (this also updates aggregate health)\n updatedDefender = applyDamageToBodyParts(\n defender,\n result.damage,\n bodyRegion,\n );\n\n // Record injury for trauma visualization (외상 시각화)\n // Use per-player injury tracking to prevent mixing injuries between characters\n // Automatically tracks bruising, cuts, and blood effects based on damage type\n // Records injury even without specific technique (defaults to BRUISE)\n if (result.damage > 0) {\n const defenderInjuryIntegration = playerInjuryManager.getIntegrationForPlayer(defender.id);\n defenderInjuryIntegration.recordCombatDamage({\n damage: result.damage,\n bodyRegion,\n damageType: (result.technique?.damageType as DamageType) || undefined,\n });\n }\n\n // Update tracking stats (applyDamageToBodyParts already sets health)\n updatedDefender = {\n ...updatedDefender,\n totalDamageReceived:\n updatedDefender.totalDamageReceived + result.damage,\n hitsTaken: updatedDefender.hitsTaken + 1,\n };\n\n if (result.effects && result.effects.length > 0) {\n updatedDefender = addEffectsToPlayer(updatedDefender, result.effects);\n }\n\n if (result.vitalPointHit) {\n updatedAttacker = {\n ...updatedAttacker,\n vitalPointHits: attacker.vitalPointHits + 1,\n };\n }\n\n if (result.isCritical) {\n updatedAttacker = {\n ...updatedAttacker,\n perfectStrikes: attacker.perfectStrikes + 1,\n };\n }\n }\n\n updatedAttacker = {\n ...updatedAttacker,\n ki: Math.max(0, attacker.ki - 5),\n stamina: Math.max(0, attacker.stamina - 10),\n totalDamageDealt:\n attacker.totalDamageDealt + (result.hit ? result.damage : 0),\n hitsLanded: attacker.hitsLanded + (result.hit ? 1 : 0),\n };\n\n return { updatedAttacker, updatedDefender };\n }\n\n /**\n * Determine body region from technique name or type\n *\n * **Korean**: 기술에서 신체 영역 결정\n *\n * @param technique - The technique used in the attack\n * @returns Body region to apply damage to\n */\n private static getBodyRegionFromTechnique(\n technique?: KoreanTechnique,\n ): BodyRegion {\n if (!technique) {\n return BodyRegion.TORSO;\n }\n\n const techniqueName = (\n technique.name?.english ||\n technique.englishName ||\n \"\"\n ).toLowerCase();\n const techniqueId = technique.id?.toLowerCase() || \"\";\n\n // Head/face targeting techniques\n if (\n techniqueName.includes(\"head\") ||\n techniqueName.includes(\"temple\") ||\n techniqueName.includes(\"jaw\") ||\n techniqueName.includes(\"face\") ||\n techniqueId.includes(\"head\") ||\n techniqueId.includes(\"skull\")\n ) {\n return BodyRegion.HEAD;\n }\n\n // Neck targeting techniques\n if (\n techniqueName.includes(\"neck\") ||\n techniqueName.includes(\"throat\") ||\n techniqueName.includes(\"choke\") ||\n techniqueId.includes(\"neck\")\n ) {\n return BodyRegion.NECK;\n }\n\n // Leg targeting techniques\n if (\n techniqueName.includes(\"kick\") ||\n techniqueName.includes(\"sweep\") ||\n techniqueName.includes(\"leg\") ||\n techniqueName.includes(\"knee\") ||\n techniqueId.includes(\"kick\") ||\n techniqueId.includes(\"leg\")\n ) {\n return Math.random() < 0.5 ? BodyRegion.LEFT_LEG : BodyRegion.RIGHT_LEG;\n }\n\n // Arm targeting techniques\n if (\n techniqueName.includes(\"arm\") ||\n techniqueName.includes(\"shoulder\") ||\n techniqueName.includes(\"elbow\") ||\n techniqueId.includes(\"arm\")\n ) {\n return Math.random() < 0.5 ? BodyRegion.LEFT_ARM : BodyRegion.RIGHT_ARM;\n }\n\n // Punch/strike techniques typically target torso\n if (\n techniqueName.includes(\"punch\") ||\n techniqueName.includes(\"strike\") ||\n techniqueName.includes(\"jab\") ||\n techniqueName.includes(\"cross\") ||\n techniqueName.includes(\"hook\") ||\n techniqueName.includes(\"uppercut\")\n ) {\n // Mix of torso and head for punches\n return Math.random() < 0.7 ? BodyRegion.TORSO : BodyRegion.HEAD;\n }\n\n // Body/core targeting techniques\n if (\n techniqueName.includes(\"body\") ||\n techniqueName.includes(\"solar\") ||\n techniqueName.includes(\"liver\") ||\n techniqueName.includes(\"ribs\") ||\n techniqueName.includes(\"abdomen\") ||\n techniqueId.includes(\"torso\") ||\n techniqueId.includes(\"core\")\n ) {\n return BodyRegion.TORSO;\n }\n\n // Default to torso for unmatched techniques\n return BodyRegion.TORSO;\n }\n\n /**\n * Fix: Add missing getAvailableTechniques method required by interface\n */\n getAvailableTechniques(player: PlayerState): readonly KoreanTechnique[] {\n const allTechniques = TRIGRAM_TECHNIQUES[player.currentStance] ?? [];\n\n // Filter techniques based on available resources using canExecuteTechnique\n return allTechniques.filter((technique) =>\n this.canExecuteTechnique(player, technique as KoreanTechnique),\n ) as readonly KoreanTechnique[];\n }\n\n /**\n * Check if attacker can execute technique\n */\n private canExecuteTechnique(\n player: PlayerState,\n technique: KoreanTechnique,\n ): boolean {\n return (\n player.ki >= technique.kiCost &&\n player.stamina >= technique.staminaCost &&\n player.currentStance === technique.stance &&\n !player.isStunned\n );\n }\n\n /**\n * Static methods for backwards compatibility\n */\n static resolveAttack(\n attacker: PlayerState,\n defender: PlayerState,\n technique: KoreanTechnique,\n ): CombatResult {\n const instance = new CombatSystem();\n return instance.executeAttack(attacker, defender, technique);\n }\n\n /**\n * Check if a player is defeated\n */\n isPlayerDefeated(player: PlayerState): boolean {\n return player.health <= 0 || player.consciousness <= 0;\n }\n\n /**\n * Update player state over time with effect management\n */\n updatePlayerState(player: PlayerState, deltaTime: number): PlayerState {\n let updatedPlayer = { ...player };\n\n updatedPlayer = removeExpiredEffects(updatedPlayer);\n\n const effectModifiers = getEffectModifiers(updatedPlayer);\n\n const regenRate = deltaTime / 1000;\n\n // Ki regeneration (slower during combat) - affected by effects\n if (updatedPlayer.ki < updatedPlayer.maxKi) {\n updatedPlayer.ki = Math.min(\n updatedPlayer.maxKi,\n updatedPlayer.ki + regenRate * 2 * effectModifiers.kiRegen,\n );\n }\n\n // Stamina regeneration - affected by effects and breathing disruption\n // Uses BASE_STAMINA_REGEN_RATE constant for better gameplay\n // Allows players to move around and attack frequently without running out\n // 체력 재생 속도 - 더 활발한 전투를 위해\n if (updatedPlayer.stamina < updatedPlayer.maxStamina) {\n const baseStaminaRegen = regenRate * BASE_STAMINA_REGEN_RATE * effectModifiers.staminaRegen;\n const modifiedStaminaRegen =\n BreathingDisruptionSystem.calculateStaminaRegen(\n updatedPlayer,\n baseStaminaRegen,\n );\n updatedPlayer.stamina = Math.min(\n updatedPlayer.maxStamina,\n updatedPlayer.stamina + modifiedStaminaRegen,\n );\n }\n\n if (\n updatedPlayer.health < updatedPlayer.maxHealth &&\n updatedPlayer.health > 0\n ) {\n updatedPlayer.health = Math.min(\n updatedPlayer.maxHealth,\n updatedPlayer.health + regenRate * 0.5,\n );\n }\n\n // Update breathing disruption effects (gradual recovery if torso health > 50%)\n updatedPlayer = updateBreathingDisruption(\n updatedPlayer,\n deltaTime,\n Date.now(),\n );\n\n const currentTime = Date.now();\n if (\n updatedPlayer.lastActionTime &&\n currentTime - updatedPlayer.lastActionTime > updatedPlayer.recoveryTime\n ) {\n updatedPlayer.isStunned = false;\n updatedPlayer.isCountering = false;\n }\n\n return updatedPlayer;\n }\n\n /**\n * Get combat statistics\n */\n getCombatStatistics(player: PlayerState): {\n healthPercent: number;\n kiPercent: number;\n staminaPercent: number;\n balancePercent: number;\n } {\n return {\n healthPercent: (player.health / player.maxHealth) * 100,\n kiPercent: (player.ki / player.maxKi) * 100,\n staminaPercent: (player.stamina / player.maxStamina) * 100,\n balancePercent: player.balance,\n };\n }\n\n /**\n * Fix: Integrate processVitalPointHit into the combat system with archetype parameters\n */\n private processVitalPointHit(\n vitalPointId: string,\n _baseDamage: number, // Unused but kept for interface compatibility\n attacker: PlayerState,\n defender: PlayerState,\n ): VitalPointHitResult {\n const vitalPoint = this.vitalPointSystem.getVitalPointById(vitalPointId);\n\n if (!vitalPoint) {\n return {\n hit: false,\n damage: 0,\n effects: [],\n severity: VitalPointSeverity.MINOR,\n };\n }\n\n return this.vitalPointSystem.processHit(\n vitalPoint.position, // Use vital point position for hit calculation\n { width: 10, height: 10 }, // Standard hit box\n vitalPointId, // Targeted vital point\n undefined, // Use current hour from system\n attacker.archetype, // Attacker archetype for offensive modifiers\n defender.archetype, // Defender archetype for defensive modifiers\n );\n }\n\n /**\n * Process defensive action and determine animation type.\n *\n * Determines the appropriate defensive animation based on:\n * - Defender's balance and stamina (defensive power)\n * - Attacker's technique power\n * - Combat readiness state\n *\n * **Korean**: 방어 행동 처리\n *\n * @param defender - Defending player state\n * @param attacker - Attacking player state (unused but kept for future enhancements)\n * @param attackPower - Power of the incoming attack\n * @returns Defensive animation type to play (parry_deflect, block_success, or guard_break)\n *\n * @example\n * ```typescript\n * const animType = combatSystem.processDefensiveAction(\n * defender,\n * attacker,\n * technique.damage\n * );\n * // Returns: 'parry_deflect', 'block_success', or 'guard_break'\n * ```\n *\n * @korean 방어행동처리\n */\n public processDefensiveAction(\n defender: PlayerState,\n _attacker: PlayerState,\n attackPower: number,\n ): Exclude<DefensiveAnimationType, \"guard_recovery\"> {\n // Guard Break: Check balance threshold first (highest priority condition)\n if (defender.balance < 30) {\n return \"guard_break\";\n }\n\n // Calculate defensive power based on balance and stamina\n // Balance represents physical stability (0-100)\n // Stamina represents energy reserves (0-100)\n const balanceFactor = defender.balance / 100;\n const staminaFactor = defender.stamina / 100;\n\n // Apply defensive modifiers from effects\n const effectModifiers = getEffectModifiers(defender);\n const defenseMultiplier = effectModifiers.defense;\n\n // Defense stat provides moderate bonus (normalized to 0.5-1.5 range for typical 5-15 defense)\n const defenseBonus = Math.max(0.5, Math.min(1.5, defender.defense / 10));\n\n const defensePower =\n balanceFactor * staminaFactor * 100 * defenseMultiplier * defenseBonus;\n\n // Determine defensive outcome based on power ratio\n // Parry: Strong defense (1.8x attack power or more) - Perfect deflection\n if (defensePower >= attackPower * 1.8) {\n return \"parry_deflect\";\n }\n // Block Success: Adequate defense (1.0x to 1.8x attack power) - Absorb impact\n else if (defensePower >= attackPower) {\n return \"block_success\";\n }\n // Guard Break: Defense insufficient against powerful attack (<60% of attack power)\n else if (defensePower < attackPower * 0.6) {\n return \"guard_break\";\n }\n // Block Success: Marginal defense (60-100% of attack power) - barely hold\n else {\n return \"block_success\";\n }\n }\n\n /**\n * Execute attack with technique\n */\n protected executeAttack(\n attacker: PlayerState,\n defender: PlayerState,\n technique: KoreanTechnique,\n ): CombatResult {\n const hitRoll = Math.random();\n const accuracy = technique.accuracy || 0.8;\n const hit = hitRoll <= accuracy;\n\n if (!hit) {\n return {\n hit: false,\n damage: 0,\n criticalHit: false,\n vitalPointHit: false,\n effects: [],\n timestamp: Date.now(),\n technique,\n attacker,\n defender,\n success: false,\n isCritical: false,\n isBlocked: false,\n };\n }\n\n const vitalPointHit: VitalPointHitResult = {\n hit: false,\n damage: 0,\n effects: [],\n severity: VitalPointSeverity.MINOR, // Use the enum directly\n };\n\n const damageResult = this.calculateDamage(\n technique,\n attacker,\n defender,\n vitalPointHit,\n );\n\n return {\n hit: true,\n damage: damageResult.totalDamage,\n criticalHit: Math.random() < (technique.critChance || 0.1),\n vitalPointHit: false,\n effects: damageResult.effectsApplied,\n timestamp: Date.now(),\n technique,\n attacker,\n defender,\n success: true,\n isCritical: false,\n isBlocked: false,\n };\n }\n\n /**\n * Fix: Add missing calculateDamage method required by interface\n */\n calculateDamage(\n technique: KoreanTechnique,\n attacker: PlayerState,\n defender: PlayerState,\n hitResult: VitalPointHitResult,\n ): {\n baseDamage: number;\n modifierDamage: number;\n totalDamage: number;\n effectsApplied: readonly StatusEffect[];\n finalDefenderState?: Partial<PlayerState>;\n } {\n const baseDamage = technique.damage || 15;\n\n const attackerBonus = attacker.attackPower * 0.1;\n\n let vitalPointMultiplier = 1.0;\n if (hitResult.hit && hitResult.vitalPointHit) {\n const severityMultipliers: Record<VitalPointSeverity, number> = {\n [VitalPointSeverity.MINOR]: 1.1,\n [VitalPointSeverity.MODERATE]: 1.3,\n [VitalPointSeverity.MAJOR]: 1.6,\n [VitalPointSeverity.CRITICAL]: 2.0,\n [VitalPointSeverity.LETHAL]: 3.0,\n };\n\n vitalPointMultiplier = severityMultipliers[hitResult.severity] ?? 1.0;\n }\n\n const modifierDamage = attackerBonus * vitalPointMultiplier;\n\n const defenseReduction = defender.defense * 0.05;\n const totalDamage = Math.max(\n 1,\n baseDamage + modifierDamage - defenseReduction,\n );\n\n const effectsApplied = [...technique.effects, ...hitResult.effects];\n\n return {\n baseDamage,\n modifierDamage,\n totalDamage: Math.floor(totalDamage),\n effectsApplied,\n finalDefenderState: {\n health: Math.max(0, defender.health - totalDamage),\n },\n };\n }\n}\n\n/**\n * Creates a standardized CombatResult with all required fields\n * Ensures both 'critical' and 'criticalHit' are present for API compatibility\n */\nexport function createCombatResult(\n partialResult: Partial<CombatResult>,\n): CombatResult {\n // Set default values\n const result: CombatResult = {\n success: partialResult.success ?? false,\n damage: partialResult.damage ?? 0,\n isCritical: partialResult.isCritical ?? partialResult.criticalHit ?? false,\n hit: partialResult.hit ?? partialResult.success ?? false,\n isBlocked: partialResult.isBlocked ?? false,\n vitalPointHit: partialResult.vitalPointHit ?? false,\n effects: partialResult.effects ?? [],\n attacker: partialResult.attacker,\n defender: partialResult.defender,\n technique: partialResult.technique,\n // Always set criticalHit to match critical for consistency\n criticalHit: partialResult.isCritical ?? partialResult.criticalHit ?? false,\n timestamp: Date.now(),\n };\n\n return result;\n}\n\nexport default CombatSystem;\n"],"mappings":"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"}
1
+ {"version":3,"file":"CombatSystem.js","names":[],"sources":["../../src/systems/CombatSystem.ts"],"sourcesContent":["import * as THREE from \"three\";\nimport { getArchetypePhysicalAttributes } from \"../data/archetypePhysicalAttributes\";\nimport { getTechniqueById } from \"../data/techniques\";\nimport { BodyRegion, CombatAttackType, CombatState, DamageType, GrappleTarget } from \"../types\";\nimport { VitalPointCategory, VitalPointSeverity } from \"../types/common\";\nimport { BASE_STAMINA_REGEN_RATE } from \"../types/physicsConstants\";\nimport { Technique } from \"../types/technique\";\nimport { calculateDistance3D } from \"../utils/math\";\nimport { calculateBodyRadius } from \"../utils/skeletonScaling\";\nimport type { DefensiveAnimationType } from \"./animation\";\nimport {\n AnimationType,\n calculateSpeedModifierForDamage,\n determineAnimationTypeForTechnique,\n getAdjustedAnimationDuration,\n getAnimationNameForType,\n isWithinHitWindow,\n} from \"./animation\";\nimport { applyDamageToBodyParts } from \"./bodypart/BodyPartDamageIntegration\";\nimport { playerInjuryManager } from \"./bodypart\";\nimport {\n applyBreathingDisruptionFromVitalPoint,\n applyBreathingDisruptionFromTorsoDamage,\n BreathingDisruptionSystem,\n causesBreathingDisruption,\n updateBreathingDisruption,\n} from \"./breathing\";\nimport BalanceSystem from \"./combat/BalanceSystem\";\nimport ConsciousnessSystem from \"./combat/ConsciousnessSystem\";\nimport {\n extractVitalPointCategory,\n isHeadTraumaHit,\n} from \"./combat/painConsciousnessUtils\";\nimport PainResponseSystem, {\n ShockPainEffect,\n} from \"./combat/PainResponseSystem\";\nimport { CombatResult, CombatSystemInterface } from \"./combat/types\";\nimport {\n CollisionDetection,\n KnockbackPhysics,\n physicalReachCalculator,\n type KnockbackConfig,\n} from \"./physics\";\nimport { PlayerState } from \"./player\";\nimport {\n addEffectsToPlayer,\n getEffectModifiers,\n removeExpiredEffects,\n} from \"./PlayerEffectManager\";\nimport { TRIGRAM_TECHNIQUES } from \"./trigram\";\nimport { TrigramSystem } from \"./TrigramSystem\";\nimport { StatusEffect } from \"./types\";\nimport { KoreanTechnique, VitalPointHitResult } from \"./vitalpoint/types\";\nimport { VitalPointSystem } from \"./VitalPointSystem\";\nimport { GrappleSystem } from \"./combat/GrappleSystem\";\n\n/**\n * Enhanced Combat System with Pain Response and Consciousness integration.\n *\n * Integrates realistic pain accumulation and consciousness tracking for\n * progressive combat impairment.\n */\nexport class CombatSystem implements CombatSystemInterface {\n private vitalPointSystem: VitalPointSystem;\n protected trigramSystem: TrigramSystem;\n private painSystem: PainResponseSystem;\n private consciousnessSystem: ConsciousnessSystem;\n private balanceSystem: BalanceSystem;\n private knockbackPhysics: KnockbackPhysics;\n private collisionDetection: CollisionDetection;\n private grappleSystem: GrappleSystem;\n\n // Track shock pain effects per player\n private shockPainEffects: Map<string, ShockPainEffect>;\n // Track last head trauma time per player for consciousness recovery\n private lastHeadTraumaTime: Map<string, number>;\n\n // Vital point severity thresholds\n private readonly SEVERITY_MAJOR_THRESHOLD = 30;\n private readonly SEVERITY_MODERATE_THRESHOLD = 20;\n\n constructor() {\n this.vitalPointSystem = new VitalPointSystem();\n this.trigramSystem = new TrigramSystem();\n this.painSystem = new PainResponseSystem();\n this.consciousnessSystem = new ConsciousnessSystem();\n this.balanceSystem = new BalanceSystem();\n this.knockbackPhysics = new KnockbackPhysics();\n this.collisionDetection = new CollisionDetection();\n this.grappleSystem = new GrappleSystem();\n this.shockPainEffects = new Map();\n this.lastHeadTraumaTime = new Map();\n }\n\n /**\n * Cleanup per-player combat state.\n *\n * Call this when a player permanently leaves the match or when\n * match-level cleanup is performed to avoid unbounded Map growth.\n *\n * @param playerId - ID of the player to cleanup\n *\n * @korean 플레이어데이터정리\n */\n public cleanupPlayerData(playerId: string): void {\n this.shockPainEffects.delete(playerId);\n this.lastHeadTraumaTime.delete(playerId);\n }\n\n /**\n * Dispose of all combat system resources.\n *\n * **Korean**: 전투 시스템 자원 정리\n *\n * Cleans up Three.js resources (geometries, raycaster) used by the collision\n * detection system to prevent memory leaks. Should be called when the\n * CombatSystem is destroyed or reinitialized.\n *\n * @korean 전투시스템자원정리\n */\n public dispose(): void {\n // Dispose collision detection resources (cached geometries, raycaster)\n this.collisionDetection.dispose();\n }\n\n /**\n * Get the balance system instance for fall checking.\n *\n * @returns BalanceSystem instance\n * @korean 균형시스템가져오기\n */\n public getBalanceSystem(): BalanceSystem {\n return this.balanceSystem;\n }\n\n /**\n * Get the consciousness system instance for fall checking.\n *\n * @returns ConsciousnessSystem instance\n * @korean 의식시스템가져오기\n */\n public getConsciousnessSystem(): ConsciousnessSystem {\n return this.consciousnessSystem;\n }\n\n /**\n * Get the collision detection system instance.\n *\n * @returns CollisionDetection instance\n * @korean 충돌감지시스템가져오기\n */\n public getCollisionDetection(): CollisionDetection {\n return this.collisionDetection;\n }\n\n /**\n * Calculate knockback physics for combat hit.\n *\n * **Korean**: 밀침 계산 (Calculate Knockback)\n *\n * Determines knockback displacement, duration, and fall state based on:\n * - Attack damage amount\n * - Defender's balance state\n * - Defender's stance resistance\n * - Attack direction vector\n *\n * @param attacker - Attacking player state\n * @param defender - Defending player state\n * @param damage - Total damage dealt\n * @returns Knockback information or undefined if no knockback\n *\n * @example\n * ```typescript\n * const knockback = this.calculateKnockback(attacker, defender, 80);\n * // Returns: { displacement: {x:2.5,y:0,z:0}, duration:0.8, recoveryWindow:0.7, shouldFall:false }\n * ```\n *\n * @private\n * @korean 밀침계산\n */\n private calculateKnockback(\n attacker: PlayerState,\n defender: PlayerState,\n damage: number,\n ): CombatResult[\"knockback\"] {\n const attackDirection = new THREE.Vector3(\n defender.position.x - attacker.position.x,\n 0, // Keep knockback on horizontal plane\n defender.position.y - attacker.position.y,\n );\n\n // If attacker and defender are at the exact same position, skip knockback to avoid NaN direction\n if (attackDirection.lengthSq() === 0) {\n return undefined;\n }\n\n attackDirection.normalize();\n\n const config: KnockbackConfig = {\n force: damage * 10, // Convert damage to force (arbitrary scaling)\n direction: attackDirection,\n duration: 0, // Will be calculated by physics engine\n balanceState: {\n current: defender.balance,\n max: 100, // Assuming max balance is always 100\n },\n currentStance: defender.currentStance,\n };\n\n const result = this.knockbackPhysics.calculateKnockback(config, damage);\n\n return {\n displacement: {\n x: result.displacement.x,\n y: result.displacement.y,\n z: result.displacement.z,\n },\n duration: result.duration,\n recoveryWindow: result.recoveryWindow,\n shouldFall: result.shouldFall,\n };\n }\n\n /**\n * Fix: Update resolveAttack to match interface signature and add animation-aware hit detection\n *\n * **Korean**: 공격 해결 (애니메이션 인식)\n *\n * Integrates animation timing and physical reach calculation for reality-based hit detection.\n * Hits only register when:\n * 1. Animation is in hit window (extension phase)\n * 2. Attacker is within effective reach based on limb length and animation\n * 3. Existing accuracy and stance checks pass\n *\n * @param attacker - Attacking player state\n * @param defender - Defending player state\n * @param technique - Technique being executed\n * @param targetedVitalPointId - Optional specific vital point target\n * @param animationContext - Optional animation timing context for reality-based hit detection\n * @returns Combat result with hit/miss and damage information\n */\n resolveAttack(\n attacker: PlayerState,\n defender: PlayerState,\n technique: KoreanTechnique,\n targetedVitalPointId?: string,\n animationContext?: {\n animationType: AnimationType;\n currentTime: number;\n },\n ): CombatResult {\n const timestamp = Date.now();\n\n if (!this.canExecuteTechnique(attacker, technique)) {\n return {\n hit: false,\n damage: 0,\n criticalHit: false,\n vitalPointHit: false,\n effects: [],\n timestamp,\n technique,\n attacker,\n defender,\n success: false,\n isCritical: false,\n isBlocked: false,\n };\n }\n\n if (attacker.combatState === CombatState.GRAPPLED) {\n return {\n hit: false,\n damage: 0,\n criticalHit: false,\n vitalPointHit: false,\n effects: [],\n timestamp,\n technique,\n attacker,\n defender,\n success: false,\n isCritical: false,\n isBlocked: false,\n };\n }\n\n if (animationContext) {\n const { animationType, currentTime } = animationContext;\n\n if (!isWithinHitWindow(animationType, currentTime)) {\n return {\n hit: false,\n damage: 0,\n criticalHit: false,\n vitalPointHit: false,\n effects: [],\n timestamp,\n technique,\n attacker,\n defender,\n success: false,\n isCritical: false,\n isBlocked: false,\n };\n }\n\n const attackerPhysical = getArchetypePhysicalAttributes(\n attacker.archetype,\n );\n const reachResult = physicalReachCalculator.calculateReach(\n attackerPhysical,\n animationType,\n currentTime,\n attacker.currentStance,\n technique.reachConfig, // Pass reachConfig for hybrid reach calculation\n );\n\n // Physics-first: Position type is 2D in METERS\n // No conversion needed - positions are already in meters\n const centerToCenterDistance = calculateDistance3D(\n [attacker.position.x, attacker.position.y, 0],\n [defender.position.x, defender.position.y, 0],\n );\n\n // Note: PhysicalReachCalculator already includes attacker body pivot/offset in reach calculation\n // (shoulder offset for punches, hip rotation for kicks), so we only subtract defender radius\n // to avoid double-counting the attacker's body dimension.\n // 타격 거리 계산: 수비자 몸체 반경만 제외 (공격자 몸체 오프셋은 이미 도달 거리에 포함됨)\n const defenderPhysical = getArchetypePhysicalAttributes(\n defender.archetype,\n );\n const defenderBodyRadius = calculateBodyRadius(defenderPhysical);\n\n // Reach is calculated from attacker center to striking limb surface (includes body pivot),\n // so we measure from attacker center to defender surface (subtracting defender radius only).\n // 유효 거리 = 중심간 거리 - 수비자 몸체 반경\n const distance = Math.max(0, centerToCenterDistance - defenderBodyRadius);\n\n if (distance > reachResult.effectiveReach) {\n return {\n hit: false,\n damage: 0,\n criticalHit: false,\n vitalPointHit: false,\n effects: [],\n timestamp,\n technique,\n attacker,\n defender,\n success: false,\n isCritical: false,\n isBlocked: false,\n };\n }\n }\n\n // Fix: Use correct method signature\n const stanceEffectiveness = this.trigramSystem.calculateStanceEffectiveness(\n attacker.currentStance,\n defender.currentStance,\n );\n\n // Calculate base hit chance\n const baseAccuracy = technique.accuracy * stanceEffectiveness;\n const hitRoll = Math.random();\n const hit = hitRoll <= baseAccuracy;\n\n if (!hit) {\n return {\n hit: false,\n damage: 0,\n criticalHit: false,\n vitalPointHit: false,\n effects: [],\n timestamp,\n technique,\n attacker,\n defender,\n success: false,\n isCritical: false,\n isBlocked: false,\n };\n }\n\n // Special handling for grapple techniques\n if (technique.type === CombatAttackType.GRAPPLE) {\n const grappleResult = this.handleGrappleTechnique(\n attacker,\n defender,\n technique,\n timestamp\n );\n\n // Grapples don't deal immediate damage, they establish control\n // Small damage on successful grapple to represent the initial impact\n const grappleDamage = grappleResult.grappleSuccess\n ? technique.damage * 0.3\n : 0;\n\n return {\n hit: grappleResult.grappleSuccess,\n damage: grappleDamage,\n criticalHit: false,\n vitalPointHit: false,\n effects: [],\n timestamp,\n technique,\n attacker: grappleResult.updatedAttacker,\n defender: grappleResult.updatedDefender,\n success: grappleResult.grappleSuccess,\n isCritical: false,\n isBlocked: false,\n animation: this.getAnimationInfoForTechnique(technique),\n };\n }\n\n // Process vital point hit if targeted (with archetype parameters)\n let vitalPointResult: VitalPointHitResult | null = null;\n if (targetedVitalPointId) {\n vitalPointResult = this.processVitalPointHit(\n targetedVitalPointId,\n technique.damage ?? 15,\n attacker,\n defender,\n );\n }\n\n const damageResult = this.calculateDamage(\n technique,\n attacker,\n defender,\n vitalPointResult ?? {\n hit: false,\n damage: 0,\n effects: [],\n severity: VitalPointSeverity.MINOR,\n },\n );\n\n const critRoll = Math.random();\n const isCritical = critRoll <= (technique.critChance ?? 0.1);\n\n const animationInfo = this.getAnimationInfoForTechnique(technique);\n\n // Calculate knockback physics (밀침 물리)\n const knockbackInfo = this.calculateKnockback(\n attacker,\n defender,\n damageResult.totalDamage,\n );\n\n return {\n hit: true,\n damage: damageResult.totalDamage,\n criticalHit: isCritical,\n vitalPointHit: vitalPointResult?.hit ?? false,\n effects: damageResult.effectsApplied,\n timestamp,\n technique,\n attacker,\n defender,\n success: true,\n isCritical: vitalPointResult?.hit ?? false,\n isBlocked: false,\n targetedVitalPointId, // Pass through the targeted vital point ID\n animation: animationInfo, // Add animation information\n knockback: knockbackInfo, // Add knockback information\n };\n }\n\n /**\n * Get animation information for a technique\n *\n * Determines the skeletal animation to play, duration, and speed modifier\n * based on technique configuration or automatic determination.\n *\n * @param technique - Korean technique to execute\n * @returns Animation information or undefined\n *\n * @private\n * @korean 기술애니메이션정보가져오기\n */\n private getAnimationInfoForTechnique(\n technique: KoreanTechnique,\n ): CombatResult[\"animation\"] {\n // Check if technique has explicit animation config (from Technique interface)\n // KoreanTechnique may not have animation field, so check the technique data\n const techniqueData = this.getTechniqueData(technique);\n\n let animationType;\n let speedModifier;\n\n if (techniqueData?.animation) {\n animationType = techniqueData.animation.type;\n speedModifier = techniqueData.animation.speedModifier;\n } else {\n const techniqueName =\n technique.name?.english || technique.englishName || \"\";\n const techniqueId = technique.id || \"\";\n const damageType = technique.damageType || \"\";\n\n animationType = determineAnimationTypeForTechnique(\n techniqueName,\n techniqueId,\n damageType,\n );\n\n speedModifier = calculateSpeedModifierForDamage(technique.damage || 15);\n }\n\n const animationName = getAnimationNameForType(animationType);\n const duration = getAdjustedAnimationDuration(animationName, speedModifier);\n const techniqueDisplayName =\n technique.name?.korean || technique.koreanName || technique.id;\n\n return {\n animationName,\n duration,\n speedModifier,\n techniqueDisplayName,\n };\n }\n\n /**\n * Get Technique data if this KoreanTechnique has an associated Technique definition\n *\n * @param technique - Korean technique\n * @returns Technique data or null\n *\n * @private\n * @korean 기술데이터가져오기\n */\n private getTechniqueData(technique: KoreanTechnique): Technique | null {\n return getTechniqueById(technique.id) ?? null;\n }\n\n /**\n * Handle grapple technique execution.\n *\n * **Korean**: 잡기 기술 처리 (Handle Grapple Technique)\n *\n * Initiates or transitions grapple control based on technique.\n *\n * @param attacker - Player executing grapple\n * @param defender - Target player\n * @param technique - Grapple technique being used\n * @param currentTime - Current game time in milliseconds\n * @returns Updated player states with grapple control\n */\n handleGrappleTechnique(\n attacker: PlayerState,\n defender: PlayerState,\n technique: KoreanTechnique,\n currentTime: number\n ): {\n updatedAttacker: PlayerState;\n updatedDefender: PlayerState;\n grappleSuccess: boolean;\n } {\n // Determine grapple target from technique\n const target = this.getGrappleTargetFromTechnique(technique);\n\n // Attempt grapple\n const result = this.grappleSystem.attemptGrapple(\n attacker,\n defender,\n target,\n currentTime\n );\n\n if (result.success && result.grappleControl) {\n // Grapple succeeded - update both players\n return {\n updatedAttacker: {\n ...attacker,\n combatState: CombatState.GRAPPLING,\n grappleControl: result.grappleControl,\n stamina: Math.max(0, attacker.stamina - result.staminaCost),\n },\n updatedDefender: {\n ...defender,\n combatState: CombatState.GRAPPLED,\n grappleControl: result.grappleControl,\n },\n grappleSuccess: true,\n };\n }\n\n // Grapple failed\n return {\n updatedAttacker: {\n ...attacker,\n stamina: Math.max(0, attacker.stamina - result.staminaCost),\n },\n updatedDefender: defender,\n grappleSuccess: false,\n };\n }\n\n /**\n * Determine grapple target from technique characteristics.\n *\n * **Korean**: 기술에서 잡기 목표 결정 (Determine Grapple Target from Technique)\n *\n * @private\n */\n private getGrappleTargetFromTechnique(\n technique: KoreanTechnique\n ): GrappleTarget {\n // Check technique name/ID for hints (both English and Korean)\n const techName = (\n technique.name?.english ||\n technique.englishName ||\n \"\"\n ).toLowerCase();\n const techNameKorean = (\n technique.name?.korean ||\n technique.koreanName ||\n \"\"\n );\n const techId = technique.id.toLowerCase();\n\n // Check for wrist/hand - 손목 (sonmok)\n if (\n techName.includes(\"wrist\") ||\n techId.includes(\"wrist\") ||\n techNameKorean.includes(\"손목\")\n ) {\n return GrappleTarget.HAND;\n }\n\n // Check for arm - 팔 (pal)\n if (\n techName.includes(\"arm\") ||\n techId.includes(\"arm\") ||\n techNameKorean.includes(\"팔\")\n ) {\n return GrappleTarget.ARM;\n }\n\n // Check for leg - 다리 (dari)\n if (\n techName.includes(\"leg\") ||\n techId.includes(\"leg\") ||\n techNameKorean.includes(\"다리\")\n ) {\n return GrappleTarget.LEG;\n }\n\n // Check for neck - 목 (mok)\n if (\n techName.includes(\"neck\") ||\n techId.includes(\"neck\") ||\n techNameKorean.includes(\"목\")\n ) {\n return GrappleTarget.NECK;\n }\n\n // Check for both arms - 양팔 (yangpal)\n if (\n techName.includes(\"both\") ||\n techName.includes(\"double\") ||\n techId.includes(\"both\") ||\n techNameKorean.includes(\"양팔\") ||\n techNameKorean.includes(\"쌍\")\n ) {\n return GrappleTarget.BOTH_ARMS;\n }\n\n // Check for torso/body/hip - 몸통 (momtong), 허리 (heori), 몸 (mom)\n if (\n techName.includes(\"torso\") ||\n techName.includes(\"body\") ||\n techName.includes(\"hip\") ||\n techId.includes(\"torso\") ||\n techNameKorean.includes(\"몸통\") ||\n techNameKorean.includes(\"허리\") ||\n techNameKorean.includes(\"몸\")\n ) {\n return GrappleTarget.TORSO;\n }\n\n // Default to arm for unspecified grapples\n return GrappleTarget.ARM;\n }\n\n /**\n * Update grapple state for players over time.\n *\n * **Korean**: 잡기 상태 업데이트 (Update Grapple State)\n *\n * @param controller - Player maintaining control\n * @param target - Player being controlled\n * @param deltaTime - Time elapsed in seconds\n * @param currentTime - Current game time in milliseconds\n * @returns Updated player states\n */\n updateGrappleState(\n controller: PlayerState,\n target: PlayerState,\n deltaTime: number,\n currentTime: number\n ): {\n updatedController: PlayerState;\n updatedTarget: PlayerState;\n } {\n if (!controller.grappleControl) {\n // No active grapple\n return {\n updatedController: controller,\n updatedTarget: target,\n };\n }\n\n // Validate grapple control consistency\n if (\n controller.grappleControl.controllerId !== controller.id ||\n controller.grappleControl.targetId !== target.id\n ) {\n // Inconsistent state - break grapple\n return {\n updatedController: {\n ...controller,\n combatState: CombatState.IDLE,\n grappleControl: null,\n },\n updatedTarget: {\n ...target,\n combatState: CombatState.IDLE,\n grappleControl: null,\n },\n };\n }\n\n // Update grapple control\n const updatedControl = this.grappleSystem.updateGrapple(\n controller.grappleControl,\n controller,\n target,\n deltaTime,\n currentTime\n );\n\n if (!updatedControl) {\n // Grapple broken - reset both players\n return {\n updatedController: {\n ...controller,\n combatState: CombatState.IDLE,\n grappleControl: null,\n },\n updatedTarget: {\n ...target,\n combatState: CombatState.IDLE,\n grappleControl: null,\n },\n };\n }\n\n // Deduct stamina from controller\n const staminaCost = updatedControl.staminaCostPerSecond * deltaTime;\n\n return {\n updatedController: {\n ...controller,\n grappleControl: updatedControl,\n stamina: Math.max(0, controller.stamina - staminaCost),\n },\n updatedTarget: {\n ...target,\n grappleControl: updatedControl,\n },\n };\n }\n\n /**\n * Fix: Make applyCombatResult non-static instance method with effect application\n * Enhanced with Pain Response and Consciousness System integration\n */\n applyCombatResult(\n result: CombatResult,\n attacker: PlayerState,\n defender: PlayerState,\n ): { updatedAttacker: PlayerState; updatedDefender: PlayerState } {\n // Start with base result\n const { updatedAttacker, updatedDefender: initialDefender } =\n CombatSystem.applyCombatResult(result, attacker, defender);\n let updatedDefender = initialDefender;\n\n if (result.hit && result.damage > 0) {\n // Determine vital point category and severity from hit result\n const category = this.getVitalPointCategory(result);\n const severity = this.getVitalPointSeverity(result);\n\n // Apply pain from damage\n const { player: defenderWithPain, shockEffect: newShockEffect } =\n this.painSystem.applyPain(\n updatedDefender,\n result.damage,\n severity,\n category,\n );\n updatedDefender = defenderWithPain;\n\n // Store shock pain effect if triggered\n if (newShockEffect) {\n this.shockPainEffects.set(updatedDefender.id, newShockEffect);\n }\n\n // Check for pain overload stun\n if (this.painSystem.shouldTriggerStun(updatedDefender)) {\n updatedDefender = {\n ...updatedDefender,\n isStunned: true,\n };\n }\n\n // Apply consciousness damage for head/neurological hits\n if (this.isHeadTrauma(result, category)) {\n updatedDefender = this.consciousnessSystem.applyDamage(\n updatedDefender,\n result.damage,\n category,\n );\n\n // Track head trauma time for recovery gating\n this.lastHeadTraumaTime.set(updatedDefender.id, Date.now());\n\n // Check incapacitation threshold\n if (\n this.consciousnessSystem.isAtIncapacitationThreshold(updatedDefender)\n ) {\n updatedDefender = {\n ...updatedDefender,\n isStunned: true,\n // Could add helpless duration tracking here if needed\n };\n }\n }\n\n // Apply pain and consciousness effects to stats\n const currentShockEffect = this.shockPainEffects.get(updatedDefender.id);\n updatedDefender = this.painSystem.applyEffects(\n updatedDefender,\n currentShockEffect,\n );\n updatedDefender = this.consciousnessSystem.applyEffects(updatedDefender);\n\n // Apply balance disruption from the hit\n // Determine body region from vital point or use default\n const bodyRegion = this.getBodyRegionFromResult(result);\n updatedDefender = this.balanceSystem.disruptBalance(\n updatedDefender,\n result.damage,\n bodyRegion,\n );\n\n // Apply breathing disruption for torso strikes\n if (result.vitalPointHit && result.targetedVitalPointId) {\n // Vital point strike to torso\n const vitalPoint = this.vitalPointSystem.getVitalPointById(\n result.targetedVitalPointId,\n );\n if (vitalPoint && causesBreathingDisruption(vitalPoint.id)) {\n updatedDefender = applyBreathingDisruptionFromVitalPoint(\n updatedDefender,\n vitalPoint,\n Date.now(),\n );\n }\n } else if (bodyRegion === BodyRegion.TORSO && result.damage >= 10) {\n // General torso damage (non-vital point hits)\n // Apply breathing disruption if damage is significant enough\n // Note: Solar plexus detection is best-effort; vital point system is primary mechanism\n const techniqueId = (result.technique?.id ?? \"\").toLowerCase();\n const isSolarPlexusArea = \n techniqueId.includes(\"solar\") ||\n techniqueId.includes(\"myeongchi\");\n updatedDefender = applyBreathingDisruptionFromTorsoDamage(\n updatedDefender,\n result.damage,\n isSolarPlexusArea,\n Date.now(),\n );\n }\n }\n\n return { updatedAttacker, updatedDefender };\n }\n\n /**\n * Determines if a hit caused head trauma (affects consciousness).\n *\n * @param result - Combat result\n * @param category - Vital point category\n * @returns True if hit should affect consciousness\n */\n private isHeadTrauma(\n result: CombatResult,\n category?: VitalPointCategory,\n ): boolean {\n return isHeadTraumaHit(result, category);\n }\n\n /**\n * Extracts vital point category from combat result.\n *\n * @param result - Combat result\n * @returns Vital point category if available\n */\n private getVitalPointCategory(\n result: CombatResult,\n ): VitalPointCategory | undefined {\n return extractVitalPointCategory(result);\n }\n\n /**\n * Extracts vital point severity from combat result.\n *\n * @param result - Combat result\n * @returns Vital point severity if critical hit\n */\n private getVitalPointSeverity(\n result: CombatResult,\n ): VitalPointSeverity | undefined {\n if (result.vitalPointHit) {\n if (result.isCritical) {\n return VitalPointSeverity.CRITICAL;\n }\n if (result.damage > this.SEVERITY_MAJOR_THRESHOLD) {\n return VitalPointSeverity.MAJOR;\n }\n if (result.damage > this.SEVERITY_MODERATE_THRESHOLD) {\n return VitalPointSeverity.MODERATE;\n }\n return VitalPointSeverity.MINOR;\n }\n return undefined;\n }\n\n /**\n * Determines body region from combat result for balance disruption.\n *\n * Maps vital points to body regions for balance system integration.\n * Uses string matching on vital point IDs as a pragmatic heuristic since\n * VitalPoint interface doesn't currently include a bodyRegion property.\n *\n * Future improvement: Add bodyRegion: BodyRegion to VitalPoint interface\n * for more robust region mapping without string pattern matching.\n *\n * @param result - Combat result\n * @returns Body region that was struck\n * @private\n * @korean 신체부위결정\n */\n private getBodyRegionFromResult(result: CombatResult): BodyRegion {\n // If we have a targeted vital point, try to determine region\n if (result.targetedVitalPointId) {\n const vitalPoint = this.vitalPointSystem.getVitalPointById(\n result.targetedVitalPointId,\n );\n\n if (vitalPoint) {\n const pointId = vitalPoint.id.toLowerCase();\n\n // Check for leg/lower body targets\n if (\n pointId.includes(\"leg\") ||\n pointId.includes(\"knee\") ||\n pointId.includes(\"ankle\") ||\n pointId.includes(\"thigh\")\n ) {\n return BodyRegion.LEFT_LEG; // Generic leg for balance disruption\n }\n\n // Check for head targets\n if (\n pointId.includes(\"head\") ||\n pointId.includes(\"temple\") ||\n pointId.includes(\"jaw\") ||\n pointId.includes(\"nose\")\n ) {\n return BodyRegion.HEAD;\n }\n\n // Check for arm targets\n if (\n pointId.includes(\"arm\") ||\n pointId.includes(\"elbow\") ||\n pointId.includes(\"wrist\") ||\n pointId.includes(\"shoulder\")\n ) {\n return BodyRegion.LEFT_ARM;\n }\n }\n }\n\n // Default to torso for general strikes\n return BodyRegion.TORSO;\n }\n\n /**\n * Updates player states for recovery (pain dissipation, consciousness recovery, balance recovery).\n * Call this regularly in game loop.\n *\n * @param player - Player to update\n * @param deltaTime - Time elapsed since last update (ms)\n * @returns Updated player state\n */\n applyRecovery(player: PlayerState, deltaTime: number): PlayerState {\n let updatedPlayer = player;\n\n // Apply pain recovery\n updatedPlayer = this.painSystem.applyDissipation(updatedPlayer, deltaTime);\n\n // Apply consciousness recovery (only if enough time since head trauma)\n const lastTrauma = this.lastHeadTraumaTime.get(player.id);\n updatedPlayer = this.consciousnessSystem.applyRecovery(\n updatedPlayer,\n deltaTime,\n lastTrauma,\n );\n\n // Apply balance recovery\n updatedPlayer = this.balanceSystem.applyRecovery(updatedPlayer, deltaTime);\n\n // Clean up expired shock pain effects\n const shockEffect = this.shockPainEffects.get(player.id);\n if (shockEffect) {\n const elapsed = Date.now() - shockEffect.startTime;\n if (elapsed >= shockEffect.duration) {\n this.shockPainEffects.delete(player.id);\n }\n }\n\n return updatedPlayer;\n }\n\n /**\n * Static version for backwards compatibility with comprehensive effect application\n * Enhanced with Pain Response and Consciousness System integration\n * Updated to apply damage to body parts for 8-body-part health visualization\n */\n static applyCombatResult(\n result: CombatResult,\n attacker: PlayerState,\n defender: PlayerState,\n ): { updatedAttacker: PlayerState; updatedDefender: PlayerState } {\n let updatedDefender = defender;\n let updatedAttacker = attacker;\n\n if (result.hit) {\n // Determine body region from technique or use random distribution\n const bodyRegion = CombatSystem.getBodyRegionFromTechnique(\n result.technique,\n );\n\n // Apply damage to body parts (this also updates aggregate health)\n updatedDefender = applyDamageToBodyParts(\n defender,\n result.damage,\n bodyRegion,\n );\n\n // Record injury for trauma visualization (외상 시각화)\n // Use per-player injury tracking to prevent mixing injuries between characters\n // Automatically tracks bruising, cuts, and blood effects based on damage type\n // Records injury even without specific technique (defaults to BRUISE)\n if (result.damage > 0) {\n const defenderInjuryIntegration = playerInjuryManager.getIntegrationForPlayer(defender.id);\n defenderInjuryIntegration.recordCombatDamage({\n damage: result.damage,\n bodyRegion,\n damageType: (result.technique?.damageType as DamageType) || undefined,\n });\n }\n\n // Update tracking stats (applyDamageToBodyParts already sets health)\n updatedDefender = {\n ...updatedDefender,\n totalDamageReceived:\n updatedDefender.totalDamageReceived + result.damage,\n hitsTaken: updatedDefender.hitsTaken + 1,\n };\n\n if (result.effects && result.effects.length > 0) {\n updatedDefender = addEffectsToPlayer(updatedDefender, result.effects);\n }\n\n if (result.vitalPointHit) {\n updatedAttacker = {\n ...updatedAttacker,\n vitalPointHits: attacker.vitalPointHits + 1,\n };\n }\n\n if (result.isCritical) {\n updatedAttacker = {\n ...updatedAttacker,\n perfectStrikes: attacker.perfectStrikes + 1,\n };\n }\n }\n\n updatedAttacker = {\n ...updatedAttacker,\n ki: Math.max(0, attacker.ki - 5),\n stamina: Math.max(0, attacker.stamina - 10),\n totalDamageDealt:\n attacker.totalDamageDealt + (result.hit ? result.damage : 0),\n hitsLanded: attacker.hitsLanded + (result.hit ? 1 : 0),\n };\n\n return { updatedAttacker, updatedDefender };\n }\n\n /**\n * Determine body region from technique name or type\n *\n * **Korean**: 기술에서 신체 영역 결정\n *\n * @param technique - The technique used in the attack\n * @returns Body region to apply damage to\n */\n private static getBodyRegionFromTechnique(\n technique?: KoreanTechnique,\n ): BodyRegion {\n if (!technique) {\n return BodyRegion.TORSO;\n }\n\n const techniqueName = (\n technique.name?.english ||\n technique.englishName ||\n \"\"\n ).toLowerCase();\n const techniqueId = technique.id?.toLowerCase() || \"\";\n\n // Head/face targeting techniques\n if (\n techniqueName.includes(\"head\") ||\n techniqueName.includes(\"temple\") ||\n techniqueName.includes(\"jaw\") ||\n techniqueName.includes(\"face\") ||\n techniqueId.includes(\"head\") ||\n techniqueId.includes(\"skull\")\n ) {\n return BodyRegion.HEAD;\n }\n\n // Neck targeting techniques\n if (\n techniqueName.includes(\"neck\") ||\n techniqueName.includes(\"throat\") ||\n techniqueName.includes(\"choke\") ||\n techniqueId.includes(\"neck\")\n ) {\n return BodyRegion.NECK;\n }\n\n // Leg targeting techniques\n if (\n techniqueName.includes(\"kick\") ||\n techniqueName.includes(\"sweep\") ||\n techniqueName.includes(\"leg\") ||\n techniqueName.includes(\"knee\") ||\n techniqueId.includes(\"kick\") ||\n techniqueId.includes(\"leg\")\n ) {\n return Math.random() < 0.5 ? BodyRegion.LEFT_LEG : BodyRegion.RIGHT_LEG;\n }\n\n // Arm targeting techniques\n if (\n techniqueName.includes(\"arm\") ||\n techniqueName.includes(\"shoulder\") ||\n techniqueName.includes(\"elbow\") ||\n techniqueId.includes(\"arm\")\n ) {\n return Math.random() < 0.5 ? BodyRegion.LEFT_ARM : BodyRegion.RIGHT_ARM;\n }\n\n // Punch/strike techniques typically target torso\n if (\n techniqueName.includes(\"punch\") ||\n techniqueName.includes(\"strike\") ||\n techniqueName.includes(\"jab\") ||\n techniqueName.includes(\"cross\") ||\n techniqueName.includes(\"hook\") ||\n techniqueName.includes(\"uppercut\")\n ) {\n // Mix of torso and head for punches\n return Math.random() < 0.7 ? BodyRegion.TORSO : BodyRegion.HEAD;\n }\n\n // Body/core targeting techniques\n if (\n techniqueName.includes(\"body\") ||\n techniqueName.includes(\"solar\") ||\n techniqueName.includes(\"liver\") ||\n techniqueName.includes(\"ribs\") ||\n techniqueName.includes(\"abdomen\") ||\n techniqueId.includes(\"torso\") ||\n techniqueId.includes(\"core\")\n ) {\n return BodyRegion.TORSO;\n }\n\n // Default to torso for unmatched techniques\n return BodyRegion.TORSO;\n }\n\n /**\n * Fix: Add missing getAvailableTechniques method required by interface\n */\n getAvailableTechniques(player: PlayerState): readonly KoreanTechnique[] {\n const allTechniques = TRIGRAM_TECHNIQUES[player.currentStance] ?? [];\n\n // Filter techniques based on available resources using canExecuteTechnique\n return allTechniques.filter((technique) =>\n this.canExecuteTechnique(player, technique as KoreanTechnique),\n ) as readonly KoreanTechnique[];\n }\n\n /**\n * Check if attacker can execute technique\n */\n private canExecuteTechnique(\n player: PlayerState,\n technique: KoreanTechnique,\n ): boolean {\n return (\n player.ki >= technique.kiCost &&\n player.stamina >= technique.staminaCost &&\n player.currentStance === technique.stance &&\n !player.isStunned\n );\n }\n\n /**\n * Static methods for backwards compatibility\n */\n static resolveAttack(\n attacker: PlayerState,\n defender: PlayerState,\n technique: KoreanTechnique,\n ): CombatResult {\n const instance = new CombatSystem();\n return instance.executeAttack(attacker, defender, technique);\n }\n\n /**\n * Check if a player is defeated\n */\n isPlayerDefeated(player: PlayerState): boolean {\n return player.health <= 0 || player.consciousness <= 0;\n }\n\n /**\n * Update player state over time with effect management\n */\n updatePlayerState(player: PlayerState, deltaTime: number): PlayerState {\n let updatedPlayer = { ...player };\n\n updatedPlayer = removeExpiredEffects(updatedPlayer);\n\n const effectModifiers = getEffectModifiers(updatedPlayer);\n\n const regenRate = deltaTime / 1000;\n\n // Ki regeneration (slower during combat) - affected by effects\n if (updatedPlayer.ki < updatedPlayer.maxKi) {\n updatedPlayer.ki = Math.min(\n updatedPlayer.maxKi,\n updatedPlayer.ki + regenRate * 2 * effectModifiers.kiRegen,\n );\n }\n\n // Stamina regeneration - affected by effects and breathing disruption\n // Uses BASE_STAMINA_REGEN_RATE constant for better gameplay\n // Allows players to move around and attack frequently without running out\n // 체력 재생 속도 - 더 활발한 전투를 위해\n if (updatedPlayer.stamina < updatedPlayer.maxStamina) {\n const baseStaminaRegen = regenRate * BASE_STAMINA_REGEN_RATE * effectModifiers.staminaRegen;\n const modifiedStaminaRegen =\n BreathingDisruptionSystem.calculateStaminaRegen(\n updatedPlayer,\n baseStaminaRegen,\n );\n updatedPlayer.stamina = Math.min(\n updatedPlayer.maxStamina,\n updatedPlayer.stamina + modifiedStaminaRegen,\n );\n }\n\n if (\n updatedPlayer.health < updatedPlayer.maxHealth &&\n updatedPlayer.health > 0\n ) {\n updatedPlayer.health = Math.min(\n updatedPlayer.maxHealth,\n updatedPlayer.health + regenRate * 0.5,\n );\n }\n\n // Update breathing disruption effects (gradual recovery if torso health > 50%)\n updatedPlayer = updateBreathingDisruption(\n updatedPlayer,\n deltaTime,\n Date.now(),\n );\n\n const currentTime = Date.now();\n if (\n updatedPlayer.lastActionTime &&\n currentTime - updatedPlayer.lastActionTime > updatedPlayer.recoveryTime\n ) {\n updatedPlayer.isStunned = false;\n updatedPlayer.isCountering = false;\n }\n\n return updatedPlayer;\n }\n\n /**\n * Get combat statistics\n */\n getCombatStatistics(player: PlayerState): {\n healthPercent: number;\n kiPercent: number;\n staminaPercent: number;\n balancePercent: number;\n } {\n return {\n healthPercent: (player.health / player.maxHealth) * 100,\n kiPercent: (player.ki / player.maxKi) * 100,\n staminaPercent: (player.stamina / player.maxStamina) * 100,\n balancePercent: player.balance,\n };\n }\n\n /**\n * Fix: Integrate processVitalPointHit into the combat system with archetype parameters\n */\n private processVitalPointHit(\n vitalPointId: string,\n _baseDamage: number, // Unused but kept for interface compatibility\n attacker: PlayerState,\n defender: PlayerState,\n ): VitalPointHitResult {\n const vitalPoint = this.vitalPointSystem.getVitalPointById(vitalPointId);\n\n if (!vitalPoint) {\n return {\n hit: false,\n damage: 0,\n effects: [],\n severity: VitalPointSeverity.MINOR,\n };\n }\n\n return this.vitalPointSystem.processHit(\n vitalPoint.position, // Use vital point position for hit calculation\n { width: 10, height: 10 }, // Standard hit box\n vitalPointId, // Targeted vital point\n undefined, // Use current hour from system\n attacker.archetype, // Attacker archetype for offensive modifiers\n defender.archetype, // Defender archetype for defensive modifiers\n );\n }\n\n /**\n * Process defensive action and determine animation type.\n *\n * Determines the appropriate defensive animation based on:\n * - Defender's balance and stamina (defensive power)\n * - Attacker's technique power\n * - Combat readiness state\n *\n * **Korean**: 방어 행동 처리\n *\n * @param defender - Defending player state\n * @param attacker - Attacking player state (unused but kept for future enhancements)\n * @param attackPower - Power of the incoming attack\n * @returns Defensive animation type to play (parry_deflect, block_success, or guard_break)\n *\n * @example\n * ```typescript\n * const animType = combatSystem.processDefensiveAction(\n * defender,\n * attacker,\n * technique.damage\n * );\n * // Returns: 'parry_deflect', 'block_success', or 'guard_break'\n * ```\n *\n * @korean 방어행동처리\n */\n public processDefensiveAction(\n defender: PlayerState,\n _attacker: PlayerState,\n attackPower: number,\n ): Exclude<DefensiveAnimationType, \"guard_recovery\"> {\n // Guard Break: Check balance threshold first (highest priority condition)\n if (defender.balance < 30) {\n return \"guard_break\";\n }\n\n // Calculate defensive power based on balance and stamina\n // Balance represents physical stability (0-100)\n // Stamina represents energy reserves (0-100)\n const balanceFactor = defender.balance / 100;\n const staminaFactor = defender.stamina / 100;\n\n // Apply defensive modifiers from effects\n const effectModifiers = getEffectModifiers(defender);\n const defenseMultiplier = effectModifiers.defense;\n\n // Defense stat provides moderate bonus (normalized to 0.5-1.5 range for typical 5-15 defense)\n const defenseBonus = Math.max(0.5, Math.min(1.5, defender.defense / 10));\n\n const defensePower =\n balanceFactor * staminaFactor * 100 * defenseMultiplier * defenseBonus;\n\n // Determine defensive outcome based on power ratio\n // Parry: Strong defense (1.8x attack power or more) - Perfect deflection\n if (defensePower >= attackPower * 1.8) {\n return \"parry_deflect\";\n }\n // Block Success: Adequate defense (1.0x to 1.8x attack power) - Absorb impact\n else if (defensePower >= attackPower) {\n return \"block_success\";\n }\n // Guard Break: Defense insufficient against powerful attack (<60% of attack power)\n else if (defensePower < attackPower * 0.6) {\n return \"guard_break\";\n }\n // Block Success: Marginal defense (60-100% of attack power) - barely hold\n else {\n return \"block_success\";\n }\n }\n\n /**\n * Execute attack with technique\n */\n protected executeAttack(\n attacker: PlayerState,\n defender: PlayerState,\n technique: KoreanTechnique,\n ): CombatResult {\n const hitRoll = Math.random();\n const accuracy = technique.accuracy || 0.8;\n const hit = hitRoll <= accuracy;\n\n if (!hit) {\n return {\n hit: false,\n damage: 0,\n criticalHit: false,\n vitalPointHit: false,\n effects: [],\n timestamp: Date.now(),\n technique,\n attacker,\n defender,\n success: false,\n isCritical: false,\n isBlocked: false,\n };\n }\n\n const vitalPointHit: VitalPointHitResult = {\n hit: false,\n damage: 0,\n effects: [],\n severity: VitalPointSeverity.MINOR, // Use the enum directly\n };\n\n const damageResult = this.calculateDamage(\n technique,\n attacker,\n defender,\n vitalPointHit,\n );\n\n return {\n hit: true,\n damage: damageResult.totalDamage,\n criticalHit: Math.random() < (technique.critChance || 0.1),\n vitalPointHit: false,\n effects: damageResult.effectsApplied,\n timestamp: Date.now(),\n technique,\n attacker,\n defender,\n success: true,\n isCritical: false,\n isBlocked: false,\n };\n }\n\n /**\n * Fix: Add missing calculateDamage method required by interface\n */\n calculateDamage(\n technique: KoreanTechnique,\n attacker: PlayerState,\n defender: PlayerState,\n hitResult: VitalPointHitResult,\n ): {\n baseDamage: number;\n modifierDamage: number;\n totalDamage: number;\n effectsApplied: readonly StatusEffect[];\n finalDefenderState?: Partial<PlayerState>;\n } {\n const baseDamage = technique.damage || 15;\n\n const attackerBonus = attacker.attackPower * 0.1;\n\n let vitalPointMultiplier = 1.0;\n if (hitResult.hit && hitResult.vitalPointHit) {\n const severityMultipliers: Record<VitalPointSeverity, number> = {\n [VitalPointSeverity.MINOR]: 1.1,\n [VitalPointSeverity.MODERATE]: 1.3,\n [VitalPointSeverity.MAJOR]: 1.6,\n [VitalPointSeverity.CRITICAL]: 2.0,\n [VitalPointSeverity.LETHAL]: 3.0,\n };\n\n vitalPointMultiplier = severityMultipliers[hitResult.severity] ?? 1.0;\n }\n\n const modifierDamage = attackerBonus * vitalPointMultiplier;\n\n const defenseReduction = defender.defense * 0.05;\n const totalDamage = Math.max(\n 1,\n baseDamage + modifierDamage - defenseReduction,\n );\n\n const effectsApplied = [...technique.effects, ...hitResult.effects];\n\n return {\n baseDamage,\n modifierDamage,\n totalDamage: Math.floor(totalDamage),\n effectsApplied,\n finalDefenderState: {\n health: Math.max(0, defender.health - totalDamage),\n },\n };\n }\n}\n\n/**\n * Creates a standardized CombatResult with all required fields\n * Ensures both 'critical' and 'criticalHit' are present for API compatibility\n */\nexport function createCombatResult(\n partialResult: Partial<CombatResult>,\n): CombatResult {\n // Set default values\n const result: CombatResult = {\n success: partialResult.success ?? false,\n damage: partialResult.damage ?? 0,\n isCritical: partialResult.isCritical ?? partialResult.criticalHit ?? false,\n hit: partialResult.hit ?? partialResult.success ?? false,\n isBlocked: partialResult.isBlocked ?? false,\n vitalPointHit: partialResult.vitalPointHit ?? false,\n effects: partialResult.effects ?? [],\n attacker: partialResult.attacker,\n defender: partialResult.defender,\n technique: partialResult.technique,\n // Always set criticalHit to match critical for consistency\n criticalHit: partialResult.isCritical ?? partialResult.criticalHit ?? false,\n timestamp: Date.now(),\n };\n\n return result;\n}\n\nexport default CombatSystem;\n"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"EffectCalculator.js","names":[],"sources":["../../src/systems/EffectCalculator.ts"],"sourcesContent":["/**\n * Effect calculation system for vital point strikes.\n * \n * **Korean**: 효과 계산 시스템 (Effect Calculation System)\n * \n * This module provides comprehensive calculations for status effect duration,\n * intensity, and application based on vital point strikes, accuracy, severity,\n * and archetype-specific modifiers.\n * \n * ## Key Features\n * \n * - **Duration Calculation**: Based on severity, accuracy, and archetype\n * - **Intensity Scaling**: Scales with hit accuracy (0.5-1.5x)\n * - **Archetype Modifiers**: Resistance and vulnerability factors\n * - **Critical Hit Enhancement**: 2x duration for accuracy > 0.9\n * - **Effect Stacking**: Manages multiple concurrent effects\n * \n * @module systems/EffectCalculator\n * @category Combat Effects\n * @korean 효과계산기\n */\n\nimport { PlayerArchetype, VitalPointSeverity } from \"../types/common\";\nimport { EffectIntensity } from \"./effects\";\nimport { StatusEffect } from \"./types\";\nimport { VitalPointEffect } from \"./vitalpoint/types\";\n\n/**\n * Severity multipliers for effect duration calculation.\n * \n * **Korean**: 심각도 배율 (Severity Multipliers)\n */\nconst SEVERITY_MULTIPLIERS: Record<VitalPointSeverity, number> = {\n [VitalPointSeverity.MINOR]: 0.5,\n [VitalPointSeverity.MODERATE]: 1.0,\n [VitalPointSeverity.MAJOR]: 1.5,\n [VitalPointSeverity.CRITICAL]: 2.0,\n [VitalPointSeverity.LETHAL]: 3.0,\n};\n\n/**\n * Critical hit accuracy threshold.\n * Strikes with accuracy >= 0.9 are considered critical.\n * \n * **Korean**: 크리티컬 정확도 기준점\n */\nconst CRITICAL_HIT_THRESHOLD = 0.9;\n\n/**\n * Critical hit duration multiplier.\n * \n * **Korean**: 크리티컬 타격 지속시간 배율\n */\nconst CRITICAL_DURATION_MULTIPLIER = 2.0;\n\n/**\n * Maximum number of concurrent status effects allowed per player.\n * \n * **Korean**: 최대 동시 효과 개수\n */\nexport const MAX_CONCURRENT_EFFECTS = 5;\n\n/**\n * Archetype resistance modifiers.\n * Positive values = resistance (effects last shorter)\n * Negative values = vulnerability (effects last longer)\n * \n * **Korean**: 원형별 저항력 배율 (Archetype Resistance Modifiers)\n */\nconst ARCHETYPE_RESISTANCE: Record<PlayerArchetype, number> = {\n [PlayerArchetype.MUSA]: 0.2, // +20% resistance (traditional warrior discipline)\n [PlayerArchetype.AMSALJA]: -0.1, // -10% resistance (glass cannon)\n [PlayerArchetype.HACKER]: 0.0, // Neutral resistance\n [PlayerArchetype.JEONGBO_YOWON]: 0.1, // +10% resistance (mental fortitude)\n [PlayerArchetype.JOJIK_POKRYEOKBAE]: 0.15, // +15% resistance (street-hardened)\n};\n\n/**\n * Archetype offensive effect modifiers.\n * Applied when archetype deals vital point strike.\n * \n * **Korean**: 원형별 공격 효과 배율 (Archetype Offensive Modifiers)\n */\nconst ARCHETYPE_OFFENSIVE: Record<PlayerArchetype, number> = {\n [PlayerArchetype.MUSA]: 1.0, // Standard effects\n [PlayerArchetype.AMSALJA]: 1.3, // +30% effect potency (assassination mastery)\n [PlayerArchetype.HACKER]: 1.15, // +15% effect potency (precision targeting)\n [PlayerArchetype.JEONGBO_YOWON]: 1.25, // +25% effect potency (anatomical knowledge)\n [PlayerArchetype.JOJIK_POKRYEOKBAE]: 1.2, // +20% effect potency (brutal efficiency)\n};\n\n/**\n * Default effect duration in milliseconds if not specified.\n * \n * **Korean**: 기본 효과 지속시간 (Default Effect Duration)\n */\nconst DEFAULT_EFFECT_DURATION = 2000;\n\n/**\n * Calculates the duration of a status effect based on multiple factors.\n * \n * **Korean**: 효과 지속시간 계산 (Calculate Effect Duration)\n * \n * Formula:\n * ```\n * duration = baseDuration * (1 + (accuracy - 0.5) * 0.5) * severityMultiplier * archetypeModifier\n * ```\n * \n * For critical hits (accuracy >= 0.9):\n * ```\n * duration = duration * 2.0\n * ```\n * \n * @param effect - Vital point effect with base duration\n * @param accuracy - Hit accuracy (0-1)\n * @param severity - Vital point severity level\n * @param attackerArchetype - Attacking player's archetype\n * @param defenderArchetype - Defending player's archetype\n * @returns Calculated duration in milliseconds\n * \n * @example\n * ```typescript\n * const duration = calculateEffectDuration(\n * { id: \"paralysis\", duration: 2000, ... },\n * 0.95, // High accuracy\n * VitalPointSeverity.MAJOR,\n * PlayerArchetype.AMSALJA, // Assassin attacker (+30%)\n * PlayerArchetype.MUSA // Warrior defender (+20% resistance)\n * );\n * // Result: 2000 * 1.225 * 1.5 * 1.3 * 0.8 * 2.0 = ~9,568ms\n * ```\n * \n * @korean 효과지속시간계산\n */\nexport function calculateEffectDuration(\n effect: VitalPointEffect,\n accuracy: number,\n severity: VitalPointSeverity,\n attackerArchetype: PlayerArchetype,\n defenderArchetype: PlayerArchetype\n): number {\n const baseDuration = effect.duration || DEFAULT_EFFECT_DURATION;\n\n const accuracyBonus = 1 + (accuracy - 0.5) * 0.5;\n\n const severityMult = SEVERITY_MULTIPLIERS[severity] || 1.0;\n\n const offensiveModifier = ARCHETYPE_OFFENSIVE[attackerArchetype] || 1.0;\n\n // Defender resistance modifier (inverted: resistance reduces duration)\n const resistanceModifier =\n 1 - (ARCHETYPE_RESISTANCE[defenderArchetype] || 0);\n\n let finalDuration =\n baseDuration * accuracyBonus * severityMult * offensiveModifier * resistanceModifier;\n\n if (accuracy >= CRITICAL_HIT_THRESHOLD) {\n finalDuration *= CRITICAL_DURATION_MULTIPLIER;\n }\n\n return Math.floor(finalDuration);\n}\n\n/**\n * Calculates the intensity of a status effect based on hit accuracy.\n * \n * **Korean**: 효과 강도 계산 (Calculate Effect Intensity)\n * \n * Scales effect intensity from 0.5x (poor accuracy) to 1.5x (perfect accuracy).\n * \n * @param baseIntensity - Base effect intensity\n * @param accuracy - Hit accuracy (0-1)\n * @returns Scaled intensity value\n * \n * @example\n * ```typescript\n * const intensity = calculateEffectIntensity(\n * EffectIntensity.MEDIUM,\n * 0.9 // High accuracy\n * );\n * // Returns scaled intensity for 90% accuracy hit\n * ```\n * \n * @korean 효과강도계산\n */\nexport function calculateEffectIntensity(\n baseIntensity: EffectIntensity,\n accuracy: number\n): EffectIntensity {\n // Map intensities to numeric scale\n const intensityMap: Record<EffectIntensity, number> = { [EffectIntensity.WEAK]: 1,\n [EffectIntensity.MINOR]: 2,\n [EffectIntensity.LOW]: 3,\n [EffectIntensity.MEDIUM]: 4,\n [EffectIntensity.MODERATE]: 5,\n [EffectIntensity.HIGH]: 6,\n [EffectIntensity.SEVERE]: 7,\n [EffectIntensity.CRITICAL]: 8,\n [EffectIntensity.EXTREME]: 9,\n };\n\n const reverseMap: EffectIntensity[] = [\n EffectIntensity.WEAK,\n EffectIntensity.MINOR,\n EffectIntensity.LOW,\n EffectIntensity.MEDIUM,\n EffectIntensity.MODERATE,\n EffectIntensity.HIGH,\n EffectIntensity.SEVERE,\n EffectIntensity.CRITICAL,\n EffectIntensity.EXTREME,\n ];\n\n const baseLevel = intensityMap[baseIntensity] || 4;\n\n // Accuracy modifier: 0.5x at 0 accuracy, 1.0x at 0.5, 1.5x at 1.0\n const accuracyModifier = 0.5 + accuracy;\n\n const scaledLevel = Math.max(\n 1,\n Math.min(9, Math.round(baseLevel * accuracyModifier))\n );\n\n return reverseMap[scaledLevel - 1];\n}\n\n/**\n * Converts a VitalPointEffect to a StatusEffect with calculated properties.\n * \n * **Korean**: 급소 효과를 상태 효과로 변환 (Convert Vital Point Effect to Status Effect)\n * \n * @param effect - Vital point effect to convert\n * @param accuracy - Hit accuracy\n * @param severity - Vital point severity\n * @param attackerArchetype - Attacker's archetype\n * @param defenderArchetype - Defender's archetype\n * @param vitalPointId - Source vital point ID\n * @param timestamp - Current timestamp (for startTime/endTime)\n * @returns StatusEffect with calculated duration and intensity\n * \n * @korean 상태효과변환\n */\nexport function convertToStatusEffect(\n effect: VitalPointEffect,\n accuracy: number,\n severity: VitalPointSeverity,\n attackerArchetype: PlayerArchetype,\n defenderArchetype: PlayerArchetype,\n vitalPointId: string,\n timestamp: number\n): StatusEffect {\n const duration = calculateEffectDuration(\n effect,\n accuracy,\n severity,\n attackerArchetype,\n defenderArchetype\n );\n\n const intensity = calculateEffectIntensity(effect.intensity, accuracy);\n\n return {\n id: `${effect.id}_${timestamp}`,\n type: effect.type,\n intensity,\n duration,\n description: effect.description,\n stackable: effect.stackable,\n source: vitalPointId,\n startTime: timestamp,\n endTime: timestamp + duration,\n };\n}\n\n/**\n * Applies effect stacking logic to a list of status effects.\n * \n * **Korean**: 효과 중첩 관리 (Manage Effect Stacking)\n * \n * Rules:\n * - Non-stackable effects: Keep only the most recent\n * - Stackable effects: Allow up to MAX_CONCURRENT_EFFECTS\n * - Expired effects: Remove automatically\n * \n * @param currentEffects - Existing active effects\n * @param newEffects - New effects to add\n * @param currentTime - Current timestamp\n * @returns Updated effect list with stacking applied\n * \n * @example\n * ```typescript\n * const updated = applyEffectStacking(\n * player.statusEffects,\n * newVitalPointEffects,\n * Date.now()\n * );\n * ```\n * \n * @korean 효과중첩적용\n */\nexport function applyEffectStacking(\n currentEffects: readonly StatusEffect[],\n newEffects: readonly StatusEffect[],\n currentTime: number\n): StatusEffect[] {\n // Remove expired effects\n let activeEffects = currentEffects.filter(\n (effect) => effect.endTime > currentTime\n );\n\n for (const newEffect of newEffects) {\n if (!newEffect.stackable) {\n // Remove existing effects of same type (non-stackable)\n activeEffects = activeEffects.filter(\n (effect) => effect.type !== newEffect.type\n );\n }\n\n activeEffects = [...activeEffects, newEffect];\n }\n\n // Limit to MAX_CONCURRENT_EFFECTS (keep most recent)\n if (activeEffects.length > MAX_CONCURRENT_EFFECTS) {\n activeEffects = activeEffects\n .sort((a, b) => b.startTime - a.startTime)\n .slice(0, MAX_CONCURRENT_EFFECTS);\n }\n\n return activeEffects;\n}\n\n/**\n * Gets the offensive effect modifier for an archetype.\n * \n * **Korean**: 원형 공격 배율 조회 (Get Archetype Offensive Modifier)\n * \n * @param archetype - Player archetype\n * @returns Offensive effect multiplier (1.0 - 1.3)\n * \n * @korean 원형공격배율조회\n */\nexport function getArchetypeOffensiveModifier(\n archetype: PlayerArchetype\n): number {\n return ARCHETYPE_OFFENSIVE[archetype] || 1.0;\n}\n\n/**\n * Gets the defensive resistance modifier for an archetype.\n * \n * **Korean**: 원형 방어 배율 조회 (Get Archetype Defensive Modifier)\n * \n * @param archetype - Player archetype\n * @returns Resistance modifier (-0.1 to 0.2)\n * \n * @korean 원형방어배율조회\n */\nexport function getArchetypeDefensiveModifier(\n archetype: PlayerArchetype\n): number {\n return ARCHETYPE_RESISTANCE[archetype] || 0;\n}\n\n/**\n * Checks if a hit qualifies as a critical hit based on accuracy.\n * \n * **Korean**: 크리티컬 타격 판정 (Check Critical Hit)\n * \n * @param accuracy - Hit accuracy (0-1)\n * @returns True if accuracy >= 0.9 (critical hit)\n * \n * @korean 크리티컬판정\n */\nexport function isCriticalHit(accuracy: number): boolean {\n return accuracy >= CRITICAL_HIT_THRESHOLD;\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAgCA,IAAM,uBAA2D;EAC9D,mBAAmB,QAAQ;EAC3B,mBAAmB,WAAW;EAC9B,mBAAmB,QAAQ;EAC3B,mBAAmB,WAAW;EAC9B,mBAAmB,SAAS;AAC/B;;;;;;;AAQA,IAAM,yBAAyB;;;;;;AAO/B,IAAM,+BAA+B;;;;;;;;AAgBrC,IAAM,uBAAwD;EAC3D,gBAAgB,OAAO;EACvB,gBAAgB,UAAU;EAC1B,gBAAgB,SAAS;EACzB,gBAAgB,gBAAgB;EAChC,gBAAgB,oBAAoB;AACvC;;;;;;;AAQA,IAAM,sBAAuD;EAC1D,gBAAgB,OAAO;EACvB,gBAAgB,UAAU;EAC1B,gBAAgB,SAAS;EACzB,gBAAgB,gBAAgB;EAChC,gBAAgB,oBAAoB;AACvC;;;;;;AAOA,IAAM,0BAA0B;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAsChC,SAAgB,wBACd,QACA,UACA,UACA,mBACA,mBACQ;CACR,MAAM,eAAe,OAAO,YAAY;CAExC,MAAM,gBAAgB,KAAK,WAAW,MAAO;CAE7C,MAAM,eAAe,qBAAqB,aAAa;CAEvD,MAAM,oBAAoB,oBAAoB,sBAAsB;CAGpE,MAAM,qBACJ,KAAK,qBAAqB,sBAAsB;CAElD,IAAI,gBACF,eAAe,gBAAgB,eAAe,oBAAoB;CAEpE,IAAI,YAAY,wBACd,iBAAiB;CAGnB,OAAO,KAAK,MAAM,aAAa;AACjC;;;;;;;;;;;;;;;;;;;;;;;AAwBA,SAAgB,yBACd,eACA,UACiB;CAEjB,MAAM,eAAgD;GAAM,gBAAgB,OAAO;GAChF,gBAAgB,QAAQ;GACxB,gBAAgB,MAAM;GACtB,gBAAgB,SAAS;GACzB,gBAAgB,WAAW;GAC3B,gBAAgB,OAAO;GACvB,gBAAgB,SAAS;GACzB,gBAAgB,WAAW;GAC3B,gBAAgB,UAAU;CAC7B;CAEA,MAAM,aAAgC;EACpC,gBAAgB;EAChB,gBAAgB;EAChB,gBAAgB;EAChB,gBAAgB;EAChB,gBAAgB;EAChB,gBAAgB;EAChB,gBAAgB;EAChB,gBAAgB;EAChB,gBAAgB;CAClB;CAEA,MAAM,YAAY,aAAa,kBAAkB;CAGjD,MAAM,mBAAmB,KAAM;CAO/B,OAAO,WALa,KAAK,IACvB,GACA,KAAK,IAAI,GAAG,KAAK,MAAM,YAAY,gBAAgB,CAAC,CAGpC,IAAc;AAClC;;;;;;;;;;;;;;;;;AAkBA,SAAgB,sBACd,QACA,UACA,UACA,mBACA,mBACA,cACA,WACc;CACd,MAAM,WAAW,wBACf,QACA,UACA,UACA,mBACA,iBACF;CAEA,MAAM,YAAY,yBAAyB,OAAO,WAAW,QAAQ;CAErE,OAAO;EACL,IAAI,GAAG,OAAO,GAAG,GAAG;EACpB,MAAM,OAAO;EACb;EACA;EACA,aAAa,OAAO;EACpB,WAAW,OAAO;EAClB,QAAQ;EACR,WAAW;EACX,SAAS,YAAY;CACvB;AACF;;;;;;;;;;;;;;;;;;;;;;;;;;;AA4BA,SAAgB,oBACd,gBACA,YACA,aACgB;CAEhB,IAAI,gBAAgB,eAAe,QAChC,WAAW,OAAO,UAAU,WAC/B;CAEA,KAAK,MAAM,aAAa,YAAY;EAClC,IAAI,CAAC,UAAU,WAEb,gBAAgB,cAAc,QAC3B,WAAW,OAAO,SAAS,UAAU,IACxC;EAGF,gBAAgB,CAAC,GAAG,eAAe,SAAS;CAC9C;CAGA,IAAI,cAAc,SAAA,GAChB,gBAAgB,cACb,MAAM,GAAG,MAAM,EAAE,YAAY,EAAE,SAAS,EACxC,MAAM,GAAA,CAAyB;CAGpC,OAAO;AACT"}
1
+ {"version":3,"file":"EffectCalculator.js","names":[],"sources":["../../src/systems/EffectCalculator.ts"],"sourcesContent":["/**\n * Effect calculation system for vital point strikes.\n * \n * **Korean**: 효과 계산 시스템 (Effect Calculation System)\n * \n * This module provides comprehensive calculations for status effect duration,\n * intensity, and application based on vital point strikes, accuracy, severity,\n * and archetype-specific modifiers.\n * \n * ## Key Features\n * \n * - **Duration Calculation**: Based on severity, accuracy, and archetype\n * - **Intensity Scaling**: Scales with hit accuracy (0.5-1.5x)\n * - **Archetype Modifiers**: Resistance and vulnerability factors\n * - **Critical Hit Enhancement**: 2x duration for accuracy > 0.9\n * - **Effect Stacking**: Manages multiple concurrent effects\n * \n * @module systems/EffectCalculator\n * @category Combat Effects\n * @korean 효과계산기\n */\n\nimport { PlayerArchetype, VitalPointSeverity } from \"../types/common\";\nimport { EffectIntensity } from \"./effects\";\nimport { StatusEffect } from \"./types\";\nimport { VitalPointEffect } from \"./vitalpoint/types\";\n\n/**\n * Severity multipliers for effect duration calculation.\n * \n * **Korean**: 심각도 배율 (Severity Multipliers)\n */\nconst SEVERITY_MULTIPLIERS: Record<VitalPointSeverity, number> = {\n [VitalPointSeverity.MINOR]: 0.5,\n [VitalPointSeverity.MODERATE]: 1.0,\n [VitalPointSeverity.MAJOR]: 1.5,\n [VitalPointSeverity.CRITICAL]: 2.0,\n [VitalPointSeverity.LETHAL]: 3.0,\n};\n\n/**\n * Critical hit accuracy threshold.\n * Strikes with accuracy >= 0.9 are considered critical.\n * \n * **Korean**: 크리티컬 정확도 기준점\n */\nconst CRITICAL_HIT_THRESHOLD = 0.9;\n\n/**\n * Critical hit duration multiplier.\n * \n * **Korean**: 크리티컬 타격 지속시간 배율\n */\nconst CRITICAL_DURATION_MULTIPLIER = 2.0;\n\n/**\n * Maximum number of concurrent status effects allowed per player.\n * \n * **Korean**: 최대 동시 효과 개수\n */\nexport const MAX_CONCURRENT_EFFECTS = 5;\n\n/**\n * Archetype resistance modifiers.\n * Positive values = resistance (effects last shorter)\n * Negative values = vulnerability (effects last longer)\n * \n * **Korean**: 원형별 저항력 배율 (Archetype Resistance Modifiers)\n */\nconst ARCHETYPE_RESISTANCE: Record<PlayerArchetype, number> = {\n [PlayerArchetype.MUSA]: 0.2, // +20% resistance (traditional warrior discipline)\n [PlayerArchetype.AMSALJA]: -0.1, // -10% resistance (glass cannon)\n [PlayerArchetype.HACKER]: 0.0, // Neutral resistance\n [PlayerArchetype.JEONGBO_YOWON]: 0.1, // +10% resistance (mental fortitude)\n [PlayerArchetype.JOJIK_POKRYEOKBAE]: 0.15, // +15% resistance (street-hardened)\n};\n\n/**\n * Archetype offensive effect modifiers.\n * Applied when archetype deals vital point strike.\n * \n * **Korean**: 원형별 공격 효과 배율 (Archetype Offensive Modifiers)\n */\nconst ARCHETYPE_OFFENSIVE: Record<PlayerArchetype, number> = {\n [PlayerArchetype.MUSA]: 1.0, // Standard effects\n [PlayerArchetype.AMSALJA]: 1.3, // +30% effect potency (assassination mastery)\n [PlayerArchetype.HACKER]: 1.15, // +15% effect potency (precision targeting)\n [PlayerArchetype.JEONGBO_YOWON]: 1.25, // +25% effect potency (anatomical knowledge)\n [PlayerArchetype.JOJIK_POKRYEOKBAE]: 1.2, // +20% effect potency (brutal efficiency)\n};\n\n/**\n * Default effect duration in milliseconds if not specified.\n * \n * **Korean**: 기본 효과 지속시간 (Default Effect Duration)\n */\nconst DEFAULT_EFFECT_DURATION = 2000;\n\n/**\n * Calculates the duration of a status effect based on multiple factors.\n * \n * **Korean**: 효과 지속시간 계산 (Calculate Effect Duration)\n * \n * Formula:\n * ```\n * duration = baseDuration * (1 + (accuracy - 0.5) * 0.5) * severityMultiplier * archetypeModifier\n * ```\n * \n * For critical hits (accuracy >= 0.9):\n * ```\n * duration = duration * 2.0\n * ```\n * \n * @param effect - Vital point effect with base duration\n * @param accuracy - Hit accuracy (0-1)\n * @param severity - Vital point severity level\n * @param attackerArchetype - Attacking player's archetype\n * @param defenderArchetype - Defending player's archetype\n * @returns Calculated duration in milliseconds\n * \n * @example\n * ```typescript\n * const duration = calculateEffectDuration(\n * { id: \"paralysis\", duration: 2000, ... },\n * 0.95, // High accuracy\n * VitalPointSeverity.MAJOR,\n * PlayerArchetype.AMSALJA, // Assassin attacker (+30%)\n * PlayerArchetype.MUSA // Warrior defender (+20% resistance)\n * );\n * // Result: 2000 * 1.225 * 1.5 * 1.3 * 0.8 * 2.0 = ~9,568ms\n * ```\n * \n * @korean 효과지속시간계산\n */\nexport function calculateEffectDuration(\n effect: VitalPointEffect,\n accuracy: number,\n severity: VitalPointSeverity,\n attackerArchetype: PlayerArchetype,\n defenderArchetype: PlayerArchetype\n): number {\n const baseDuration = effect.duration || DEFAULT_EFFECT_DURATION;\n\n const accuracyBonus = 1 + (accuracy - 0.5) * 0.5;\n\n const severityMult = SEVERITY_MULTIPLIERS[severity] || 1.0;\n\n const offensiveModifier = ARCHETYPE_OFFENSIVE[attackerArchetype] || 1.0;\n\n // Defender resistance modifier (inverted: resistance reduces duration)\n const resistanceModifier =\n 1 - (ARCHETYPE_RESISTANCE[defenderArchetype] || 0);\n\n let finalDuration =\n baseDuration * accuracyBonus * severityMult * offensiveModifier * resistanceModifier;\n\n if (accuracy >= CRITICAL_HIT_THRESHOLD) {\n finalDuration *= CRITICAL_DURATION_MULTIPLIER;\n }\n\n return Math.floor(finalDuration);\n}\n\n/**\n * Calculates the intensity of a status effect based on hit accuracy.\n * \n * **Korean**: 효과 강도 계산 (Calculate Effect Intensity)\n * \n * Scales effect intensity from 0.5x (poor accuracy) to 1.5x (perfect accuracy).\n * \n * @param baseIntensity - Base effect intensity\n * @param accuracy - Hit accuracy (0-1)\n * @returns Scaled intensity value\n * \n * @example\n * ```typescript\n * const intensity = calculateEffectIntensity(\n * EffectIntensity.MEDIUM,\n * 0.9 // High accuracy\n * );\n * // Returns scaled intensity for 90% accuracy hit\n * ```\n * \n * @korean 효과강도계산\n */\nexport function calculateEffectIntensity(\n baseIntensity: EffectIntensity,\n accuracy: number\n): EffectIntensity {\n // Map intensities to numeric scale\n const intensityMap: Record<EffectIntensity, number> = { [EffectIntensity.WEAK]: 1,\n [EffectIntensity.MINOR]: 2,\n [EffectIntensity.LOW]: 3,\n [EffectIntensity.MEDIUM]: 4,\n [EffectIntensity.MODERATE]: 5,\n [EffectIntensity.HIGH]: 6,\n [EffectIntensity.SEVERE]: 7,\n [EffectIntensity.CRITICAL]: 8,\n [EffectIntensity.EXTREME]: 9,\n };\n\n const reverseMap: EffectIntensity[] = [\n EffectIntensity.WEAK,\n EffectIntensity.MINOR,\n EffectIntensity.LOW,\n EffectIntensity.MEDIUM,\n EffectIntensity.MODERATE,\n EffectIntensity.HIGH,\n EffectIntensity.SEVERE,\n EffectIntensity.CRITICAL,\n EffectIntensity.EXTREME,\n ];\n\n const baseLevel = intensityMap[baseIntensity] || 4;\n\n // Accuracy modifier: 0.5x at 0 accuracy, 1.0x at 0.5, 1.5x at 1.0\n const accuracyModifier = 0.5 + accuracy;\n\n const scaledLevel = Math.max(\n 1,\n Math.min(9, Math.round(baseLevel * accuracyModifier))\n );\n\n return reverseMap[scaledLevel - 1];\n}\n\n/**\n * Converts a VitalPointEffect to a StatusEffect with calculated properties.\n * \n * **Korean**: 급소 효과를 상태 효과로 변환 (Convert Vital Point Effect to Status Effect)\n * \n * @param effect - Vital point effect to convert\n * @param accuracy - Hit accuracy\n * @param severity - Vital point severity\n * @param attackerArchetype - Attacker's archetype\n * @param defenderArchetype - Defender's archetype\n * @param vitalPointId - Source vital point ID\n * @param timestamp - Current timestamp (for startTime/endTime)\n * @returns StatusEffect with calculated duration and intensity\n * \n * @korean 상태효과변환\n */\nexport function convertToStatusEffect(\n effect: VitalPointEffect,\n accuracy: number,\n severity: VitalPointSeverity,\n attackerArchetype: PlayerArchetype,\n defenderArchetype: PlayerArchetype,\n vitalPointId: string,\n timestamp: number\n): StatusEffect {\n const duration = calculateEffectDuration(\n effect,\n accuracy,\n severity,\n attackerArchetype,\n defenderArchetype\n );\n\n const intensity = calculateEffectIntensity(effect.intensity, accuracy);\n\n return {\n id: `${effect.id}_${timestamp}`,\n type: effect.type,\n intensity,\n duration,\n description: effect.description,\n stackable: effect.stackable,\n source: vitalPointId,\n startTime: timestamp,\n endTime: timestamp + duration,\n };\n}\n\n/**\n * Applies effect stacking logic to a list of status effects.\n * \n * **Korean**: 효과 중첩 관리 (Manage Effect Stacking)\n * \n * Rules:\n * - Non-stackable effects: Keep only the most recent\n * - Stackable effects: Allow up to MAX_CONCURRENT_EFFECTS\n * - Expired effects: Remove automatically\n * \n * @param currentEffects - Existing active effects\n * @param newEffects - New effects to add\n * @param currentTime - Current timestamp\n * @returns Updated effect list with stacking applied\n * \n * @example\n * ```typescript\n * const updated = applyEffectStacking(\n * player.statusEffects,\n * newVitalPointEffects,\n * Date.now()\n * );\n * ```\n * \n * @korean 효과중첩적용\n */\nexport function applyEffectStacking(\n currentEffects: readonly StatusEffect[],\n newEffects: readonly StatusEffect[],\n currentTime: number\n): StatusEffect[] {\n // Remove expired effects\n let activeEffects = currentEffects.filter(\n (effect) => effect.endTime > currentTime\n );\n\n for (const newEffect of newEffects) {\n if (!newEffect.stackable) {\n // Remove existing effects of same type (non-stackable)\n activeEffects = activeEffects.filter(\n (effect) => effect.type !== newEffect.type\n );\n }\n\n activeEffects = [...activeEffects, newEffect];\n }\n\n // Limit to MAX_CONCURRENT_EFFECTS (keep most recent)\n if (activeEffects.length > MAX_CONCURRENT_EFFECTS) {\n activeEffects = activeEffects\n .sort((a, b) => b.startTime - a.startTime)\n .slice(0, MAX_CONCURRENT_EFFECTS);\n }\n\n return activeEffects;\n}\n\n/**\n * Gets the offensive effect modifier for an archetype.\n * \n * **Korean**: 원형 공격 배율 조회 (Get Archetype Offensive Modifier)\n * \n * @param archetype - Player archetype\n * @returns Offensive effect multiplier (1.0 - 1.3)\n * \n * @korean 원형공격배율조회\n */\nexport function getArchetypeOffensiveModifier(\n archetype: PlayerArchetype\n): number {\n return ARCHETYPE_OFFENSIVE[archetype] || 1.0;\n}\n\n/**\n * Gets the defensive resistance modifier for an archetype.\n * \n * **Korean**: 원형 방어 배율 조회 (Get Archetype Defensive Modifier)\n * \n * @param archetype - Player archetype\n * @returns Resistance modifier (-0.1 to 0.2)\n * \n * @korean 원형방어배율조회\n */\nexport function getArchetypeDefensiveModifier(\n archetype: PlayerArchetype\n): number {\n return ARCHETYPE_RESISTANCE[archetype] || 0;\n}\n\n/**\n * Checks if a hit qualifies as a critical hit based on accuracy.\n * \n * **Korean**: 크리티컬 타격 판정 (Check Critical Hit)\n * \n * @param accuracy - Hit accuracy (0-1)\n * @returns True if accuracy >= 0.9 (critical hit)\n * \n * @korean 크리티컬판정\n */\nexport function isCriticalHit(accuracy: number): boolean {\n return accuracy >= CRITICAL_HIT_THRESHOLD;\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAgCA,IAAM,uBAA2D;EAC9D,mBAAmB,QAAQ;EAC3B,mBAAmB,WAAW;EAC9B,mBAAmB,QAAQ;EAC3B,mBAAmB,WAAW;EAC9B,mBAAmB,SAAS;AAC/B;;;;;;;AAQA,IAAM,yBAAyB;;;;;;AAO/B,IAAM,+BAA+B;;;;;;;;AAgBrC,IAAM,uBAAwD;EAC3D,gBAAgB,OAAO;EACvB,gBAAgB,UAAU;EAC1B,gBAAgB,SAAS;EACzB,gBAAgB,gBAAgB;EAChC,gBAAgB,oBAAoB;AACvC;;;;;;;AAQA,IAAM,sBAAuD;EAC1D,gBAAgB,OAAO;EACvB,gBAAgB,UAAU;EAC1B,gBAAgB,SAAS;EACzB,gBAAgB,gBAAgB;EAChC,gBAAgB,oBAAoB;AACvC;;;;;;AAOA,IAAM,0BAA0B;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAsChC,SAAgB,wBACd,QACA,UACA,UACA,mBACA,mBACQ;CACR,MAAM,eAAe,OAAO,YAAY;CAExC,MAAM,gBAAgB,KAAK,WAAW,MAAO;CAE7C,MAAM,eAAe,qBAAqB,aAAa;CAEvD,MAAM,oBAAoB,oBAAoB,sBAAsB;CAGpE,MAAM,qBACJ,KAAK,qBAAqB,sBAAsB;CAElD,IAAI,gBACF,eAAe,gBAAgB,eAAe,oBAAoB;CAEpE,IAAI,YAAY,wBACd,iBAAiB;CAGnB,OAAO,KAAK,MAAM,aAAa;AACjC;;;;;;;;;;;;;;;;;;;;;;;AAwBA,SAAgB,yBACd,eACA,UACiB;CAEjB,MAAM,eAAgD;GAAM,gBAAgB,OAAO;GAChF,gBAAgB,QAAQ;GACxB,gBAAgB,MAAM;GACtB,gBAAgB,SAAS;GACzB,gBAAgB,WAAW;GAC3B,gBAAgB,OAAO;GACvB,gBAAgB,SAAS;GACzB,gBAAgB,WAAW;GAC3B,gBAAgB,UAAU;CAC7B;CAEA,MAAM,aAAgC;EACpC,gBAAgB;EAChB,gBAAgB;EAChB,gBAAgB;EAChB,gBAAgB;EAChB,gBAAgB;EAChB,gBAAgB;EAChB,gBAAgB;EAChB,gBAAgB;EAChB,gBAAgB;CAClB;CAEA,MAAM,YAAY,aAAa,kBAAkB;CAGjD,MAAM,mBAAmB,KAAM;CAO/B,OAAO,WALa,KAAK,IACvB,GACA,KAAK,IAAI,GAAG,KAAK,MAAM,YAAY,gBAAgB,CAAC,CAGpC,IAAc;AAClC;;;;;;;;;;;;;;;;;AAkBA,SAAgB,sBACd,QACA,UACA,UACA,mBACA,mBACA,cACA,WACc;CACd,MAAM,WAAW,wBACf,QACA,UACA,UACA,mBACA,iBACF;CAEA,MAAM,YAAY,yBAAyB,OAAO,WAAW,QAAQ;CAErE,OAAO;EACL,IAAI,GAAG,OAAO,GAAG,GAAG;EACpB,MAAM,OAAO;EACb;EACA;EACA,aAAa,OAAO;EACpB,WAAW,OAAO;EAClB,QAAQ;EACR,WAAW;EACX,SAAS,YAAY;CACvB;AACF;;;;;;;;;;;;;;;;;;;;;;;;;;;AA4BA,SAAgB,oBACd,gBACA,YACA,aACgB;CAEhB,IAAI,gBAAgB,eAAe,QAChC,WAAW,OAAO,UAAU,WAC/B;CAEA,KAAK,MAAM,aAAa,YAAY;EAClC,IAAI,CAAC,UAAU,WAEb,gBAAgB,cAAc,QAC3B,WAAW,OAAO,SAAS,UAAU,IACxC;EAGF,gBAAgB,CAAC,GAAG,eAAe,SAAS;CAC9C;CAGA,IAAI,cAAc,SAAA,GAChB,gBAAgB,cACb,MAAM,GAAG,MAAM,EAAE,YAAY,EAAE,SAAS,CAAC,CACzC,MAAM,GAAA,CAAyB;CAGpC,OAAO;AACT"}
@@ -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 * @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 * @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 * @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 * @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 * @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 * @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 * @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 * @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 * @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 * @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 * @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 * @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 * @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 * @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 * @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 * @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 * @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 * @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 * @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 * @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 * @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 * @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 * @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 * @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 * @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 * @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 * @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 * @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 * @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 * @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 * @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 * @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 * @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 * @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 * @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 * @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 * @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 * @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 * @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 * @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 * @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 * @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 * @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 * @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 * @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 * @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 * @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 * @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 * @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 * @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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"}