blacktrigram 0.7.39 → 0.7.40

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (354) hide show
  1. package/lib/App2.js.map +1 -1
  2. package/lib/audio/AudioAssetLoader.js.map +1 -1
  3. package/lib/audio/AudioAssetRegistry.js.map +1 -1
  4. package/lib/audio/AudioCache.js.map +1 -1
  5. package/lib/audio/AudioManager.js.map +1 -1
  6. package/lib/audio/AudioMonitor.js.map +1 -1
  7. package/lib/audio/AudioPool.js.map +1 -1
  8. package/lib/audio/AudioProvider.js.map +1 -1
  9. package/lib/audio/AudioUtils.js.map +1 -1
  10. package/lib/audio/BoneImpactAudioMap.js.map +1 -1
  11. package/lib/audio/VariantSelector.js.map +1 -1
  12. package/lib/audio/types.js.map +1 -1
  13. package/lib/components/screens/combat/CombatScreen3D.js.map +1 -1
  14. package/lib/components/screens/combat/components/controls/CombatButtons.js.map +1 -1
  15. package/lib/components/screens/combat/components/controls/CombatControlsPanel.js.map +1 -1
  16. package/lib/components/screens/combat/components/controls/ControlsGuide.js.map +1 -1
  17. package/lib/components/screens/combat/components/controls/KeyboardHints.js.map +1 -1
  18. package/lib/components/screens/combat/components/controls/PauseMenu.js.map +1 -1
  19. package/lib/components/screens/combat/components/controls/PauseMenuButton.js.map +1 -1
  20. package/lib/components/screens/combat/components/controls/QuickSettings.js.map +1 -1
  21. package/lib/components/screens/combat/components/effects/BloodDecals3D.js.map +1 -1
  22. package/lib/components/screens/combat/components/effects/BloodLossOverlayHtml.js.map +1 -1
  23. package/lib/components/screens/combat/components/effects/BloodParticles3D.js.map +1 -1
  24. package/lib/components/screens/combat/components/effects/BloodViscosity3D.js.map +1 -1
  25. package/lib/components/screens/combat/components/effects/CombatParticleEffects3D.js.map +1 -1
  26. package/lib/components/screens/combat/components/effects/ConsciousnessBlur.js.map +1 -1
  27. package/lib/components/screens/combat/components/effects/InternalDamage3D.js.map +1 -1
  28. package/lib/components/screens/combat/components/effects/PainVignette.js.map +1 -1
  29. package/lib/components/screens/combat/components/effects/ParticleAudio3D.js.map +1 -1
  30. package/lib/components/screens/combat/components/effects/TraumaOverlay3D.js.map +1 -1
  31. package/lib/components/screens/combat/components/feedback/MatchCountdown.js.map +1 -1
  32. package/lib/components/screens/combat/components/feedback/RoundAnnouncementOverlayHtml.js.map +1 -1
  33. package/lib/components/screens/combat/components/feedback/RoundDisplayStatus.js.map +1 -1
  34. package/lib/components/screens/combat/components/feedback/RoundStartAnnouncementOverlayHtml.js.map +1 -1
  35. package/lib/components/screens/combat/components/hud/CombatBottomHUD.js.map +1 -1
  36. package/lib/components/screens/combat/components/hud/CombatLeftHUD.js.map +1 -1
  37. package/lib/components/screens/combat/components/hud/CombatPortraitStatusStrip.js.map +1 -1
  38. package/lib/components/screens/combat/components/hud/CombatRightHUD.js.map +1 -1
  39. package/lib/components/screens/combat/components/hud/CombatTopHUD.js.map +1 -1
  40. package/lib/components/screens/combat/components/hud/DifficultyIndicator.js.map +1 -1
  41. package/lib/components/screens/combat/components/hud/FPSMonitor.js.map +1 -1
  42. package/lib/components/screens/combat/components/hud/MobileControlsWrapper.js.map +1 -1
  43. package/lib/components/screens/combat/components/hud/PlayerStateOverlayHtml.js.map +1 -1
  44. package/lib/components/screens/combat/components/indicators/BalanceIndicator.js.map +1 -1
  45. package/lib/components/screens/combat/components/indicators/InputBufferDisplay.js.map +1 -1
  46. package/lib/components/screens/combat/components/indicators/StaminaWarning.js.map +1 -1
  47. package/lib/components/screens/combat/components/indicators/TechniqueNameDisplay.js.map +1 -1
  48. package/lib/components/screens/combat/helpers/AnimationUpdater.js.map +1 -1
  49. package/lib/components/screens/combat/helpers/combatHelpers.js.map +1 -1
  50. package/lib/components/screens/combat/hooks/useAICombat.js.map +1 -1
  51. package/lib/components/screens/combat/hooks/useCombatActions.js.map +1 -1
  52. package/lib/components/screens/combat/hooks/useCombatAttackMovement.js.map +1 -1
  53. package/lib/components/screens/combat/hooks/useCombatAudio.js.map +1 -1
  54. package/lib/components/screens/combat/hooks/useCombatLayout.js.map +1 -1
  55. package/lib/components/screens/combat/hooks/useCombatState.js.map +1 -1
  56. package/lib/components/screens/controls/ControlsScreen3D.js.map +1 -1
  57. package/lib/components/screens/controls/components/ControlBindingsOverlayHtml.js.map +1 -1
  58. package/lib/components/screens/controls/components/ControlCategoryTabsOverlayHtml.js.map +1 -1
  59. package/lib/components/screens/controls/components/GamepadVisualization3D.js.map +1 -1
  60. package/lib/components/screens/controls/components/InteractiveControlDemoOverlayHtml.js.map +1 -1
  61. package/lib/components/screens/controls/components/Key3D.js.map +1 -1
  62. package/lib/components/screens/controls/components/VisualKeyboard3D.js.map +1 -1
  63. package/lib/components/screens/controls/constants/ControlsConstants.js.map +1 -1
  64. package/lib/components/screens/controls/hooks/useControlsState.js.map +1 -1
  65. package/lib/components/screens/endscreen/EndScreen3D.js.map +1 -1
  66. package/lib/components/screens/endscreen/components/DefeatAnimation3D.js.map +1 -1
  67. package/lib/components/screens/endscreen/components/MatchStatisticsDisplayOverlayHtml.js.map +1 -1
  68. package/lib/components/screens/endscreen/components/NavigationButtonsOverlayHtml.js.map +1 -1
  69. package/lib/components/screens/endscreen/components/PerformanceBreakdownOverlayHtml.js.map +1 -1
  70. package/lib/components/screens/endscreen/components/PerformanceRatingOverlayHtml.js.map +1 -1
  71. package/lib/components/screens/endscreen/components/VictoryAnimation3D.js.map +1 -1
  72. package/lib/components/screens/endscreen/components/WinnerDisplayOverlayHtml.js.map +1 -1
  73. package/lib/components/screens/intro/IntroScreen3D.js +1 -1
  74. package/lib/components/screens/intro/IntroScreen3D.js.map +1 -1
  75. package/lib/components/screens/intro/components/AbilityListOverlayHtml.js.map +1 -1
  76. package/lib/components/screens/intro/components/ArchetypeCardGridOverlayHtml.js.map +1 -1
  77. package/lib/components/screens/intro/components/ArchetypeCardOverlayHtml.js.map +1 -1
  78. package/lib/components/screens/intro/components/ArchetypeDisplayOverlayHtml.js.map +1 -1
  79. package/lib/components/screens/intro/components/EnhancedArchetypeDisplayOverlayHtml.js.map +1 -1
  80. package/lib/components/screens/intro/components/MenuButtonsOverlayHtml.js.map +1 -1
  81. package/lib/components/screens/intro/components/MenuSectionOverlayHtml.js.map +1 -1
  82. package/lib/components/screens/intro/components/StatBarOverlayHtml.js.map +1 -1
  83. package/lib/components/screens/philosophy/PhilosophyScreen3D.js.map +1 -1
  84. package/lib/components/screens/training/TrainingScreen3D.js.map +1 -1
  85. package/lib/components/screens/training/components/AnatomyControlsOverlayHtml.js.map +1 -1
  86. package/lib/components/screens/training/components/AnatomyOverlay3D.js.map +1 -1
  87. package/lib/components/screens/training/components/FootPlacementMarkers3D.js.map +1 -1
  88. package/lib/components/screens/training/components/FootworkDrillsOverlayHtml.js.map +1 -1
  89. package/lib/components/screens/training/components/HitFeedbackEffect3D.js.map +1 -1
  90. package/lib/components/screens/training/components/TrainingButtonsOverlayHtml.js.map +1 -1
  91. package/lib/components/screens/training/components/TrainingControlsOverlayHtml.js.map +1 -1
  92. package/lib/components/screens/training/components/TrainingDummy3D.js.map +1 -1
  93. package/lib/components/screens/training/components/TrainingFeedbackOverlayHtml.js.map +1 -1
  94. package/lib/components/screens/training/components/TrainingModeSelectorOverlayHtml.js.map +1 -1
  95. package/lib/components/screens/training/components/TrainingStatsOverlayHtml.js.map +1 -1
  96. package/lib/components/screens/training/components/VitalPointMarker3D.js.map +1 -1
  97. package/lib/components/screens/training/components/VitalPointTrainingOverlayHtml.js.map +1 -1
  98. package/lib/components/screens/training/components/hud/TrainingBottomHUD.js.map +1 -1
  99. package/lib/components/screens/training/components/hud/TrainingLeftHUD.js.map +1 -1
  100. package/lib/components/screens/training/components/hud/TrainingRightHUD.js.map +1 -1
  101. package/lib/components/screens/training/components/hud/TrainingTopHUD.js.map +1 -1
  102. package/lib/components/screens/training/hooks/useAttackMovement.js.map +1 -1
  103. package/lib/components/screens/training/hooks/useTrainingActions.js.map +1 -1
  104. package/lib/components/screens/training/hooks/useTrainingLayout.js.map +1 -1
  105. package/lib/components/screens/training/hooks/useTrainingState.js.map +1 -1
  106. package/lib/components/shared/base/BaseButton.js.map +1 -1
  107. package/lib/components/shared/base/BaseButtonOverlayHtml.js.map +1 -1
  108. package/lib/components/shared/base/BasePanel.js.map +1 -1
  109. package/lib/components/shared/base/BaseText.js.map +1 -1
  110. package/lib/components/shared/base/useKoreanTheme.js.map +1 -1
  111. package/lib/components/shared/debug/PerformanceDebugOverlayHtml.js.map +1 -1
  112. package/lib/components/shared/mobile/ActionButtons.js.map +1 -1
  113. package/lib/components/shared/mobile/GestureRecognizerPure.js.map +1 -1
  114. package/lib/components/shared/mobile/HapticController.js.map +1 -1
  115. package/lib/components/shared/mobile/MobileControlsPure.js.map +1 -1
  116. package/lib/components/shared/mobile/StanceWheelPure.js.map +1 -1
  117. package/lib/components/shared/mobile/TouchOptimizer.js.map +1 -1
  118. package/lib/components/shared/mobile/VirtualDPad.js.map +1 -1
  119. package/lib/components/shared/three/anatomy/BodySurface.js.map +1 -1
  120. package/lib/components/shared/three/anatomy/BoneAttachedMuscles.js.map +1 -1
  121. package/lib/components/shared/three/anatomy/BoneClothing.js.map +1 -1
  122. package/lib/components/shared/three/anatomy/BoneRenderer.js.map +1 -1
  123. package/lib/components/shared/three/anatomy/Face3D.js.map +1 -1
  124. package/lib/components/shared/three/anatomy/Foot3D.js.map +1 -1
  125. package/lib/components/shared/three/anatomy/Hand3D.js.map +1 -1
  126. package/lib/components/shared/three/effects/ActionFeedback.js.map +1 -1
  127. package/lib/components/shared/three/effects/DamageNumbers.js.map +1 -1
  128. package/lib/components/shared/three/effects/HitEffects3D.js.map +1 -1
  129. package/lib/components/shared/three/effects/PlayerStateIndicators.js.map +1 -1
  130. package/lib/components/shared/three/effects/StanceSymbol3D.js.map +1 -1
  131. package/lib/components/shared/three/effects/StanceTransitionEffect.js.map +1 -1
  132. package/lib/components/shared/three/effects/VitalPointMarkers3D.js.map +1 -1
  133. package/lib/components/shared/three/indicators/ElementalColorSystem.js.map +1 -1
  134. package/lib/components/shared/three/indicators/GuardIndicator.js.map +1 -1
  135. package/lib/components/shared/three/indicators/HapticFeedback.js.map +1 -1
  136. package/lib/components/shared/three/indicators/StanceChangeIndicator.js.map +1 -1
  137. package/lib/components/shared/three/models/Player3DWithTransitions.js.map +1 -1
  138. package/lib/components/shared/three/models/SkeletalPlayer3D.js.map +1 -1
  139. package/lib/components/shared/three/optimization/AdaptiveQuality.js.map +1 -1
  140. package/lib/components/shared/three/scene/AtmosphericParticles3D.js.map +1 -1
  141. package/lib/components/shared/three/scene/BackgroundScene3D.js.map +1 -1
  142. package/lib/components/shared/three/scene/CombatArena3D.js.map +1 -1
  143. package/lib/components/shared/three/scene/KoreanSignage3D.js.map +1 -1
  144. package/lib/components/shared/three/ui/ArchetypeCard.js.map +1 -1
  145. package/lib/components/shared/three/ui/BodyPartHealthDisplay.js.map +1 -1
  146. package/lib/components/shared/three/ui/BreathingIndicator2.js.map +1 -1
  147. package/lib/components/shared/three/ui/CombatReadinessBar.js.map +1 -1
  148. package/lib/components/shared/three/ui/ComboCounter.js.map +1 -1
  149. package/lib/components/shared/three/ui/HealthBar.js.map +1 -1
  150. package/lib/components/shared/three/ui/KoreanButton.js.map +1 -1
  151. package/lib/components/shared/three/ui/KoreanPanel.js.map +1 -1
  152. package/lib/components/shared/three/ui/KoreanText.js.map +1 -1
  153. package/lib/components/shared/three/ui/MenuList.js.map +1 -1
  154. package/lib/components/shared/three/ui/PlayerHUD.js.map +1 -1
  155. package/lib/components/shared/three/ui/ProgressBar.js.map +1 -1
  156. package/lib/components/shared/three/ui/SpeedIndicatorHUD.js.map +1 -1
  157. package/lib/components/shared/three/ui/StaminaBar.js.map +1 -1
  158. package/lib/components/shared/three/ui/TechniqueBar.js.map +1 -1
  159. package/lib/components/shared/three/ui/TechniqueCard.js.map +1 -1
  160. package/lib/components/shared/three/ui/VitalPointOverlayControlsHtml.js.map +1 -1
  161. package/lib/components/shared/ui/BackButton.js.map +1 -1
  162. package/lib/components/shared/ui/BaseHUDContainer.js.map +1 -1
  163. package/lib/components/shared/ui/CombatTimer.js.map +1 -1
  164. package/lib/components/shared/ui/ErrorModal.js.map +1 -1
  165. package/lib/components/shared/ui/LoadingState.js.map +1 -1
  166. package/lib/components/shared/ui/SplashScreen.js +2 -2
  167. package/lib/components/shared/ui/SplashScreen.js.map +1 -1
  168. package/lib/components/shared/ui/VitalPointOverlayControlsPure.js.map +1 -1
  169. package/lib/components/shared/ui/VolumeControl.js.map +1 -1
  170. package/lib/components/shared/ui/shared/ConfirmDialog.js.map +1 -1
  171. package/lib/components/ui/combat/BalanceIndicatorOverlayHtml.js.map +1 -1
  172. package/lib/constants/bodyDimensions.js.map +1 -1
  173. package/lib/constants/bodyRenderingConstants.js.map +1 -1
  174. package/lib/data/archetypeClothing.js.map +1 -1
  175. package/lib/data/archetypePhysicalAttributes.js.map +1 -1
  176. package/lib/data/techniqueMappings.js.map +1 -1
  177. package/lib/data/techniques.js.map +1 -1
  178. package/lib/hooks/useActionFeedback.js.map +1 -1
  179. package/lib/hooks/useBalanceAnimations.js.map +1 -1
  180. package/lib/hooks/useCombatTimer.js.map +1 -1
  181. package/lib/hooks/useDebounce.js.map +1 -1
  182. package/lib/hooks/useHUDLayout.js.map +1 -1
  183. package/lib/hooks/useHandPoseTransitions.js.map +1 -1
  184. package/lib/hooks/useKeyboardControls.js.map +1 -1
  185. package/lib/hooks/useMatchCountdown.js.map +1 -1
  186. package/lib/hooks/useMuscleActivation.js.map +1 -1
  187. package/lib/hooks/usePauseMenu.js.map +1 -1
  188. package/lib/hooks/usePlayerAnimation.js.map +1 -1
  189. package/lib/hooks/useResponsiveLayout.js.map +1 -1
  190. package/lib/hooks/useRoundTransition.js.map +1 -1
  191. package/lib/hooks/useSkeletalAnimation.js.map +1 -1
  192. package/lib/hooks/useTechniqueSelection.js.map +1 -1
  193. package/lib/hooks/useThrottle.js.map +1 -1
  194. package/lib/hooks/useTouchControls.js.map +1 -1
  195. package/lib/hooks/useWebGLContextLossHandler.js.map +1 -1
  196. package/lib/hooks/useWindowSize.js.map +1 -1
  197. package/lib/systems/CombatSystem.js.map +1 -1
  198. package/lib/systems/EffectCalculator.js.map +1 -1
  199. package/lib/systems/LayoutSystem.js.map +1 -1
  200. package/lib/systems/PlayerEffectManager.js.map +1 -1
  201. package/lib/systems/ResponsiveScaling.js.map +1 -1
  202. package/lib/systems/TrigramSystem.js.map +1 -1
  203. package/lib/systems/VitalPointSystem.js.map +1 -1
  204. package/lib/systems/ai/AIPersonality.js.map +1 -1
  205. package/lib/systems/ai/AdaptiveDifficulty.js.map +1 -1
  206. package/lib/systems/ai/ArchetypeEnforcer.js.map +1 -1
  207. package/lib/systems/ai/ComboSystem.js.map +1 -1
  208. package/lib/systems/ai/DecisionTree.js.map +1 -1
  209. package/lib/systems/ai/TrainingAI.js.map +1 -1
  210. package/lib/systems/ai/types.js.map +1 -1
  211. package/lib/systems/animation/builders/AnimationBuilder.js.map +1 -1
  212. package/lib/systems/animation/builders/HandPoseApplicator.js.map +1 -1
  213. package/lib/systems/animation/builders/HandPoses.js.map +1 -1
  214. package/lib/systems/animation/builders/KeyframeConfig.js.map +1 -1
  215. package/lib/systems/animation/builders/KeyframeInterpolation.js +3 -90
  216. package/lib/systems/animation/builders/KeyframeInterpolation.js.map +1 -1
  217. package/lib/systems/animation/builders/KickPhaseApplicator.js.map +1 -1
  218. package/lib/systems/animation/builders/KoreanGuardPositions.js.map +1 -1
  219. package/lib/systems/animation/builders/MartialArtsAnimationBuilder.js.map +1 -1
  220. package/lib/systems/animation/builders/MartialArtsConstants.js.map +1 -1
  221. package/lib/systems/animation/builders/MartialPoseApplicator.js.map +1 -1
  222. package/lib/systems/animation/builders/PunchPhaseApplicator.js.map +1 -1
  223. package/lib/systems/animation/builders/SkeletonRig.js.map +1 -1
  224. package/lib/systems/animation/builders/TrigramGuardApplicator.js.map +1 -1
  225. package/lib/systems/animation/catalogs/DefensiveAnimations.js.map +1 -1
  226. package/lib/systems/animation/catalogs/FootworkSkeletalAnimations.js.map +1 -1
  227. package/lib/systems/animation/catalogs/RecoveryAnimations.js.map +1 -1
  228. package/lib/systems/animation/catalogs/StanceAnimations.js.map +1 -1
  229. package/lib/systems/animation/catalogs/StanceAttackAnimations.js.map +1 -1
  230. package/lib/systems/animation/catalogs/StanceGuardPoses.js.map +1 -1
  231. package/lib/systems/animation/catalogs/StanceIdleAnimations.js.map +1 -1
  232. package/lib/systems/animation/catalogs/StanceLocomotionAnimations.js.map +1 -1
  233. package/lib/systems/animation/catalogs/StepSkeletalAnimations.js.map +1 -1
  234. package/lib/systems/animation/core/AnimationHitTiming.js.map +1 -1
  235. package/lib/systems/animation/core/AnimationOptimizations.js.map +1 -1
  236. package/lib/systems/animation/core/AnimationPriority.js.map +1 -1
  237. package/lib/systems/animation/core/AnimationRegistry.js.map +1 -1
  238. package/lib/systems/animation/core/AnimationStateMachine.js.map +1 -1
  239. package/lib/systems/animation/core/AnimationTransitions.js.map +1 -1
  240. package/lib/systems/animation/core/LateralityTransform.js.map +1 -1
  241. package/lib/systems/animation/core/RecoveryPhaseEnhancer.js.map +1 -1
  242. package/lib/systems/animation/core/TechniqueAnimationMapper.js.map +1 -1
  243. package/lib/systems/animation/core/TechniqueAnimationMapping.js.map +1 -1
  244. package/lib/systems/animation/core/types.js.map +1 -1
  245. package/lib/systems/animation/systems/AdvancedJointMovements.js.map +1 -1
  246. package/lib/systems/animation/systems/BodyFacingSystem.js.map +1 -1
  247. package/lib/systems/animation/systems/FacialExpressions.js.map +1 -1
  248. package/lib/systems/animation/systems/FallAnimations.js.map +1 -1
  249. package/lib/systems/animation/systems/MuscleActivation.js.map +1 -1
  250. package/lib/systems/bodypart/BodyPartDamageIntegration.js.map +1 -1
  251. package/lib/systems/bodypart/BodyPartHealthSystem.js.map +1 -1
  252. package/lib/systems/bodypart/BodyPartPositionMapping.js.map +1 -1
  253. package/lib/systems/bodypart/CombatInjuryIntegration.js.map +1 -1
  254. package/lib/systems/bodypart/InjuryIntegration.js.map +1 -1
  255. package/lib/systems/bodypart/InjuryTracker.js.map +1 -1
  256. package/lib/systems/bodypart/MovementPenaltySystem.js.map +1 -1
  257. package/lib/systems/bodypart/PlayerInjuryTrackingManager.js.map +1 -1
  258. package/lib/systems/bodypart/types.js.map +1 -1
  259. package/lib/systems/breathing/BreathingDisruptionSystem.js.map +1 -1
  260. package/lib/systems/breathing/feedback.js.map +1 -1
  261. package/lib/systems/breathing/integration.js.map +1 -1
  262. package/lib/systems/combat/BalanceSystem.js.map +1 -1
  263. package/lib/systems/combat/BreakingStatusEffects.js.map +1 -1
  264. package/lib/systems/combat/CombatStateSystem.js.map +1 -1
  265. package/lib/systems/combat/ConsciousnessSystem.js.map +1 -1
  266. package/lib/systems/combat/FallIntegration.js.map +1 -1
  267. package/lib/systems/combat/GrappleSystem.js.map +1 -1
  268. package/lib/systems/combat/LimbExposureSystem.js.map +1 -1
  269. package/lib/systems/combat/PainResponseSystem.js.map +1 -1
  270. package/lib/systems/combat/TrainingCombatSystem.js.map +1 -1
  271. package/lib/systems/combat/painConsciousnessUtils.js.map +1 -1
  272. package/lib/systems/combat/typeGuards.js.map +1 -1
  273. package/lib/systems/effects.js.map +1 -1
  274. package/lib/systems/game.js.map +1 -1
  275. package/lib/systems/movement/InjuryMovementModifier.js.map +1 -1
  276. package/lib/systems/movement/helpers/AccelerationUpdater.js.map +1 -1
  277. package/lib/systems/movement/helpers/accelerationUtils.js.map +1 -1
  278. package/lib/systems/movement/integration.js.map +1 -1
  279. package/lib/systems/physics/AttackMovementPhysics.js.map +1 -1
  280. package/lib/systems/physics/CollisionDetection.js.map +1 -1
  281. package/lib/systems/physics/CoordinateMapper.js.map +1 -1
  282. package/lib/systems/physics/KnockbackPhysics.js.map +1 -1
  283. package/lib/systems/physics/MovementPhysics.js.map +1 -1
  284. package/lib/systems/physics/PhysicalReachCalculator.js.map +1 -1
  285. package/lib/systems/physics/SpeedModifierSystem.js.map +1 -1
  286. package/lib/systems/trigram/KoreanCulture.js.map +1 -1
  287. package/lib/systems/trigram/KoreanTechniques.js.map +1 -1
  288. package/lib/systems/trigram/StanceManager.js.map +1 -1
  289. package/lib/systems/trigram/TransitionCalculator.js.map +1 -1
  290. package/lib/systems/trigram/TrigramCalculator.js.map +1 -1
  291. package/lib/systems/trigram/techniques/DarkOpsTechniques.js.map +1 -1
  292. package/lib/systems/trigram/techniques/GamTechniques.js.map +1 -1
  293. package/lib/systems/trigram/techniques/GanTechniques.js.map +1 -1
  294. package/lib/systems/trigram/techniques/GonTechniques.js.map +1 -1
  295. package/lib/systems/trigram/techniques/SonTechniques.js.map +1 -1
  296. package/lib/systems/trigram/techniques/TechniqueConfig.js.map +1 -1
  297. package/lib/systems/trigram/techniques/index.js.map +1 -1
  298. package/lib/systems/trigram/types/GonTechniqueExtensions.js.map +1 -1
  299. package/lib/systems/trigram/types.js.map +1 -1
  300. package/lib/systems/types.js.map +1 -1
  301. package/lib/systems/vitalpoint/DamageCalculator.js.map +1 -1
  302. package/lib/systems/vitalpoint/HitDetection.js.map +1 -1
  303. package/lib/systems/vitalpoint/KoreanAnatomy.js.map +1 -1
  304. package/lib/systems/vitalpoint/KoreanVitalPoints.js.map +1 -1
  305. package/lib/systems/vitalpoint/MeridianVitalPointMapping.js.map +1 -1
  306. package/lib/types/AccessibilityTypes.js.map +1 -1
  307. package/lib/types/PhysicsTypes.js.map +1 -1
  308. package/lib/types/common.js.map +1 -1
  309. package/lib/types/constants/colors.js.map +1 -1
  310. package/lib/types/constants/designSystem.js.map +1 -1
  311. package/lib/types/constants/layout.js.map +1 -1
  312. package/lib/types/constants/performance.js.map +1 -1
  313. package/lib/types/constants/typography.js.map +1 -1
  314. package/lib/types/facial.js.map +1 -1
  315. package/lib/types/hand-animation.js.map +1 -1
  316. package/lib/types/injury.js.map +1 -1
  317. package/lib/types/physics.js.map +1 -1
  318. package/lib/types/skeletal.js.map +1 -1
  319. package/lib/types/techniqueId.js.map +1 -1
  320. package/lib/utils/accessibility.js.map +1 -1
  321. package/lib/utils/arenaWorldDimensions.js.map +1 -1
  322. package/lib/utils/assetConfig.js.map +1 -1
  323. package/lib/utils/characterScaling.js.map +1 -1
  324. package/lib/utils/colorHelpers.js.map +1 -1
  325. package/lib/utils/colorUtils.js.map +1 -1
  326. package/lib/utils/combatReadiness.js.map +1 -1
  327. package/lib/utils/controlMapping.js.map +1 -1
  328. package/lib/utils/deviceDetection.js.map +1 -1
  329. package/lib/utils/effectUtils.js.map +1 -1
  330. package/lib/utils/fabricTextures.js.map +1 -1
  331. package/lib/utils/hapticFeedback.js.map +1 -1
  332. package/lib/utils/haptics.js.map +1 -1
  333. package/lib/utils/htmlOverlayHelpers.js.map +1 -1
  334. package/lib/utils/inputSystem.js.map +1 -1
  335. package/lib/utils/koreanThemeHelpers.js.map +1 -1
  336. package/lib/utils/math.js.map +1 -1
  337. package/lib/utils/mobileLayoutHelpers.js.map +1 -1
  338. package/lib/utils/mobileUIUtils.js.map +1 -1
  339. package/lib/utils/performance/PerformanceMonitor.js.map +1 -1
  340. package/lib/utils/performance/PerformanceOverlay3D.js.map +1 -1
  341. package/lib/utils/performance/usePerformanceMonitor.js.map +1 -1
  342. package/lib/utils/performanceOptimization.js.map +1 -1
  343. package/lib/utils/player3DHelpers.js.map +1 -1
  344. package/lib/utils/playerUtils.js.map +1 -1
  345. package/lib/utils/responsiveLayout.js.map +1 -1
  346. package/lib/utils/responsiveLayoutHelpers.js.map +1 -1
  347. package/lib/utils/responsiveOrientationConstants.js.map +1 -1
  348. package/lib/utils/safeAreaUtils.js.map +1 -1
  349. package/lib/utils/sharedPhysicsConfig.js.map +1 -1
  350. package/lib/utils/skeletonScaling.js.map +1 -1
  351. package/lib/utils/stanceHelpers.js.map +1 -1
  352. package/lib/utils/threeObjectPool.js.map +1 -1
  353. package/lib/utils/visualEffects.js.map +1 -1
  354. package/package.json +5 -5
@@ -1 +1 @@
1
- {"version":3,"file":"CombatScreen3D.js","names":[],"sources":["../../../../src/components/screens/combat/CombatScreen3D.tsx"],"sourcesContent":["/**\n * CombatScreen3D - Three.js-based combat screen (Black Trigram 흑괘)\n *\n * Maintains all existing combat logic and state management\n * Uses Html overlays for UI and 3D meshes for game objects\n */\n\nimport { Canvas } from \"@react-three/fiber\";\nimport { Html } from \"@react-three/drei\";\nimport {\n Bloom,\n EffectComposer,\n Noise,\n Vignette,\n} from \"@react-three/postprocessing\";\nimport React, {\n useCallback,\n useEffect,\n useMemo,\n useRef,\n useState,\n} from \"react\";\nimport { useAudio } from \"../../../audio/AudioProvider\";\nimport { useActionFeedback } from \"../../../hooks/useActionFeedback\";\nimport { useCombatTimer } from \"../../../hooks/useCombatTimer\";\nimport { useKeyboardControls } from \"../../../hooks/useKeyboardControls\";\nimport { usePlayerAnimation } from \"../../../hooks/usePlayerAnimation\";\nimport { useRoundTransition } from \"../../../hooks/useRoundTransition\";\nimport { useTechniqueSelection } from \"../../../hooks/useTechniqueSelection\";\nimport { useWebGLContextLossHandler } from \"../../../hooks/useWebGLContextLossHandler\";\nimport { useCombatAttackMovement } from \"./hooks/useCombatAttackMovement\";\nimport { HitEffect, PlayerState } from \"../../../systems\";\nimport { CombatSystem } from \"../../../systems/CombatSystem\";\nimport {\n AdaptiveDifficulty,\n getPersonalityByArchetype,\n} from \"../../../systems/ai\";\nimport {\n AnimationEvents,\n AnimationState,\n AnimationType,\n determineRecoveryType,\n getAnimation,\n getRecoveryAnimationState,\n resolveTechniqueAnimation,\n} from \"../../../systems/animation\";\nimport { BalanceSystem } from \"../../../systems/combat/BalanceSystem\";\nimport type { BalancePlayerState } from \"../../../systems/combat/BalanceSystem\";\nimport { HitEffectType } from \"../../../systems/effects\";\nimport { injuryMovementModifier } from \"../../../systems/movement/InjuryMovementModifier\";\nimport { TRIGRAM_STANCES_ORDER } from \"../../../systems/trigram/types\";\nimport { TRIGRAM_TECHNIQUES } from \"../../../systems/trigram/techniques\";\nimport type { KoreanTechnique } from \"../../../systems/vitalpoint/types\";\nimport {\n CombatState,\n GameMode,\n PlayerArchetype,\n Position,\n TrigramStance,\n} from \"../../../types\";\nimport { Injury, InjuryType } from \"../../../types/injury\";\nimport { Z_INDEX } from \"../../../types/LayoutTypes\";\nimport { getMobileControlsBottom } from \"../../../types/constants/layout\";\nimport {\n FONT_FAMILY,\n getPerformanceSettings,\n KOREAN_COLORS,\n ROUND_ANNOUNCEMENT_TIMINGS,\n} from \"@/types/constants\";\nimport { getAnimationTypeForTechnique } from \"../../../data/techniqueMappings\";\nimport { toHexColor } from \"../../../utils/colorHelpers\";\nimport { usePlayerMovement } from \"../../../utils/inputSystem\";\nimport { PerformanceOverlay3D } from \"../../../utils/performance\";\nimport { createPlayerFromArchetype } from \"../../../utils/playerUtils\";\nimport { createCameraConfig } from \"../../../utils/sharedPhysicsConfig\";\nimport { useAdaptiveQuality } from \"../../shared/three/optimization\";\nimport { useKoreanTheme } from \"../../shared/base/useKoreanTheme\";\nimport {\n ActionFeedback,\n TechniqueName,\n} from \"../../shared/three/effects/ActionFeedback\";\nimport { DamageNumbers } from \"../../shared/three/effects/DamageNumbers\";\nimport HitEffects3D from \"../../shared/three/effects/HitEffects3D\";\nimport { VitalPointMarkers3D } from \"../../shared/three/effects/VitalPointMarkers3D\";\nimport { StanceChangeIndicator } from \"../../shared/three/indicators/StanceChangeIndicator\";\nimport { CombatArena3D } from \"../../shared/three/scene/CombatArena3D\";\nimport { BreathingIndicator } from \"../../shared/three/ui/BreathingIndicator\";\nimport { ComboCounter } from \"../../shared/three/ui/ComboCounter\";\nimport { VitalPointOverlayControlsHtml } from \"../../shared/three/ui/VitalPointOverlayControlsHtml\";\nimport { KeyboardHints } from \"./components/controls/KeyboardHints\";\nimport { MatchCountdown } from \"./components/feedback/MatchCountdown\";\nimport { RoundAnnouncement } from \"./components/feedback/RoundAnnouncementOverlayHtml\";\nimport { RoundDisplayStatus } from \"./components/feedback/RoundDisplayStatus\";\nimport { RoundStartAnnouncement } from \"./components/feedback/RoundStartAnnouncementOverlayHtml\";\nimport { InputBufferDisplay } from \"./components/indicators/InputBufferDisplay\";\nimport { GestureEvent } from \"../../../hooks/useTouchControls\";\nimport {\n MovementType,\n SpeedModifierSystem,\n} from \"../../../systems/physics/SpeedModifierSystem\";\nimport { Technique } from \"../../../types\";\nimport {\n animationStateToPlayerAnimation,\n convertPlayerStateToProps,\n getBalanceState,\n} from \"../../../utils/player3DHelpers\";\nimport {\n GestureRecognizerPure,\n MobileControlsOverlay,\n StanceWheelPure,\n} from \"../../shared/mobile\";\nimport { ButtonEventType } from \"../../shared/mobile/ActionButtons\";\nimport { Direction, DPadEventType } from \"../../shared/mobile/VirtualDPad\";\nimport { Player3DWithTransitions } from \"../../shared/three/models/Player3DWithTransitions\";\nimport { PauseMenu } from \"./components/controls/PauseMenu\";\nimport { TraumaOverlay3D } from \"./components/effects/TraumaOverlay3D\";\nimport { CombatParticleEffects3D } from \"./components/effects/CombatParticleEffects3D\";\nimport {\n CombatBottomHUD,\n CombatLeftHUD,\n CombatPortraitStatusStrip,\n CombatRightHUD,\n CombatTopHUD,\n} from \"./components/hud\";\nimport { FPSMonitor } from \"./components/hud/FPSMonitor\";\nimport { PlayerStateOverlayHtml } from \"./components/hud/PlayerStateOverlayHtml\";\nimport { BalanceIndicatorOverlayHtml } from \"../../ui/combat/BalanceIndicatorOverlayHtml\";\nimport {\n ANNOUNCEMENT_FADE_OUT_DELAY,\n calculateAccuracy,\n STANCE_INDEX_MAP,\n} from \"./helpers\";\nimport { AnimationUpdater } from \"./helpers/AnimationUpdater\";\nimport { AccelerationUpdater } from \"../../../systems/movement/helpers/AccelerationUpdater\";\nimport { isRunningSpeed } from \"../../../systems/movement/helpers/accelerationUtils\";\nimport { useAICombat } from \"./hooks/useAICombat\";\nimport { useCombatActions } from \"./hooks/useCombatActions\";\nimport { useCombatAudio } from \"./hooks/useCombatAudio\";\nimport { useCombatLayout } from \"./hooks/useCombatLayout\";\nimport { useCombatState } from \"./hooks/useCombatState\";\n\n/**\n * Props for the CombatScreen3D component.\n * Provides all state and callbacks required for the 3D combat screen.\n */\nexport interface CombatScreen3DProps {\n /**\n * Array of player states (expects exactly 2 players).\n * Each PlayerState contains all combat and status information for a player.\n */\n readonly players: readonly PlayerState[];\n /**\n * Callback to update a player's state by index.\n * @param playerIndex - Index of the player to update (0 or 1).\n * @param updates - Partial PlayerState with updated fields.\n */\n readonly onPlayerUpdate: (\n playerIndex: number,\n updates: Partial<PlayerState>,\n ) => void;\n /**\n * Current round number (1-based).\n */\n readonly currentRound: number;\n /**\n * Remaining time in seconds for the current round.\n */\n readonly timeRemaining: number;\n /**\n * Whether combat is currently paused.\n */\n readonly isPaused: boolean;\n /**\n * Callback when the user exits to the menu.\n */\n readonly onReturnToMenu: () => void;\n /**\n * Callback when the match ends, with the winner's index (0 or 1).\n * @param winner - Index of the winning player.\n */\n readonly onGameEnd: (winner: number) => void;\n /**\n * Optional game mode (affects rules/behavior).\n */\n readonly gameMode?: GameMode;\n /**\n * Canvas width in pixels. Defaults to 1200.\n */\n readonly width?: number;\n /**\n * Canvas height in pixels. Defaults to 800.\n */\n readonly height?: number;\n /**\n * Enable adaptive quality adjustment (default: true on mobile)\n */\n readonly enableAdaptiveQuality?: boolean;\n /**\n * Show performance overlay in dev mode (default: import.meta.env.DEV)\n */\n readonly showPerformanceOverlay?: boolean;\n}\n\n/**\n * CombatScreen3D Component\n * Three.js-based combat screen with 3D characters and effects\n */\n\n/**\n * AdaptiveQualityWrapper - Internal component to use adaptive quality hook\n * Must be inside Canvas to use useFrame from @react-three/fiber\n *\n * Hoisted outside CombatScreen3D to avoid \"Cannot create components during render\"\n * warnings from react-hooks/component-creation. Keeps the component type stable\n * across renders of the parent.\n */\nconst AdaptiveQualityWrapper: React.FC<{\n readonly enabled: boolean;\n readonly isMobile: boolean;\n readonly children: React.ReactNode;\n}> = ({ enabled, isMobile, children }) => {\n // Monitor FPS and adjust quality dynamically\n // Quality settings are logged but not currently applied to rendering\n // Future: Pass quality settings to child components for dynamic adjustments\n useAdaptiveQuality(enabled, isMobile, (newQuality) => {\n if (import.meta.env.DEV) {\n console.log(`[CombatScreen3D] Quality adjusted to: ${newQuality}`);\n }\n });\n\n return <>{children}</>;\n};\n\nexport const CombatScreen3D: React.FC<CombatScreen3DProps> = ({\n players,\n onPlayerUpdate,\n currentRound,\n timeRemaining,\n isPaused,\n onReturnToMenu,\n onGameEnd,\n width = 1200,\n height = 800,\n enableAdaptiveQuality,\n showPerformanceOverlay = import.meta.env.DEV,\n}) => {\n // Track when content is ready to render (prevents flash of empty content)\n const [contentReady, setContentReady] = useState(false);\n\n // Track context loss count for debugging\n const contextLossCountRef = useRef(0);\n\n // Handle WebGL context loss and restoration\n useWebGLContextLossHandler({\n onContextLost: () => {\n console.warn(\"⚠️ WebGL context lost in CombatScreen\");\n contextLossCountRef.current += 1;\n setContentReady(false);\n },\n onContextRestored: () => {\n // Context restored - re-enable content after short delay\n setTimeout(() => setContentReady(true), 100);\n },\n autoRestore: true,\n });\n\n // Ensure content renders after component is mounted and stable\n useEffect(() => {\n const timer = setTimeout(() => setContentReady(true), 50);\n return () => clearTimeout(timer);\n }, []);\n\n // Audio context for button interactions\n const audio = useAudio();\n\n // Performance marks - only in dev mode and memoized\n useEffect(() => {\n if (import.meta.env.DEV) {\n performance.mark(\"combat-3d-render-start\");\n return () => {\n performance.mark(\"combat-3d-render-end\");\n performance.measure(\n \"combat-3d-render\",\n \"combat-3d-render-start\",\n \"combat-3d-render-end\",\n );\n };\n }\n }, []);\n\n // Layout calculations\n const { arenaBounds, isMobile, isPortrait, screenSize, layoutConstants } =\n useCombatLayout(width, height);\n\n // Use Korean theme hook for consistent theming\n const theme = useKoreanTheme({\n variant: \"primary\",\n size: \"md\",\n isMobile,\n });\n\n // Screen size scaling for 4K and large displays\n // Uses SPACING_SCALE_MAP values: mobile=0.5, tablet=0.75, desktop=1.0, large=1.25, xlarge=1.5\n const positionScale = useMemo(() => {\n if (isMobile) {\n return 1.0;\n }\n\n switch (screenSize) {\n case \"mobile\":\n return 1.0; // Mobile already has special handling\n case \"tablet\":\n return 1.0;\n case \"desktop\":\n return 1.0;\n case \"large\":\n return 1.25;\n case \"xlarge\":\n return 1.5; // 4K displays need 1.5x offsets\n default:\n return 1.0;\n }\n }, [isMobile, screenSize]);\n\n // Camera and rendering configuration based on device\n // Use shared physics config for consistent camera setup across screens\n // In portrait we pull the camera back on Z and widen the FOV so both\n // fighters fit the narrow viewport without getting clipped.\n // 세로 모드에서는 카메라를 뒤로 빼고 FOV를 넓힘\n const cameraConfig = useMemo(() => {\n const base = createCameraConfig(isMobile);\n if (!isPortrait) return base;\n return {\n ...base,\n fov: Math.min(80, base.fov + 15),\n position: [base.position[0], base.position[1], base.position[2] + 4] as [\n number,\n number,\n number,\n ],\n };\n }, [isMobile, isPortrait]);\n\n // Rendering quality based on device (optimize for 60fps on mobile)\n // Uses performance tier system for extra-small, mobile, and desktop devices\n const renderConfig = useMemo(() => {\n const performanceSettings = getPerformanceSettings(width, isMobile);\n\n return {\n shadowMapSize: performanceSettings.shadowMapSize,\n dpr: performanceSettings.dpr,\n antialias: performanceSettings.antialias,\n maxParticles: performanceSettings.maxParticles,\n postProcessing: performanceSettings.postProcessing,\n };\n }, [isMobile, width]);\n\n // Adaptive quality - default to enabled on mobile, optional on desktop\n const shouldEnableAdaptiveQuality = enableAdaptiveQuality ?? isMobile;\n\n // Combat state management\n const { state: combatState, actions: combatActions } = useCombatState();\n\n // ═══════════════════════════════════════════════════════════════════════════\n // Vital Point Overlay Controls State\n // ═══════════════════════════════════════════════════════════════════════════\n\n // Vital point overlay state (for both players)\n const [overlayVisible, setOverlayVisible] = useState(false);\n const [severityFilters, setSeverityFilters] = useState<\n import(\"../../../types/common\").VitalPointSeverity[]\n >([]);\n const [regionFilter, setRegionFilter] =\n useState<\n import(\"../../shared/three/ui/VitalPointOverlayControlsHtml\").BodyRegionFilter\n >(\"all\");\n const [searchQuery, setSearchQuery] = useState(\"\");\n const [showLabels, setShowLabels] = useState(true);\n const [animated, setAnimated] = useState(true);\n const [scale, setScale] = useState(1.2); // Larger scale for better visibility in combat\n // Performance monitor visibility toggle (F9 key)\n const [showPerformanceMonitor, setShowPerformanceMonitor] = useState(false);\n\n // Keyboard shortcut for toggling overlay (V key) and performance monitor (F9)\n // Ctrl key removed - running now acceleration-based\n useEffect(() => {\n const handleKeyDown = (e: KeyboardEvent) => {\n if (e.key === \"v\" || e.key === \"V\") {\n setOverlayVisible((prev) => !prev);\n audio.playSFX(\"menu_select\");\n }\n // F9 key toggles performance monitor (development only)\n // Note: P key is reserved for Philosophy screen\n if (e.key === \"F9\" && import.meta.env.DEV) {\n e.preventDefault();\n setShowPerformanceMonitor((prev) => !prev);\n }\n };\n\n window.addEventListener(\"keydown\", handleKeyDown);\n return () => {\n window.removeEventListener(\"keydown\", handleKeyDown);\n };\n }, [audio]);\n\n // Action feedback system for damage numbers, combo counter, and technique names\n const { state: feedbackState, actions: feedbackActions } = useActionFeedback({\n damageNumberDuration: 1500,\n actionFeedbackDuration: 1200,\n techniqueDuration: 2000,\n comboResetTime: 2000,\n });\n\n // Combat audio\n const combatAudio = useCombatAudio();\n const { playStanceChangeSound } = combatAudio;\n\n // Match score tracking - use ref for internal updates, state for rendering\n const [matchScore, setMatchScore] = useState({ player1: 0, player2: 0 });\n const matchScoreRef = useRef(matchScore);\n\n // Helper to update match score without triggering setState in effects\n const updateMatchScore = useCallback((winner: 0 | 1) => {\n const newScore = {\n player1:\n winner === 0\n ? matchScoreRef.current.player1 + 1\n : matchScoreRef.current.player1,\n player2:\n winner === 1\n ? matchScoreRef.current.player2 + 1\n : matchScoreRef.current.player2,\n };\n matchScoreRef.current = newScore;\n // Use setTimeout to defer the setState call outside the effect\n setTimeout(() => setMatchScore(newScore), 0);\n }, []);\n\n // Internal round tracking (since parent may always pass currentRound=1)\n const [internalRound, setInternalRound] = useState(currentRound);\n\n // Current time for balance indicators and other time-dependent UI\n // Updated every 200ms to avoid calling Date.now() on every render (60fps)\n // Use lazy initializer so Date.now() is not called during render (React purity)\n const [currentTime, setCurrentTime] = useState(() => Date.now());\n\n // Match countdown state - DISABLED: skip countdown and start combat immediately\n // Using state for hasShownMatchCountdown to avoid ref access during render\n const [hasShownMatchCountdown, setHasShownMatchCountdown] = useState(true); // Already shown (skipped)\n const [showMatchCountdown, setShowMatchCountdown] = useState(false); // Don't show\n const [showRoundStart, setShowRoundStart] = useState(false);\n const [matchCountdownComplete, setMatchCountdownComplete] = useState(true); // Already complete (skipped)\n\n // Player 1 position in METERS (relative to arena center)\n // Player 1 starts at 10% left of center for realistic combat distance (~1.6m apart)\n // Allows kicks to land with 1-2 steps, punches with 2-3 steps (realistic fighting)\n const [player1Position, setPlayer1Position] = useState<Position>({\n x: arenaBounds.worldWidthMeters * -0.1, // 10% left of center (~0.8m for 8m arena)\n y: 0, // Centered\n });\n\n // Pause menu state - local state for pause menu visibility\n // Local state for pause menu UI visibility\n // Note: isPaused (prop) controls game pause from parent, showPauseMenu (state) controls menu UI\n // Both need to be checked because:\n // - isPaused: External pause state (e.g., from parent component or global game state)\n // - showPauseMenu: Local UI state for pause menu overlay\n // They can be out of sync intentionally (e.g., parent pauses game but menu not shown)\n const [showPauseMenu, setShowPauseMenu] = useState(false);\n\n // Pause menu handlers\n const handlePause = useCallback(() => {\n setShowPauseMenu(true);\n audio.playSFX(\"menu_select\");\n }, [audio]);\n\n const handleResume = useCallback(() => {\n setShowPauseMenu(false);\n audio.playSFX(\"menu_select\");\n }, [audio]);\n\n const handleRestart = useCallback(() => {\n // Note: React 19 automatically batches all these state updates into a single render\n // This ensures atomic state transitions without race conditions\n\n // Reset to first round\n setInternalRound(1);\n setMatchScore({ player1: 0, player2: 0 });\n matchScoreRef.current = { player1: 0, player2: 0 };\n\n // Reset combat state\n combatActions.setRoundEnded(false);\n combatActions.setRoundStarted(false);\n combatActions.setRoundDisplayStatus(null);\n\n // Reset player health, resources, combat state, and visual state\n // Includes consciousness, pain, balance, combatState to clear any blur/vignette effects\n // and reset from fallen/stunned states\n onPlayerUpdate(0, {\n health: 100,\n stamina: 100,\n ki: 100,\n consciousness: 100,\n pain: 0,\n balance: 100,\n combatState: CombatState.IDLE,\n isStunned: false,\n isBlocking: false,\n });\n onPlayerUpdate(1, {\n health: 100,\n stamina: 100,\n ki: 100,\n consciousness: 100,\n pain: 0,\n balance: 100,\n combatState: CombatState.IDLE,\n isStunned: false,\n isBlocking: false,\n });\n\n // Reset player positions to starting positions for rematch\n // Use meter-based positions for physics-first system consistency\n setPlayer1Position({\n x: arenaBounds.worldWidthMeters * -0.1, // 10% left of center in meters\n y: 0, // Centered\n });\n onPlayerUpdate(1, {\n position: {\n x: arenaBounds.worldWidthMeters * 0.1, // 10% right of center in meters\n y: 0, // Centered\n },\n });\n\n // Close pause menu and start first round\n // The timer will reset automatically via the initialTime prop when round starts\n setShowPauseMenu(false);\n setShowRoundStart(true);\n\n audio.playSFX(\"menu_select\");\n }, [audio, combatActions, onPlayerUpdate, arenaBounds, setPlayer1Position]);\n\n // Round transition complete handler - checks for match end or starts next round\n const handleRoundTransitionComplete = useCallback(() => {\n if (import.meta.env.DEV) {\n console.log(\"[DEV] Round transition complete, checking match status\");\n }\n // Check if match is over (best of 3 - first to 2 wins)\n const currentScore = matchScoreRef.current;\n if (currentScore.player1 >= 2 || currentScore.player2 >= 2) {\n // Match is over - call onGameEnd instead of starting next round\n const matchWinner = currentScore.player1 >= 2 ? 0 : 1;\n if (import.meta.env.DEV) {\n console.log(\"[DEV] Match over, winner:\", matchWinner);\n }\n onGameEnd(matchWinner);\n return; // Don't start next round\n }\n\n // Reset all combat state for next round (combos, hit effects, messages cleared)\n combatActions.resetRoundState();\n\n // Increment internal round counter for next round\n // Use the updater function to get the new value and trigger announcement\n setInternalRound((prev) => {\n const nextRound = prev + 1;\n if (import.meta.env.DEV) {\n console.log(\"[DEV] Incrementing round from\", prev, \"to\", nextRound);\n }\n // Wait for transition duration plus fade-out to complete before showing next round announcement\n setTimeout(() => {\n if (import.meta.env.DEV) {\n console.log(\n \"[DEV] Showing round start announcement for round\",\n nextRound,\n );\n }\n setShowRoundStart(true);\n }, ANNOUNCEMENT_FADE_OUT_DELAY);\n return nextRound;\n });\n\n // Reset player health, resources, combat state, and visual state for next round\n // Includes consciousness, pain, balance, combatState to clear any blur/vignette effects\n // and reset from fallen/stunned states\n onPlayerUpdate(0, {\n health: 100,\n stamina: 100,\n ki: 100,\n consciousness: 100,\n pain: 0,\n balance: 100,\n combatState: CombatState.IDLE,\n isStunned: false,\n isBlocking: false,\n });\n onPlayerUpdate(1, {\n health: 100,\n stamina: 100,\n ki: 100,\n consciousness: 100,\n pain: 0,\n balance: 100,\n combatState: CombatState.IDLE,\n isStunned: false,\n isBlocking: false,\n });\n\n // Reset player positions to starting positions for new round\n // Use meter-based positions for physics-first system consistency\n setPlayer1Position({\n x: arenaBounds.worldWidthMeters * -0.1, // 10% left of center in meters\n y: 0, // Centered\n });\n // Player 2 position is reset via onPlayerUpdate\n onPlayerUpdate(1, {\n position: {\n x: arenaBounds.worldWidthMeters * 0.1, // 10% right of center in meters\n y: 0, // Centered\n },\n });\n }, [\n combatActions,\n onGameEnd,\n onPlayerUpdate,\n arenaBounds,\n // Note: setPlayer1Position is a stable React setState function and won't cause unnecessary re-renders\n setPlayer1Position,\n ]);\n\n // Round transition management\n const {\n showAnnouncement,\n roundWinner,\n currentRoundNumber: transitionRoundNumber,\n skipCountdown,\n startTransition,\n } = useRoundTransition(\n {\n announcementDuration: ROUND_ANNOUNCEMENT_TIMINGS.ANNOUNCEMENT_DURATION,\n countdownDuration: ROUND_ANNOUNCEMENT_TIMINGS.COUNTDOWN_DURATION,\n transitionDuration: ROUND_ANNOUNCEMENT_TIMINGS.TRANSITION_DURATION,\n },\n handleRoundTransitionComplete,\n );\n\n // Player 2 position in METERS (relative to arena center) - AI-controlled\n // Player 2 starts at 10% right of center for realistic combat distance (~1.6m apart)\n // Allows kicks to land with 1-2 steps, punches with 2-3 steps (realistic fighting)\n const player2Position = useMemo<Position>(() => {\n if (players.length >= 2 && players[1].position) {\n return players[1].position;\n }\n return {\n x: arenaBounds.worldWidthMeters * 0.1, // 10% right of center (~0.8m for 8m arena)\n y: 0, // Centered\n };\n }, [players, arenaBounds]);\n\n // Combined positions for backward compatibility with existing code\n const playerPositions = useMemo<Position[]>(() => {\n return [player1Position, player2Position];\n }, [player1Position, player2Position]);\n\n // Physics-first: positions are already in METERS\n // Direct conversion to 3D world coordinates - no pixel math needed\n const player1Position3D: [number, number, number] = useMemo(() => {\n // playerPosition.x is lateral position in meters (- = left, + = right)\n // playerPosition.y is forward/backward position in meters (- = toward camera, + = away)\n return [playerPositions[0].x, 0, playerPositions[0].y];\n }, [playerPositions]);\n\n const player2Position3D: [number, number, number] = useMemo(() => {\n return [playerPositions[1].x, 0, playerPositions[1].y];\n }, [playerPositions]);\n\n // Calculate dynamic rotations so players always face each other\n // atan2(dx, dz) gives the Y-axis rotation needed to face direction (dx, dz)\n // This is the standard formula for rotating around Y to point at a target\n // NOTE: player1Rotation is calculated after usePlayerMovement below\n\n const player2Rotation = useMemo(() => {\n const dx = player1Position3D[0] - player2Position3D[0];\n const dz = player1Position3D[2] - player2Position3D[2];\n return Math.atan2(dx, dz);\n }, [player1Position3D, player2Position3D]);\n\n // Combat system\n const combatSystem = useMemo(() => new CombatSystem(), []);\n\n // Balance system for recovery mechanics\n const balanceSystem = useMemo(() => new BalanceSystem(), []);\n\n // Speed Modifier System for dynamic movement speed calculations\n const speedModifierSystem = useMemo(() => new SpeedModifierSystem(), []);\n\n // Track speed modifiers for HUD display\n // Initial values match SpeedModifierSystem.BASE_WALKING_SPEED and BASE_ACCELERATION\n const [player1SpeedModifiers, setPlayer1SpeedModifiers] = useState({\n finalSpeed: 6.0, // BASE_WALK_SPEED (6.0 m/s)\n baseSpeed: 6.0,\n finalAcceleration: 12.0, // BASE_ACCELERATION (12.0 m/s²)\n });\n const [player2SpeedModifiers, setPlayer2SpeedModifiers] = useState({\n finalSpeed: 6.0, // BASE_WALK_SPEED (6.0 m/s)\n baseSpeed: 6.0,\n finalAcceleration: 12.0, // BASE_ACCELERATION (12.0 m/s²)\n });\n\n // Track walk/run speeds for acceleration interpolation (archetype-aware)\n const [player1WalkRunSpeeds, setPlayer1WalkRunSpeeds] = useState({\n walkSpeed: 6.0,\n runSpeed: 10.0,\n });\n\n // Calculate speed modifiers for both players when state changes\n // Updates at 5Hz (every 200ms) to balance responsiveness and performance\n // Get both walk and run speeds for acceleration interpolation\n useEffect(() => {\n const updateSpeedModifiers = () => {\n if (players.length >= 2) {\n // Player 1 speed modifiers - calculate both walk and run\n const player1WalkModifiers =\n speedModifierSystem.calculateSpeedModifiers(\n players[0],\n MovementType.WALKING,\n false, // isCrouching\n );\n const player1RunModifiers = speedModifierSystem.calculateSpeedModifiers(\n players[0],\n MovementType.RUNNING,\n false, // isCrouching\n );\n\n setPlayer1SpeedModifiers({\n finalSpeed: player1WalkModifiers.finalSpeed,\n baseSpeed: player1WalkModifiers.baseSpeed,\n finalAcceleration: player1WalkModifiers.finalAcceleration,\n });\n\n // Store walk/run speeds for acceleration interpolation\n // These account for archetype speeds and stance modifiers\n setPlayer1WalkRunSpeeds({\n walkSpeed: player1WalkModifiers.finalSpeed,\n runSpeed: player1RunModifiers.finalSpeed,\n });\n\n // Player 2 speed modifiers\n const player2Modifiers = speedModifierSystem.calculateSpeedModifiers(\n players[1],\n MovementType.WALKING,\n false,\n );\n setPlayer2SpeedModifiers({\n finalSpeed: player2Modifiers.finalSpeed,\n baseSpeed: player2Modifiers.baseSpeed,\n finalAcceleration: player2Modifiers.finalAcceleration,\n });\n }\n };\n\n // Initial calculation\n updateSpeedModifiers();\n\n // Update every 200ms (5Hz) for responsive feedback without excessive re-renders\n const intervalId = setInterval(updateSpeedModifiers, 200);\n\n return () => clearInterval(intervalId);\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [players]); // speedModifierSystem is memoized and never changes\n\n // Update current time for balance indicators at 5Hz (200ms)\n // This avoids calling Date.now() on every render (60fps = 60 times/second)\n // Instead, we update 5 times/second, which is sufficient for UI feedback\n useEffect(() => {\n const updateTime = () => {\n setCurrentTime(Date.now());\n };\n\n // Update every 200ms (5Hz) - same rate as speed modifiers\n const intervalId = setInterval(updateTime, 200);\n\n return () => clearInterval(intervalId);\n }, []);\n\n // Calculate leg injury factor for physics-based movement\n // Averages left and right leg health to determine speed penalty\n const calculateLegInjuryFactor = useCallback(\n (player: PlayerState): number => {\n if (!player.bodyPartHealth) return 0;\n\n const leftLeg = player.bodyPartHealth.legLeft ?? player.maxHealth;\n const rightLeg = player.bodyPartHealth.legRight ?? player.maxHealth;\n const maxHealth = player.maxHealth;\n\n const averageLegHealth = (leftLeg + rightLeg) / (2 * maxHealth);\n return Math.max(0, Math.min(1, 1.0 - averageLegHealth)); // 0 = healthy, 1 = critical\n },\n [],\n );\n\n // Get player1 data for movement physics\n // Memoize based on player1 reference (React Compiler prefers less specific dependencies)\n const player1 = players.length > 0 ? players[0] : undefined;\n const player1Data = useMemo(() => {\n const p1 = player1 ?? createPlayerFromArchetype(PlayerArchetype.MUSA, 0);\n return {\n currentStance: p1.currentStance,\n legInjuryFactor: calculateLegInjuryFactor(p1),\n };\n }, [player1, calculateLegInjuryFactor]);\n\n // Track current attack animation for each player\n // Used to determine which skeletal animation to play during attacks\n // 각 플레이어의 현재 공격 애니메이션 추적\n const [player1AttackAnimation, setPlayer1AttackAnimation] = useState<\n string | undefined\n >(undefined);\n const [player2AttackAnimation, setPlayer2AttackAnimation] = useState<\n string | undefined\n >(undefined);\n\n // Track current technique ID for enum-based animation lookup\n // 열거형 기반 애니메이션 조회를 위한 현재 기술 ID 추적\n const [player1TechniqueId, setPlayer1TechniqueId] = useState<\n string | undefined\n >(undefined);\n const [player2TechniqueId, setPlayer2TechniqueId] = useState<\n string | undefined\n >(undefined);\n\n // Track injuries for trauma visualization (TraumaOverlay3D)\n // 외상 시각화를 위한 부상 추적\n const [player1Injuries, setPlayer1Injuries] = useState<readonly Injury[]>([]);\n const [player2Injuries, setPlayer2Injuries] = useState<readonly Injury[]>([]);\n\n // Clear injuries when component unmounts or when a new match starts\n // 컴포넌트 언마운트 시 또는 새 매치 시작 시 부상 초기화\n useEffect(() => {\n return () => {\n // Clear injury state on unmount to avoid leaking injuries across screens\n setPlayer1Injuries([]);\n setPlayer2Injuries([]);\n };\n }, []);\n\n // Clear injuries when a new match starts (both players at full health)\n // 새 매치 시작 시 부상 초기화 (양 플레이어 모두 최대 체력)\n useEffect(() => {\n // Only clear if we have valid players with health data\n if (players.length >= 2) {\n const player1 = players[0];\n const player2 = players[1];\n\n // Check if both players are at max health (indicates new match start)\n if (\n player1?.health === player1?.maxHealth &&\n player2?.health === player2?.maxHealth\n ) {\n setPlayer1Injuries([]);\n setPlayer2Injuries([]);\n }\n }\n }, [players]);\n\n // CRITICAL FIX: Memoize onPositionChange to prevent usePlayerMovement callback recreation\n // Without this, a new function is created every render, causing animation frame cancellation\n const handlePlayer1PositionChange = useCallback(\n (newPosition: Position) => {\n setPlayer1Position(newPosition);\n onPlayerUpdate(0, { position: newPosition });\n },\n [onPlayerUpdate],\n );\n\n // CRITICAL FIX: Memoize bounds object to prevent usePlayerMovement callback recreation\n // Without this, a new object reference is created every render, causing animation frame cancellation\n const movementBounds = useMemo(\n () => ({\n worldWidthMeters: arenaBounds.worldWidthMeters,\n worldDepthMeters: arenaBounds.worldDepthMeters,\n }),\n [arenaBounds.worldWidthMeters, arenaBounds.worldDepthMeters],\n );\n\n // ═══════════════════════════════════════════════════════════════════════════\n // Acceleration-Based Running System for Player 1\n // ═══════════════════════════════════════════════════════════════════════════\n\n // Track continuous movement time for acceleration-based running\n const player1MovementTimeRef = useRef(0);\n const player1LastDirectionRef = useRef<{ x: number; y: number }>({\n x: 0,\n y: 0,\n });\n\n // Track acceleration-based speed (interpolated between walk and run speeds)\n // This applies archetype speeds and stance modifiers\n const [player1AccelerationBasedSpeed, setPlayer1AccelerationBasedSpeed] =\n useState(player1WalkRunSpeeds.walkSpeed);\n\n // Determine if currently running using utility function with archetype run speed\n const player1IsRunning = isRunningSpeed(\n player1AccelerationBasedSpeed,\n player1WalkRunSpeeds.runSpeed,\n );\n\n // Player movement with physics-based acceleration and stance modifiers\n // All positions in METERS - no pixel conversions\n const { isMoving: player1IsMoving, velocity: player1Velocity } =\n usePlayerMovement({\n enabled:\n !isPaused &&\n combatState.roundStarted &&\n !combatState.roundEnded &&\n matchCountdownComplete &&\n !showRoundStart,\n bounds: movementBounds, // Use memoized bounds object\n onPositionChange: handlePlayer1PositionChange, // Use memoized callback\n initialPositionMeters: player1Position,\n // Physics parameters for realistic movement (always enabled)\n currentStance: player1Data.currentStance,\n legInjuryFactor: player1Data.legInjuryFactor,\n isRunning: player1IsRunning, // Use computed acceleration-based running state\n useTacticalSteps: false,\n // Use interpolated speed between modifier-aware walk/run speeds\n // This preserves archetype speeds and stance modifiers\n maxSpeedOverride: player1AccelerationBasedSpeed,\n accelerationOverride: player1SpeedModifiers.finalAcceleration,\n });\n\n // Player 1 rotation: face movement direction when moving, opponent when idle\n // 이동 중에는 이동 방향을, 정지 시에는 상대를 향함\n const player1Rotation = useMemo(() => {\n if (\n player1IsMoving &&\n player1Velocity &&\n (player1Velocity.x !== 0 || player1Velocity.y !== 0)\n ) {\n // When moving: face movement direction\n // velocity.x is lateral (left/right), velocity.y is forward/backward (Z in 3D)\n // Use velocity.y directly (not negated) so down arrow faces correctly\n const movementRotation = Math.atan2(player1Velocity.x, player1Velocity.y);\n return movementRotation;\n } else {\n // When idle: face opponent\n const dx = player2Position3D[0] - player1Position3D[0];\n const dz = player2Position3D[2] - player1Position3D[2];\n const targetRotation = Math.atan2(dx, dz);\n return targetRotation;\n }\n }, [player1IsMoving, player1Velocity, player1Position3D, player2Position3D]);\n\n // Use ref to store attack handler to avoid circular dependencies\n const handleAttackRef = useRef<(() => void) | null>(null);\n\n // Ref for player1Animation to avoid circular dependencies in animation events\n const player1AnimationRef = useRef<ReturnType<\n typeof usePlayerAnimation\n > | null>(null);\n\n // Ref for validPlayers to avoid circular dependencies\n const validPlayersRefForAnimation = useRef<[PlayerState, PlayerState] | null>(\n null,\n );\n\n // Tracks the frame at which the attack hit should fire (set per-technique)\n // and whether it has already been fired for the current attack\n const player1HitTriggerFrameRef = useRef<number>(6);\n const player1AttackHitFiredRef = useRef<boolean>(false);\n\n // Track attack animation durations for physics synchronization\n // 물리 동기화를 위한 공격 애니메이션 지속시간 추적\n // Use state so changes trigger re-render and propagate to useCombatAttackMovement\n // (previously these were refs but that meant the hook received stale values until\n // an unrelated re-render happened, and reading ref.current during render violated\n // react-hooks/refs purity rules)\n const [player1AttackDuration, setPlayer1AttackDuration] =\n useState<number>(0.55);\n const [player2AttackDuration, setPlayer2AttackDuration] =\n useState<number>(0.55);\n\n // Refs to clear attack animations after completion\n const clearPlayer1AttackAnimation = useRef<() => void>(() => {\n setPlayer1AttackAnimation(undefined);\n setPlayer1TechniqueId(undefined);\n });\n const clearPlayer2AttackAnimation = useRef<() => void>(() => {\n setPlayer2AttackAnimation(undefined);\n setPlayer2TechniqueId(undefined);\n });\n\n // Player animation state machines - manages animation transitions at 60fps\n // Memoize events object to maintain stability (required by usePlayerAnimation)\n const player1AnimationEvents = useMemo<AnimationEvents>(\n () => ({\n onFrame: (frame, state) => {\n // Execute attack when strike reaches peak extension\n // The hit trigger frame is set dynamically when attack starts\n // 공격 히트 트리거 프레임은 공격 시작 시 동적으로 설정됨\n if (\n state === AnimationState.ATTACK &&\n frame >= player1HitTriggerFrameRef.current &&\n !player1AttackHitFiredRef.current\n ) {\n player1AttackHitFiredRef.current = true;\n handleAttackRef.current?.();\n }\n },\n onAnimationComplete: (state) => {\n // Handle animation completion\n if (\n state === AnimationState.ATTACK ||\n state === AnimationState.DEFEND\n ) {\n combatActions.setExecutingTechnique(false);\n // Clear attack animation when attack completes\n // 공격 완료 시 공격 애니메이션 초기화\n if (state === AnimationState.ATTACK) {\n clearPlayer1AttackAnimation.current();\n }\n } else if (state === AnimationState.STANCE_CHANGE) {\n // Stance change animation completed - transition to stance guard\n // 자세 변경 완료 - 자세 가드로 전환\n audio.playSFX(\"menu_select\");\n const players = validPlayersRefForAnimation.current;\n const currentStance = players?.[0]?.currentStance;\n if (currentStance && player1AnimationRef.current) {\n player1AnimationRef.current.transitionToStanceGuard(currentStance);\n }\n }\n },\n }),\n [combatActions, audio],\n );\n\n const player1Animation = usePlayerAnimation({\n events: player1AnimationEvents,\n });\n\n // Store animation ref for use in event callbacks\n useEffect(() => {\n player1AnimationRef.current = player1Animation;\n }, [player1Animation]);\n\n const player2Animation = usePlayerAnimation({\n events: {\n onFrame: (frame, state) => {\n if (state === AnimationState.ATTACK && frame === 6) {\n // AI attack hit detection\n }\n },\n onAnimationComplete: (state) => {\n // Clear AI attack animation when attack completes\n // AI 공격 완료 시 공격 애니메이션 초기화\n if (state === AnimationState.ATTACK) {\n clearPlayer2AttackAnimation.current();\n }\n },\n },\n });\n\n // Sync movement with player 1 animation (avoid circular dependency)\n // 플레이어 1 이동-애니메이션 동기화\n const prevPlayer1IsMovingRef = useRef<boolean>(player1IsMoving);\n const prevPlayer1IsRunningRef = useRef<boolean>(player1IsRunning);\n useEffect(() => {\n // Check for movement or running state changes\n const movementChanged = prevPlayer1IsMovingRef.current !== player1IsMoving;\n const runningChanged = prevPlayer1IsRunningRef.current !== player1IsRunning;\n\n if (movementChanged || runningChanged) {\n if (player1IsMoving) {\n // Transition to RUN or WALK based on speed\n const targetState = player1IsRunning\n ? AnimationState.RUN\n : AnimationState.WALK;\n if (player1Animation.currentState !== targetState) {\n player1Animation.transitionTo(targetState);\n }\n } else if (\n player1Animation.currentState === AnimationState.WALK ||\n player1Animation.currentState === AnimationState.RUN\n ) {\n // When stopping movement, transition to stance-specific guard animation\n // 이동 중지 시 자세별 가드 애니메이션으로 전환\n player1Animation.transitionToStanceGuard(player1Data.currentStance);\n }\n prevPlayer1IsMovingRef.current = player1IsMoving;\n prevPlayer1IsRunningRef.current = player1IsRunning;\n }\n }, [\n player1IsMoving,\n player1IsRunning,\n player1Animation,\n player1Data.currentStance,\n ]);\n\n // Movement detection threshold for AI animation sync (in pixels)\n // Lower values = more sensitive to small movements, higher values = smoother transitions\n const MOVEMENT_DETECTION_THRESHOLD = 0.5;\n\n // Get player 2 stance for animation transitions\n // 플레이어 2 자세 (애니메이션 전환용)\n const player2Stance = useMemo(() => {\n return players[1]?.currentStance ?? TrigramStance.GEON;\n }, [players]);\n\n // Sync movement with player 2 animation (AI movement detection)\n const prevPlayer2PositionRef = useRef(player2Position);\n useEffect(() => {\n const currentPos = playerPositions[1];\n const prevPos = prevPlayer2PositionRef.current;\n\n // Detect if Player2 (AI) is moving by comparing positions\n const isMoving =\n Math.abs(currentPos.x - prevPos.x) > MOVEMENT_DETECTION_THRESHOLD ||\n Math.abs(currentPos.y - prevPos.y) > MOVEMENT_DETECTION_THRESHOLD;\n\n if (isMoving) {\n // AI is moving - transition to walk animation\n if (\n player2Animation.currentState !== AnimationState.WALK &&\n player2Animation.currentState !== AnimationState.ATTACK\n ) {\n player2Animation.transitionTo(AnimationState.WALK);\n }\n } else {\n // AI stopped - transition to stance-specific guard animation\n // AI 이동 중지 - 자세별 가드 애니메이션으로 전환\n if (player2Animation.currentState === AnimationState.WALK) {\n player2Animation.transitionToStanceGuard(player2Stance);\n }\n }\n\n prevPlayer2PositionRef.current = currentPos;\n }, [playerPositions, player2Animation, player2Stance]);\n\n // Valid players with complete state\n const validPlayers = useMemo((): [PlayerState, PlayerState] => {\n if (players.length === 0) {\n const player1 = createPlayerFromArchetype(PlayerArchetype.MUSA, 0);\n const player2 = createPlayerFromArchetype(PlayerArchetype.AMSALJA, 1);\n\n return [\n { ...player1, position: playerPositions[0] },\n { ...player2, position: playerPositions[1] },\n ];\n }\n\n const player1 = players[0];\n const player2 =\n players[1] ?? createPlayerFromArchetype(PlayerArchetype.AMSALJA, 1);\n\n return [\n { ...player1, position: playerPositions[0] },\n { ...player2, position: playerPositions[1] },\n ];\n }, [players, playerPositions]);\n\n // Use ref to access latest player health without causing re-renders\n const validPlayersRef = useRef<[PlayerState, PlayerState]>(validPlayers);\n useEffect(() => {\n validPlayersRef.current = validPlayers;\n validPlayersRefForAnimation.current = validPlayers;\n }, [validPlayers]);\n\n // Helper function to get AnimationType from technique ID\n // Uses enum-based mapping for type safety and proper technique support\n // 기술 ID에서 AnimationType을 가져오는 헬퍼 함수\n // 타입 안전성과 적절한 기술 지원을 위해 열거형 기반 매핑 사용\n const getTechniqueAnimationType = useCallback(\n (techniqueId: string | undefined): AnimationType | undefined => {\n if (!techniqueId) return undefined;\n\n // Use enum-based lookup for all techniques\n // This properly handles stance-specific techniques like \"geon_heaven_strike\"\n // 모든 기술에 대해 열거형 기반 조회 사용\n // \"geon_heaven_strike\"와 같은 자세별 기술을 올바르게 처리\n return getAnimationTypeForTechnique(techniqueId);\n },\n [],\n );\n\n // Attack movement integration - track attack states for physics-based forward movement\n // 공격 이동 통합 - 물리 기반 전방 이동을 위한 공격 상태 추적\n // Note: Must be called after validPlayers, player animations, and 3D positions are defined\n const {\n player1Position: player1PositionWithAttackMovement,\n player2Position: player2PositionWithAttackMovement,\n } = useCombatAttackMovement({\n player1Attacking: player1Animation.currentState === AnimationState.ATTACK,\n player1AnimationType: getTechniqueAnimationType(player1TechniqueId),\n player1Stance: player1Data.currentStance,\n player1BasePosition: player1Position3D,\n player1AnimationDuration: player1AttackDuration,\n player2Attacking: player2Animation.currentState === AnimationState.ATTACK,\n player2AnimationType: getTechniqueAnimationType(player2TechniqueId),\n player2Stance: validPlayers[1].currentStance,\n player2BasePosition: player2Position3D,\n player2AnimationDuration: player2AttackDuration,\n });\n\n // ═══════════════════════════════════════════════════════════════════════════\n // Local stance state for Player 1 - Enables immediate technique bar updates\n // ═══════════════════════════════════════════════════════════════════════════\n // The player stance from props requires a round-trip through App.tsx, causing\n // the technique bar to update with a delay. This local state updates immediately\n // when stance changes, then syncs with the parent state.\n // (이 로컬 상태는 자세 변경 시 즉시 업데이트되어 기술바가 바로 반응함)\n const [player1LocalStance, setPlayer1LocalStance] = useState<TrigramStance>(\n validPlayers[0].currentStance,\n );\n\n // Sync local stance with prop when it changes (e.g., on round restart)\n useEffect(() => {\n setPlayer1LocalStance(validPlayers[0].currentStance);\n }, [validPlayers]);\n\n // Create player object with local stance for immediate technique bar updates\n // This ensures useTechniqueSelection gets the updated stance synchronously\n const player1WithLocalStance = useMemo(\n (): PlayerState => ({\n ...validPlayers[0],\n currentStance: player1LocalStance,\n }),\n [validPlayers, player1LocalStance],\n );\n\n // Use refs for stable access to startTransition and internalRound\n const startTransitionRef = useRef(startTransition);\n const internalRoundRef = useRef(internalRound);\n useEffect(() => {\n startTransitionRef.current = startTransition;\n internalRoundRef.current = internalRound;\n }, [startTransition, internalRound]);\n\n // Combat messages\n const addCombatMessage = useCallback(\n (korean: string, english: string) => {\n const message = `${korean} | ${english}`;\n combatActions.addCombatMessage(message);\n },\n [combatActions],\n );\n\n // Combat timer with warnings and time up handler\n const handleTimeUp = useCallback(() => {\n // End round when time runs out\n if (!combatState.roundEnded) {\n combatActions.setRoundEnded(true);\n addCombatMessage(\"시간 종료!\", \"Time's Up!\");\n\n // Use refs to get latest values without dependency issues\n const currentPlayers = validPlayersRef.current;\n const player1Health = currentPlayers[0].health;\n const player2Health = currentPlayers[1].health;\n\n if (player1Health > player2Health) {\n // Player 1 wins - update match score\n updateMatchScore(0);\n startTransitionRef.current(currentPlayers[0], internalRoundRef.current); // Player 1 wins round\n } else if (player2Health > player1Health) {\n // Player 2 wins - update match score\n updateMatchScore(1);\n startTransitionRef.current(currentPlayers[1], internalRoundRef.current); // Player 2 wins round\n } else {\n // Tie - no winner for this round, no score update\n startTransitionRef.current(null, internalRoundRef.current);\n }\n }\n }, [\n combatState.roundEnded,\n combatActions,\n addCombatMessage,\n updateMatchScore,\n ]);\n\n // Ref pattern to stabilize onTimeUp callback for timer\n const handleTimeUpRef = useRef(handleTimeUp);\n useEffect(() => {\n handleTimeUpRef.current = handleTimeUp;\n }, [handleTimeUp]);\n\n // Create a unique key for timer reset based on round number\n // This forces the timer to reset when a new round starts\n const timerResetKey = `round-${internalRound}`;\n\n const timerState = useCombatTimer({\n initialTime: Math.max(0, timeRemaining),\n isPaused:\n isPaused ||\n !combatState.roundStarted ||\n combatState.roundEnded ||\n !matchCountdownComplete ||\n showRoundStart,\n onTimeUp: useCallback(() => handleTimeUpRef.current(), []),\n warningThreshold: 10,\n urgentThreshold: 5,\n // Pass round number to force timer reset on new rounds\n resetKey: timerResetKey,\n });\n\n // Shared round start logic - forces round to start regardless of current state\n // This is called after state has been reset, so we trust the caller\n // Note: Using useCallback with complete dependencies to ensure closure has latest state\n const startRound = useCallback(() => {\n // Always start the round when this is called - the caller is responsible\n // for ensuring this is the right time to start\n if (import.meta.env.DEV) {\n console.log(\"[DEV] Starting round, setting roundStarted=true\");\n }\n combatActions.setRoundStarted(true);\n combatActions.setRoundEnded(false); // Ensure roundEnded is false\n addCombatMessage(\"라운드 시작!\", \"Round Start!\");\n\n const player = validPlayers[0];\n if (player?.archetype) {\n const playerArchetype = player.archetype.toLowerCase();\n combatAudio.playArchetypeMusic(playerArchetype, 2000);\n } else {\n combatAudio.playCombatMusic(2000);\n }\n }, [combatActions, addCombatMessage, validPlayers, combatAudio]);\n\n // Auto-start first round since countdown is disabled\n // Use a separate ref for the timer to avoid cleanup canceling the start\n const hasAutoStartedRef = useRef(false);\n\n useEffect(() => {\n if (matchCountdownComplete && !hasAutoStartedRef.current) {\n hasAutoStartedRef.current = true;\n // Directly set roundStarted=true without going through startRound callback\n // This avoids dependency issues with the callback reference\n combatActions.setRoundStarted(true);\n addCombatMessage(\"라운드 시작!\", \"Round Start!\");\n\n // Start music\n const player = validPlayers[0];\n if (player?.archetype) {\n const playerArchetype = player.archetype.toLowerCase();\n combatAudio.playArchetypeMusic(playerArchetype, 2000);\n } else {\n combatAudio.playCombatMusic(2000);\n }\n }\n }, [\n matchCountdownComplete,\n combatActions,\n addCombatMessage,\n validPlayers,\n combatAudio,\n ]);\n\n // AI systems\n const adaptiveDifficulty = useMemo(() => new AdaptiveDifficulty(), []);\n\n // Persist AI difficulty metrics\n useEffect(() => {\n try {\n const savedMetrics = localStorage.getItem(\"ai_difficulty_metrics\");\n if (savedMetrics) {\n adaptiveDifficulty.importMetrics(savedMetrics);\n }\n } catch (err) {\n console.warn(\"Failed to load AI difficulty metrics:\", err);\n }\n\n return () => {\n try {\n const metrics = adaptiveDifficulty.exportMetrics();\n localStorage.setItem(\"ai_difficulty_metrics\", metrics);\n } catch (err) {\n console.warn(\"Failed to save AI difficulty metrics:\", err);\n }\n };\n }, [adaptiveDifficulty]);\n\n const aiPersonality = useMemo(\n () => getPersonalityByArchetype(validPlayers[1].archetype),\n [validPlayers],\n );\n\n // AI stance change handler\n const handleAIStanceChange = useCallback(\n (stance: TrigramStance) => {\n const currentStance = validPlayers[1].currentStance;\n\n // Start stance-specific transition animation for AI\n player2Animation.transitionToStanceChange(currentStance, stance);\n\n onPlayerUpdate(1, { currentStance: stance });\n addCombatMessage(\n `AI 자세 변경: ${stance}`,\n `AI Stance Change: ${stance}`,\n );\n },\n [validPlayers, player2Animation, onPlayerUpdate, addCombatMessage],\n );\n\n // Hit effect handlers\n const handleEffectComplete = useCallback(\n (effectId: string) => {\n combatActions.removeHitEffect(effectId);\n },\n [combatActions],\n );\n\n const createHitEffect = useCallback(\n (\n id: string,\n type: HitEffectType,\n position: Position,\n intensity: number,\n ): HitEffect => ({\n id,\n type,\n attackerId: \"player1\",\n defenderId: \"player2\",\n timestamp: Date.now(),\n duration: 1000,\n position,\n intensity,\n startTime: Date.now(),\n }),\n [],\n );\n\n const addHitEffect = useCallback(\n (type: HitEffectType, position: Position, intensity: number = 1) => {\n const effect = createHitEffect(\n `effect_${Date.now()}`,\n type,\n position,\n intensity,\n );\n combatActions.addHitEffect(effect);\n },\n [createHitEffect, combatActions],\n );\n\n // Callback for updating player positions after knockback\n const handlePlayerPositionUpdate = useCallback(\n (playerIndex: number, position: Position) => {\n if (playerIndex === 0) {\n setPlayer1Position(position);\n onPlayerUpdate(0, { position });\n } else if (playerIndex === 1) {\n // For AI, update position through onPlayerUpdate\n // This will be reflected in player2Position which is derived from players prop\n onPlayerUpdate(1, { position });\n }\n },\n [onPlayerUpdate, setPlayer1Position],\n );\n\n // Callback for creating injuries from combat damage with progressive bruising\n // 전투 피해로부터 부상 생성 콜백 (점진적 타박상 포함)\n const handleInjuryCreated = useCallback(\n (injury: Injury, targetPlayerIndex: number) => {\n const updateInjuries = (prev: readonly Injury[]): readonly Injury[] => {\n // Check for recent hits to the same body region (within 5 seconds)\n const recentHitTime = 5000; // 5 seconds\n const now = Date.now();\n\n const recentHit = prev.find(\n (existing) =>\n existing.region === injury.region &&\n now - existing.timestamp < recentHitTime &&\n existing.type === InjuryType.BRUISE &&\n injury.type === InjuryType.BRUISE,\n );\n\n if (recentHit) {\n // Progressive bruising: increase hit count and severity\n const newHitCount = recentHit.hitCount + 1;\n const escalatedSeverity = Math.min(1.0, recentHit.severity + 0.15);\n\n // Update the existing injury with increased severity and hit count\n // Only update existing injury, do not add a duplicate\n return prev.map((existing) =>\n existing.id === recentHit.id\n ? {\n ...existing,\n hitCount: newHitCount,\n severity: escalatedSeverity,\n timestamp: now, // Update timestamp for progressive tracking\n }\n : existing,\n );\n }\n\n // No recent hit to same region - add as new injury\n return [...prev, injury];\n };\n\n if (targetPlayerIndex === 0) {\n setPlayer1Injuries(updateInjuries);\n } else if (targetPlayerIndex === 1) {\n setPlayer2Injuries(updateInjuries);\n }\n },\n [],\n );\n\n // Combat action handlers\n const {\n handleAttack,\n handleDefend,\n handleStanceSwitch,\n handleStanceSideSwitch,\n handleAIAttack,\n handleAIDefend,\n handleAITechnique,\n moveAIPlayer,\n } = useCombatActions({\n validPlayers,\n playerPositions: [playerPositions[0], playerPositions[1]],\n combatState,\n combatActions,\n combatSystem,\n onPlayerUpdate,\n onPlayerPositionUpdate: handlePlayerPositionUpdate,\n onLateralityUpdate: (playerIndex, laterality) => {\n combatActions.setPlayerLateralityIndex(playerIndex as 0 | 1, laterality);\n },\n onInjuryCreated: handleInjuryCreated,\n addCombatMessage,\n addHitEffect,\n arenaBounds,\n combatAudio,\n playerAnimations: {\n player1: player1Animation,\n player2: player2Animation,\n },\n });\n\n // Store handleAttack in ref for animation callback (avoid circular dependency)\n useEffect(() => {\n handleAttackRef.current = handleAttack;\n }, [handleAttack]);\n\n // Technique selection and execution\n // Uses player1WithLocalStance for immediate technique bar updates when stance changes\n // (자세 변경 시 기술바가 즉시 업데이트되도록 로컬 자세 상태 사용)\n const techniqueSelection = useTechniqueSelection({\n player: player1WithLocalStance,\n enabled:\n !isPaused &&\n combatState.roundStarted &&\n !combatState.roundEnded &&\n matchCountdownComplete &&\n !showRoundStart,\n onTechniqueExecute: useCallback(\n (technique: Technique) => {\n // Show technique name in action feedback\n feedbackActions.showTechnique(\n technique.name.korean,\n technique.name.english,\n );\n\n // Set attack animation based on technique\n // 기술에 따른 공격 애니메이션 설정\n // Uses resolveTechniqueAnimation so that stance-specific animations\n // (e.g., \"geon_heaven_strike\") are preferred over the regex fallback\n // that previously collapsed every attack to the jab animation.\n const animationName = resolveTechniqueAnimation(technique);\n setPlayer1AttackAnimation(animationName);\n\n // Store technique ID for enum-based AnimationType lookup\n // 열거형 기반 AnimationType 조회를 위한 기술 ID 저장\n setPlayer1TechniqueId(technique.id);\n\n // Get the actual skeletal animation duration for this technique\n // so the state machine stays in ATTACK for the full animation.\n // 기술의 실제 스켈레탈 애니메이션 지속시간을 가져와서\n // 상태 머신이 전체 애니메이션 동안 ATTACK 상태를 유지하도록 함\n const skeletalAnim = getAnimation(animationName);\n const attackDuration = skeletalAnim?.duration ?? 0.55;\n\n // Trigger attack animation transition with technique-matched duration\n // 기술 매칭 지속시간으로 공격 애니메이션 전환 트리거\n const attackFrames = Math.max(1, Math.round(attackDuration * 60));\n // Trigger hit at ~40% of the attack (when strike reaches peak extension)\n player1HitTriggerFrameRef.current = Math.round(attackFrames * 0.4);\n player1AttackHitFiredRef.current = false;\n // Store duration for physics hook\n setPlayer1AttackDuration(attackDuration);\n player1Animation.transitionToAttack(attackDuration);\n combatActions.setExecutingTechnique(true);\n\n // Deduct resources\n onPlayerUpdate(0, {\n stamina: Math.max(0, validPlayers[0].stamina - technique.staminaCost),\n ki: Math.max(0, validPlayers[0].ki - technique.kiCost),\n });\n\n // Execute attack WITH the selected technique (not basic attack)\n handleAttack(technique);\n\n // Add combat message\n addCombatMessage(\n `${technique.name.korean} 사용!`,\n `Used ${technique.name.english}!`,\n );\n },\n [\n validPlayers,\n onPlayerUpdate,\n feedbackActions,\n handleAttack,\n addCombatMessage,\n player1Animation,\n combatActions,\n ],\n ),\n });\n\n // Convert cooldowns to Map for TechniqueBar\n const cooldownsMap = useMemo(() => {\n const map = new Map<string, number>();\n techniqueSelection.activeCooldowns.forEach((cd) => {\n map.set(cd.techniqueId, cd.remaining);\n });\n return map;\n }, [techniqueSelection.activeCooldowns]);\n\n // Track previous stance for visual feedback\n const [previousStance, setPreviousStance] = useState<number>(0);\n\n // Get current stance index for visual feedback using optimized lookup\n const currentPlayerStance = validPlayers[0].currentStance;\n const currentStanceIndex = useMemo(() => {\n return STANCE_INDEX_MAP.get(currentPlayerStance) ?? 0;\n }, [currentPlayerStance]);\n\n // Handler for stance changes with animation integration\n const handleStanceChangeWithAnimation = useCallback(\n (newStance: TrigramStance) => {\n const currentStance = validPlayers[0].currentStance;\n\n // Transition directly to stance guard (skip transitional animation)\n // The transitional STANCE_CHANGE animation uses idle_stance which loops forever\n // and never completes, so we go directly to the target stance guard\n const success = player1Animation.transitionToStanceGuard(newStance);\n\n if (success) {\n // Capture previous stance for visual feedback\n const prevStance = STANCE_INDEX_MAP.get(currentStance) ?? 0;\n setPreviousStance(prevStance);\n\n // Update local stance immediately for instant technique bar update\n // This ensures the technique bar updates before the prop round-trip completes\n // (기술바가 즉시 업데이트되도록 로컬 자세 상태를 먼저 변경)\n setPlayer1LocalStance(newStance);\n\n // Update combat state (triggers prop update through App.tsx)\n handleStanceSwitch(newStance);\n\n // Play stance change sound\n playStanceChangeSound();\n }\n },\n [validPlayers, player1Animation, handleStanceSwitch, playStanceChangeSound],\n );\n\n // Extract player health values for dependency arrays\n const player1Health = validPlayers[0].health;\n const player2Health = validPlayers[1].health;\n\n // Watch for player 2 health decrease to trigger damage feedback\n const lastPlayer2HealthRef = useRef(player2Health);\n useEffect(() => {\n const currentHealth = player2Health;\n const previousHealth = lastPlayer2HealthRef.current;\n const damageDone = previousHealth - currentHealth;\n\n if (damageDone > 0 && combatState.roundStarted && !combatState.roundEnded) {\n // Determine damage type based on amount\n const getDamageType = (): \"critical\" | \"vital\" | \"normal\" => {\n if (damageDone >= 25) return \"critical\";\n if (damageDone >= 20) return \"vital\";\n return \"normal\";\n };\n const damageType = getDamageType();\n\n // Add damage number at opponent position\n feedbackActions.addDamageNumber(\n Math.round(damageDone),\n playerPositions[1],\n damageType,\n );\n\n // Increment combo\n feedbackActions.incrementCombo();\n\n // Add action feedback for critical hits\n if (damageType === \"critical\") {\n feedbackActions.addActionFeedback(\n \"critical\",\n \"Critical!\",\n \"치명타!\",\n playerPositions[0],\n );\n }\n }\n\n lastPlayer2HealthRef.current = currentHealth;\n }, [\n player2Health,\n validPlayers,\n playerPositions,\n feedbackActions,\n combatState.roundStarted,\n combatState.roundEnded,\n ]);\n\n // Watch for player 1 health decrease (AI attacks player)\n const lastPlayer1HealthRef = useRef(player1Health);\n useEffect(() => {\n const currentHealth = player1Health;\n const previousHealth = lastPlayer1HealthRef.current;\n const damageDone = previousHealth - currentHealth;\n\n if (damageDone > 0 && combatState.roundStarted && !combatState.roundEnded) {\n // Add damage number at player position for AI hits\n const damageType =\n damageDone >= 20 ? (\"critical\" as const) : (\"normal\" as const);\n feedbackActions.addDamageNumber(\n Math.round(damageDone),\n playerPositions[0],\n damageType,\n );\n }\n\n lastPlayer1HealthRef.current = currentHealth;\n }, [\n player1Health,\n validPlayers,\n playerPositions,\n feedbackActions,\n combatState.roundStarted,\n combatState.roundEnded,\n ]);\n\n // Create enhanced attack handler with action feedback and animation\n const handleAttackWithFeedback = useCallback(() => {\n // For the \"basic\" attack (space bar / on-screen Attack button) we use\n // the first technique available in the player's current stance so the\n // skeletal animation matches the stance (e.g., Geon → \"geon_heaven_strike\",\n // Li → \"li_precision_jab\"). This prevents every stance from producing\n // the same jab visual.\n // 기본 공격 애니메이션 설정 - 현재 자세의 첫 번째 기술 사용\n const basicTechnique = techniqueSelection.availableTechniques[0];\n const animationName = basicTechnique\n ? resolveTechniqueAnimation(basicTechnique)\n : \"jab\";\n setPlayer1AttackAnimation(animationName);\n if (basicTechnique?.id) {\n setPlayer1TechniqueId(basicTechnique.id);\n }\n\n // Try to transition to attack animation\n const success = player1Animation.transitionTo(AnimationState.ATTACK);\n if (success) {\n // Attack execution will happen at frame 6 in onFrame callback\n combatActions.setExecutingTechnique(true);\n } else {\n // Fallback: animation transition failed, execute attack logic immediately\n console.warn(\n \"Attack animation transition failed; executing attack logic directly.\",\n );\n handleAttack();\n }\n }, [\n player1Animation,\n combatActions,\n handleAttack,\n techniqueSelection.availableTechniques,\n ]);\n\n // Create enhanced defend handler with action feedback and animation\n const handleDefendWithFeedback = useCallback(() => {\n const defenderPos = playerPositions[0];\n // Try to transition to defend animation\n const success = player1Animation.transitionTo(AnimationState.DEFEND);\n if (success) {\n handleDefend();\n feedbackActions.addActionFeedback(\n \"blocked\",\n \"Blocked\",\n \"방어!\",\n defenderPos,\n );\n } else {\n // Fallback: animation transition failed, execute defend logic immediately\n console.warn(\n \"Defend animation transition failed; executing defend logic directly.\",\n );\n handleDefend();\n feedbackActions.addActionFeedback(\n \"blocked\",\n \"Blocked\",\n \"방어!\",\n defenderPos,\n );\n }\n }, [handleDefend, playerPositions, feedbackActions, player1Animation]);\n\n /**\n * Helper function to execute fallback recovery animation\n * when a specific recovery type cannot be performed.\n *\n * Determines the appropriate recovery type based on ground state\n * and transitions to that animation.\n *\n * @korean 대체회복실행\n */\n const executeFallbackRecovery = useCallback(() => {\n const groundState = balanceSystem.getGroundState(\n player1Animation.currentState,\n );\n if (groundState) {\n const recoveryType = determineRecoveryType(groundState);\n const animationState = getRecoveryAnimationState(recoveryType);\n player1Animation.transitionTo(animationState as AnimationState);\n }\n }, [balanceSystem, player1Animation]);\n\n // Use keyboard controls hook for enhanced input handling with visual feedback\n const { queuedInputs, showHints } = useKeyboardControls({\n onStanceChange: useCallback(\n (stanceIndex: number) => {\n const stance = TRIGRAM_STANCES_ORDER[stanceIndex];\n if (stance) {\n // Use integrated stance transition animation\n handleStanceChangeWithAnimation(stance);\n }\n },\n [handleStanceChangeWithAnimation],\n ),\n onAction: useCallback(\n (action: string) => {\n switch (action) {\n case \"attack\":\n // Execute currently selected technique via technique selection system\n techniqueSelection.executeTechnique();\n break;\n case \"block\":\n handleDefendWithFeedback();\n break;\n // Recovery actions (기상 액션)\n case \"recovery_quick\": {\n // Quick recovery: determine type based on ground state\n executeFallbackRecovery();\n break;\n }\n case \"recovery_roll\": {\n // Roll recovery: costs stamina but fastest\n const player1 = players[0];\n if (balanceSystem.canRecoverWithType(player1, \"roll_recovery\")) {\n const updatedPlayer = balanceSystem.applyRecoveryCost(\n player1,\n \"roll_recovery\",\n );\n onPlayerUpdate(0, { stamina: updatedPlayer.stamina });\n player1Animation.transitionTo(AnimationState.RECOVERY_ROLL);\n } else {\n // Not enough stamina, fallback to quick recovery\n audio.playSFX(\"menu_error\");\n const player1Pos = playerPositions[0];\n feedbackActions.addActionFeedback(\n \"blocked\",\n \"Not enough stamina!\",\n \"체력 부족!\",\n player1Pos,\n );\n executeFallbackRecovery();\n }\n break;\n }\n case \"recovery_defensive\": {\n // Defensive getup: slow but protected\n player1Animation.transitionTo(AnimationState.RECOVERY_DEFENSIVE);\n break;\n }\n\n // Footwork pattern actions\n case \"footwork_circular_left\":\n case \"footwork_circular_right\":\n case \"footwork_slide_forward\":\n case \"footwork_slide_back\":\n case \"footwork_pivot_left\":\n case \"footwork_pivot_right\":\n case \"footwork_shuffle\":\n // Execute footwork animation\n player1Animation.transitionTo(action as AnimationState);\n break;\n\n // Stance side switch\n case \"stance_side_switch\":\n // Switch front foot (mirror stance)\n player1Animation.transitionTo(AnimationState.STANCE_SIDE_SWITCH);\n break;\n\n // Movement and other actions handled by existing system\n }\n },\n [\n techniqueSelection,\n handleDefendWithFeedback,\n executeFallbackRecovery,\n balanceSystem,\n player1Animation,\n players,\n onPlayerUpdate,\n audio,\n feedbackActions,\n playerPositions,\n ],\n ),\n enabled:\n !isPaused &&\n !showPauseMenu &&\n combatState.roundStarted &&\n !combatState.roundEnded &&\n matchCountdownComplete &&\n !showRoundStart &&\n !combatState.isExecutingTechnique,\n currentStance: currentStanceIndex,\n playSFX: audio.playSFX,\n currentAnimationState: player1Animation.currentState,\n });\n\n // Mobile touch control state - stance wheel for mobile stance changes\n const [stanceWheelExpanded, setStanceWheelExpanded] = useState(false);\n const activeMobileKeyRef = useRef<string | null>(null);\n\n /**\n * Mobile touch control handler - Converts VirtualDPad touch inputs to keyboard events\n *\n * Dispatches synthetic KeyboardEvents with proper properties to ensure compatibility\n * with usePlayerMovement hook. The synthetic events include:\n * - key: The character key (w/a/s/d)\n * - code: The physical key code (KeyW/KeyA/KeyS/KeyD)\n * - bubbles: true - Allows event to propagate through DOM\n * - cancelable: true - Allows event to be prevented\n *\n * These properties are essential for the keyboard event listeners in inputSystem.ts\n * to properly recognize and process the movement commands.\n *\n * @param direction - The D-pad direction or null\n * @param eventType - 'start' for press, 'end' for release\n */\n const handleMobileMove = useCallback(\n (direction: Direction | null, eventType: DPadEventType) => {\n // Map D-pad directions to movement keys (WASD)\n const directionMap: Record<Direction, string> = {\n up: \"w\",\n \"up-right\": \"w\", // Diagonal simplified to primary direction\n right: \"d\",\n \"down-right\": \"s\",\n down: \"s\",\n \"down-left\": \"s\",\n left: \"a\",\n \"up-left\": \"w\",\n };\n\n if (eventType === \"start\" && direction) {\n // Release previous key if different (prevents stuck keys)\n if (\n activeMobileKeyRef.current &&\n activeMobileKeyRef.current !== directionMap[direction]\n ) {\n const prevKey = activeMobileKeyRef.current;\n window.dispatchEvent(\n new KeyboardEvent(\"keyup\", {\n key: prevKey,\n code: `Key${prevKey.toUpperCase()}`,\n bubbles: true,\n cancelable: true,\n }),\n );\n }\n\n // Press new key with proper keyboard event properties\n // These properties ensure usePlayerMovement recognizes the synthetic event\n const key = directionMap[direction];\n activeMobileKeyRef.current = key;\n window.dispatchEvent(\n new KeyboardEvent(\"keydown\", {\n key,\n code: `Key${key.toUpperCase()}`,\n bubbles: true,\n cancelable: true,\n }),\n );\n } else if (eventType === \"end\") {\n // Release active key when D-pad released\n if (activeMobileKeyRef.current) {\n const key = activeMobileKeyRef.current;\n window.dispatchEvent(\n new KeyboardEvent(\"keyup\", {\n key,\n code: `Key${key.toUpperCase()}`,\n bubbles: true,\n cancelable: true,\n }),\n );\n activeMobileKeyRef.current = null;\n }\n }\n },\n [],\n );\n\n const handleMobileAttack = useCallback(() => {\n // Execute currently selected technique via technique selection system\n techniqueSelection.executeTechnique();\n }, [techniqueSelection]);\n\n const handleMobileBlock = useCallback(\n (eventType: ButtonEventType) => {\n if (eventType === \"start\") {\n handleDefendWithFeedback();\n }\n },\n [handleDefendWithFeedback],\n );\n\n // Mobile stance wheel and gesture controls\n const handleMobileStanceChange = useCallback(\n (stanceIndex: number) => {\n const stance = TRIGRAM_STANCES_ORDER[stanceIndex];\n if (stance) {\n // Use integrated stance transition animation\n handleStanceChangeWithAnimation(stance);\n }\n },\n [handleStanceChangeWithAnimation],\n );\n\n const handleMobileGesture = useCallback(\n (gesture: GestureEvent) => {\n switch (gesture.type) {\n case \"swipe-right\":\n // Advance toward opponent - dispatch keydown then keyup to prevent stuck movement\n window.dispatchEvent(new KeyboardEvent(\"keydown\", { key: \"d\" }));\n setTimeout(() => {\n window.dispatchEvent(new KeyboardEvent(\"keyup\", { key: \"d\" }));\n }, 100);\n break;\n case \"swipe-left\":\n // Retreat from opponent - dispatch keydown then keyup to prevent stuck movement\n window.dispatchEvent(new KeyboardEvent(\"keydown\", { key: \"a\" }));\n setTimeout(() => {\n window.dispatchEvent(new KeyboardEvent(\"keyup\", { key: \"a\" }));\n }, 100);\n break;\n case \"swipe-up\":\n // High attack mode - execute selected technique\n techniqueSelection.executeTechnique();\n break;\n case \"swipe-down\":\n // Low attack mode - execute selected technique\n techniqueSelection.executeTechnique();\n break;\n case \"two-finger-tap\":\n // Activate vital point targeting mode\n // TODO: Implement vital point mode toggle\n audio.playSFX(\"menu_select\");\n break;\n }\n },\n [techniqueSelection, audio],\n );\n\n const toggleStanceWheel = useCallback(() => {\n setStanceWheelExpanded((prev) => !prev);\n }, []);\n\n // Check if mobile controls should be enabled\n const mobileControlsEnabled =\n isMobile &&\n !isPaused &&\n !showPauseMenu &&\n combatState.roundStarted &&\n !combatState.roundEnded &&\n matchCountdownComplete &&\n !showRoundStart &&\n !combatState.isExecutingTechnique;\n\n // Note: Player 1 position is updated via the onPositionChange callback\n // in usePlayerMovement config above, not via useEffect\n\n // Round management\n useEffect(() => {\n if (isPaused) return;\n\n if (timeRemaining <= 0 && !combatState.roundEnded) {\n combatActions.setRoundEnded(true);\n combatActions.setRoundStarted(false);\n combatActions.setRoundDisplayStatus(\"end\");\n\n combatAudio.stopCombatMusic(1000);\n\n const winner = validPlayers[0].health > validPlayers[1].health ? 0 : 1;\n const roundWinner = validPlayers[winner];\n\n // Update match score using deferred callback\n updateMatchScore(winner);\n\n addCombatMessage(\"라운드 종료!\", \"Round Over!\");\n\n // Start round transition instead of immediately ending game\n setTimeout(() => {\n startTransition(roundWinner, internalRound);\n }, 1500);\n }\n // Note: Round start is now triggered by MatchCountdown and RoundStartAnnouncement components\n // via their onComplete callbacks to ensure proper sequencing with countdown animations\n }, [\n timeRemaining,\n combatState.roundEnded,\n combatState.roundStarted,\n validPlayers,\n onGameEnd,\n addCombatMessage,\n internalRound,\n isPaused,\n combatActions,\n combatAudio,\n startTransition,\n updateMatchScore,\n ]);\n\n // Create a ref for the callback to avoid circular dependency\n const executeAIActionCallbackRef = useRef<\n | ((\n action: string,\n targetPos?: Position,\n selectedTechnique?: KoreanTechnique,\n targetVitalPoint?: string,\n ) => void)\n | undefined\n >(undefined);\n\n // AI Combat System - connects AI decisions to executeAIActionCallbackRef via onExecuteAction (action/technique/vital point params)\n const { updateDifficultyTarget } = useAICombat({\n player: validPlayers[1],\n opponent: validPlayers[0],\n personality: aiPersonality,\n adaptiveDifficulty,\n isPaused,\n roundStarted: combatState.roundStarted,\n roundEnded: combatState.roundEnded,\n arenaBounds,\n onExecuteAction: (action, targetPos, selectedTechnique, targetVitalPoint) =>\n executeAIActionCallbackRef.current?.(\n action,\n targetPos,\n selectedTechnique,\n targetVitalPoint,\n ),\n onStanceChange: handleAIStanceChange,\n onLateralityChange: () => handleStanceSideSwitch(1), // AI player (index 1)\n playerLaterality: combatState.playerLaterality[1], // AI's own laterality\n opponentLaterality: combatState.playerLaterality[0], // Opponent (human) laterality\n });\n\n // Calculate current difficulty tier for display\n // Force recalculation when the adaptive difficulty system changes\n const currentDifficultyTier = useMemo(\n () => adaptiveDifficulty.getDifficultyTier(),\n [adaptiveDifficulty],\n );\n\n // Adaptive difficulty adjustment every 2-3 rounds after round ends\n useEffect(() => {\n // Only adjust after round ends (not during round or at start)\n if (!combatState.roundEnded || internalRound < 1) {\n return;\n }\n\n // Adjust every 2 rounds (after rounds 2, 4, 6, etc.)\n // This ensures at least 2 rounds between adjustments for smooth progression\n const roundsCompleted = internalRound;\n if (roundsCompleted % 2 === 0) {\n // Update AI skill metrics based on player performance.\n // NOTE (Phase 1 adaptive difficulty):\n // - Only hits/attacks/damage metrics are currently sourced from PlayerState.\n // - Combo, block, reaction-time, vital-point, and stance-change metrics are\n // intentionally stubbed here (0 or constant) until PlayerState and telemetry\n // are extended in a follow-up phase.\n // This keeps the system functional while we iterate on richer skill tracking.\n const player1 = validPlayersRef.current[0];\n\n adaptiveDifficulty.updateSkillMetrics({\n hitsLanded: player1.hitsLanded ?? 0,\n totalAttacks: (player1.hitsLanded ?? 0) + (player1.misses ?? 0),\n combosExecuted: 0, // TODO (Phase 2): Track combo count in PlayerState\n perfectBlockCount: 0, // TODO (Phase 2): Track perfect blocks in PlayerState\n avgReactionTimeMs: 600, // TODO (Phase 2): Track player reaction time\n vitalPointsHit: 0, // TODO (Phase 2): Track vital point hits\n effectiveStanceChanges: 0, // TODO (Phase 2): Track stance changes\n damageDealt: player1.totalDamageDealt ?? 0,\n damageTaken: player1.totalDamageReceived ?? 0,\n });\n\n // Get new difficulty parameters and update AI\n const newParams = adaptiveDifficulty.getDifficultyParameters();\n updateDifficultyTarget(newParams);\n\n if (import.meta.env.DEV) {\n const tier = adaptiveDifficulty.getDifficultyTier();\n console.log(\n `[DEV] Difficulty adjusted after round ${roundsCompleted}, new tier: ${tier}`,\n );\n }\n }\n }, [\n combatState.roundEnded,\n internalRound,\n adaptiveDifficulty,\n updateDifficultyTarget,\n ]);\n\n // AI action execution - receives technique and vital point directly\n const executeAIActionCallback = useCallback(\n (\n action: string,\n targetPos?: Position,\n selectedTechnique?: KoreanTechnique,\n targetVitalPoint?: string,\n ) => {\n // Resolve AI's fallback animation from its current stance so that\n // a stance-less AI decision still plays a stance-appropriate visual\n // instead of the legacy \"jab\"/\"cross\" literals that made every AI\n // action look identical. Defensive chaining covers the (expected-\n // unreachable) cases where `validPlayers[1]` is missing or where a\n // stance has no techniques registered yet — in both cases we fall\n // through to the literal \"jab\" as the absolute last resort.\n // AI의 현재 자세에서 기본 애니메이션을 도출\n const aiStance = validPlayers[1]?.currentStance ?? TrigramStance.GEON;\n const aiFallbackTechnique = TRIGRAM_TECHNIQUES[aiStance]?.[0];\n const aiFallbackAnim = aiFallbackTechnique\n ? resolveTechniqueAnimation(aiFallbackTechnique)\n : \"jab\";\n switch (action) {\n case \"attack\":\n // Set AI attack animation based on technique\n // AI 공격 애니메이션 설정\n if (selectedTechnique) {\n const p2AttackAnimName = resolveTechniqueAnimation(selectedTechnique);\n setPlayer2AttackAnimation(p2AttackAnimName);\n\n // Store technique ID for enum-based AnimationType lookup\n // 열거형 기반 AnimationType 조회를 위한 기술 ID 저장\n if (selectedTechnique.id) {\n setPlayer2TechniqueId(selectedTechnique.id);\n }\n const p2AttackAnim = getAnimation(p2AttackAnimName);\n const p2AttackDur = p2AttackAnim?.duration ?? 0.55;\n setPlayer2AttackDuration(p2AttackDur);\n player2Animation.transitionToAttack(p2AttackDur);\n } else {\n setPlayer2AttackAnimation(aiFallbackAnim);\n if (aiFallbackTechnique?.id) {\n setPlayer2TechniqueId(aiFallbackTechnique.id);\n }\n const p2FallbackAnim = getAnimation(aiFallbackAnim);\n const p2FallbackDur = p2FallbackAnim?.duration ?? 0.55;\n setPlayer2AttackDuration(p2FallbackDur);\n player2Animation.transitionToAttack(p2FallbackDur);\n }\n handleAIAttack(selectedTechnique, targetVitalPoint);\n break;\n case \"defend\":\n player2Animation.transitionTo(AnimationState.DEFEND);\n handleAIDefend();\n break;\n case \"technique\":\n case \"combo\":\n // Set AI attack animation based on technique\n // AI 기술 애니메이션 설정\n if (selectedTechnique) {\n const p2TechAnimName = resolveTechniqueAnimation(selectedTechnique);\n setPlayer2AttackAnimation(p2TechAnimName);\n\n // Store technique ID for enum-based AnimationType lookup\n // 열거형 기반 AnimationType 조회를 위한 기술 ID 저장\n if (selectedTechnique.id) {\n setPlayer2TechniqueId(selectedTechnique.id);\n }\n const p2TechAnim = getAnimation(p2TechAnimName);\n const p2TechDur = p2TechAnim?.duration ?? 0.6;\n setPlayer2AttackDuration(p2TechDur);\n player2Animation.transitionToAttack(p2TechDur);\n } else {\n setPlayer2AttackAnimation(aiFallbackAnim);\n if (aiFallbackTechnique?.id) {\n setPlayer2TechniqueId(aiFallbackTechnique.id);\n }\n const p2FallbackAnim = getAnimation(aiFallbackAnim);\n const p2FallbackDur = p2FallbackAnim?.duration ?? 0.6;\n setPlayer2AttackDuration(p2FallbackDur);\n player2Animation.transitionToAttack(p2FallbackDur);\n }\n handleAITechnique(selectedTechnique, targetVitalPoint);\n break;\n case \"approach\":\n case \"retreat\":\n case \"circle\":\n if (targetPos) {\n moveAIPlayer(targetPos);\n }\n break;\n case \"feint\":\n {\n const playerPos = validPlayers[0].position;\n // Feint offset in METERS (about half a meter of random movement)\n const feintOffsetMeters = 0.5;\n const feintPos = {\n x: playerPos.x + (Math.random() - 0.5) * feintOffsetMeters,\n y: playerPos.y + (Math.random() - 0.5) * feintOffsetMeters,\n };\n moveAIPlayer(feintPos);\n addCombatMessage(\"AI 페인트\", \"AI Feint\");\n\n setTimeout(() => {\n if (\n !combatState.roundEnded &&\n combatState.roundStarted &&\n validPlayers.length >= 2\n ) {\n const currentPlayerPos = validPlayers[0].position;\n const currentAiPos = validPlayers[1].position;\n const dx = currentAiPos.x - currentPlayerPos.x;\n const dy = currentAiPos.y - currentPlayerPos.y;\n const dist = Math.sqrt(dx * dx + dy * dy) || 1;\n // Retreat distance in METERS (about 0.8 meters back)\n const retreatDistanceMeters = 0.8;\n // Use world meter bounds instead of pixel bounds\n const halfWidth = arenaBounds.worldWidthMeters / 2;\n const halfDepth = arenaBounds.worldDepthMeters / 2;\n const retreatPos = {\n x: Math.max(\n -halfWidth + 0.5, // 0.5m from edge\n Math.min(\n halfWidth - 0.5,\n currentPlayerPos.x + (dx / dist) * retreatDistanceMeters,\n ),\n ),\n y: Math.max(\n -halfDepth + 0.5, // 0.5m from edge\n Math.min(\n halfDepth - 0.5,\n currentPlayerPos.y + (dy / dist) * retreatDistanceMeters,\n ),\n ),\n };\n moveAIPlayer(retreatPos);\n }\n }, 200);\n }\n break;\n case \"counter\":\n // Set AI attack animation for counter attack\n // AI 반격 애니메이션 설정\n if (selectedTechnique) {\n const p2CounterAnimName = resolveTechniqueAnimation(selectedTechnique);\n setPlayer2AttackAnimation(p2CounterAnimName);\n\n // Store technique ID for enum-based AnimationType lookup\n // 열거형 기반 AnimationType 조회를 위한 기술 ID 저장\n if (selectedTechnique.id) {\n setPlayer2TechniqueId(selectedTechnique.id);\n }\n const p2CounterAnim = getAnimation(p2CounterAnimName);\n const p2CounterDur = p2CounterAnim?.duration ?? 0.6;\n setPlayer2AttackDuration(p2CounterDur);\n player2Animation.transitionToAttack(p2CounterDur);\n } else {\n setPlayer2AttackAnimation(aiFallbackAnim);\n if (aiFallbackTechnique?.id) {\n setPlayer2TechniqueId(aiFallbackTechnique.id);\n }\n const p2FallbackAnim = getAnimation(aiFallbackAnim);\n const p2FallbackDur = p2FallbackAnim?.duration ?? 0.6;\n setPlayer2AttackDuration(p2FallbackDur);\n player2Animation.transitionToAttack(p2FallbackDur);\n }\n handleAIAttack(selectedTechnique, targetVitalPoint);\n addCombatMessage(\"AI 반격!\", \"AI Counter!\");\n break;\n }\n },\n [\n handleAIAttack,\n handleAIDefend,\n handleAITechnique,\n moveAIPlayer,\n addCombatMessage,\n validPlayers,\n arenaBounds,\n combatState.roundEnded,\n combatState.roundStarted,\n player2Animation,\n ],\n );\n\n // Update the ref\n useEffect(() => {\n executeAIActionCallbackRef.current = executeAIActionCallback;\n }, [executeAIActionCallback]);\n\n // Update adaptive difficulty metrics\n useEffect(() => {\n if (combatState.roundEnded && validPlayers.length === 2) {\n const player = validPlayers[0];\n const totalAttacks = (player.hitsLanded ?? 0) + (player.hitsTaken ?? 0);\n\n if (totalAttacks === 0) return;\n\n adaptiveDifficulty.updateSkillMetrics({\n hitsLanded: player.hitsLanded ?? 0,\n totalAttacks,\n combosExecuted: player.comboCount ?? 0,\n perfectBlockCount: 0,\n avgReactionTimeMs: 500,\n vitalPointsHit: player.vitalPointHits ?? 0,\n effectiveStanceChanges: 0,\n damageDealt: player.totalDamageDealt ?? 0,\n damageTaken: player.totalDamageReceived ?? 0,\n });\n }\n }, [combatState.roundEnded, adaptiveDifficulty, validPlayers]);\n\n // Check game end conditions\n const checkGameEnd = useCallback(() => {\n if (combatState.roundEnded) return;\n\n const p1Defeated = validPlayers[0].health <= 0;\n const p2Defeated = validPlayers[1].health <= 0;\n\n if (p1Defeated || p2Defeated) {\n combatActions.setRoundEnded(true);\n combatActions.setRoundStarted(false);\n combatActions.setRoundDisplayStatus(\"ko\");\n const winner = p1Defeated ? 1 : 0;\n const roundWinner = validPlayers[winner];\n\n // Update match score using deferred callback\n updateMatchScore(winner);\n\n addCombatMessage(\n p1Defeated ? \"플레이어 1 패배\" : \"플레이어 1 승리!\",\n p1Defeated ? \"Player 1 Defeated\" : \"Player 1 Victory!\",\n );\n\n // Start round transition\n setTimeout(() => {\n startTransition(roundWinner, internalRound);\n }, 1500);\n }\n }, [\n validPlayers,\n addCombatMessage,\n combatState.roundEnded,\n combatActions,\n internalRound,\n startTransition,\n updateMatchScore,\n ]);\n\n useEffect(() => {\n checkGameEnd();\n }, [player1Health, player2Health, checkGameEnd]);\n\n // Keyboard input handling\n useEffect(() => {\n const handleCombatInput = (event: KeyboardEvent) => {\n // ESC key toggles pause menu (unless pause menu is already open)\n if (event.key === \"Escape\") {\n event.preventDefault();\n if (showPauseMenu) {\n handleResume();\n } else {\n handlePause();\n }\n return;\n }\n\n // Block all other combat inputs when paused or during pause menu\n // isPaused: External pause state from parent component\n // showPauseMenu: Local pause menu UI is visible\n // Both conditions block input to prevent combat actions while game is paused\n if (isPaused || showPauseMenu) {\n return;\n }\n\n // Block all combat inputs during countdown or round start announcement\n if (!matchCountdownComplete || showRoundStart) {\n return;\n }\n\n if (\n !combatState.roundStarted ||\n combatState.roundEnded ||\n combatState.isExecutingTechnique\n ) {\n return;\n }\n\n const key = event.key.toLowerCase();\n\n if (key >= \"1\" && key <= \"8\") {\n const stanceIndex = parseInt(key) - 1;\n const stances: TrigramStance[] = [\n TrigramStance.GEON,\n TrigramStance.TAE,\n TrigramStance.LI,\n TrigramStance.JIN,\n TrigramStance.SON,\n TrigramStance.GAM,\n TrigramStance.GAN,\n TrigramStance.GON,\n ];\n handleStanceSwitch(stances[stanceIndex]);\n event.preventDefault();\n }\n\n if (key === \" \") {\n handleAttackWithFeedback();\n event.preventDefault();\n }\n\n if (event.key === \"Shift\") {\n handleDefendWithFeedback();\n event.preventDefault();\n }\n\n // F key for stance side switching (laterality)\n // Using \"F\" (Flip/Footwork) because:\n // - Tab conflicts with browser navigation and accessibility\n // - Q-W-E-R-T-Y-U-Y are reserved for combat techniques\n // - Shift is already used for blocking\n // - CapsLock has inconsistent key events across browsers/OSes\n if (event.key === \"f\" || event.key === \"F\") {\n handleStanceSideSwitch(0); // Human player\n event.preventDefault();\n }\n };\n\n window.addEventListener(\"keydown\", handleCombatInput);\n return () => window.removeEventListener(\"keydown\", handleCombatInput);\n }, [\n combatState.roundStarted,\n combatState.roundEnded,\n combatState.isExecutingTechnique,\n matchCountdownComplete,\n showRoundStart,\n isPaused,\n showPauseMenu,\n handleStanceSwitch,\n handleStanceSideSwitch,\n handleAttackWithFeedback,\n handleDefendWithFeedback,\n handlePause,\n handleResume,\n ]);\n\n // Calculate movement state for visual feedback using InjuryMovementModifier\n // This provides bilingual status text and injury severity information\n const player1MovementState = useMemo(() => {\n if (!validPlayers[0]?.bodyPartHealth) {\n return {\n statusText: { korean: \"정상\", english: \"Normal\" },\n isLimping: false,\n isSevereLimp: false,\n };\n }\n\n const result = injuryMovementModifier.calculateMovementSpeed(\n 1.0,\n validPlayers[0].bodyPartHealth,\n validPlayers[0].currentStance ?? TrigramStance.GEON,\n validPlayers[0].pain ?? 0,\n );\n\n return {\n statusText: result.statusText,\n isLimping: result.isLimping,\n isSevereLimp: result.isSevereLimp,\n speedMultiplier: result.speedMultiplier,\n };\n }, [validPlayers]);\n\n const player2MovementState = useMemo(() => {\n if (!validPlayers[1]?.bodyPartHealth) {\n return {\n statusText: { korean: \"정상\", english: \"Normal\" },\n isLimping: false,\n isSevereLimp: false,\n };\n }\n\n const result = injuryMovementModifier.calculateMovementSpeed(\n 1.0,\n validPlayers[1].bodyPartHealth,\n validPlayers[1].currentStance ?? TrigramStance.GEON,\n validPlayers[1].pain ?? 0,\n );\n\n return {\n statusText: result.statusText,\n isLimping: result.isLimping,\n isSevereLimp: result.isSevereLimp,\n speedMultiplier: result.speedMultiplier,\n };\n }, [validPlayers]);\n\n return (\n <div\n style={{\n width: `${width}px`,\n height: `${height}px`,\n position: \"relative\",\n backgroundColor: toHexColor(KOREAN_COLORS.UI_BACKGROUND_DARK),\n overflow: \"hidden\", // Prevent content from extending beyond container\n }}\n data-testid=\"combat-screen\"\n >\n {/* Three.js Canvas for 3D rendering */}\n <Canvas\n style={{ width: `${width}px`, height: `${height}px` }}\n camera={{\n position: cameraConfig.position,\n fov: cameraConfig.fov,\n near: cameraConfig.near,\n far: cameraConfig.far,\n }}\n gl={{\n antialias: renderConfig.antialias,\n alpha: false,\n powerPreference: \"high-performance\",\n // Add failIfMajorPerformanceCaveat to detect GPU issues\n failIfMajorPerformanceCaveat: false,\n }}\n dpr={renderConfig.dpr}\n shadows={false} // Temporarily disable shadows\n onCreated={({ gl }) => {\n gl.setClearColor(theme.colors.UI_BACKGROUND_DARK, 1);\n }}\n >\n {/* Lighting - CombatArena3D provides ambient, we add directional for shadows */}\n <ambientLight intensity={0.6} />\n <directionalLight position={[10, 10, 5]} intensity={1.2} />\n\n {/* Adaptive Quality Wrapper monitors FPS and adjusts quality */}\n <AdaptiveQualityWrapper\n enabled={shouldEnableAdaptiveQuality}\n isMobile={isMobile}\n >\n {/* Performance overlay (dev mode) - controlled by showPerformanceOverlay prop */}\n {showPerformanceOverlay && !isMobile && !showPerformanceMonitor && (\n <PerformanceOverlay3D />\n )}\n\n {/* Combat Arena 3D Environment - uses physics-based world dimensions */}\n <CombatArena3D\n lighting=\"cyberpunk\"\n scale={arenaBounds.scale}\n worldWidthMeters={arenaBounds.worldWidthMeters}\n worldDepthMeters={arenaBounds.worldDepthMeters}\n />\n\n {/* Animation updater - updates both player animations at 60fps */}\n <AnimationUpdater\n player1Animation={player1Animation}\n player2Animation={player2Animation}\n />\n\n {/* Acceleration updater - tracks player 1 movement time and updates speed */}\n <AccelerationUpdater\n isMoving={player1IsMoving}\n velocity={player1Velocity}\n movementTimeRef={player1MovementTimeRef}\n lastDirectionRef={player1LastDirectionRef}\n onSpeedUpdate={setPlayer1AccelerationBasedSpeed}\n walkSpeed={player1WalkRunSpeeds.walkSpeed}\n runSpeed={player1WalkRunSpeeds.runSpeed}\n />\n\n {/* Player 1 (Human) */}\n <Player3DWithTransitions\n {...convertPlayerStateToProps(\n validPlayers[0],\n player1PositionWithAttackMovement,\n player1Rotation,\n {\n isMobile,\n facing: \"right\",\n enableFacialExpressions: true,\n enableEyeTracking: true,\n opponentPosition: player2PositionWithAttackMovement,\n },\n )}\n currentAnimation={animationStateToPlayerAnimation(\n player1Animation.currentState,\n )}\n attackAnimation={player1AttackAnimation}\n laterality={combatState.playerLaterality[0]}\n enableTransitionEffects={!isMobile}\n enableStanceSymbol={!isMobile}\n enableStanceAudio={true}\n />\n\n {/* Player 2 (AI) */}\n <Player3DWithTransitions\n {...convertPlayerStateToProps(\n validPlayers[1],\n player2PositionWithAttackMovement,\n player2Rotation,\n {\n isMobile,\n facing: \"right\",\n enableFacialExpressions: true,\n enableEyeTracking: true,\n opponentPosition: player1PositionWithAttackMovement,\n },\n )}\n currentAnimation={animationStateToPlayerAnimation(\n player2Animation.currentState,\n )}\n attackAnimation={player2AttackAnimation}\n laterality={combatState.playerLaterality[1]}\n enableTransitionEffects={!isMobile}\n enableStanceSymbol={!isMobile}\n enableStanceAudio={true}\n />\n\n {/* Movement Status Indicators - Korean/English Bilingual */}\n {/* Player 1 Movement Status */}\n {(player1MovementState.isLimping ||\n player1MovementState.isSevereLimp) && (\n <Html\n position={[\n player1Position3D[0],\n player1Position3D[1] + 2.5,\n player1Position3D[2],\n ]}\n center\n data-testid=\"player1-movement-status\"\n >\n <div\n style={{\n fontSize: isMobile ? \"12px\" : \"14px\",\n color: player1MovementState.isSevereLimp\n ? toHexColor(KOREAN_COLORS.TEXT_ERROR)\n : toHexColor(KOREAN_COLORS.ACCENT_GOLD),\n fontFamily: FONT_FAMILY.KOREAN,\n fontWeight: \"bold\",\n textShadow: \"0 0 4px rgba(0,0,0,0.8)\",\n background: \"rgba(0, 0, 0, 0.6)\",\n padding: \"4px 8px\",\n borderRadius: \"4px\",\n whiteSpace: \"nowrap\",\n }}\n >\n {player1MovementState.statusText.korean} |{\" \"}\n {player1MovementState.statusText.english}\n </div>\n </Html>\n )}\n\n {/* Player 2 Movement Status */}\n {(player2MovementState.isLimping ||\n player2MovementState.isSevereLimp) && (\n <Html\n position={[\n player2Position3D[0],\n player2Position3D[1] + 2.5,\n player2Position3D[2],\n ]}\n center\n data-testid=\"player2-movement-status\"\n >\n <div\n style={{\n fontSize: isMobile ? \"12px\" : \"14px\",\n color: player2MovementState.isSevereLimp\n ? toHexColor(KOREAN_COLORS.TEXT_ERROR)\n : toHexColor(KOREAN_COLORS.ACCENT_GOLD),\n fontFamily: FONT_FAMILY.KOREAN,\n fontWeight: \"bold\",\n textShadow: \"0 0 4px rgba(0,0,0,0.8)\",\n background: \"rgba(0, 0, 0, 0.6)\",\n padding: \"4px 8px\",\n borderRadius: \"4px\",\n whiteSpace: \"nowrap\",\n }}\n >\n {player2MovementState.statusText.korean} |{\" \"}\n {player2MovementState.statusText.english}\n </div>\n </Html>\n )}\n\n {/* Trauma Overlays - Injury Visualization (외상 오버레이 - 부상 시각화) */}\n {/* Player 1 Injuries */}\n <TraumaOverlay3D\n playerId=\"player\"\n health={validPlayers[0].health}\n injuries={player1Injuries}\n characterPosition={player1Position3D}\n isMobile={isMobile}\n showFractures={true}\n />\n\n {/* Player 2 Injuries */}\n <TraumaOverlay3D\n playerId=\"enemy\"\n health={validPlayers[1].health}\n injuries={player2Injuries}\n characterPosition={player2Position3D}\n isMobile={isMobile}\n showFractures={true}\n />\n\n {/* Hit Effects */}\n <HitEffects3D\n effects={combatState.hitEffects}\n onEffectComplete={handleEffectComplete}\n arenaBounds={arenaBounds}\n />\n\n {/* Combat Particle Effects - Blood viscosity, organ damage, audio (전투 입자 효과) */}\n <CombatParticleEffects3D\n hitEffects={combatState.hitEffects}\n enabled={true}\n isMobile={isMobile}\n />\n\n {/* Vital Point Overlay - Show on both players when V is pressed */}\n {overlayVisible && (\n <>\n {/* Player 1 Vital Points */}\n <VitalPointMarkers3D\n position={player1Position3D}\n visible={overlayVisible}\n severityFilter={severityFilters}\n regionFilter={regionFilter}\n searchQuery={searchQuery}\n showLabels={showLabels}\n scale={scale}\n animated={animated}\n onPointClick={() => {\n // Optional: Add point targeting in combat\n }}\n />\n\n {/* Player 2 Vital Points */}\n <VitalPointMarkers3D\n position={player2Position3D}\n visible={overlayVisible}\n severityFilter={severityFilters}\n regionFilter={regionFilter}\n searchQuery={searchQuery}\n showLabels={showLabels}\n scale={scale}\n animated={animated}\n onPointClick={() => {\n // Optional: Add point targeting in combat\n }}\n />\n\n {/* Vital Point Overlay Controls - only visible when overlay is active */}\n <VitalPointOverlayControlsHtml\n screenPosition={{\n top: `${layoutConstants.hudHeight + layoutConstants.padding}px`,\n left: `${layoutConstants.padding}px`,\n }}\n visible={overlayVisible}\n onVisibleChange={setOverlayVisible}\n severityFilters={severityFilters}\n onSeverityFiltersChange={setSeverityFilters}\n regionFilter={regionFilter}\n onRegionFilterChange={setRegionFilter}\n searchQuery={searchQuery}\n onSearchQueryChange={setSearchQuery}\n showLabels={showLabels}\n onShowLabelsChange={setShowLabels}\n animated={animated}\n onAnimatedChange={setAnimated}\n scale={scale}\n onScaleChange={setScale}\n isMobile={isMobile}\n />\n </>\n )}\n\n {/* Action Feedback - Damage Numbers */}\n <DamageNumbers\n damages={feedbackState.damageNumbers}\n isMobile={isMobile}\n arenaBounds={arenaBounds}\n />\n\n {/* Action Feedback - Action Indicators */}\n <ActionFeedback\n feedbacks={feedbackState.actionFeedbacks}\n isMobile={isMobile}\n arenaBounds={arenaBounds}\n />\n\n {/* Combo Counter */}\n <ComboCounter combo={feedbackState.comboCount} isMobile={isMobile} />\n\n {/* Technique Name Display */}\n {feedbackState.currentTechnique && (\n <TechniqueName\n korean={feedbackState.currentTechnique.korean}\n english={feedbackState.currentTechnique.english}\n isMobile={isMobile}\n onComplete={() => feedbackActions.hideTechnique()}\n />\n )}\n\n {/* Performance Overlay (Development Only) - Toggle with P key */}\n {import.meta.env.DEV && showPerformanceMonitor && (\n <PerformanceOverlay3D visible={true} />\n )}\n\n {/* Visual Feedback Components for Keyboard Controls */}\n <StanceChangeIndicator\n currentStance={currentStanceIndex}\n previousStance={previousStance}\n isMobile={isMobile}\n />\n\n <KeyboardHints\n visible={showHints}\n currentStance={currentStanceIndex}\n isMobile={isMobile}\n />\n\n <InputBufferDisplay queuedInputs={queuedInputs} isMobile={isMobile} />\n\n {/* 3D Balance Indicators - Positioned below top HUD, to the right of side HUDs */}\n {/* Player 1 Balance Indicator - Upper left area, below top HUD */}\n {validPlayers[0] && (\n <BalanceIndicatorOverlayHtml\n player={validPlayers[0] as BalancePlayerState}\n currentTime={currentTime}\n position={[\n -2.5, // Left side of arena (to the right of left HUD in 3D space)\n 2.5, // Upper area (below top HUD)\n -1.0, // Slightly forward toward camera\n ]}\n isMobile={isMobile}\n />\n )}\n\n {/* Player 2 Balance Indicator - Upper right area, below top HUD */}\n {validPlayers[1] && (\n <BalanceIndicatorOverlayHtml\n player={validPlayers[1] as BalancePlayerState}\n currentTime={currentTime}\n position={[\n 2.5, // Right side of arena (to the left of right HUD in 3D space)\n 2.5, // Upper area (below top HUD)\n -1.0, // Slightly forward toward camera\n ]}\n isMobile={isMobile}\n />\n )}\n\n {/* Mobile Touch Controls moved outside Canvas - using MobileControlsOverlay for reliable touch events */}\n\n {/* Performance Monitoring - FPS display (dev mode, toggle with P key) */}\n {process.env.NODE_ENV === \"development\" && showPerformanceMonitor && (\n <FPSMonitor\n enabled={true}\n warningThreshold={50}\n criticalThreshold={30}\n />\n )}\n </AdaptiveQualityWrapper>\n\n {/* Post-processing Effects - desktop high tier only for Android WebGL stability */}\n {renderConfig.postProcessing && (\n <EffectComposer multisampling={4}>\n <Bloom\n luminanceThreshold={0.9}\n luminanceSmoothing={0.9}\n mipmapBlur\n intensity={0.8}\n radius={0.4}\n />\n <Noise opacity={0.03} />\n <Vignette eskil={false} offset={0.1} darkness={0.3} />\n </EffectComposer>\n )}\n </Canvas>\n\n {/* Html UI Overlays (positioned absolutely over Canvas) */}\n <div\n style={{\n position: \"absolute\",\n top: 0,\n left: 0,\n width: \"100%\",\n height: \"100%\",\n pointerEvents: \"none\",\n zIndex: Z_INDEX.HUD,\n // Use 'clip' for pure clipping without creating a scroll container\n // Note: Both 'clip' and 'hidden' will clip box/text shadows; ensure\n // any required shadow space is handled via padding on parent containers\n overflow: \"clip\",\n }}\n >\n {/* Top HUD - Round info, timer, return to menu */}\n <CombatTopHUD\n width={width}\n height={height}\n isMobile={isMobile}\n positionScale={positionScale}\n currentRound={internalRound}\n totalRounds={3}\n timerState={timerState}\n showTimer={\n combatState.roundStarted &&\n !combatState.roundEnded &&\n matchCountdownComplete &&\n !showRoundStart\n }\n onReturnToMenu={onReturnToMenu}\n isPaused={isPaused || showPauseMenu}\n />\n\n {/* Left HUD - Player 1 stats.\n On mobile, side HUDs occlude the 3D arena in both portrait and\n landscape; collapse them away so the arena stays fully visible.\n Player status remains available via CombatPortraitStatusStrip and\n bottom combat controls. */}\n {!isMobile && (\n <CombatLeftHUD\n width={width}\n height={height}\n isMobile={isMobile}\n positionScale={positionScale}\n player={validPlayers[0]}\n laterality={combatState.playerLaterality[0]}\n isInGuard={player1Animation.isInStanceGuard()}\n speedModifiers={player1SpeedModifiers}\n />\n )}\n\n {/* Right HUD - Player 2/AI stats with difficulty indicator */}\n {!isMobile && (\n <CombatRightHUD\n width={width}\n height={height}\n isMobile={isMobile}\n positionScale={positionScale}\n player={validPlayers[1]}\n laterality={combatState.playerLaterality[1]}\n speedModifiers={player2SpeedModifiers}\n difficultyTier={currentDifficultyTier}\n />\n )}\n\n {/* Portrait-mobile HP/stamina strip. Replaces the hidden side HUDs\n so both players can still see their health at a glance without\n re-introducing arena occlusion. */}\n {isMobile && isPortrait && (\n <CombatPortraitStatusStrip\n width={width}\n height={height}\n player1={validPlayers[0]}\n player2={validPlayers[1]}\n positionScale={positionScale}\n topOffset={layoutConstants.hudHeight}\n />\n )}\n\n {/* Bottom HUD - Technique bar, volume, messages */}\n <CombatBottomHUD\n width={width}\n height={height}\n isMobile={isMobile}\n positionScale={positionScale}\n visible={\n combatState.roundStarted &&\n !combatState.roundEnded &&\n matchCountdownComplete &&\n !showRoundStart\n }\n techniques={techniqueSelection.availableTechniques}\n player={validPlayers[0]}\n selectedIndex={techniqueSelection.selectedIndex}\n cooldowns={cooldownsMap}\n onTechniqueSelect={techniqueSelection.selectTechnique}\n combatMessages={combatState.combatMessages}\n />\n\n {/* Player State Visual Indicators */}\n {/* Player 1 State Overlay - includes consciousness blur, pain vignette, etc.\n In portrait mobile the arena is already rendered in a compressed 3:4\n aspect ratio, so halve the fullscreen vignette/blur/flash intensity\n to avoid further obscuring the scene. */}\n <PlayerStateOverlayHtml\n pain={validPlayers[0].pain}\n balanceState={getBalanceState(validPlayers[0].balance)}\n position=\"left\"\n consciousness={validPlayers[0].consciousness}\n bloodLoss={0} // FIXME: bloodLoss property not yet added to PlayerState interface - overlay will not display until implemented\n stamina={validPlayers[0].stamina}\n isMobile={isMobile}\n intensityScale={isMobile && isPortrait ? 0.5 : 1}\n />\n\n {/* Note: Player 2 (AI) does not get fullscreen state overlays like consciousness blur */}\n {/* as those effects would incorrectly affect the player's view */}\n\n {/* Breathing Disruption Indicators */}\n {/* Player 1 Breathing Indicator - positioned near left HUD */}\n <div\n style={{\n position: \"absolute\",\n left: isMobile ? \"10px\" : \"20px\",\n top: isMobile ? \"120px\" : \"160px\",\n zIndex: Z_INDEX.HUD + 1,\n pointerEvents: \"none\",\n }}\n data-testid=\"player1-breathing-indicator-container\"\n >\n <BreathingIndicator player={validPlayers[0]} isMobile={isMobile} />\n </div>\n\n {/* Player 2 Breathing Indicator - positioned near right HUD */}\n <div\n style={{\n position: \"absolute\",\n right: isMobile ? \"10px\" : \"20px\",\n top: isMobile ? \"120px\" : \"160px\",\n zIndex: Z_INDEX.HUD + 1,\n pointerEvents: \"none\",\n }}\n data-testid=\"player2-breathing-indicator-container\"\n >\n <BreathingIndicator player={validPlayers[1]} isMobile={isMobile} />\n </div>\n\n {/* Pause Menu Overlay */}\n {(isPaused || showPauseMenu) && (\n <PauseMenu\n onResume={handleResume}\n onRestart={handleRestart}\n onReturnToMenu={onReturnToMenu}\n isMobile={isMobile}\n />\n )}\n </div>\n\n {/* Round Announcement Overlay */}\n {showAnnouncement && roundWinner && (\n <RoundAnnouncement\n roundNumber={transitionRoundNumber}\n roundWinner={roundWinner}\n currentScore={matchScore}\n roundStats={{\n damageDealt: roundWinner.totalDamageDealt ?? 0,\n hitsLanded: roundWinner.hitsLanded ?? 0,\n vitalPointsHit: roundWinner.vitalPointHits ?? 0,\n accuracy: calculateAccuracy(roundWinner),\n }}\n onCountdownComplete={() => {\n // Check if match is over (best of 3)\n if (matchScore.player1 >= 2 || matchScore.player2 >= 2) {\n const winner = matchScore.player1 >= 2 ? 0 : 1;\n onGameEnd(winner);\n } else {\n // Match continues - trigger transition to next round\n skipCountdown();\n }\n }}\n onSkip={() => {\n // Check if match is over (best of 3) before skipping\n if (matchScore.player1 >= 2 || matchScore.player2 >= 2) {\n const winner = matchScore.player1 >= 2 ? 0 : 1;\n onGameEnd(winner);\n } else {\n skipCountdown();\n }\n }}\n isMobile={isMobile}\n totalRounds={3}\n />\n )}\n\n {/* Match Start Countdown Overlay - only shows once at match start */}\n {showMatchCountdown && !hasShownMatchCountdown && (\n <MatchCountdown\n onComplete={() => {\n setHasShownMatchCountdown(true);\n setShowMatchCountdown(false);\n setMatchCountdownComplete(true);\n // Start the first round after countdown\n startRound();\n }}\n isMobile={isMobile}\n showSkip={false}\n />\n )}\n\n {/* Round Start Announcement for subsequent rounds */}\n {/* Note: showRoundStart is only set to true after round 1 ends, so no need for internalRound > 1 check */}\n {showRoundStart && (\n <RoundStartAnnouncement\n roundNumber={internalRound}\n duration={2}\n onComplete={() => {\n if (import.meta.env.DEV) {\n console.log(\n \"[DEV] Round start announcement complete for round\",\n internalRound,\n );\n }\n setShowRoundStart(false);\n // Start combat for this round\n startRound();\n }}\n isMobile={isMobile}\n />\n )}\n\n {/* Round Display Status - Brief status messages */}\n {contentReady && combatState.roundDisplayStatus && (\n <RoundDisplayStatus\n status={combatState.roundDisplayStatus}\n isMobile={isMobile}\n />\n )}\n\n {/* Mobile Controls - Pure DOM, rendered OUTSIDE Canvas for reliable touch events */}\n {/* Uses pure DOM handlers instead of drei's Html which can block touch events on mobile */}\n {isMobile && (\n <>\n <MobileControlsOverlay\n onMove={handleMobileMove}\n onAttack={handleMobileAttack}\n onBlock={handleMobileBlock}\n bottom={getMobileControlsBottom(height)}\n viewportWidth={width}\n viewportHeight={height}\n />\n\n <StanceWheelPure\n currentStance={currentStanceIndex}\n onStanceChange={handleMobileStanceChange}\n expanded={stanceWheelExpanded}\n onToggle={toggleStanceWheel}\n disabled={!mobileControlsEnabled}\n opacity={0.8}\n />\n\n <GestureRecognizerPure\n onGesture={handleMobileGesture}\n enabled={mobileControlsEnabled}\n showFeedback={true}\n minSwipeDistance={50}\n />\n </>\n )}\n </div>\n );\n};\n\nexport default CombatScreen3D;\n"],"mappings":"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"}
1
+ {"version":3,"file":"CombatScreen3D.js","names":[],"sources":["../../../../src/components/screens/combat/CombatScreen3D.tsx"],"sourcesContent":["/**\n * CombatScreen3D - Three.js-based combat screen (Black Trigram 흑괘)\n *\n * Maintains all existing combat logic and state management\n * Uses Html overlays for UI and 3D meshes for game objects\n */\n\nimport { Canvas } from \"@react-three/fiber\";\nimport { Html } from \"@react-three/drei\";\nimport {\n Bloom,\n EffectComposer,\n Noise,\n Vignette,\n} from \"@react-three/postprocessing\";\nimport React, {\n useCallback,\n useEffect,\n useMemo,\n useRef,\n useState,\n} from \"react\";\nimport { useAudio } from \"../../../audio/AudioProvider\";\nimport { useActionFeedback } from \"../../../hooks/useActionFeedback\";\nimport { useCombatTimer } from \"../../../hooks/useCombatTimer\";\nimport { useKeyboardControls } from \"../../../hooks/useKeyboardControls\";\nimport { usePlayerAnimation } from \"../../../hooks/usePlayerAnimation\";\nimport { useRoundTransition } from \"../../../hooks/useRoundTransition\";\nimport { useTechniqueSelection } from \"../../../hooks/useTechniqueSelection\";\nimport { useWebGLContextLossHandler } from \"../../../hooks/useWebGLContextLossHandler\";\nimport { useCombatAttackMovement } from \"./hooks/useCombatAttackMovement\";\nimport { HitEffect, PlayerState } from \"../../../systems\";\nimport { CombatSystem } from \"../../../systems/CombatSystem\";\nimport {\n AdaptiveDifficulty,\n getPersonalityByArchetype,\n} from \"../../../systems/ai\";\nimport {\n AnimationEvents,\n AnimationState,\n AnimationType,\n determineRecoveryType,\n getAnimation,\n getRecoveryAnimationState,\n resolveTechniqueAnimation,\n} from \"../../../systems/animation\";\nimport { BalanceSystem } from \"../../../systems/combat/BalanceSystem\";\nimport type { BalancePlayerState } from \"../../../systems/combat/BalanceSystem\";\nimport { HitEffectType } from \"../../../systems/effects\";\nimport { injuryMovementModifier } from \"../../../systems/movement/InjuryMovementModifier\";\nimport { TRIGRAM_STANCES_ORDER } from \"../../../systems/trigram/types\";\nimport { TRIGRAM_TECHNIQUES } from \"../../../systems/trigram/techniques\";\nimport type { KoreanTechnique } from \"../../../systems/vitalpoint/types\";\nimport {\n CombatState,\n GameMode,\n PlayerArchetype,\n Position,\n TrigramStance,\n} from \"../../../types\";\nimport { Injury, InjuryType } from \"../../../types/injury\";\nimport { Z_INDEX } from \"../../../types/LayoutTypes\";\nimport { getMobileControlsBottom } from \"../../../types/constants/layout\";\nimport {\n FONT_FAMILY,\n getPerformanceSettings,\n KOREAN_COLORS,\n ROUND_ANNOUNCEMENT_TIMINGS,\n} from \"@/types/constants\";\nimport { getAnimationTypeForTechnique } from \"../../../data/techniqueMappings\";\nimport { toHexColor } from \"../../../utils/colorHelpers\";\nimport { usePlayerMovement } from \"../../../utils/inputSystem\";\nimport { PerformanceOverlay3D } from \"../../../utils/performance\";\nimport { createPlayerFromArchetype } from \"../../../utils/playerUtils\";\nimport { createCameraConfig } from \"../../../utils/sharedPhysicsConfig\";\nimport { useAdaptiveQuality } from \"../../shared/three/optimization\";\nimport { useKoreanTheme } from \"../../shared/base/useKoreanTheme\";\nimport {\n ActionFeedback,\n TechniqueName,\n} from \"../../shared/three/effects/ActionFeedback\";\nimport { DamageNumbers } from \"../../shared/three/effects/DamageNumbers\";\nimport HitEffects3D from \"../../shared/three/effects/HitEffects3D\";\nimport { VitalPointMarkers3D } from \"../../shared/three/effects/VitalPointMarkers3D\";\nimport { StanceChangeIndicator } from \"../../shared/three/indicators/StanceChangeIndicator\";\nimport { CombatArena3D } from \"../../shared/three/scene/CombatArena3D\";\nimport { BreathingIndicator } from \"../../shared/three/ui/BreathingIndicator\";\nimport { ComboCounter } from \"../../shared/three/ui/ComboCounter\";\nimport { VitalPointOverlayControlsHtml } from \"../../shared/three/ui/VitalPointOverlayControlsHtml\";\nimport { KeyboardHints } from \"./components/controls/KeyboardHints\";\nimport { MatchCountdown } from \"./components/feedback/MatchCountdown\";\nimport { RoundAnnouncement } from \"./components/feedback/RoundAnnouncementOverlayHtml\";\nimport { RoundDisplayStatus } from \"./components/feedback/RoundDisplayStatus\";\nimport { RoundStartAnnouncement } from \"./components/feedback/RoundStartAnnouncementOverlayHtml\";\nimport { InputBufferDisplay } from \"./components/indicators/InputBufferDisplay\";\nimport { GestureEvent } from \"../../../hooks/useTouchControls\";\nimport {\n MovementType,\n SpeedModifierSystem,\n} from \"../../../systems/physics/SpeedModifierSystem\";\nimport { Technique } from \"../../../types\";\nimport {\n animationStateToPlayerAnimation,\n convertPlayerStateToProps,\n getBalanceState,\n} from \"../../../utils/player3DHelpers\";\nimport {\n GestureRecognizerPure,\n MobileControlsOverlay,\n StanceWheelPure,\n} from \"../../shared/mobile\";\nimport { ButtonEventType } from \"../../shared/mobile/ActionButtons\";\nimport { Direction, DPadEventType } from \"../../shared/mobile/VirtualDPad\";\nimport { Player3DWithTransitions } from \"../../shared/three/models/Player3DWithTransitions\";\nimport { PauseMenu } from \"./components/controls/PauseMenu\";\nimport { TraumaOverlay3D } from \"./components/effects/TraumaOverlay3D\";\nimport { CombatParticleEffects3D } from \"./components/effects/CombatParticleEffects3D\";\nimport {\n CombatBottomHUD,\n CombatLeftHUD,\n CombatPortraitStatusStrip,\n CombatRightHUD,\n CombatTopHUD,\n} from \"./components/hud\";\nimport { FPSMonitor } from \"./components/hud/FPSMonitor\";\nimport { PlayerStateOverlayHtml } from \"./components/hud/PlayerStateOverlayHtml\";\nimport { BalanceIndicatorOverlayHtml } from \"../../ui/combat/BalanceIndicatorOverlayHtml\";\nimport {\n ANNOUNCEMENT_FADE_OUT_DELAY,\n calculateAccuracy,\n STANCE_INDEX_MAP,\n} from \"./helpers\";\nimport { AnimationUpdater } from \"./helpers/AnimationUpdater\";\nimport { AccelerationUpdater } from \"../../../systems/movement/helpers/AccelerationUpdater\";\nimport { isRunningSpeed } from \"../../../systems/movement/helpers/accelerationUtils\";\nimport { useAICombat } from \"./hooks/useAICombat\";\nimport { useCombatActions } from \"./hooks/useCombatActions\";\nimport { useCombatAudio } from \"./hooks/useCombatAudio\";\nimport { useCombatLayout } from \"./hooks/useCombatLayout\";\nimport { useCombatState } from \"./hooks/useCombatState\";\n\n/**\n * Props for the CombatScreen3D component.\n * Provides all state and callbacks required for the 3D combat screen.\n */\nexport interface CombatScreen3DProps {\n /**\n * Array of player states (expects exactly 2 players).\n * Each PlayerState contains all combat and status information for a player.\n */\n readonly players: readonly PlayerState[];\n /**\n * Callback to update a player's state by index.\n * @param playerIndex - Index of the player to update (0 or 1).\n * @param updates - Partial PlayerState with updated fields.\n */\n readonly onPlayerUpdate: (\n playerIndex: number,\n updates: Partial<PlayerState>,\n ) => void;\n /**\n * Current round number (1-based).\n */\n readonly currentRound: number;\n /**\n * Remaining time in seconds for the current round.\n */\n readonly timeRemaining: number;\n /**\n * Whether combat is currently paused.\n */\n readonly isPaused: boolean;\n /**\n * Callback when the user exits to the menu.\n */\n readonly onReturnToMenu: () => void;\n /**\n * Callback when the match ends, with the winner's index (0 or 1).\n * @param winner - Index of the winning player.\n */\n readonly onGameEnd: (winner: number) => void;\n /**\n * Optional game mode (affects rules/behavior).\n */\n readonly gameMode?: GameMode;\n /**\n * Canvas width in pixels. Defaults to 1200.\n */\n readonly width?: number;\n /**\n * Canvas height in pixels. Defaults to 800.\n */\n readonly height?: number;\n /**\n * Enable adaptive quality adjustment (default: true on mobile)\n */\n readonly enableAdaptiveQuality?: boolean;\n /**\n * Show performance overlay in dev mode (default: import.meta.env.DEV)\n */\n readonly showPerformanceOverlay?: boolean;\n}\n\n/**\n * CombatScreen3D Component\n * Three.js-based combat screen with 3D characters and effects\n */\n\n/**\n * AdaptiveQualityWrapper - Internal component to use adaptive quality hook\n * Must be inside Canvas to use useFrame from @react-three/fiber\n *\n * Hoisted outside CombatScreen3D to avoid \"Cannot create components during render\"\n * warnings from react-hooks/component-creation. Keeps the component type stable\n * across renders of the parent.\n */\nconst AdaptiveQualityWrapper: React.FC<{\n readonly enabled: boolean;\n readonly isMobile: boolean;\n readonly children: React.ReactNode;\n}> = ({ enabled, isMobile, children }) => {\n // Monitor FPS and adjust quality dynamically\n // Quality settings are logged but not currently applied to rendering\n // Future: Pass quality settings to child components for dynamic adjustments\n useAdaptiveQuality(enabled, isMobile, (newQuality) => {\n if (import.meta.env.DEV) {\n console.log(`[CombatScreen3D] Quality adjusted to: ${newQuality}`);\n }\n });\n\n return <>{children}</>;\n};\n\nexport const CombatScreen3D: React.FC<CombatScreen3DProps> = ({\n players,\n onPlayerUpdate,\n currentRound,\n timeRemaining,\n isPaused,\n onReturnToMenu,\n onGameEnd,\n width = 1200,\n height = 800,\n enableAdaptiveQuality,\n showPerformanceOverlay = import.meta.env.DEV,\n}) => {\n // Track when content is ready to render (prevents flash of empty content)\n const [contentReady, setContentReady] = useState(false);\n\n // Track context loss count for debugging\n const contextLossCountRef = useRef(0);\n\n // Handle WebGL context loss and restoration\n useWebGLContextLossHandler({\n onContextLost: () => {\n console.warn(\"⚠️ WebGL context lost in CombatScreen\");\n contextLossCountRef.current += 1;\n setContentReady(false);\n },\n onContextRestored: () => {\n // Context restored - re-enable content after short delay\n setTimeout(() => setContentReady(true), 100);\n },\n autoRestore: true,\n });\n\n // Ensure content renders after component is mounted and stable\n useEffect(() => {\n const timer = setTimeout(() => setContentReady(true), 50);\n return () => clearTimeout(timer);\n }, []);\n\n // Audio context for button interactions\n const audio = useAudio();\n\n // Performance marks - only in dev mode and memoized\n useEffect(() => {\n if (import.meta.env.DEV) {\n performance.mark(\"combat-3d-render-start\");\n return () => {\n performance.mark(\"combat-3d-render-end\");\n performance.measure(\n \"combat-3d-render\",\n \"combat-3d-render-start\",\n \"combat-3d-render-end\",\n );\n };\n }\n }, []);\n\n // Layout calculations\n const { arenaBounds, isMobile, isPortrait, screenSize, layoutConstants } =\n useCombatLayout(width, height);\n\n // Use Korean theme hook for consistent theming\n const theme = useKoreanTheme({\n variant: \"primary\",\n size: \"md\",\n isMobile,\n });\n\n // Screen size scaling for 4K and large displays\n // Uses SPACING_SCALE_MAP values: mobile=0.5, tablet=0.75, desktop=1.0, large=1.25, xlarge=1.5\n const positionScale = useMemo(() => {\n if (isMobile) {\n return 1.0;\n }\n\n switch (screenSize) {\n case \"mobile\":\n return 1.0; // Mobile already has special handling\n case \"tablet\":\n return 1.0;\n case \"desktop\":\n return 1.0;\n case \"large\":\n return 1.25;\n case \"xlarge\":\n return 1.5; // 4K displays need 1.5x offsets\n default:\n return 1.0;\n }\n }, [isMobile, screenSize]);\n\n // Camera and rendering configuration based on device\n // Use shared physics config for consistent camera setup across screens\n // In portrait we pull the camera back on Z and widen the FOV so both\n // fighters fit the narrow viewport without getting clipped.\n // 세로 모드에서는 카메라를 뒤로 빼고 FOV를 넓힘\n const cameraConfig = useMemo(() => {\n const base = createCameraConfig(isMobile);\n if (!isPortrait) return base;\n return {\n ...base,\n fov: Math.min(80, base.fov + 15),\n position: [base.position[0], base.position[1], base.position[2] + 4] as [\n number,\n number,\n number,\n ],\n };\n }, [isMobile, isPortrait]);\n\n // Rendering quality based on device (optimize for 60fps on mobile)\n // Uses performance tier system for extra-small, mobile, and desktop devices\n const renderConfig = useMemo(() => {\n const performanceSettings = getPerformanceSettings(width, isMobile);\n\n return {\n shadowMapSize: performanceSettings.shadowMapSize,\n dpr: performanceSettings.dpr,\n antialias: performanceSettings.antialias,\n maxParticles: performanceSettings.maxParticles,\n postProcessing: performanceSettings.postProcessing,\n };\n }, [isMobile, width]);\n\n // Adaptive quality - default to enabled on mobile, optional on desktop\n const shouldEnableAdaptiveQuality = enableAdaptiveQuality ?? isMobile;\n\n // Combat state management\n const { state: combatState, actions: combatActions } = useCombatState();\n\n // ═══════════════════════════════════════════════════════════════════════════\n // Vital Point Overlay Controls State\n // ═══════════════════════════════════════════════════════════════════════════\n\n // Vital point overlay state (for both players)\n const [overlayVisible, setOverlayVisible] = useState(false);\n const [severityFilters, setSeverityFilters] = useState<\n import(\"../../../types/common\").VitalPointSeverity[]\n >([]);\n const [regionFilter, setRegionFilter] =\n useState<\n import(\"../../shared/three/ui/VitalPointOverlayControlsHtml\").BodyRegionFilter\n >(\"all\");\n const [searchQuery, setSearchQuery] = useState(\"\");\n const [showLabels, setShowLabels] = useState(true);\n const [animated, setAnimated] = useState(true);\n const [scale, setScale] = useState(1.2); // Larger scale for better visibility in combat\n // Performance monitor visibility toggle (F9 key)\n const [showPerformanceMonitor, setShowPerformanceMonitor] = useState(false);\n\n // Keyboard shortcut for toggling overlay (V key) and performance monitor (F9)\n // Ctrl key removed - running now acceleration-based\n useEffect(() => {\n const handleKeyDown = (e: KeyboardEvent) => {\n if (e.key === \"v\" || e.key === \"V\") {\n setOverlayVisible((prev) => !prev);\n audio.playSFX(\"menu_select\");\n }\n // F9 key toggles performance monitor (development only)\n // Note: P key is reserved for Philosophy screen\n if (e.key === \"F9\" && import.meta.env.DEV) {\n e.preventDefault();\n setShowPerformanceMonitor((prev) => !prev);\n }\n };\n\n window.addEventListener(\"keydown\", handleKeyDown);\n return () => {\n window.removeEventListener(\"keydown\", handleKeyDown);\n };\n }, [audio]);\n\n // Action feedback system for damage numbers, combo counter, and technique names\n const { state: feedbackState, actions: feedbackActions } = useActionFeedback({\n damageNumberDuration: 1500,\n actionFeedbackDuration: 1200,\n techniqueDuration: 2000,\n comboResetTime: 2000,\n });\n\n // Combat audio\n const combatAudio = useCombatAudio();\n const { playStanceChangeSound } = combatAudio;\n\n // Match score tracking - use ref for internal updates, state for rendering\n const [matchScore, setMatchScore] = useState({ player1: 0, player2: 0 });\n const matchScoreRef = useRef(matchScore);\n\n // Helper to update match score without triggering setState in effects\n const updateMatchScore = useCallback((winner: 0 | 1) => {\n const newScore = {\n player1:\n winner === 0\n ? matchScoreRef.current.player1 + 1\n : matchScoreRef.current.player1,\n player2:\n winner === 1\n ? matchScoreRef.current.player2 + 1\n : matchScoreRef.current.player2,\n };\n matchScoreRef.current = newScore;\n // Use setTimeout to defer the setState call outside the effect\n setTimeout(() => setMatchScore(newScore), 0);\n }, []);\n\n // Internal round tracking (since parent may always pass currentRound=1)\n const [internalRound, setInternalRound] = useState(currentRound);\n\n // Current time for balance indicators and other time-dependent UI\n // Updated every 200ms to avoid calling Date.now() on every render (60fps)\n // Use lazy initializer so Date.now() is not called during render (React purity)\n const [currentTime, setCurrentTime] = useState(() => Date.now());\n\n // Match countdown state - DISABLED: skip countdown and start combat immediately\n // Using state for hasShownMatchCountdown to avoid ref access during render\n const [hasShownMatchCountdown, setHasShownMatchCountdown] = useState(true); // Already shown (skipped)\n const [showMatchCountdown, setShowMatchCountdown] = useState(false); // Don't show\n const [showRoundStart, setShowRoundStart] = useState(false);\n const [matchCountdownComplete, setMatchCountdownComplete] = useState(true); // Already complete (skipped)\n\n // Player 1 position in METERS (relative to arena center)\n // Player 1 starts at 10% left of center for realistic combat distance (~1.6m apart)\n // Allows kicks to land with 1-2 steps, punches with 2-3 steps (realistic fighting)\n const [player1Position, setPlayer1Position] = useState<Position>({\n x: arenaBounds.worldWidthMeters * -0.1, // 10% left of center (~0.8m for 8m arena)\n y: 0, // Centered\n });\n\n // Pause menu state - local state for pause menu visibility\n // Local state for pause menu UI visibility\n // Note: isPaused (prop) controls game pause from parent, showPauseMenu (state) controls menu UI\n // Both need to be checked because:\n // - isPaused: External pause state (e.g., from parent component or global game state)\n // - showPauseMenu: Local UI state for pause menu overlay\n // They can be out of sync intentionally (e.g., parent pauses game but menu not shown)\n const [showPauseMenu, setShowPauseMenu] = useState(false);\n\n // Pause menu handlers\n const handlePause = useCallback(() => {\n setShowPauseMenu(true);\n audio.playSFX(\"menu_select\");\n }, [audio]);\n\n const handleResume = useCallback(() => {\n setShowPauseMenu(false);\n audio.playSFX(\"menu_select\");\n }, [audio]);\n\n const handleRestart = useCallback(() => {\n // Note: React 19 automatically batches all these state updates into a single render\n // This ensures atomic state transitions without race conditions\n\n // Reset to first round\n setInternalRound(1);\n setMatchScore({ player1: 0, player2: 0 });\n matchScoreRef.current = { player1: 0, player2: 0 };\n\n // Reset combat state\n combatActions.setRoundEnded(false);\n combatActions.setRoundStarted(false);\n combatActions.setRoundDisplayStatus(null);\n\n // Reset player health, resources, combat state, and visual state\n // Includes consciousness, pain, balance, combatState to clear any blur/vignette effects\n // and reset from fallen/stunned states\n onPlayerUpdate(0, {\n health: 100,\n stamina: 100,\n ki: 100,\n consciousness: 100,\n pain: 0,\n balance: 100,\n combatState: CombatState.IDLE,\n isStunned: false,\n isBlocking: false,\n });\n onPlayerUpdate(1, {\n health: 100,\n stamina: 100,\n ki: 100,\n consciousness: 100,\n pain: 0,\n balance: 100,\n combatState: CombatState.IDLE,\n isStunned: false,\n isBlocking: false,\n });\n\n // Reset player positions to starting positions for rematch\n // Use meter-based positions for physics-first system consistency\n setPlayer1Position({\n x: arenaBounds.worldWidthMeters * -0.1, // 10% left of center in meters\n y: 0, // Centered\n });\n onPlayerUpdate(1, {\n position: {\n x: arenaBounds.worldWidthMeters * 0.1, // 10% right of center in meters\n y: 0, // Centered\n },\n });\n\n // Close pause menu and start first round\n // The timer will reset automatically via the initialTime prop when round starts\n setShowPauseMenu(false);\n setShowRoundStart(true);\n\n audio.playSFX(\"menu_select\");\n }, [audio, combatActions, onPlayerUpdate, arenaBounds, setPlayer1Position]);\n\n // Round transition complete handler - checks for match end or starts next round\n const handleRoundTransitionComplete = useCallback(() => {\n if (import.meta.env.DEV) {\n console.log(\"[DEV] Round transition complete, checking match status\");\n }\n // Check if match is over (best of 3 - first to 2 wins)\n const currentScore = matchScoreRef.current;\n if (currentScore.player1 >= 2 || currentScore.player2 >= 2) {\n // Match is over - call onGameEnd instead of starting next round\n const matchWinner = currentScore.player1 >= 2 ? 0 : 1;\n if (import.meta.env.DEV) {\n console.log(\"[DEV] Match over, winner:\", matchWinner);\n }\n onGameEnd(matchWinner);\n return; // Don't start next round\n }\n\n // Reset all combat state for next round (combos, hit effects, messages cleared)\n combatActions.resetRoundState();\n\n // Increment internal round counter for next round\n // Use the updater function to get the new value and trigger announcement\n setInternalRound((prev) => {\n const nextRound = prev + 1;\n if (import.meta.env.DEV) {\n console.log(\"[DEV] Incrementing round from\", prev, \"to\", nextRound);\n }\n // Wait for transition duration plus fade-out to complete before showing next round announcement\n setTimeout(() => {\n if (import.meta.env.DEV) {\n console.log(\n \"[DEV] Showing round start announcement for round\",\n nextRound,\n );\n }\n setShowRoundStart(true);\n }, ANNOUNCEMENT_FADE_OUT_DELAY);\n return nextRound;\n });\n\n // Reset player health, resources, combat state, and visual state for next round\n // Includes consciousness, pain, balance, combatState to clear any blur/vignette effects\n // and reset from fallen/stunned states\n onPlayerUpdate(0, {\n health: 100,\n stamina: 100,\n ki: 100,\n consciousness: 100,\n pain: 0,\n balance: 100,\n combatState: CombatState.IDLE,\n isStunned: false,\n isBlocking: false,\n });\n onPlayerUpdate(1, {\n health: 100,\n stamina: 100,\n ki: 100,\n consciousness: 100,\n pain: 0,\n balance: 100,\n combatState: CombatState.IDLE,\n isStunned: false,\n isBlocking: false,\n });\n\n // Reset player positions to starting positions for new round\n // Use meter-based positions for physics-first system consistency\n setPlayer1Position({\n x: arenaBounds.worldWidthMeters * -0.1, // 10% left of center in meters\n y: 0, // Centered\n });\n // Player 2 position is reset via onPlayerUpdate\n onPlayerUpdate(1, {\n position: {\n x: arenaBounds.worldWidthMeters * 0.1, // 10% right of center in meters\n y: 0, // Centered\n },\n });\n }, [\n combatActions,\n onGameEnd,\n onPlayerUpdate,\n arenaBounds,\n // Note: setPlayer1Position is a stable React setState function and won't cause unnecessary re-renders\n setPlayer1Position,\n ]);\n\n // Round transition management\n const {\n showAnnouncement,\n roundWinner,\n currentRoundNumber: transitionRoundNumber,\n skipCountdown,\n startTransition,\n } = useRoundTransition(\n {\n announcementDuration: ROUND_ANNOUNCEMENT_TIMINGS.ANNOUNCEMENT_DURATION,\n countdownDuration: ROUND_ANNOUNCEMENT_TIMINGS.COUNTDOWN_DURATION,\n transitionDuration: ROUND_ANNOUNCEMENT_TIMINGS.TRANSITION_DURATION,\n },\n handleRoundTransitionComplete,\n );\n\n // Player 2 position in METERS (relative to arena center) - AI-controlled\n // Player 2 starts at 10% right of center for realistic combat distance (~1.6m apart)\n // Allows kicks to land with 1-2 steps, punches with 2-3 steps (realistic fighting)\n const player2Position = useMemo<Position>(() => {\n if (players.length >= 2 && players[1].position) {\n return players[1].position;\n }\n return {\n x: arenaBounds.worldWidthMeters * 0.1, // 10% right of center (~0.8m for 8m arena)\n y: 0, // Centered\n };\n }, [players, arenaBounds]);\n\n // Combined positions for backward compatibility with existing code\n const playerPositions = useMemo<Position[]>(() => {\n return [player1Position, player2Position];\n }, [player1Position, player2Position]);\n\n // Physics-first: positions are already in METERS\n // Direct conversion to 3D world coordinates - no pixel math needed\n const player1Position3D: [number, number, number] = useMemo(() => {\n // playerPosition.x is lateral position in meters (- = left, + = right)\n // playerPosition.y is forward/backward position in meters (- = toward camera, + = away)\n return [playerPositions[0].x, 0, playerPositions[0].y];\n }, [playerPositions]);\n\n const player2Position3D: [number, number, number] = useMemo(() => {\n return [playerPositions[1].x, 0, playerPositions[1].y];\n }, [playerPositions]);\n\n // Calculate dynamic rotations so players always face each other\n // atan2(dx, dz) gives the Y-axis rotation needed to face direction (dx, dz)\n // This is the standard formula for rotating around Y to point at a target\n // NOTE: player1Rotation is calculated after usePlayerMovement below\n\n const player2Rotation = useMemo(() => {\n const dx = player1Position3D[0] - player2Position3D[0];\n const dz = player1Position3D[2] - player2Position3D[2];\n return Math.atan2(dx, dz);\n }, [player1Position3D, player2Position3D]);\n\n // Combat system\n const combatSystem = useMemo(() => new CombatSystem(), []);\n\n // Balance system for recovery mechanics\n const balanceSystem = useMemo(() => new BalanceSystem(), []);\n\n // Speed Modifier System for dynamic movement speed calculations\n const speedModifierSystem = useMemo(() => new SpeedModifierSystem(), []);\n\n // Track speed modifiers for HUD display\n // Initial values match SpeedModifierSystem.BASE_WALKING_SPEED and BASE_ACCELERATION\n const [player1SpeedModifiers, setPlayer1SpeedModifiers] = useState({\n finalSpeed: 6.0, // BASE_WALK_SPEED (6.0 m/s)\n baseSpeed: 6.0,\n finalAcceleration: 12.0, // BASE_ACCELERATION (12.0 m/s²)\n });\n const [player2SpeedModifiers, setPlayer2SpeedModifiers] = useState({\n finalSpeed: 6.0, // BASE_WALK_SPEED (6.0 m/s)\n baseSpeed: 6.0,\n finalAcceleration: 12.0, // BASE_ACCELERATION (12.0 m/s²)\n });\n\n // Track walk/run speeds for acceleration interpolation (archetype-aware)\n const [player1WalkRunSpeeds, setPlayer1WalkRunSpeeds] = useState({\n walkSpeed: 6.0,\n runSpeed: 10.0,\n });\n\n // Calculate speed modifiers for both players when state changes\n // Updates at 5Hz (every 200ms) to balance responsiveness and performance\n // Get both walk and run speeds for acceleration interpolation\n useEffect(() => {\n const updateSpeedModifiers = () => {\n if (players.length >= 2) {\n // Player 1 speed modifiers - calculate both walk and run\n const player1WalkModifiers =\n speedModifierSystem.calculateSpeedModifiers(\n players[0],\n MovementType.WALKING,\n false, // isCrouching\n );\n const player1RunModifiers = speedModifierSystem.calculateSpeedModifiers(\n players[0],\n MovementType.RUNNING,\n false, // isCrouching\n );\n\n setPlayer1SpeedModifiers({\n finalSpeed: player1WalkModifiers.finalSpeed,\n baseSpeed: player1WalkModifiers.baseSpeed,\n finalAcceleration: player1WalkModifiers.finalAcceleration,\n });\n\n // Store walk/run speeds for acceleration interpolation\n // These account for archetype speeds and stance modifiers\n setPlayer1WalkRunSpeeds({\n walkSpeed: player1WalkModifiers.finalSpeed,\n runSpeed: player1RunModifiers.finalSpeed,\n });\n\n // Player 2 speed modifiers\n const player2Modifiers = speedModifierSystem.calculateSpeedModifiers(\n players[1],\n MovementType.WALKING,\n false,\n );\n setPlayer2SpeedModifiers({\n finalSpeed: player2Modifiers.finalSpeed,\n baseSpeed: player2Modifiers.baseSpeed,\n finalAcceleration: player2Modifiers.finalAcceleration,\n });\n }\n };\n\n // Initial calculation\n updateSpeedModifiers();\n\n // Update every 200ms (5Hz) for responsive feedback without excessive re-renders\n const intervalId = setInterval(updateSpeedModifiers, 200);\n\n return () => clearInterval(intervalId);\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [players]); // speedModifierSystem is memoized and never changes\n\n // Update current time for balance indicators at 5Hz (200ms)\n // This avoids calling Date.now() on every render (60fps = 60 times/second)\n // Instead, we update 5 times/second, which is sufficient for UI feedback\n useEffect(() => {\n const updateTime = () => {\n setCurrentTime(Date.now());\n };\n\n // Update every 200ms (5Hz) - same rate as speed modifiers\n const intervalId = setInterval(updateTime, 200);\n\n return () => clearInterval(intervalId);\n }, []);\n\n // Calculate leg injury factor for physics-based movement\n // Averages left and right leg health to determine speed penalty\n const calculateLegInjuryFactor = useCallback(\n (player: PlayerState): number => {\n if (!player.bodyPartHealth) return 0;\n\n const leftLeg = player.bodyPartHealth.legLeft ?? player.maxHealth;\n const rightLeg = player.bodyPartHealth.legRight ?? player.maxHealth;\n const maxHealth = player.maxHealth;\n\n const averageLegHealth = (leftLeg + rightLeg) / (2 * maxHealth);\n return Math.max(0, Math.min(1, 1.0 - averageLegHealth)); // 0 = healthy, 1 = critical\n },\n [],\n );\n\n // Get player1 data for movement physics\n // Memoize based on player1 reference (React Compiler prefers less specific dependencies)\n const player1 = players.length > 0 ? players[0] : undefined;\n const player1Data = useMemo(() => {\n const p1 = player1 ?? createPlayerFromArchetype(PlayerArchetype.MUSA, 0);\n return {\n currentStance: p1.currentStance,\n legInjuryFactor: calculateLegInjuryFactor(p1),\n };\n }, [player1, calculateLegInjuryFactor]);\n\n // Track current attack animation for each player\n // Used to determine which skeletal animation to play during attacks\n // 각 플레이어의 현재 공격 애니메이션 추적\n const [player1AttackAnimation, setPlayer1AttackAnimation] = useState<\n string | undefined\n >(undefined);\n const [player2AttackAnimation, setPlayer2AttackAnimation] = useState<\n string | undefined\n >(undefined);\n\n // Track current technique ID for enum-based animation lookup\n // 열거형 기반 애니메이션 조회를 위한 현재 기술 ID 추적\n const [player1TechniqueId, setPlayer1TechniqueId] = useState<\n string | undefined\n >(undefined);\n const [player2TechniqueId, setPlayer2TechniqueId] = useState<\n string | undefined\n >(undefined);\n\n // Track injuries for trauma visualization (TraumaOverlay3D)\n // 외상 시각화를 위한 부상 추적\n const [player1Injuries, setPlayer1Injuries] = useState<readonly Injury[]>([]);\n const [player2Injuries, setPlayer2Injuries] = useState<readonly Injury[]>([]);\n\n // Clear injuries when component unmounts or when a new match starts\n // 컴포넌트 언마운트 시 또는 새 매치 시작 시 부상 초기화\n useEffect(() => {\n return () => {\n // Clear injury state on unmount to avoid leaking injuries across screens\n setPlayer1Injuries([]);\n setPlayer2Injuries([]);\n };\n }, []);\n\n // Clear injuries when a new match starts (both players at full health)\n // 새 매치 시작 시 부상 초기화 (양 플레이어 모두 최대 체력)\n useEffect(() => {\n // Only clear if we have valid players with health data\n if (players.length >= 2) {\n const player1 = players[0];\n const player2 = players[1];\n\n // Check if both players are at max health (indicates new match start)\n if (\n player1?.health === player1?.maxHealth &&\n player2?.health === player2?.maxHealth\n ) {\n setPlayer1Injuries([]);\n setPlayer2Injuries([]);\n }\n }\n }, [players]);\n\n // CRITICAL FIX: Memoize onPositionChange to prevent usePlayerMovement callback recreation\n // Without this, a new function is created every render, causing animation frame cancellation\n const handlePlayer1PositionChange = useCallback(\n (newPosition: Position) => {\n setPlayer1Position(newPosition);\n onPlayerUpdate(0, { position: newPosition });\n },\n [onPlayerUpdate],\n );\n\n // CRITICAL FIX: Memoize bounds object to prevent usePlayerMovement callback recreation\n // Without this, a new object reference is created every render, causing animation frame cancellation\n const movementBounds = useMemo(\n () => ({\n worldWidthMeters: arenaBounds.worldWidthMeters,\n worldDepthMeters: arenaBounds.worldDepthMeters,\n }),\n [arenaBounds.worldWidthMeters, arenaBounds.worldDepthMeters],\n );\n\n // ═══════════════════════════════════════════════════════════════════════════\n // Acceleration-Based Running System for Player 1\n // ═══════════════════════════════════════════════════════════════════════════\n\n // Track continuous movement time for acceleration-based running\n const player1MovementTimeRef = useRef(0);\n const player1LastDirectionRef = useRef<{ x: number; y: number }>({\n x: 0,\n y: 0,\n });\n\n // Track acceleration-based speed (interpolated between walk and run speeds)\n // This applies archetype speeds and stance modifiers\n const [player1AccelerationBasedSpeed, setPlayer1AccelerationBasedSpeed] =\n useState(player1WalkRunSpeeds.walkSpeed);\n\n // Determine if currently running using utility function with archetype run speed\n const player1IsRunning = isRunningSpeed(\n player1AccelerationBasedSpeed,\n player1WalkRunSpeeds.runSpeed,\n );\n\n // Player movement with physics-based acceleration and stance modifiers\n // All positions in METERS - no pixel conversions\n const { isMoving: player1IsMoving, velocity: player1Velocity } =\n usePlayerMovement({\n enabled:\n !isPaused &&\n combatState.roundStarted &&\n !combatState.roundEnded &&\n matchCountdownComplete &&\n !showRoundStart,\n bounds: movementBounds, // Use memoized bounds object\n onPositionChange: handlePlayer1PositionChange, // Use memoized callback\n initialPositionMeters: player1Position,\n // Physics parameters for realistic movement (always enabled)\n currentStance: player1Data.currentStance,\n legInjuryFactor: player1Data.legInjuryFactor,\n isRunning: player1IsRunning, // Use computed acceleration-based running state\n useTacticalSteps: false,\n // Use interpolated speed between modifier-aware walk/run speeds\n // This preserves archetype speeds and stance modifiers\n maxSpeedOverride: player1AccelerationBasedSpeed,\n accelerationOverride: player1SpeedModifiers.finalAcceleration,\n });\n\n // Player 1 rotation: face movement direction when moving, opponent when idle\n // 이동 중에는 이동 방향을, 정지 시에는 상대를 향함\n const player1Rotation = useMemo(() => {\n if (\n player1IsMoving &&\n player1Velocity &&\n (player1Velocity.x !== 0 || player1Velocity.y !== 0)\n ) {\n // When moving: face movement direction\n // velocity.x is lateral (left/right), velocity.y is forward/backward (Z in 3D)\n // Use velocity.y directly (not negated) so down arrow faces correctly\n const movementRotation = Math.atan2(player1Velocity.x, player1Velocity.y);\n return movementRotation;\n } else {\n // When idle: face opponent\n const dx = player2Position3D[0] - player1Position3D[0];\n const dz = player2Position3D[2] - player1Position3D[2];\n const targetRotation = Math.atan2(dx, dz);\n return targetRotation;\n }\n }, [player1IsMoving, player1Velocity, player1Position3D, player2Position3D]);\n\n // Use ref to store attack handler to avoid circular dependencies\n const handleAttackRef = useRef<(() => void) | null>(null);\n\n // Ref for player1Animation to avoid circular dependencies in animation events\n const player1AnimationRef = useRef<ReturnType<\n typeof usePlayerAnimation\n > | null>(null);\n\n // Ref for validPlayers to avoid circular dependencies\n const validPlayersRefForAnimation = useRef<[PlayerState, PlayerState] | null>(\n null,\n );\n\n // Tracks the frame at which the attack hit should fire (set per-technique)\n // and whether it has already been fired for the current attack\n const player1HitTriggerFrameRef = useRef<number>(6);\n const player1AttackHitFiredRef = useRef<boolean>(false);\n\n // Track attack animation durations for physics synchronization\n // 물리 동기화를 위한 공격 애니메이션 지속시간 추적\n // Use state so changes trigger re-render and propagate to useCombatAttackMovement\n // (previously these were refs but that meant the hook received stale values until\n // an unrelated re-render happened, and reading ref.current during render violated\n // react-hooks/refs purity rules)\n const [player1AttackDuration, setPlayer1AttackDuration] =\n useState<number>(0.55);\n const [player2AttackDuration, setPlayer2AttackDuration] =\n useState<number>(0.55);\n\n // Refs to clear attack animations after completion\n const clearPlayer1AttackAnimation = useRef<() => void>(() => {\n setPlayer1AttackAnimation(undefined);\n setPlayer1TechniqueId(undefined);\n });\n const clearPlayer2AttackAnimation = useRef<() => void>(() => {\n setPlayer2AttackAnimation(undefined);\n setPlayer2TechniqueId(undefined);\n });\n\n // Player animation state machines - manages animation transitions at 60fps\n // Memoize events object to maintain stability (required by usePlayerAnimation)\n const player1AnimationEvents = useMemo<AnimationEvents>(\n () => ({\n onFrame: (frame, state) => {\n // Execute attack when strike reaches peak extension\n // The hit trigger frame is set dynamically when attack starts\n // 공격 히트 트리거 프레임은 공격 시작 시 동적으로 설정됨\n if (\n state === AnimationState.ATTACK &&\n frame >= player1HitTriggerFrameRef.current &&\n !player1AttackHitFiredRef.current\n ) {\n player1AttackHitFiredRef.current = true;\n handleAttackRef.current?.();\n }\n },\n onAnimationComplete: (state) => {\n // Handle animation completion\n if (\n state === AnimationState.ATTACK ||\n state === AnimationState.DEFEND\n ) {\n combatActions.setExecutingTechnique(false);\n // Clear attack animation when attack completes\n // 공격 완료 시 공격 애니메이션 초기화\n if (state === AnimationState.ATTACK) {\n clearPlayer1AttackAnimation.current();\n }\n } else if (state === AnimationState.STANCE_CHANGE) {\n // Stance change animation completed - transition to stance guard\n // 자세 변경 완료 - 자세 가드로 전환\n audio.playSFX(\"menu_select\");\n const players = validPlayersRefForAnimation.current;\n const currentStance = players?.[0]?.currentStance;\n if (currentStance && player1AnimationRef.current) {\n player1AnimationRef.current.transitionToStanceGuard(currentStance);\n }\n }\n },\n }),\n [combatActions, audio],\n );\n\n const player1Animation = usePlayerAnimation({\n events: player1AnimationEvents,\n });\n\n // Store animation ref for use in event callbacks\n useEffect(() => {\n player1AnimationRef.current = player1Animation;\n }, [player1Animation]);\n\n const player2Animation = usePlayerAnimation({\n events: {\n onFrame: (frame, state) => {\n if (state === AnimationState.ATTACK && frame === 6) {\n // AI attack hit detection\n }\n },\n onAnimationComplete: (state) => {\n // Clear AI attack animation when attack completes\n // AI 공격 완료 시 공격 애니메이션 초기화\n if (state === AnimationState.ATTACK) {\n clearPlayer2AttackAnimation.current();\n }\n },\n },\n });\n\n // Sync movement with player 1 animation (avoid circular dependency)\n // 플레이어 1 이동-애니메이션 동기화\n const prevPlayer1IsMovingRef = useRef<boolean>(player1IsMoving);\n const prevPlayer1IsRunningRef = useRef<boolean>(player1IsRunning);\n useEffect(() => {\n // Check for movement or running state changes\n const movementChanged = prevPlayer1IsMovingRef.current !== player1IsMoving;\n const runningChanged = prevPlayer1IsRunningRef.current !== player1IsRunning;\n\n if (movementChanged || runningChanged) {\n if (player1IsMoving) {\n // Transition to RUN or WALK based on speed\n const targetState = player1IsRunning\n ? AnimationState.RUN\n : AnimationState.WALK;\n if (player1Animation.currentState !== targetState) {\n player1Animation.transitionTo(targetState);\n }\n } else if (\n player1Animation.currentState === AnimationState.WALK ||\n player1Animation.currentState === AnimationState.RUN\n ) {\n // When stopping movement, transition to stance-specific guard animation\n // 이동 중지 시 자세별 가드 애니메이션으로 전환\n player1Animation.transitionToStanceGuard(player1Data.currentStance);\n }\n prevPlayer1IsMovingRef.current = player1IsMoving;\n prevPlayer1IsRunningRef.current = player1IsRunning;\n }\n }, [\n player1IsMoving,\n player1IsRunning,\n player1Animation,\n player1Data.currentStance,\n ]);\n\n // Movement detection threshold for AI animation sync (in pixels)\n // Lower values = more sensitive to small movements, higher values = smoother transitions\n const MOVEMENT_DETECTION_THRESHOLD = 0.5;\n\n // Get player 2 stance for animation transitions\n // 플레이어 2 자세 (애니메이션 전환용)\n const player2Stance = useMemo(() => {\n return players[1]?.currentStance ?? TrigramStance.GEON;\n }, [players]);\n\n // Sync movement with player 2 animation (AI movement detection)\n const prevPlayer2PositionRef = useRef(player2Position);\n useEffect(() => {\n const currentPos = playerPositions[1];\n const prevPos = prevPlayer2PositionRef.current;\n\n // Detect if Player2 (AI) is moving by comparing positions\n const isMoving =\n Math.abs(currentPos.x - prevPos.x) > MOVEMENT_DETECTION_THRESHOLD ||\n Math.abs(currentPos.y - prevPos.y) > MOVEMENT_DETECTION_THRESHOLD;\n\n if (isMoving) {\n // AI is moving - transition to walk animation\n if (\n player2Animation.currentState !== AnimationState.WALK &&\n player2Animation.currentState !== AnimationState.ATTACK\n ) {\n player2Animation.transitionTo(AnimationState.WALK);\n }\n } else {\n // AI stopped - transition to stance-specific guard animation\n // AI 이동 중지 - 자세별 가드 애니메이션으로 전환\n if (player2Animation.currentState === AnimationState.WALK) {\n player2Animation.transitionToStanceGuard(player2Stance);\n }\n }\n\n prevPlayer2PositionRef.current = currentPos;\n }, [playerPositions, player2Animation, player2Stance]);\n\n // Valid players with complete state\n const validPlayers = useMemo((): [PlayerState, PlayerState] => {\n if (players.length === 0) {\n const player1 = createPlayerFromArchetype(PlayerArchetype.MUSA, 0);\n const player2 = createPlayerFromArchetype(PlayerArchetype.AMSALJA, 1);\n\n return [\n { ...player1, position: playerPositions[0] },\n { ...player2, position: playerPositions[1] },\n ];\n }\n\n const player1 = players[0];\n const player2 =\n players[1] ?? createPlayerFromArchetype(PlayerArchetype.AMSALJA, 1);\n\n return [\n { ...player1, position: playerPositions[0] },\n { ...player2, position: playerPositions[1] },\n ];\n }, [players, playerPositions]);\n\n // Use ref to access latest player health without causing re-renders\n const validPlayersRef = useRef<[PlayerState, PlayerState]>(validPlayers);\n useEffect(() => {\n validPlayersRef.current = validPlayers;\n validPlayersRefForAnimation.current = validPlayers;\n }, [validPlayers]);\n\n // Helper function to get AnimationType from technique ID\n // Uses enum-based mapping for type safety and proper technique support\n // 기술 ID에서 AnimationType을 가져오는 헬퍼 함수\n // 타입 안전성과 적절한 기술 지원을 위해 열거형 기반 매핑 사용\n const getTechniqueAnimationType = useCallback(\n (techniqueId: string | undefined): AnimationType | undefined => {\n if (!techniqueId) return undefined;\n\n // Use enum-based lookup for all techniques\n // This properly handles stance-specific techniques like \"geon_heaven_strike\"\n // 모든 기술에 대해 열거형 기반 조회 사용\n // \"geon_heaven_strike\"와 같은 자세별 기술을 올바르게 처리\n return getAnimationTypeForTechnique(techniqueId);\n },\n [],\n );\n\n // Attack movement integration - track attack states for physics-based forward movement\n // 공격 이동 통합 - 물리 기반 전방 이동을 위한 공격 상태 추적\n // Note: Must be called after validPlayers, player animations, and 3D positions are defined\n const {\n player1Position: player1PositionWithAttackMovement,\n player2Position: player2PositionWithAttackMovement,\n } = useCombatAttackMovement({\n player1Attacking: player1Animation.currentState === AnimationState.ATTACK,\n player1AnimationType: getTechniqueAnimationType(player1TechniqueId),\n player1Stance: player1Data.currentStance,\n player1BasePosition: player1Position3D,\n player1AnimationDuration: player1AttackDuration,\n player2Attacking: player2Animation.currentState === AnimationState.ATTACK,\n player2AnimationType: getTechniqueAnimationType(player2TechniqueId),\n player2Stance: validPlayers[1].currentStance,\n player2BasePosition: player2Position3D,\n player2AnimationDuration: player2AttackDuration,\n });\n\n // ═══════════════════════════════════════════════════════════════════════════\n // Local stance state for Player 1 - Enables immediate technique bar updates\n // ═══════════════════════════════════════════════════════════════════════════\n // The player stance from props requires a round-trip through App.tsx, causing\n // the technique bar to update with a delay. This local state updates immediately\n // when stance changes, then syncs with the parent state.\n // (이 로컬 상태는 자세 변경 시 즉시 업데이트되어 기술바가 바로 반응함)\n const [player1LocalStance, setPlayer1LocalStance] = useState<TrigramStance>(\n validPlayers[0].currentStance,\n );\n\n // Sync local stance with prop when it changes (e.g., on round restart)\n useEffect(() => {\n setPlayer1LocalStance(validPlayers[0].currentStance);\n }, [validPlayers]);\n\n // Create player object with local stance for immediate technique bar updates\n // This ensures useTechniqueSelection gets the updated stance synchronously\n const player1WithLocalStance = useMemo(\n (): PlayerState => ({\n ...validPlayers[0],\n currentStance: player1LocalStance,\n }),\n [validPlayers, player1LocalStance],\n );\n\n // Use refs for stable access to startTransition and internalRound\n const startTransitionRef = useRef(startTransition);\n const internalRoundRef = useRef(internalRound);\n useEffect(() => {\n startTransitionRef.current = startTransition;\n internalRoundRef.current = internalRound;\n }, [startTransition, internalRound]);\n\n // Combat messages\n const addCombatMessage = useCallback(\n (korean: string, english: string) => {\n const message = `${korean} | ${english}`;\n combatActions.addCombatMessage(message);\n },\n [combatActions],\n );\n\n // Combat timer with warnings and time up handler\n const handleTimeUp = useCallback(() => {\n // End round when time runs out\n if (!combatState.roundEnded) {\n combatActions.setRoundEnded(true);\n addCombatMessage(\"시간 종료!\", \"Time's Up!\");\n\n // Use refs to get latest values without dependency issues\n const currentPlayers = validPlayersRef.current;\n const player1Health = currentPlayers[0].health;\n const player2Health = currentPlayers[1].health;\n\n if (player1Health > player2Health) {\n // Player 1 wins - update match score\n updateMatchScore(0);\n startTransitionRef.current(currentPlayers[0], internalRoundRef.current); // Player 1 wins round\n } else if (player2Health > player1Health) {\n // Player 2 wins - update match score\n updateMatchScore(1);\n startTransitionRef.current(currentPlayers[1], internalRoundRef.current); // Player 2 wins round\n } else {\n // Tie - no winner for this round, no score update\n startTransitionRef.current(null, internalRoundRef.current);\n }\n }\n }, [\n combatState.roundEnded,\n combatActions,\n addCombatMessage,\n updateMatchScore,\n ]);\n\n // Ref pattern to stabilize onTimeUp callback for timer\n const handleTimeUpRef = useRef(handleTimeUp);\n useEffect(() => {\n handleTimeUpRef.current = handleTimeUp;\n }, [handleTimeUp]);\n\n // Create a unique key for timer reset based on round number\n // This forces the timer to reset when a new round starts\n const timerResetKey = `round-${internalRound}`;\n\n const timerState = useCombatTimer({\n initialTime: Math.max(0, timeRemaining),\n isPaused:\n isPaused ||\n !combatState.roundStarted ||\n combatState.roundEnded ||\n !matchCountdownComplete ||\n showRoundStart,\n onTimeUp: useCallback(() => handleTimeUpRef.current(), []),\n warningThreshold: 10,\n urgentThreshold: 5,\n // Pass round number to force timer reset on new rounds\n resetKey: timerResetKey,\n });\n\n // Shared round start logic - forces round to start regardless of current state\n // This is called after state has been reset, so we trust the caller\n // Note: Using useCallback with complete dependencies to ensure closure has latest state\n const startRound = useCallback(() => {\n // Always start the round when this is called - the caller is responsible\n // for ensuring this is the right time to start\n if (import.meta.env.DEV) {\n console.log(\"[DEV] Starting round, setting roundStarted=true\");\n }\n combatActions.setRoundStarted(true);\n combatActions.setRoundEnded(false); // Ensure roundEnded is false\n addCombatMessage(\"라운드 시작!\", \"Round Start!\");\n\n const player = validPlayers[0];\n if (player?.archetype) {\n const playerArchetype = player.archetype.toLowerCase();\n combatAudio.playArchetypeMusic(playerArchetype, 2000);\n } else {\n combatAudio.playCombatMusic(2000);\n }\n }, [combatActions, addCombatMessage, validPlayers, combatAudio]);\n\n // Auto-start first round since countdown is disabled\n // Use a separate ref for the timer to avoid cleanup canceling the start\n const hasAutoStartedRef = useRef(false);\n\n useEffect(() => {\n if (matchCountdownComplete && !hasAutoStartedRef.current) {\n hasAutoStartedRef.current = true;\n // Directly set roundStarted=true without going through startRound callback\n // This avoids dependency issues with the callback reference\n combatActions.setRoundStarted(true);\n addCombatMessage(\"라운드 시작!\", \"Round Start!\");\n\n // Start music\n const player = validPlayers[0];\n if (player?.archetype) {\n const playerArchetype = player.archetype.toLowerCase();\n combatAudio.playArchetypeMusic(playerArchetype, 2000);\n } else {\n combatAudio.playCombatMusic(2000);\n }\n }\n }, [\n matchCountdownComplete,\n combatActions,\n addCombatMessage,\n validPlayers,\n combatAudio,\n ]);\n\n // AI systems\n const adaptiveDifficulty = useMemo(() => new AdaptiveDifficulty(), []);\n\n // Persist AI difficulty metrics\n useEffect(() => {\n try {\n const savedMetrics = localStorage.getItem(\"ai_difficulty_metrics\");\n if (savedMetrics) {\n adaptiveDifficulty.importMetrics(savedMetrics);\n }\n } catch (err) {\n console.warn(\"Failed to load AI difficulty metrics:\", err);\n }\n\n return () => {\n try {\n const metrics = adaptiveDifficulty.exportMetrics();\n localStorage.setItem(\"ai_difficulty_metrics\", metrics);\n } catch (err) {\n console.warn(\"Failed to save AI difficulty metrics:\", err);\n }\n };\n }, [adaptiveDifficulty]);\n\n const aiPersonality = useMemo(\n () => getPersonalityByArchetype(validPlayers[1].archetype),\n [validPlayers],\n );\n\n // AI stance change handler\n const handleAIStanceChange = useCallback(\n (stance: TrigramStance) => {\n const currentStance = validPlayers[1].currentStance;\n\n // Start stance-specific transition animation for AI\n player2Animation.transitionToStanceChange(currentStance, stance);\n\n onPlayerUpdate(1, { currentStance: stance });\n addCombatMessage(\n `AI 자세 변경: ${stance}`,\n `AI Stance Change: ${stance}`,\n );\n },\n [validPlayers, player2Animation, onPlayerUpdate, addCombatMessage],\n );\n\n // Hit effect handlers\n const handleEffectComplete = useCallback(\n (effectId: string) => {\n combatActions.removeHitEffect(effectId);\n },\n [combatActions],\n );\n\n const createHitEffect = useCallback(\n (\n id: string,\n type: HitEffectType,\n position: Position,\n intensity: number,\n ): HitEffect => ({\n id,\n type,\n attackerId: \"player1\",\n defenderId: \"player2\",\n timestamp: Date.now(),\n duration: 1000,\n position,\n intensity,\n startTime: Date.now(),\n }),\n [],\n );\n\n const addHitEffect = useCallback(\n (type: HitEffectType, position: Position, intensity: number = 1) => {\n const effect = createHitEffect(\n `effect_${Date.now()}`,\n type,\n position,\n intensity,\n );\n combatActions.addHitEffect(effect);\n },\n [createHitEffect, combatActions],\n );\n\n // Callback for updating player positions after knockback\n const handlePlayerPositionUpdate = useCallback(\n (playerIndex: number, position: Position) => {\n if (playerIndex === 0) {\n setPlayer1Position(position);\n onPlayerUpdate(0, { position });\n } else if (playerIndex === 1) {\n // For AI, update position through onPlayerUpdate\n // This will be reflected in player2Position which is derived from players prop\n onPlayerUpdate(1, { position });\n }\n },\n [onPlayerUpdate, setPlayer1Position],\n );\n\n // Callback for creating injuries from combat damage with progressive bruising\n // 전투 피해로부터 부상 생성 콜백 (점진적 타박상 포함)\n const handleInjuryCreated = useCallback(\n (injury: Injury, targetPlayerIndex: number) => {\n const updateInjuries = (prev: readonly Injury[]): readonly Injury[] => {\n // Check for recent hits to the same body region (within 5 seconds)\n const recentHitTime = 5000; // 5 seconds\n const now = Date.now();\n\n const recentHit = prev.find(\n (existing) =>\n existing.region === injury.region &&\n now - existing.timestamp < recentHitTime &&\n existing.type === InjuryType.BRUISE &&\n injury.type === InjuryType.BRUISE,\n );\n\n if (recentHit) {\n // Progressive bruising: increase hit count and severity\n const newHitCount = recentHit.hitCount + 1;\n const escalatedSeverity = Math.min(1.0, recentHit.severity + 0.15);\n\n // Update the existing injury with increased severity and hit count\n // Only update existing injury, do not add a duplicate\n return prev.map((existing) =>\n existing.id === recentHit.id\n ? {\n ...existing,\n hitCount: newHitCount,\n severity: escalatedSeverity,\n timestamp: now, // Update timestamp for progressive tracking\n }\n : existing,\n );\n }\n\n // No recent hit to same region - add as new injury\n return [...prev, injury];\n };\n\n if (targetPlayerIndex === 0) {\n setPlayer1Injuries(updateInjuries);\n } else if (targetPlayerIndex === 1) {\n setPlayer2Injuries(updateInjuries);\n }\n },\n [],\n );\n\n // Combat action handlers\n const {\n handleAttack,\n handleDefend,\n handleStanceSwitch,\n handleStanceSideSwitch,\n handleAIAttack,\n handleAIDefend,\n handleAITechnique,\n moveAIPlayer,\n } = useCombatActions({\n validPlayers,\n playerPositions: [playerPositions[0], playerPositions[1]],\n combatState,\n combatActions,\n combatSystem,\n onPlayerUpdate,\n onPlayerPositionUpdate: handlePlayerPositionUpdate,\n onLateralityUpdate: (playerIndex, laterality) => {\n combatActions.setPlayerLateralityIndex(playerIndex as 0 | 1, laterality);\n },\n onInjuryCreated: handleInjuryCreated,\n addCombatMessage,\n addHitEffect,\n arenaBounds,\n combatAudio,\n playerAnimations: {\n player1: player1Animation,\n player2: player2Animation,\n },\n });\n\n // Store handleAttack in ref for animation callback (avoid circular dependency)\n useEffect(() => {\n handleAttackRef.current = handleAttack;\n }, [handleAttack]);\n\n // Technique selection and execution\n // Uses player1WithLocalStance for immediate technique bar updates when stance changes\n // (자세 변경 시 기술바가 즉시 업데이트되도록 로컬 자세 상태 사용)\n const techniqueSelection = useTechniqueSelection({\n player: player1WithLocalStance,\n enabled:\n !isPaused &&\n combatState.roundStarted &&\n !combatState.roundEnded &&\n matchCountdownComplete &&\n !showRoundStart,\n onTechniqueExecute: useCallback(\n (technique: Technique) => {\n // Show technique name in action feedback\n feedbackActions.showTechnique(\n technique.name.korean,\n technique.name.english,\n );\n\n // Set attack animation based on technique\n // 기술에 따른 공격 애니메이션 설정\n // Uses resolveTechniqueAnimation so that stance-specific animations\n // (e.g., \"geon_heaven_strike\") are preferred over the regex fallback\n // that previously collapsed every attack to the jab animation.\n const animationName = resolveTechniqueAnimation(technique);\n setPlayer1AttackAnimation(animationName);\n\n // Store technique ID for enum-based AnimationType lookup\n // 열거형 기반 AnimationType 조회를 위한 기술 ID 저장\n setPlayer1TechniqueId(technique.id);\n\n // Get the actual skeletal animation duration for this technique\n // so the state machine stays in ATTACK for the full animation.\n // 기술의 실제 스켈레탈 애니메이션 지속시간을 가져와서\n // 상태 머신이 전체 애니메이션 동안 ATTACK 상태를 유지하도록 함\n const skeletalAnim = getAnimation(animationName);\n const attackDuration = skeletalAnim?.duration ?? 0.55;\n\n // Trigger attack animation transition with technique-matched duration\n // 기술 매칭 지속시간으로 공격 애니메이션 전환 트리거\n const attackFrames = Math.max(1, Math.round(attackDuration * 60));\n // Trigger hit at ~40% of the attack (when strike reaches peak extension)\n player1HitTriggerFrameRef.current = Math.round(attackFrames * 0.4);\n player1AttackHitFiredRef.current = false;\n // Store duration for physics hook\n setPlayer1AttackDuration(attackDuration);\n player1Animation.transitionToAttack(attackDuration);\n combatActions.setExecutingTechnique(true);\n\n // Deduct resources\n onPlayerUpdate(0, {\n stamina: Math.max(0, validPlayers[0].stamina - technique.staminaCost),\n ki: Math.max(0, validPlayers[0].ki - technique.kiCost),\n });\n\n // Execute attack WITH the selected technique (not basic attack)\n handleAttack(technique);\n\n // Add combat message\n addCombatMessage(\n `${technique.name.korean} 사용!`,\n `Used ${technique.name.english}!`,\n );\n },\n [\n validPlayers,\n onPlayerUpdate,\n feedbackActions,\n handleAttack,\n addCombatMessage,\n player1Animation,\n combatActions,\n ],\n ),\n });\n\n // Convert cooldowns to Map for TechniqueBar\n const cooldownsMap = useMemo(() => {\n const map = new Map<string, number>();\n techniqueSelection.activeCooldowns.forEach((cd) => {\n map.set(cd.techniqueId, cd.remaining);\n });\n return map;\n }, [techniqueSelection.activeCooldowns]);\n\n // Track previous stance for visual feedback\n const [previousStance, setPreviousStance] = useState<number>(0);\n\n // Get current stance index for visual feedback using optimized lookup\n const currentPlayerStance = validPlayers[0].currentStance;\n const currentStanceIndex = useMemo(() => {\n return STANCE_INDEX_MAP.get(currentPlayerStance) ?? 0;\n }, [currentPlayerStance]);\n\n // Handler for stance changes with animation integration\n const handleStanceChangeWithAnimation = useCallback(\n (newStance: TrigramStance) => {\n const currentStance = validPlayers[0].currentStance;\n\n // Transition directly to stance guard (skip transitional animation)\n // The transitional STANCE_CHANGE animation uses idle_stance which loops forever\n // and never completes, so we go directly to the target stance guard\n const success = player1Animation.transitionToStanceGuard(newStance);\n\n if (success) {\n // Capture previous stance for visual feedback\n const prevStance = STANCE_INDEX_MAP.get(currentStance) ?? 0;\n setPreviousStance(prevStance);\n\n // Update local stance immediately for instant technique bar update\n // This ensures the technique bar updates before the prop round-trip completes\n // (기술바가 즉시 업데이트되도록 로컬 자세 상태를 먼저 변경)\n setPlayer1LocalStance(newStance);\n\n // Update combat state (triggers prop update through App.tsx)\n handleStanceSwitch(newStance);\n\n // Play stance change sound\n playStanceChangeSound();\n }\n },\n [validPlayers, player1Animation, handleStanceSwitch, playStanceChangeSound],\n );\n\n // Extract player health values for dependency arrays\n const player1Health = validPlayers[0].health;\n const player2Health = validPlayers[1].health;\n\n // Watch for player 2 health decrease to trigger damage feedback\n const lastPlayer2HealthRef = useRef(player2Health);\n useEffect(() => {\n const currentHealth = player2Health;\n const previousHealth = lastPlayer2HealthRef.current;\n const damageDone = previousHealth - currentHealth;\n\n if (damageDone > 0 && combatState.roundStarted && !combatState.roundEnded) {\n // Determine damage type based on amount\n const getDamageType = (): \"critical\" | \"vital\" | \"normal\" => {\n if (damageDone >= 25) return \"critical\";\n if (damageDone >= 20) return \"vital\";\n return \"normal\";\n };\n const damageType = getDamageType();\n\n // Add damage number at opponent position\n feedbackActions.addDamageNumber(\n Math.round(damageDone),\n playerPositions[1],\n damageType,\n );\n\n // Increment combo\n feedbackActions.incrementCombo();\n\n // Add action feedback for critical hits\n if (damageType === \"critical\") {\n feedbackActions.addActionFeedback(\n \"critical\",\n \"Critical!\",\n \"치명타!\",\n playerPositions[0],\n );\n }\n }\n\n lastPlayer2HealthRef.current = currentHealth;\n }, [\n player2Health,\n validPlayers,\n playerPositions,\n feedbackActions,\n combatState.roundStarted,\n combatState.roundEnded,\n ]);\n\n // Watch for player 1 health decrease (AI attacks player)\n const lastPlayer1HealthRef = useRef(player1Health);\n useEffect(() => {\n const currentHealth = player1Health;\n const previousHealth = lastPlayer1HealthRef.current;\n const damageDone = previousHealth - currentHealth;\n\n if (damageDone > 0 && combatState.roundStarted && !combatState.roundEnded) {\n // Add damage number at player position for AI hits\n const damageType =\n damageDone >= 20 ? (\"critical\" as const) : (\"normal\" as const);\n feedbackActions.addDamageNumber(\n Math.round(damageDone),\n playerPositions[0],\n damageType,\n );\n }\n\n lastPlayer1HealthRef.current = currentHealth;\n }, [\n player1Health,\n validPlayers,\n playerPositions,\n feedbackActions,\n combatState.roundStarted,\n combatState.roundEnded,\n ]);\n\n // Create enhanced attack handler with action feedback and animation\n const handleAttackWithFeedback = useCallback(() => {\n // For the \"basic\" attack (space bar / on-screen Attack button) we use\n // the first technique available in the player's current stance so the\n // skeletal animation matches the stance (e.g., Geon → \"geon_heaven_strike\",\n // Li → \"li_precision_jab\"). This prevents every stance from producing\n // the same jab visual.\n // 기본 공격 애니메이션 설정 - 현재 자세의 첫 번째 기술 사용\n const basicTechnique = techniqueSelection.availableTechniques[0];\n const animationName = basicTechnique\n ? resolveTechniqueAnimation(basicTechnique)\n : \"jab\";\n setPlayer1AttackAnimation(animationName);\n if (basicTechnique?.id) {\n setPlayer1TechniqueId(basicTechnique.id);\n }\n\n // Try to transition to attack animation\n const success = player1Animation.transitionTo(AnimationState.ATTACK);\n if (success) {\n // Attack execution will happen at frame 6 in onFrame callback\n combatActions.setExecutingTechnique(true);\n } else {\n // Fallback: animation transition failed, execute attack logic immediately\n console.warn(\n \"Attack animation transition failed; executing attack logic directly.\",\n );\n handleAttack();\n }\n }, [\n player1Animation,\n combatActions,\n handleAttack,\n techniqueSelection.availableTechniques,\n ]);\n\n // Create enhanced defend handler with action feedback and animation\n const handleDefendWithFeedback = useCallback(() => {\n const defenderPos = playerPositions[0];\n // Try to transition to defend animation\n const success = player1Animation.transitionTo(AnimationState.DEFEND);\n if (success) {\n handleDefend();\n feedbackActions.addActionFeedback(\n \"blocked\",\n \"Blocked\",\n \"방어!\",\n defenderPos,\n );\n } else {\n // Fallback: animation transition failed, execute defend logic immediately\n console.warn(\n \"Defend animation transition failed; executing defend logic directly.\",\n );\n handleDefend();\n feedbackActions.addActionFeedback(\n \"blocked\",\n \"Blocked\",\n \"방어!\",\n defenderPos,\n );\n }\n }, [handleDefend, playerPositions, feedbackActions, player1Animation]);\n\n /**\n * Helper function to execute fallback recovery animation\n * when a specific recovery type cannot be performed.\n *\n * Determines the appropriate recovery type based on ground state\n * and transitions to that animation.\n *\n * @korean 대체회복실행\n */\n const executeFallbackRecovery = useCallback(() => {\n const groundState = balanceSystem.getGroundState(\n player1Animation.currentState,\n );\n if (groundState) {\n const recoveryType = determineRecoveryType(groundState);\n const animationState = getRecoveryAnimationState(recoveryType);\n player1Animation.transitionTo(animationState as AnimationState);\n }\n }, [balanceSystem, player1Animation]);\n\n // Use keyboard controls hook for enhanced input handling with visual feedback\n const { queuedInputs, showHints } = useKeyboardControls({\n onStanceChange: useCallback(\n (stanceIndex: number) => {\n const stance = TRIGRAM_STANCES_ORDER[stanceIndex];\n if (stance) {\n // Use integrated stance transition animation\n handleStanceChangeWithAnimation(stance);\n }\n },\n [handleStanceChangeWithAnimation],\n ),\n onAction: useCallback(\n (action: string) => {\n switch (action) {\n case \"attack\":\n // Execute currently selected technique via technique selection system\n techniqueSelection.executeTechnique();\n break;\n case \"block\":\n handleDefendWithFeedback();\n break;\n // Recovery actions (기상 액션)\n case \"recovery_quick\": {\n // Quick recovery: determine type based on ground state\n executeFallbackRecovery();\n break;\n }\n case \"recovery_roll\": {\n // Roll recovery: costs stamina but fastest\n const player1 = players[0];\n if (balanceSystem.canRecoverWithType(player1, \"roll_recovery\")) {\n const updatedPlayer = balanceSystem.applyRecoveryCost(\n player1,\n \"roll_recovery\",\n );\n onPlayerUpdate(0, { stamina: updatedPlayer.stamina });\n player1Animation.transitionTo(AnimationState.RECOVERY_ROLL);\n } else {\n // Not enough stamina, fallback to quick recovery\n audio.playSFX(\"menu_error\");\n const player1Pos = playerPositions[0];\n feedbackActions.addActionFeedback(\n \"blocked\",\n \"Not enough stamina!\",\n \"체력 부족!\",\n player1Pos,\n );\n executeFallbackRecovery();\n }\n break;\n }\n case \"recovery_defensive\": {\n // Defensive getup: slow but protected\n player1Animation.transitionTo(AnimationState.RECOVERY_DEFENSIVE);\n break;\n }\n\n // Footwork pattern actions\n case \"footwork_circular_left\":\n case \"footwork_circular_right\":\n case \"footwork_slide_forward\":\n case \"footwork_slide_back\":\n case \"footwork_pivot_left\":\n case \"footwork_pivot_right\":\n case \"footwork_shuffle\":\n // Execute footwork animation\n player1Animation.transitionTo(action as AnimationState);\n break;\n\n // Stance side switch\n case \"stance_side_switch\":\n // Switch front foot (mirror stance)\n player1Animation.transitionTo(AnimationState.STANCE_SIDE_SWITCH);\n break;\n\n // Movement and other actions handled by existing system\n }\n },\n [\n techniqueSelection,\n handleDefendWithFeedback,\n executeFallbackRecovery,\n balanceSystem,\n player1Animation,\n players,\n onPlayerUpdate,\n audio,\n feedbackActions,\n playerPositions,\n ],\n ),\n enabled:\n !isPaused &&\n !showPauseMenu &&\n combatState.roundStarted &&\n !combatState.roundEnded &&\n matchCountdownComplete &&\n !showRoundStart &&\n !combatState.isExecutingTechnique,\n currentStance: currentStanceIndex,\n playSFX: audio.playSFX,\n currentAnimationState: player1Animation.currentState,\n });\n\n // Mobile touch control state - stance wheel for mobile stance changes\n const [stanceWheelExpanded, setStanceWheelExpanded] = useState(false);\n const activeMobileKeyRef = useRef<string | null>(null);\n\n /**\n * Mobile touch control handler - Converts VirtualDPad touch inputs to keyboard events\n *\n * Dispatches synthetic KeyboardEvents with proper properties to ensure compatibility\n * with usePlayerMovement hook. The synthetic events include:\n * - key: The character key (w/a/s/d)\n * - code: The physical key code (KeyW/KeyA/KeyS/KeyD)\n * - bubbles: true - Allows event to propagate through DOM\n * - cancelable: true - Allows event to be prevented\n *\n * These properties are essential for the keyboard event listeners in inputSystem.ts\n * to properly recognize and process the movement commands.\n *\n * @param direction - The D-pad direction or null\n * @param eventType - 'start' for press, 'end' for release\n */\n const handleMobileMove = useCallback(\n (direction: Direction | null, eventType: DPadEventType) => {\n // Map D-pad directions to movement keys (WASD)\n const directionMap: Record<Direction, string> = {\n up: \"w\",\n \"up-right\": \"w\", // Diagonal simplified to primary direction\n right: \"d\",\n \"down-right\": \"s\",\n down: \"s\",\n \"down-left\": \"s\",\n left: \"a\",\n \"up-left\": \"w\",\n };\n\n if (eventType === \"start\" && direction) {\n // Release previous key if different (prevents stuck keys)\n if (\n activeMobileKeyRef.current &&\n activeMobileKeyRef.current !== directionMap[direction]\n ) {\n const prevKey = activeMobileKeyRef.current;\n window.dispatchEvent(\n new KeyboardEvent(\"keyup\", {\n key: prevKey,\n code: `Key${prevKey.toUpperCase()}`,\n bubbles: true,\n cancelable: true,\n }),\n );\n }\n\n // Press new key with proper keyboard event properties\n // These properties ensure usePlayerMovement recognizes the synthetic event\n const key = directionMap[direction];\n activeMobileKeyRef.current = key;\n window.dispatchEvent(\n new KeyboardEvent(\"keydown\", {\n key,\n code: `Key${key.toUpperCase()}`,\n bubbles: true,\n cancelable: true,\n }),\n );\n } else if (eventType === \"end\") {\n // Release active key when D-pad released\n if (activeMobileKeyRef.current) {\n const key = activeMobileKeyRef.current;\n window.dispatchEvent(\n new KeyboardEvent(\"keyup\", {\n key,\n code: `Key${key.toUpperCase()}`,\n bubbles: true,\n cancelable: true,\n }),\n );\n activeMobileKeyRef.current = null;\n }\n }\n },\n [],\n );\n\n const handleMobileAttack = useCallback(() => {\n // Execute currently selected technique via technique selection system\n techniqueSelection.executeTechnique();\n }, [techniqueSelection]);\n\n const handleMobileBlock = useCallback(\n (eventType: ButtonEventType) => {\n if (eventType === \"start\") {\n handleDefendWithFeedback();\n }\n },\n [handleDefendWithFeedback],\n );\n\n // Mobile stance wheel and gesture controls\n const handleMobileStanceChange = useCallback(\n (stanceIndex: number) => {\n const stance = TRIGRAM_STANCES_ORDER[stanceIndex];\n if (stance) {\n // Use integrated stance transition animation\n handleStanceChangeWithAnimation(stance);\n }\n },\n [handleStanceChangeWithAnimation],\n );\n\n const handleMobileGesture = useCallback(\n (gesture: GestureEvent) => {\n switch (gesture.type) {\n case \"swipe-right\":\n // Advance toward opponent - dispatch keydown then keyup to prevent stuck movement\n window.dispatchEvent(new KeyboardEvent(\"keydown\", { key: \"d\" }));\n setTimeout(() => {\n window.dispatchEvent(new KeyboardEvent(\"keyup\", { key: \"d\" }));\n }, 100);\n break;\n case \"swipe-left\":\n // Retreat from opponent - dispatch keydown then keyup to prevent stuck movement\n window.dispatchEvent(new KeyboardEvent(\"keydown\", { key: \"a\" }));\n setTimeout(() => {\n window.dispatchEvent(new KeyboardEvent(\"keyup\", { key: \"a\" }));\n }, 100);\n break;\n case \"swipe-up\":\n // High attack mode - execute selected technique\n techniqueSelection.executeTechnique();\n break;\n case \"swipe-down\":\n // Low attack mode - execute selected technique\n techniqueSelection.executeTechnique();\n break;\n case \"two-finger-tap\":\n // Activate vital point targeting mode\n // TODO: Implement vital point mode toggle\n audio.playSFX(\"menu_select\");\n break;\n }\n },\n [techniqueSelection, audio],\n );\n\n const toggleStanceWheel = useCallback(() => {\n setStanceWheelExpanded((prev) => !prev);\n }, []);\n\n // Check if mobile controls should be enabled\n const mobileControlsEnabled =\n isMobile &&\n !isPaused &&\n !showPauseMenu &&\n combatState.roundStarted &&\n !combatState.roundEnded &&\n matchCountdownComplete &&\n !showRoundStart &&\n !combatState.isExecutingTechnique;\n\n // Note: Player 1 position is updated via the onPositionChange callback\n // in usePlayerMovement config above, not via useEffect\n\n // Round management\n useEffect(() => {\n if (isPaused) return;\n\n if (timeRemaining <= 0 && !combatState.roundEnded) {\n combatActions.setRoundEnded(true);\n combatActions.setRoundStarted(false);\n combatActions.setRoundDisplayStatus(\"end\");\n\n combatAudio.stopCombatMusic(1000);\n\n const winner = validPlayers[0].health > validPlayers[1].health ? 0 : 1;\n const roundWinner = validPlayers[winner];\n\n // Update match score using deferred callback\n updateMatchScore(winner);\n\n addCombatMessage(\"라운드 종료!\", \"Round Over!\");\n\n // Start round transition instead of immediately ending game\n setTimeout(() => {\n startTransition(roundWinner, internalRound);\n }, 1500);\n }\n // Note: Round start is now triggered by MatchCountdown and RoundStartAnnouncement components\n // via their onComplete callbacks to ensure proper sequencing with countdown animations\n }, [\n timeRemaining,\n combatState.roundEnded,\n combatState.roundStarted,\n validPlayers,\n onGameEnd,\n addCombatMessage,\n internalRound,\n isPaused,\n combatActions,\n combatAudio,\n startTransition,\n updateMatchScore,\n ]);\n\n // Create a ref for the callback to avoid circular dependency\n const executeAIActionCallbackRef = useRef<\n | ((\n action: string,\n targetPos?: Position,\n selectedTechnique?: KoreanTechnique,\n targetVitalPoint?: string,\n ) => void)\n | undefined\n >(undefined);\n\n // AI Combat System - connects AI decisions to executeAIActionCallbackRef via onExecuteAction (action/technique/vital point params)\n const { updateDifficultyTarget } = useAICombat({\n player: validPlayers[1],\n opponent: validPlayers[0],\n personality: aiPersonality,\n adaptiveDifficulty,\n isPaused,\n roundStarted: combatState.roundStarted,\n roundEnded: combatState.roundEnded,\n arenaBounds,\n onExecuteAction: (action, targetPos, selectedTechnique, targetVitalPoint) =>\n executeAIActionCallbackRef.current?.(\n action,\n targetPos,\n selectedTechnique,\n targetVitalPoint,\n ),\n onStanceChange: handleAIStanceChange,\n onLateralityChange: () => handleStanceSideSwitch(1), // AI player (index 1)\n playerLaterality: combatState.playerLaterality[1], // AI's own laterality\n opponentLaterality: combatState.playerLaterality[0], // Opponent (human) laterality\n });\n\n // Calculate current difficulty tier for display\n // Force recalculation when the adaptive difficulty system changes\n const currentDifficultyTier = useMemo(\n () => adaptiveDifficulty.getDifficultyTier(),\n [adaptiveDifficulty],\n );\n\n // Adaptive difficulty adjustment every 2-3 rounds after round ends\n useEffect(() => {\n // Only adjust after round ends (not during round or at start)\n if (!combatState.roundEnded || internalRound < 1) {\n return;\n }\n\n // Adjust every 2 rounds (after rounds 2, 4, 6, etc.)\n // This ensures at least 2 rounds between adjustments for smooth progression\n const roundsCompleted = internalRound;\n if (roundsCompleted % 2 === 0) {\n // Update AI skill metrics based on player performance.\n // NOTE (Phase 1 adaptive difficulty):\n // - Only hits/attacks/damage metrics are currently sourced from PlayerState.\n // - Combo, block, reaction-time, vital-point, and stance-change metrics are\n // intentionally stubbed here (0 or constant) until PlayerState and telemetry\n // are extended in a follow-up phase.\n // This keeps the system functional while we iterate on richer skill tracking.\n const player1 = validPlayersRef.current[0];\n\n adaptiveDifficulty.updateSkillMetrics({\n hitsLanded: player1.hitsLanded ?? 0,\n totalAttacks: (player1.hitsLanded ?? 0) + (player1.misses ?? 0),\n combosExecuted: 0, // TODO (Phase 2): Track combo count in PlayerState\n perfectBlockCount: 0, // TODO (Phase 2): Track perfect blocks in PlayerState\n avgReactionTimeMs: 600, // TODO (Phase 2): Track player reaction time\n vitalPointsHit: 0, // TODO (Phase 2): Track vital point hits\n effectiveStanceChanges: 0, // TODO (Phase 2): Track stance changes\n damageDealt: player1.totalDamageDealt ?? 0,\n damageTaken: player1.totalDamageReceived ?? 0,\n });\n\n // Get new difficulty parameters and update AI\n const newParams = adaptiveDifficulty.getDifficultyParameters();\n updateDifficultyTarget(newParams);\n\n if (import.meta.env.DEV) {\n const tier = adaptiveDifficulty.getDifficultyTier();\n console.log(\n `[DEV] Difficulty adjusted after round ${roundsCompleted}, new tier: ${tier}`,\n );\n }\n }\n }, [\n combatState.roundEnded,\n internalRound,\n adaptiveDifficulty,\n updateDifficultyTarget,\n ]);\n\n // AI action execution - receives technique and vital point directly\n const executeAIActionCallback = useCallback(\n (\n action: string,\n targetPos?: Position,\n selectedTechnique?: KoreanTechnique,\n targetVitalPoint?: string,\n ) => {\n // Resolve AI's fallback animation from its current stance so that\n // a stance-less AI decision still plays a stance-appropriate visual\n // instead of the legacy \"jab\"/\"cross\" literals that made every AI\n // action look identical. Defensive chaining covers the (expected-\n // unreachable) cases where `validPlayers[1]` is missing or where a\n // stance has no techniques registered yet — in both cases we fall\n // through to the literal \"jab\" as the absolute last resort.\n // AI의 현재 자세에서 기본 애니메이션을 도출\n const aiStance = validPlayers[1]?.currentStance ?? TrigramStance.GEON;\n const aiFallbackTechnique = TRIGRAM_TECHNIQUES[aiStance]?.[0];\n const aiFallbackAnim = aiFallbackTechnique\n ? resolveTechniqueAnimation(aiFallbackTechnique)\n : \"jab\";\n switch (action) {\n case \"attack\":\n // Set AI attack animation based on technique\n // AI 공격 애니메이션 설정\n if (selectedTechnique) {\n const p2AttackAnimName = resolveTechniqueAnimation(selectedTechnique);\n setPlayer2AttackAnimation(p2AttackAnimName);\n\n // Store technique ID for enum-based AnimationType lookup\n // 열거형 기반 AnimationType 조회를 위한 기술 ID 저장\n if (selectedTechnique.id) {\n setPlayer2TechniqueId(selectedTechnique.id);\n }\n const p2AttackAnim = getAnimation(p2AttackAnimName);\n const p2AttackDur = p2AttackAnim?.duration ?? 0.55;\n setPlayer2AttackDuration(p2AttackDur);\n player2Animation.transitionToAttack(p2AttackDur);\n } else {\n setPlayer2AttackAnimation(aiFallbackAnim);\n if (aiFallbackTechnique?.id) {\n setPlayer2TechniqueId(aiFallbackTechnique.id);\n }\n const p2FallbackAnim = getAnimation(aiFallbackAnim);\n const p2FallbackDur = p2FallbackAnim?.duration ?? 0.55;\n setPlayer2AttackDuration(p2FallbackDur);\n player2Animation.transitionToAttack(p2FallbackDur);\n }\n handleAIAttack(selectedTechnique, targetVitalPoint);\n break;\n case \"defend\":\n player2Animation.transitionTo(AnimationState.DEFEND);\n handleAIDefend();\n break;\n case \"technique\":\n case \"combo\":\n // Set AI attack animation based on technique\n // AI 기술 애니메이션 설정\n if (selectedTechnique) {\n const p2TechAnimName = resolveTechniqueAnimation(selectedTechnique);\n setPlayer2AttackAnimation(p2TechAnimName);\n\n // Store technique ID for enum-based AnimationType lookup\n // 열거형 기반 AnimationType 조회를 위한 기술 ID 저장\n if (selectedTechnique.id) {\n setPlayer2TechniqueId(selectedTechnique.id);\n }\n const p2TechAnim = getAnimation(p2TechAnimName);\n const p2TechDur = p2TechAnim?.duration ?? 0.6;\n setPlayer2AttackDuration(p2TechDur);\n player2Animation.transitionToAttack(p2TechDur);\n } else {\n setPlayer2AttackAnimation(aiFallbackAnim);\n if (aiFallbackTechnique?.id) {\n setPlayer2TechniqueId(aiFallbackTechnique.id);\n }\n const p2FallbackAnim = getAnimation(aiFallbackAnim);\n const p2FallbackDur = p2FallbackAnim?.duration ?? 0.6;\n setPlayer2AttackDuration(p2FallbackDur);\n player2Animation.transitionToAttack(p2FallbackDur);\n }\n handleAITechnique(selectedTechnique, targetVitalPoint);\n break;\n case \"approach\":\n case \"retreat\":\n case \"circle\":\n if (targetPos) {\n moveAIPlayer(targetPos);\n }\n break;\n case \"feint\":\n {\n const playerPos = validPlayers[0].position;\n // Feint offset in METERS (about half a meter of random movement)\n const feintOffsetMeters = 0.5;\n const feintPos = {\n x: playerPos.x + (Math.random() - 0.5) * feintOffsetMeters,\n y: playerPos.y + (Math.random() - 0.5) * feintOffsetMeters,\n };\n moveAIPlayer(feintPos);\n addCombatMessage(\"AI 페인트\", \"AI Feint\");\n\n setTimeout(() => {\n if (\n !combatState.roundEnded &&\n combatState.roundStarted &&\n validPlayers.length >= 2\n ) {\n const currentPlayerPos = validPlayers[0].position;\n const currentAiPos = validPlayers[1].position;\n const dx = currentAiPos.x - currentPlayerPos.x;\n const dy = currentAiPos.y - currentPlayerPos.y;\n const dist = Math.sqrt(dx * dx + dy * dy) || 1;\n // Retreat distance in METERS (about 0.8 meters back)\n const retreatDistanceMeters = 0.8;\n // Use world meter bounds instead of pixel bounds\n const halfWidth = arenaBounds.worldWidthMeters / 2;\n const halfDepth = arenaBounds.worldDepthMeters / 2;\n const retreatPos = {\n x: Math.max(\n -halfWidth + 0.5, // 0.5m from edge\n Math.min(\n halfWidth - 0.5,\n currentPlayerPos.x + (dx / dist) * retreatDistanceMeters,\n ),\n ),\n y: Math.max(\n -halfDepth + 0.5, // 0.5m from edge\n Math.min(\n halfDepth - 0.5,\n currentPlayerPos.y + (dy / dist) * retreatDistanceMeters,\n ),\n ),\n };\n moveAIPlayer(retreatPos);\n }\n }, 200);\n }\n break;\n case \"counter\":\n // Set AI attack animation for counter attack\n // AI 반격 애니메이션 설정\n if (selectedTechnique) {\n const p2CounterAnimName = resolveTechniqueAnimation(selectedTechnique);\n setPlayer2AttackAnimation(p2CounterAnimName);\n\n // Store technique ID for enum-based AnimationType lookup\n // 열거형 기반 AnimationType 조회를 위한 기술 ID 저장\n if (selectedTechnique.id) {\n setPlayer2TechniqueId(selectedTechnique.id);\n }\n const p2CounterAnim = getAnimation(p2CounterAnimName);\n const p2CounterDur = p2CounterAnim?.duration ?? 0.6;\n setPlayer2AttackDuration(p2CounterDur);\n player2Animation.transitionToAttack(p2CounterDur);\n } else {\n setPlayer2AttackAnimation(aiFallbackAnim);\n if (aiFallbackTechnique?.id) {\n setPlayer2TechniqueId(aiFallbackTechnique.id);\n }\n const p2FallbackAnim = getAnimation(aiFallbackAnim);\n const p2FallbackDur = p2FallbackAnim?.duration ?? 0.6;\n setPlayer2AttackDuration(p2FallbackDur);\n player2Animation.transitionToAttack(p2FallbackDur);\n }\n handleAIAttack(selectedTechnique, targetVitalPoint);\n addCombatMessage(\"AI 반격!\", \"AI Counter!\");\n break;\n }\n },\n [\n handleAIAttack,\n handleAIDefend,\n handleAITechnique,\n moveAIPlayer,\n addCombatMessage,\n validPlayers,\n arenaBounds,\n combatState.roundEnded,\n combatState.roundStarted,\n player2Animation,\n ],\n );\n\n // Update the ref\n useEffect(() => {\n executeAIActionCallbackRef.current = executeAIActionCallback;\n }, [executeAIActionCallback]);\n\n // Update adaptive difficulty metrics\n useEffect(() => {\n if (combatState.roundEnded && validPlayers.length === 2) {\n const player = validPlayers[0];\n const totalAttacks = (player.hitsLanded ?? 0) + (player.hitsTaken ?? 0);\n\n if (totalAttacks === 0) return;\n\n adaptiveDifficulty.updateSkillMetrics({\n hitsLanded: player.hitsLanded ?? 0,\n totalAttacks,\n combosExecuted: player.comboCount ?? 0,\n perfectBlockCount: 0,\n avgReactionTimeMs: 500,\n vitalPointsHit: player.vitalPointHits ?? 0,\n effectiveStanceChanges: 0,\n damageDealt: player.totalDamageDealt ?? 0,\n damageTaken: player.totalDamageReceived ?? 0,\n });\n }\n }, [combatState.roundEnded, adaptiveDifficulty, validPlayers]);\n\n // Check game end conditions\n const checkGameEnd = useCallback(() => {\n if (combatState.roundEnded) return;\n\n const p1Defeated = validPlayers[0].health <= 0;\n const p2Defeated = validPlayers[1].health <= 0;\n\n if (p1Defeated || p2Defeated) {\n combatActions.setRoundEnded(true);\n combatActions.setRoundStarted(false);\n combatActions.setRoundDisplayStatus(\"ko\");\n const winner = p1Defeated ? 1 : 0;\n const roundWinner = validPlayers[winner];\n\n // Update match score using deferred callback\n updateMatchScore(winner);\n\n addCombatMessage(\n p1Defeated ? \"플레이어 1 패배\" : \"플레이어 1 승리!\",\n p1Defeated ? \"Player 1 Defeated\" : \"Player 1 Victory!\",\n );\n\n // Start round transition\n setTimeout(() => {\n startTransition(roundWinner, internalRound);\n }, 1500);\n }\n }, [\n validPlayers,\n addCombatMessage,\n combatState.roundEnded,\n combatActions,\n internalRound,\n startTransition,\n updateMatchScore,\n ]);\n\n useEffect(() => {\n checkGameEnd();\n }, [player1Health, player2Health, checkGameEnd]);\n\n // Keyboard input handling\n useEffect(() => {\n const handleCombatInput = (event: KeyboardEvent) => {\n // ESC key toggles pause menu (unless pause menu is already open)\n if (event.key === \"Escape\") {\n event.preventDefault();\n if (showPauseMenu) {\n handleResume();\n } else {\n handlePause();\n }\n return;\n }\n\n // Block all other combat inputs when paused or during pause menu\n // isPaused: External pause state from parent component\n // showPauseMenu: Local pause menu UI is visible\n // Both conditions block input to prevent combat actions while game is paused\n if (isPaused || showPauseMenu) {\n return;\n }\n\n // Block all combat inputs during countdown or round start announcement\n if (!matchCountdownComplete || showRoundStart) {\n return;\n }\n\n if (\n !combatState.roundStarted ||\n combatState.roundEnded ||\n combatState.isExecutingTechnique\n ) {\n return;\n }\n\n const key = event.key.toLowerCase();\n\n if (key >= \"1\" && key <= \"8\") {\n const stanceIndex = parseInt(key) - 1;\n const stances: TrigramStance[] = [\n TrigramStance.GEON,\n TrigramStance.TAE,\n TrigramStance.LI,\n TrigramStance.JIN,\n TrigramStance.SON,\n TrigramStance.GAM,\n TrigramStance.GAN,\n TrigramStance.GON,\n ];\n handleStanceSwitch(stances[stanceIndex]);\n event.preventDefault();\n }\n\n if (key === \" \") {\n handleAttackWithFeedback();\n event.preventDefault();\n }\n\n if (event.key === \"Shift\") {\n handleDefendWithFeedback();\n event.preventDefault();\n }\n\n // F key for stance side switching (laterality)\n // Using \"F\" (Flip/Footwork) because:\n // - Tab conflicts with browser navigation and accessibility\n // - Q-W-E-R-T-Y-U-Y are reserved for combat techniques\n // - Shift is already used for blocking\n // - CapsLock has inconsistent key events across browsers/OSes\n if (event.key === \"f\" || event.key === \"F\") {\n handleStanceSideSwitch(0); // Human player\n event.preventDefault();\n }\n };\n\n window.addEventListener(\"keydown\", handleCombatInput);\n return () => window.removeEventListener(\"keydown\", handleCombatInput);\n }, [\n combatState.roundStarted,\n combatState.roundEnded,\n combatState.isExecutingTechnique,\n matchCountdownComplete,\n showRoundStart,\n isPaused,\n showPauseMenu,\n handleStanceSwitch,\n handleStanceSideSwitch,\n handleAttackWithFeedback,\n handleDefendWithFeedback,\n handlePause,\n handleResume,\n ]);\n\n // Calculate movement state for visual feedback using InjuryMovementModifier\n // This provides bilingual status text and injury severity information\n const player1MovementState = useMemo(() => {\n if (!validPlayers[0]?.bodyPartHealth) {\n return {\n statusText: { korean: \"정상\", english: \"Normal\" },\n isLimping: false,\n isSevereLimp: false,\n };\n }\n\n const result = injuryMovementModifier.calculateMovementSpeed(\n 1.0,\n validPlayers[0].bodyPartHealth,\n validPlayers[0].currentStance ?? TrigramStance.GEON,\n validPlayers[0].pain ?? 0,\n );\n\n return {\n statusText: result.statusText,\n isLimping: result.isLimping,\n isSevereLimp: result.isSevereLimp,\n speedMultiplier: result.speedMultiplier,\n };\n }, [validPlayers]);\n\n const player2MovementState = useMemo(() => {\n if (!validPlayers[1]?.bodyPartHealth) {\n return {\n statusText: { korean: \"정상\", english: \"Normal\" },\n isLimping: false,\n isSevereLimp: false,\n };\n }\n\n const result = injuryMovementModifier.calculateMovementSpeed(\n 1.0,\n validPlayers[1].bodyPartHealth,\n validPlayers[1].currentStance ?? TrigramStance.GEON,\n validPlayers[1].pain ?? 0,\n );\n\n return {\n statusText: result.statusText,\n isLimping: result.isLimping,\n isSevereLimp: result.isSevereLimp,\n speedMultiplier: result.speedMultiplier,\n };\n }, [validPlayers]);\n\n return (\n <div\n style={{\n width: `${width}px`,\n height: `${height}px`,\n position: \"relative\",\n backgroundColor: toHexColor(KOREAN_COLORS.UI_BACKGROUND_DARK),\n overflow: \"hidden\", // Prevent content from extending beyond container\n }}\n data-testid=\"combat-screen\"\n >\n {/* Three.js Canvas for 3D rendering */}\n <Canvas\n style={{ width: `${width}px`, height: `${height}px` }}\n camera={{\n position: cameraConfig.position,\n fov: cameraConfig.fov,\n near: cameraConfig.near,\n far: cameraConfig.far,\n }}\n gl={{\n antialias: renderConfig.antialias,\n alpha: false,\n powerPreference: \"high-performance\",\n // Add failIfMajorPerformanceCaveat to detect GPU issues\n failIfMajorPerformanceCaveat: false,\n }}\n dpr={renderConfig.dpr}\n shadows={false} // Temporarily disable shadows\n onCreated={({ gl }) => {\n gl.setClearColor(theme.colors.UI_BACKGROUND_DARK, 1);\n }}\n >\n {/* Lighting - CombatArena3D provides ambient, we add directional for shadows */}\n <ambientLight intensity={0.6} />\n <directionalLight position={[10, 10, 5]} intensity={1.2} />\n\n {/* Adaptive Quality Wrapper monitors FPS and adjusts quality */}\n <AdaptiveQualityWrapper\n enabled={shouldEnableAdaptiveQuality}\n isMobile={isMobile}\n >\n {/* Performance overlay (dev mode) - controlled by showPerformanceOverlay prop */}\n {showPerformanceOverlay && !isMobile && !showPerformanceMonitor && (\n <PerformanceOverlay3D />\n )}\n\n {/* Combat Arena 3D Environment - uses physics-based world dimensions */}\n <CombatArena3D\n lighting=\"cyberpunk\"\n scale={arenaBounds.scale}\n worldWidthMeters={arenaBounds.worldWidthMeters}\n worldDepthMeters={arenaBounds.worldDepthMeters}\n />\n\n {/* Animation updater - updates both player animations at 60fps */}\n <AnimationUpdater\n player1Animation={player1Animation}\n player2Animation={player2Animation}\n />\n\n {/* Acceleration updater - tracks player 1 movement time and updates speed */}\n <AccelerationUpdater\n isMoving={player1IsMoving}\n velocity={player1Velocity}\n movementTimeRef={player1MovementTimeRef}\n lastDirectionRef={player1LastDirectionRef}\n onSpeedUpdate={setPlayer1AccelerationBasedSpeed}\n walkSpeed={player1WalkRunSpeeds.walkSpeed}\n runSpeed={player1WalkRunSpeeds.runSpeed}\n />\n\n {/* Player 1 (Human) */}\n <Player3DWithTransitions\n {...convertPlayerStateToProps(\n validPlayers[0],\n player1PositionWithAttackMovement,\n player1Rotation,\n {\n isMobile,\n facing: \"right\",\n enableFacialExpressions: true,\n enableEyeTracking: true,\n opponentPosition: player2PositionWithAttackMovement,\n },\n )}\n currentAnimation={animationStateToPlayerAnimation(\n player1Animation.currentState,\n )}\n attackAnimation={player1AttackAnimation}\n laterality={combatState.playerLaterality[0]}\n enableTransitionEffects={!isMobile}\n enableStanceSymbol={!isMobile}\n enableStanceAudio={true}\n />\n\n {/* Player 2 (AI) */}\n <Player3DWithTransitions\n {...convertPlayerStateToProps(\n validPlayers[1],\n player2PositionWithAttackMovement,\n player2Rotation,\n {\n isMobile,\n facing: \"right\",\n enableFacialExpressions: true,\n enableEyeTracking: true,\n opponentPosition: player1PositionWithAttackMovement,\n },\n )}\n currentAnimation={animationStateToPlayerAnimation(\n player2Animation.currentState,\n )}\n attackAnimation={player2AttackAnimation}\n laterality={combatState.playerLaterality[1]}\n enableTransitionEffects={!isMobile}\n enableStanceSymbol={!isMobile}\n enableStanceAudio={true}\n />\n\n {/* Movement Status Indicators - Korean/English Bilingual */}\n {/* Player 1 Movement Status */}\n {(player1MovementState.isLimping ||\n player1MovementState.isSevereLimp) && (\n <Html\n position={[\n player1Position3D[0],\n player1Position3D[1] + 2.5,\n player1Position3D[2],\n ]}\n center\n data-testid=\"player1-movement-status\"\n >\n <div\n style={{\n fontSize: isMobile ? \"12px\" : \"14px\",\n color: player1MovementState.isSevereLimp\n ? toHexColor(KOREAN_COLORS.TEXT_ERROR)\n : toHexColor(KOREAN_COLORS.ACCENT_GOLD),\n fontFamily: FONT_FAMILY.KOREAN,\n fontWeight: \"bold\",\n textShadow: \"0 0 4px rgba(0,0,0,0.8)\",\n background: \"rgba(0, 0, 0, 0.6)\",\n padding: \"4px 8px\",\n borderRadius: \"4px\",\n whiteSpace: \"nowrap\",\n }}\n >\n {player1MovementState.statusText.korean} |{\" \"}\n {player1MovementState.statusText.english}\n </div>\n </Html>\n )}\n\n {/* Player 2 Movement Status */}\n {(player2MovementState.isLimping ||\n player2MovementState.isSevereLimp) && (\n <Html\n position={[\n player2Position3D[0],\n player2Position3D[1] + 2.5,\n player2Position3D[2],\n ]}\n center\n data-testid=\"player2-movement-status\"\n >\n <div\n style={{\n fontSize: isMobile ? \"12px\" : \"14px\",\n color: player2MovementState.isSevereLimp\n ? toHexColor(KOREAN_COLORS.TEXT_ERROR)\n : toHexColor(KOREAN_COLORS.ACCENT_GOLD),\n fontFamily: FONT_FAMILY.KOREAN,\n fontWeight: \"bold\",\n textShadow: \"0 0 4px rgba(0,0,0,0.8)\",\n background: \"rgba(0, 0, 0, 0.6)\",\n padding: \"4px 8px\",\n borderRadius: \"4px\",\n whiteSpace: \"nowrap\",\n }}\n >\n {player2MovementState.statusText.korean} |{\" \"}\n {player2MovementState.statusText.english}\n </div>\n </Html>\n )}\n\n {/* Trauma Overlays - Injury Visualization (외상 오버레이 - 부상 시각화) */}\n {/* Player 1 Injuries */}\n <TraumaOverlay3D\n playerId=\"player\"\n health={validPlayers[0].health}\n injuries={player1Injuries}\n characterPosition={player1Position3D}\n isMobile={isMobile}\n showFractures={true}\n />\n\n {/* Player 2 Injuries */}\n <TraumaOverlay3D\n playerId=\"enemy\"\n health={validPlayers[1].health}\n injuries={player2Injuries}\n characterPosition={player2Position3D}\n isMobile={isMobile}\n showFractures={true}\n />\n\n {/* Hit Effects */}\n <HitEffects3D\n effects={combatState.hitEffects}\n onEffectComplete={handleEffectComplete}\n arenaBounds={arenaBounds}\n />\n\n {/* Combat Particle Effects - Blood viscosity, organ damage, audio (전투 입자 효과) */}\n <CombatParticleEffects3D\n hitEffects={combatState.hitEffects}\n enabled={true}\n isMobile={isMobile}\n />\n\n {/* Vital Point Overlay - Show on both players when V is pressed */}\n {overlayVisible && (\n <>\n {/* Player 1 Vital Points */}\n <VitalPointMarkers3D\n position={player1Position3D}\n visible={overlayVisible}\n severityFilter={severityFilters}\n regionFilter={regionFilter}\n searchQuery={searchQuery}\n showLabels={showLabels}\n scale={scale}\n animated={animated}\n onPointClick={() => {\n // Optional: Add point targeting in combat\n }}\n />\n\n {/* Player 2 Vital Points */}\n <VitalPointMarkers3D\n position={player2Position3D}\n visible={overlayVisible}\n severityFilter={severityFilters}\n regionFilter={regionFilter}\n searchQuery={searchQuery}\n showLabels={showLabels}\n scale={scale}\n animated={animated}\n onPointClick={() => {\n // Optional: Add point targeting in combat\n }}\n />\n\n {/* Vital Point Overlay Controls - only visible when overlay is active */}\n <VitalPointOverlayControlsHtml\n screenPosition={{\n top: `${layoutConstants.hudHeight + layoutConstants.padding}px`,\n left: `${layoutConstants.padding}px`,\n }}\n visible={overlayVisible}\n onVisibleChange={setOverlayVisible}\n severityFilters={severityFilters}\n onSeverityFiltersChange={setSeverityFilters}\n regionFilter={regionFilter}\n onRegionFilterChange={setRegionFilter}\n searchQuery={searchQuery}\n onSearchQueryChange={setSearchQuery}\n showLabels={showLabels}\n onShowLabelsChange={setShowLabels}\n animated={animated}\n onAnimatedChange={setAnimated}\n scale={scale}\n onScaleChange={setScale}\n isMobile={isMobile}\n />\n </>\n )}\n\n {/* Action Feedback - Damage Numbers */}\n <DamageNumbers\n damages={feedbackState.damageNumbers}\n isMobile={isMobile}\n arenaBounds={arenaBounds}\n />\n\n {/* Action Feedback - Action Indicators */}\n <ActionFeedback\n feedbacks={feedbackState.actionFeedbacks}\n isMobile={isMobile}\n arenaBounds={arenaBounds}\n />\n\n {/* Combo Counter */}\n <ComboCounter combo={feedbackState.comboCount} isMobile={isMobile} />\n\n {/* Technique Name Display */}\n {feedbackState.currentTechnique && (\n <TechniqueName\n korean={feedbackState.currentTechnique.korean}\n english={feedbackState.currentTechnique.english}\n isMobile={isMobile}\n onComplete={() => feedbackActions.hideTechnique()}\n />\n )}\n\n {/* Performance Overlay (Development Only) - Toggle with P key */}\n {import.meta.env.DEV && showPerformanceMonitor && (\n <PerformanceOverlay3D visible={true} />\n )}\n\n {/* Visual Feedback Components for Keyboard Controls */}\n <StanceChangeIndicator\n currentStance={currentStanceIndex}\n previousStance={previousStance}\n isMobile={isMobile}\n />\n\n <KeyboardHints\n visible={showHints}\n currentStance={currentStanceIndex}\n isMobile={isMobile}\n />\n\n <InputBufferDisplay queuedInputs={queuedInputs} isMobile={isMobile} />\n\n {/* 3D Balance Indicators - Positioned below top HUD, to the right of side HUDs */}\n {/* Player 1 Balance Indicator - Upper left area, below top HUD */}\n {validPlayers[0] && (\n <BalanceIndicatorOverlayHtml\n player={validPlayers[0] as BalancePlayerState}\n currentTime={currentTime}\n position={[\n -2.5, // Left side of arena (to the right of left HUD in 3D space)\n 2.5, // Upper area (below top HUD)\n -1.0, // Slightly forward toward camera\n ]}\n isMobile={isMobile}\n />\n )}\n\n {/* Player 2 Balance Indicator - Upper right area, below top HUD */}\n {validPlayers[1] && (\n <BalanceIndicatorOverlayHtml\n player={validPlayers[1] as BalancePlayerState}\n currentTime={currentTime}\n position={[\n 2.5, // Right side of arena (to the left of right HUD in 3D space)\n 2.5, // Upper area (below top HUD)\n -1.0, // Slightly forward toward camera\n ]}\n isMobile={isMobile}\n />\n )}\n\n {/* Mobile Touch Controls moved outside Canvas - using MobileControlsOverlay for reliable touch events */}\n\n {/* Performance Monitoring - FPS display (dev mode, toggle with P key) */}\n {process.env.NODE_ENV === \"development\" && showPerformanceMonitor && (\n <FPSMonitor\n enabled={true}\n warningThreshold={50}\n criticalThreshold={30}\n />\n )}\n </AdaptiveQualityWrapper>\n\n {/* Post-processing Effects - desktop high tier only for Android WebGL stability */}\n {renderConfig.postProcessing && (\n <EffectComposer multisampling={4}>\n <Bloom\n luminanceThreshold={0.9}\n luminanceSmoothing={0.9}\n mipmapBlur\n intensity={0.8}\n radius={0.4}\n />\n <Noise opacity={0.03} />\n <Vignette eskil={false} offset={0.1} darkness={0.3} />\n </EffectComposer>\n )}\n </Canvas>\n\n {/* Html UI Overlays (positioned absolutely over Canvas) */}\n <div\n style={{\n position: \"absolute\",\n top: 0,\n left: 0,\n width: \"100%\",\n height: \"100%\",\n pointerEvents: \"none\",\n zIndex: Z_INDEX.HUD,\n // Use 'clip' for pure clipping without creating a scroll container\n // Note: Both 'clip' and 'hidden' will clip box/text shadows; ensure\n // any required shadow space is handled via padding on parent containers\n overflow: \"clip\",\n }}\n >\n {/* Top HUD - Round info, timer, return to menu */}\n <CombatTopHUD\n width={width}\n height={height}\n isMobile={isMobile}\n positionScale={positionScale}\n currentRound={internalRound}\n totalRounds={3}\n timerState={timerState}\n showTimer={\n combatState.roundStarted &&\n !combatState.roundEnded &&\n matchCountdownComplete &&\n !showRoundStart\n }\n onReturnToMenu={onReturnToMenu}\n isPaused={isPaused || showPauseMenu}\n />\n\n {/* Left HUD - Player 1 stats.\n On mobile, side HUDs occlude the 3D arena in both portrait and\n landscape; collapse them away so the arena stays fully visible.\n Player status remains available via CombatPortraitStatusStrip and\n bottom combat controls. */}\n {!isMobile && (\n <CombatLeftHUD\n width={width}\n height={height}\n isMobile={isMobile}\n positionScale={positionScale}\n player={validPlayers[0]}\n laterality={combatState.playerLaterality[0]}\n isInGuard={player1Animation.isInStanceGuard()}\n speedModifiers={player1SpeedModifiers}\n />\n )}\n\n {/* Right HUD - Player 2/AI stats with difficulty indicator */}\n {!isMobile && (\n <CombatRightHUD\n width={width}\n height={height}\n isMobile={isMobile}\n positionScale={positionScale}\n player={validPlayers[1]}\n laterality={combatState.playerLaterality[1]}\n speedModifiers={player2SpeedModifiers}\n difficultyTier={currentDifficultyTier}\n />\n )}\n\n {/* Portrait-mobile HP/stamina strip. Replaces the hidden side HUDs\n so both players can still see their health at a glance without\n re-introducing arena occlusion. */}\n {isMobile && isPortrait && (\n <CombatPortraitStatusStrip\n width={width}\n height={height}\n player1={validPlayers[0]}\n player2={validPlayers[1]}\n positionScale={positionScale}\n topOffset={layoutConstants.hudHeight}\n />\n )}\n\n {/* Bottom HUD - Technique bar, volume, messages */}\n <CombatBottomHUD\n width={width}\n height={height}\n isMobile={isMobile}\n positionScale={positionScale}\n visible={\n combatState.roundStarted &&\n !combatState.roundEnded &&\n matchCountdownComplete &&\n !showRoundStart\n }\n techniques={techniqueSelection.availableTechniques}\n player={validPlayers[0]}\n selectedIndex={techniqueSelection.selectedIndex}\n cooldowns={cooldownsMap}\n onTechniqueSelect={techniqueSelection.selectTechnique}\n combatMessages={combatState.combatMessages}\n />\n\n {/* Player State Visual Indicators */}\n {/* Player 1 State Overlay - includes consciousness blur, pain vignette, etc.\n In portrait mobile the arena is already rendered in a compressed 3:4\n aspect ratio, so halve the fullscreen vignette/blur/flash intensity\n to avoid further obscuring the scene. */}\n <PlayerStateOverlayHtml\n pain={validPlayers[0].pain}\n balanceState={getBalanceState(validPlayers[0].balance)}\n position=\"left\"\n consciousness={validPlayers[0].consciousness}\n bloodLoss={0} // FIXME: bloodLoss property not yet added to PlayerState interface - overlay will not display until implemented\n stamina={validPlayers[0].stamina}\n isMobile={isMobile}\n intensityScale={isMobile && isPortrait ? 0.5 : 1}\n />\n\n {/* Note: Player 2 (AI) does not get fullscreen state overlays like consciousness blur */}\n {/* as those effects would incorrectly affect the player's view */}\n\n {/* Breathing Disruption Indicators */}\n {/* Player 1 Breathing Indicator - positioned near left HUD */}\n <div\n style={{\n position: \"absolute\",\n left: isMobile ? \"10px\" : \"20px\",\n top: isMobile ? \"120px\" : \"160px\",\n zIndex: Z_INDEX.HUD + 1,\n pointerEvents: \"none\",\n }}\n data-testid=\"player1-breathing-indicator-container\"\n >\n <BreathingIndicator player={validPlayers[0]} isMobile={isMobile} />\n </div>\n\n {/* Player 2 Breathing Indicator - positioned near right HUD */}\n <div\n style={{\n position: \"absolute\",\n right: isMobile ? \"10px\" : \"20px\",\n top: isMobile ? \"120px\" : \"160px\",\n zIndex: Z_INDEX.HUD + 1,\n pointerEvents: \"none\",\n }}\n data-testid=\"player2-breathing-indicator-container\"\n >\n <BreathingIndicator player={validPlayers[1]} isMobile={isMobile} />\n </div>\n\n {/* Pause Menu Overlay */}\n {(isPaused || showPauseMenu) && (\n <PauseMenu\n onResume={handleResume}\n onRestart={handleRestart}\n onReturnToMenu={onReturnToMenu}\n isMobile={isMobile}\n />\n )}\n </div>\n\n {/* Round Announcement Overlay */}\n {showAnnouncement && roundWinner && (\n <RoundAnnouncement\n roundNumber={transitionRoundNumber}\n roundWinner={roundWinner}\n currentScore={matchScore}\n roundStats={{\n damageDealt: roundWinner.totalDamageDealt ?? 0,\n hitsLanded: roundWinner.hitsLanded ?? 0,\n vitalPointsHit: roundWinner.vitalPointHits ?? 0,\n accuracy: calculateAccuracy(roundWinner),\n }}\n onCountdownComplete={() => {\n // Check if match is over (best of 3)\n if (matchScore.player1 >= 2 || matchScore.player2 >= 2) {\n const winner = matchScore.player1 >= 2 ? 0 : 1;\n onGameEnd(winner);\n } else {\n // Match continues - trigger transition to next round\n skipCountdown();\n }\n }}\n onSkip={() => {\n // Check if match is over (best of 3) before skipping\n if (matchScore.player1 >= 2 || matchScore.player2 >= 2) {\n const winner = matchScore.player1 >= 2 ? 0 : 1;\n onGameEnd(winner);\n } else {\n skipCountdown();\n }\n }}\n isMobile={isMobile}\n totalRounds={3}\n />\n )}\n\n {/* Match Start Countdown Overlay - only shows once at match start */}\n {showMatchCountdown && !hasShownMatchCountdown && (\n <MatchCountdown\n onComplete={() => {\n setHasShownMatchCountdown(true);\n setShowMatchCountdown(false);\n setMatchCountdownComplete(true);\n // Start the first round after countdown\n startRound();\n }}\n isMobile={isMobile}\n showSkip={false}\n />\n )}\n\n {/* Round Start Announcement for subsequent rounds */}\n {/* Note: showRoundStart is only set to true after round 1 ends, so no need for internalRound > 1 check */}\n {showRoundStart && (\n <RoundStartAnnouncement\n roundNumber={internalRound}\n duration={2}\n onComplete={() => {\n if (import.meta.env.DEV) {\n console.log(\n \"[DEV] Round start announcement complete for round\",\n internalRound,\n );\n }\n setShowRoundStart(false);\n // Start combat for this round\n startRound();\n }}\n isMobile={isMobile}\n />\n )}\n\n {/* Round Display Status - Brief status messages */}\n {contentReady && combatState.roundDisplayStatus && (\n <RoundDisplayStatus\n status={combatState.roundDisplayStatus}\n isMobile={isMobile}\n />\n )}\n\n {/* Mobile Controls - Pure DOM, rendered OUTSIDE Canvas for reliable touch events */}\n {/* Uses pure DOM handlers instead of drei's Html which can block touch events on mobile */}\n {isMobile && (\n <>\n <MobileControlsOverlay\n onMove={handleMobileMove}\n onAttack={handleMobileAttack}\n onBlock={handleMobileBlock}\n bottom={getMobileControlsBottom(height)}\n viewportWidth={width}\n viewportHeight={height}\n />\n\n <StanceWheelPure\n currentStance={currentStanceIndex}\n onStanceChange={handleMobileStanceChange}\n expanded={stanceWheelExpanded}\n onToggle={toggleStanceWheel}\n disabled={!mobileControlsEnabled}\n opacity={0.8}\n />\n\n <GestureRecognizerPure\n onGesture={handleMobileGesture}\n enabled={mobileControlsEnabled}\n showFeedback={true}\n minSwipeDistance={50}\n />\n </>\n )}\n </div>\n );\n};\n\nexport default CombatScreen3D;\n"],"mappings":"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"}