blacktrigram 0.7.39 → 0.7.41
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.
- package/lib/App2.js.map +1 -1
- package/lib/audio/AudioAssetLoader.js.map +1 -1
- package/lib/audio/AudioAssetRegistry.js.map +1 -1
- package/lib/audio/AudioCache.js.map +1 -1
- package/lib/audio/AudioManager.js.map +1 -1
- package/lib/audio/AudioMonitor.js.map +1 -1
- package/lib/audio/AudioPool.js.map +1 -1
- package/lib/audio/AudioProvider.js.map +1 -1
- package/lib/audio/AudioUtils.js.map +1 -1
- package/lib/audio/BoneImpactAudioMap.js.map +1 -1
- package/lib/audio/VariantSelector.js.map +1 -1
- package/lib/audio/types.js.map +1 -1
- package/lib/components/screens/combat/CombatScreen3D.js.map +1 -1
- package/lib/components/screens/combat/components/controls/CombatButtons.js.map +1 -1
- package/lib/components/screens/combat/components/controls/CombatControlsPanel.js.map +1 -1
- package/lib/components/screens/combat/components/controls/ControlsGuide.js.map +1 -1
- package/lib/components/screens/combat/components/controls/KeyboardHints.js.map +1 -1
- package/lib/components/screens/combat/components/controls/PauseMenu.js.map +1 -1
- package/lib/components/screens/combat/components/controls/PauseMenuButton.js.map +1 -1
- package/lib/components/screens/combat/components/controls/QuickSettings.js.map +1 -1
- package/lib/components/screens/combat/components/effects/BloodDecals3D.js.map +1 -1
- package/lib/components/screens/combat/components/effects/BloodLossOverlayHtml.js.map +1 -1
- package/lib/components/screens/combat/components/effects/BloodParticles3D.js.map +1 -1
- package/lib/components/screens/combat/components/effects/BloodViscosity3D.js.map +1 -1
- package/lib/components/screens/combat/components/effects/CombatParticleEffects3D.js.map +1 -1
- package/lib/components/screens/combat/components/effects/ConsciousnessBlur.js.map +1 -1
- package/lib/components/screens/combat/components/effects/InternalDamage3D.js.map +1 -1
- package/lib/components/screens/combat/components/effects/PainVignette.js.map +1 -1
- package/lib/components/screens/combat/components/effects/ParticleAudio3D.js.map +1 -1
- package/lib/components/screens/combat/components/effects/TraumaOverlay3D.js.map +1 -1
- package/lib/components/screens/combat/components/feedback/MatchCountdown.js.map +1 -1
- package/lib/components/screens/combat/components/feedback/RoundAnnouncementOverlayHtml.js.map +1 -1
- package/lib/components/screens/combat/components/feedback/RoundDisplayStatus.js.map +1 -1
- package/lib/components/screens/combat/components/feedback/RoundStartAnnouncementOverlayHtml.js.map +1 -1
- package/lib/components/screens/combat/components/hud/CombatBottomHUD.js.map +1 -1
- package/lib/components/screens/combat/components/hud/CombatLeftHUD.js.map +1 -1
- package/lib/components/screens/combat/components/hud/CombatPortraitStatusStrip.js.map +1 -1
- package/lib/components/screens/combat/components/hud/CombatRightHUD.js.map +1 -1
- package/lib/components/screens/combat/components/hud/CombatTopHUD.js.map +1 -1
- package/lib/components/screens/combat/components/hud/DifficultyIndicator.js.map +1 -1
- package/lib/components/screens/combat/components/hud/FPSMonitor.js.map +1 -1
- package/lib/components/screens/combat/components/hud/MobileControlsWrapper.js.map +1 -1
- package/lib/components/screens/combat/components/hud/PlayerStateOverlayHtml.js.map +1 -1
- package/lib/components/screens/combat/components/indicators/BalanceIndicator.js.map +1 -1
- package/lib/components/screens/combat/components/indicators/InputBufferDisplay.js.map +1 -1
- package/lib/components/screens/combat/components/indicators/StaminaWarning.js.map +1 -1
- package/lib/components/screens/combat/components/indicators/TechniqueNameDisplay.js.map +1 -1
- package/lib/components/screens/combat/helpers/AnimationUpdater.js.map +1 -1
- package/lib/components/screens/combat/helpers/combatHelpers.js.map +1 -1
- package/lib/components/screens/combat/hooks/useAICombat.js.map +1 -1
- package/lib/components/screens/combat/hooks/useCombatActions.js.map +1 -1
- package/lib/components/screens/combat/hooks/useCombatAttackMovement.js.map +1 -1
- package/lib/components/screens/combat/hooks/useCombatAudio.js.map +1 -1
- package/lib/components/screens/combat/hooks/useCombatLayout.js.map +1 -1
- package/lib/components/screens/combat/hooks/useCombatState.js.map +1 -1
- package/lib/components/screens/controls/ControlsScreen3D.js.map +1 -1
- package/lib/components/screens/controls/components/ControlBindingsOverlayHtml.js.map +1 -1
- package/lib/components/screens/controls/components/ControlCategoryTabsOverlayHtml.js.map +1 -1
- package/lib/components/screens/controls/components/GamepadVisualization3D.js.map +1 -1
- package/lib/components/screens/controls/components/InteractiveControlDemoOverlayHtml.js.map +1 -1
- package/lib/components/screens/controls/components/Key3D.js.map +1 -1
- package/lib/components/screens/controls/components/VisualKeyboard3D.js.map +1 -1
- package/lib/components/screens/controls/constants/ControlsConstants.js.map +1 -1
- package/lib/components/screens/controls/hooks/useControlsState.js.map +1 -1
- package/lib/components/screens/endscreen/EndScreen3D.js.map +1 -1
- package/lib/components/screens/endscreen/components/DefeatAnimation3D.js.map +1 -1
- package/lib/components/screens/endscreen/components/MatchStatisticsDisplayOverlayHtml.js.map +1 -1
- package/lib/components/screens/endscreen/components/NavigationButtonsOverlayHtml.js.map +1 -1
- package/lib/components/screens/endscreen/components/PerformanceBreakdownOverlayHtml.js.map +1 -1
- package/lib/components/screens/endscreen/components/PerformanceRatingOverlayHtml.js.map +1 -1
- package/lib/components/screens/endscreen/components/VictoryAnimation3D.js.map +1 -1
- package/lib/components/screens/endscreen/components/WinnerDisplayOverlayHtml.js.map +1 -1
- package/lib/components/screens/intro/IntroScreen3D.js +1 -1
- package/lib/components/screens/intro/IntroScreen3D.js.map +1 -1
- package/lib/components/screens/intro/components/AbilityListOverlayHtml.js.map +1 -1
- package/lib/components/screens/intro/components/ArchetypeCardGridOverlayHtml.js.map +1 -1
- package/lib/components/screens/intro/components/ArchetypeCardOverlayHtml.js.map +1 -1
- package/lib/components/screens/intro/components/ArchetypeDisplayOverlayHtml.js.map +1 -1
- package/lib/components/screens/intro/components/EnhancedArchetypeDisplayOverlayHtml.js.map +1 -1
- package/lib/components/screens/intro/components/MenuButtonsOverlayHtml.js.map +1 -1
- package/lib/components/screens/intro/components/MenuSectionOverlayHtml.js.map +1 -1
- package/lib/components/screens/intro/components/StatBarOverlayHtml.js.map +1 -1
- package/lib/components/screens/philosophy/PhilosophyScreen3D.js.map +1 -1
- package/lib/components/screens/training/TrainingScreen3D.js.map +1 -1
- package/lib/components/screens/training/components/AnatomyControlsOverlayHtml.js.map +1 -1
- package/lib/components/screens/training/components/AnatomyOverlay3D.js.map +1 -1
- package/lib/components/screens/training/components/FootPlacementMarkers3D.js.map +1 -1
- package/lib/components/screens/training/components/FootworkDrillsOverlayHtml.js.map +1 -1
- package/lib/components/screens/training/components/HitFeedbackEffect3D.js.map +1 -1
- package/lib/components/screens/training/components/TrainingButtonsOverlayHtml.js.map +1 -1
- package/lib/components/screens/training/components/TrainingControlsOverlayHtml.js.map +1 -1
- package/lib/components/screens/training/components/TrainingDummy3D.js.map +1 -1
- package/lib/components/screens/training/components/TrainingFeedbackOverlayHtml.js.map +1 -1
- package/lib/components/screens/training/components/TrainingModeSelectorOverlayHtml.js.map +1 -1
- package/lib/components/screens/training/components/TrainingStatsOverlayHtml.js.map +1 -1
- package/lib/components/screens/training/components/VitalPointMarker3D.js.map +1 -1
- package/lib/components/screens/training/components/VitalPointTrainingOverlayHtml.js.map +1 -1
- package/lib/components/screens/training/components/hud/TrainingBottomHUD.js.map +1 -1
- package/lib/components/screens/training/components/hud/TrainingLeftHUD.js.map +1 -1
- package/lib/components/screens/training/components/hud/TrainingRightHUD.js.map +1 -1
- package/lib/components/screens/training/components/hud/TrainingTopHUD.js.map +1 -1
- package/lib/components/screens/training/hooks/useAttackMovement.js.map +1 -1
- package/lib/components/screens/training/hooks/useTrainingActions.js.map +1 -1
- package/lib/components/screens/training/hooks/useTrainingLayout.js.map +1 -1
- package/lib/components/screens/training/hooks/useTrainingState.js.map +1 -1
- package/lib/components/shared/base/BaseButton.js.map +1 -1
- package/lib/components/shared/base/BaseButtonOverlayHtml.js.map +1 -1
- package/lib/components/shared/base/BasePanel.js.map +1 -1
- package/lib/components/shared/base/BaseText.js.map +1 -1
- package/lib/components/shared/base/useKoreanTheme.js.map +1 -1
- package/lib/components/shared/debug/PerformanceDebugOverlayHtml.js.map +1 -1
- package/lib/components/shared/mobile/ActionButtons.js.map +1 -1
- package/lib/components/shared/mobile/GestureRecognizerPure.js.map +1 -1
- package/lib/components/shared/mobile/HapticController.js.map +1 -1
- package/lib/components/shared/mobile/MobileControlsPure.js.map +1 -1
- package/lib/components/shared/mobile/StanceWheelPure.js.map +1 -1
- package/lib/components/shared/mobile/TouchOptimizer.js.map +1 -1
- package/lib/components/shared/mobile/VirtualDPad.js.map +1 -1
- package/lib/components/shared/three/anatomy/BodySurface.js.map +1 -1
- package/lib/components/shared/three/anatomy/BoneAttachedMuscles.js.map +1 -1
- package/lib/components/shared/three/anatomy/BoneClothing.js.map +1 -1
- package/lib/components/shared/three/anatomy/BoneRenderer.js.map +1 -1
- package/lib/components/shared/three/anatomy/Face3D.js.map +1 -1
- package/lib/components/shared/three/anatomy/Foot3D.js.map +1 -1
- package/lib/components/shared/three/anatomy/Hand3D.js.map +1 -1
- package/lib/components/shared/three/effects/ActionFeedback.js.map +1 -1
- package/lib/components/shared/three/effects/DamageNumbers.js.map +1 -1
- package/lib/components/shared/three/effects/HitEffects3D.js.map +1 -1
- package/lib/components/shared/three/effects/PlayerStateIndicators.js.map +1 -1
- package/lib/components/shared/three/effects/StanceSymbol3D.js.map +1 -1
- package/lib/components/shared/three/effects/StanceTransitionEffect.js.map +1 -1
- package/lib/components/shared/three/effects/VitalPointMarkers3D.js.map +1 -1
- package/lib/components/shared/three/indicators/ElementalColorSystem.js.map +1 -1
- package/lib/components/shared/three/indicators/GuardIndicator.js.map +1 -1
- package/lib/components/shared/three/indicators/HapticFeedback.js.map +1 -1
- package/lib/components/shared/three/indicators/StanceChangeIndicator.js.map +1 -1
- package/lib/components/shared/three/models/Player3DWithTransitions.js.map +1 -1
- package/lib/components/shared/three/models/SkeletalPlayer3D.js.map +1 -1
- package/lib/components/shared/three/optimization/AdaptiveQuality.js.map +1 -1
- package/lib/components/shared/three/scene/AtmosphericParticles3D.js.map +1 -1
- package/lib/components/shared/three/scene/BackgroundScene3D.js.map +1 -1
- package/lib/components/shared/three/scene/CombatArena3D.js.map +1 -1
- package/lib/components/shared/three/scene/KoreanSignage3D.js.map +1 -1
- package/lib/components/shared/three/ui/ArchetypeCard.js.map +1 -1
- package/lib/components/shared/three/ui/BodyPartHealthDisplay.js.map +1 -1
- package/lib/components/shared/three/ui/BreathingIndicator2.js.map +1 -1
- package/lib/components/shared/three/ui/CombatReadinessBar.js.map +1 -1
- package/lib/components/shared/three/ui/ComboCounter.js.map +1 -1
- package/lib/components/shared/three/ui/HealthBar.js.map +1 -1
- package/lib/components/shared/three/ui/KoreanButton.js.map +1 -1
- package/lib/components/shared/three/ui/KoreanPanel.js.map +1 -1
- package/lib/components/shared/three/ui/KoreanText.js.map +1 -1
- package/lib/components/shared/three/ui/MenuList.js.map +1 -1
- package/lib/components/shared/three/ui/PlayerHUD.js.map +1 -1
- package/lib/components/shared/three/ui/ProgressBar.js.map +1 -1
- package/lib/components/shared/three/ui/SpeedIndicatorHUD.js.map +1 -1
- package/lib/components/shared/three/ui/StaminaBar.js.map +1 -1
- package/lib/components/shared/three/ui/TechniqueBar.js.map +1 -1
- package/lib/components/shared/three/ui/TechniqueCard.js.map +1 -1
- package/lib/components/shared/three/ui/VitalPointOverlayControlsHtml.js.map +1 -1
- package/lib/components/shared/ui/BackButton.js.map +1 -1
- package/lib/components/shared/ui/BaseHUDContainer.js.map +1 -1
- package/lib/components/shared/ui/CombatTimer.js.map +1 -1
- package/lib/components/shared/ui/ErrorModal.js.map +1 -1
- package/lib/components/shared/ui/LoadingState.js.map +1 -1
- package/lib/components/shared/ui/SplashScreen.js +2 -2
- package/lib/components/shared/ui/SplashScreen.js.map +1 -1
- package/lib/components/shared/ui/VitalPointOverlayControlsPure.js.map +1 -1
- package/lib/components/shared/ui/VolumeControl.js.map +1 -1
- package/lib/components/shared/ui/shared/ConfirmDialog.js.map +1 -1
- package/lib/components/ui/combat/BalanceIndicatorOverlayHtml.js.map +1 -1
- package/lib/constants/bodyDimensions.js.map +1 -1
- package/lib/constants/bodyRenderingConstants.js.map +1 -1
- package/lib/data/archetypeClothing.js.map +1 -1
- package/lib/data/archetypePhysicalAttributes.js.map +1 -1
- package/lib/data/techniqueMappings.js.map +1 -1
- package/lib/data/techniques.js.map +1 -1
- package/lib/hooks/useActionFeedback.js.map +1 -1
- package/lib/hooks/useBalanceAnimations.js.map +1 -1
- package/lib/hooks/useCombatTimer.js.map +1 -1
- package/lib/hooks/useDebounce.js.map +1 -1
- package/lib/hooks/useHUDLayout.js.map +1 -1
- package/lib/hooks/useHandPoseTransitions.js.map +1 -1
- package/lib/hooks/useKeyboardControls.js.map +1 -1
- package/lib/hooks/useMatchCountdown.js.map +1 -1
- package/lib/hooks/useMuscleActivation.js.map +1 -1
- package/lib/hooks/usePauseMenu.js.map +1 -1
- package/lib/hooks/usePlayerAnimation.js.map +1 -1
- package/lib/hooks/useResponsiveLayout.js.map +1 -1
- package/lib/hooks/useRoundTransition.js.map +1 -1
- package/lib/hooks/useSkeletalAnimation.js.map +1 -1
- package/lib/hooks/useTechniqueSelection.js.map +1 -1
- package/lib/hooks/useThrottle.js.map +1 -1
- package/lib/hooks/useTouchControls.js.map +1 -1
- package/lib/hooks/useWebGLContextLossHandler.js.map +1 -1
- package/lib/hooks/useWindowSize.js.map +1 -1
- package/lib/systems/CombatSystem.js.map +1 -1
- package/lib/systems/EffectCalculator.js.map +1 -1
- package/lib/systems/LayoutSystem.js.map +1 -1
- package/lib/systems/PlayerEffectManager.js.map +1 -1
- package/lib/systems/ResponsiveScaling.js.map +1 -1
- package/lib/systems/TrigramSystem.js.map +1 -1
- package/lib/systems/VitalPointSystem.js.map +1 -1
- package/lib/systems/ai/AIPersonality.js.map +1 -1
- package/lib/systems/ai/AdaptiveDifficulty.js.map +1 -1
- package/lib/systems/ai/ArchetypeEnforcer.js.map +1 -1
- package/lib/systems/ai/ComboSystem.js.map +1 -1
- package/lib/systems/ai/DecisionTree.js.map +1 -1
- package/lib/systems/ai/TrainingAI.js.map +1 -1
- package/lib/systems/ai/types.js.map +1 -1
- package/lib/systems/animation/builders/AnimationBuilder.js.map +1 -1
- package/lib/systems/animation/builders/HandPoseApplicator.js.map +1 -1
- package/lib/systems/animation/builders/HandPoses.js.map +1 -1
- package/lib/systems/animation/builders/KeyframeConfig.js.map +1 -1
- package/lib/systems/animation/builders/KeyframeInterpolation.js +3 -90
- package/lib/systems/animation/builders/KeyframeInterpolation.js.map +1 -1
- package/lib/systems/animation/builders/KickPhaseApplicator.js.map +1 -1
- package/lib/systems/animation/builders/KoreanGuardPositions.js.map +1 -1
- package/lib/systems/animation/builders/MartialArtsAnimationBuilder.js.map +1 -1
- package/lib/systems/animation/builders/MartialArtsConstants.js.map +1 -1
- package/lib/systems/animation/builders/MartialPoseApplicator.js.map +1 -1
- package/lib/systems/animation/builders/PunchPhaseApplicator.js.map +1 -1
- package/lib/systems/animation/builders/SkeletonRig.js.map +1 -1
- package/lib/systems/animation/builders/TrigramGuardApplicator.js.map +1 -1
- package/lib/systems/animation/catalogs/DefensiveAnimations.js.map +1 -1
- package/lib/systems/animation/catalogs/FootworkSkeletalAnimations.js.map +1 -1
- package/lib/systems/animation/catalogs/RecoveryAnimations.js.map +1 -1
- package/lib/systems/animation/catalogs/StanceAnimations.js.map +1 -1
- package/lib/systems/animation/catalogs/StanceAttackAnimations.js.map +1 -1
- package/lib/systems/animation/catalogs/StanceGuardPoses.js.map +1 -1
- package/lib/systems/animation/catalogs/StanceIdleAnimations.js.map +1 -1
- package/lib/systems/animation/catalogs/StanceLocomotionAnimations.js.map +1 -1
- package/lib/systems/animation/catalogs/StepSkeletalAnimations.js.map +1 -1
- package/lib/systems/animation/core/AnimationHitTiming.js.map +1 -1
- package/lib/systems/animation/core/AnimationOptimizations.js.map +1 -1
- package/lib/systems/animation/core/AnimationPriority.js.map +1 -1
- package/lib/systems/animation/core/AnimationRegistry.js.map +1 -1
- package/lib/systems/animation/core/AnimationStateMachine.js.map +1 -1
- package/lib/systems/animation/core/AnimationTransitions.js.map +1 -1
- package/lib/systems/animation/core/LateralityTransform.js.map +1 -1
- package/lib/systems/animation/core/RecoveryPhaseEnhancer.js.map +1 -1
- package/lib/systems/animation/core/TechniqueAnimationMapper.js.map +1 -1
- package/lib/systems/animation/core/TechniqueAnimationMapping.js.map +1 -1
- package/lib/systems/animation/core/types.js.map +1 -1
- package/lib/systems/animation/systems/AdvancedJointMovements.js.map +1 -1
- package/lib/systems/animation/systems/BodyFacingSystem.js.map +1 -1
- package/lib/systems/animation/systems/FacialExpressions.js.map +1 -1
- package/lib/systems/animation/systems/FallAnimations.js.map +1 -1
- package/lib/systems/animation/systems/MuscleActivation.js.map +1 -1
- package/lib/systems/bodypart/BodyPartDamageIntegration.js.map +1 -1
- package/lib/systems/bodypart/BodyPartHealthSystem.js.map +1 -1
- package/lib/systems/bodypart/BodyPartPositionMapping.js.map +1 -1
- package/lib/systems/bodypart/CombatInjuryIntegration.js.map +1 -1
- package/lib/systems/bodypart/InjuryIntegration.js.map +1 -1
- package/lib/systems/bodypart/InjuryTracker.js.map +1 -1
- package/lib/systems/bodypart/MovementPenaltySystem.js.map +1 -1
- package/lib/systems/bodypart/PlayerInjuryTrackingManager.js.map +1 -1
- package/lib/systems/bodypart/types.js.map +1 -1
- package/lib/systems/breathing/BreathingDisruptionSystem.js.map +1 -1
- package/lib/systems/breathing/feedback.js.map +1 -1
- package/lib/systems/breathing/integration.js.map +1 -1
- package/lib/systems/combat/BalanceSystem.js.map +1 -1
- package/lib/systems/combat/BreakingStatusEffects.js.map +1 -1
- package/lib/systems/combat/CombatStateSystem.js.map +1 -1
- package/lib/systems/combat/ConsciousnessSystem.js.map +1 -1
- package/lib/systems/combat/FallIntegration.js.map +1 -1
- package/lib/systems/combat/GrappleSystem.js.map +1 -1
- package/lib/systems/combat/LimbExposureSystem.js.map +1 -1
- package/lib/systems/combat/PainResponseSystem.js.map +1 -1
- package/lib/systems/combat/TrainingCombatSystem.js.map +1 -1
- package/lib/systems/combat/painConsciousnessUtils.js.map +1 -1
- package/lib/systems/combat/typeGuards.js.map +1 -1
- package/lib/systems/effects.js.map +1 -1
- package/lib/systems/game.js.map +1 -1
- package/lib/systems/movement/InjuryMovementModifier.js.map +1 -1
- package/lib/systems/movement/helpers/AccelerationUpdater.js.map +1 -1
- package/lib/systems/movement/helpers/accelerationUtils.js.map +1 -1
- package/lib/systems/movement/integration.js.map +1 -1
- package/lib/systems/physics/AttackMovementPhysics.js.map +1 -1
- package/lib/systems/physics/CollisionDetection.js.map +1 -1
- package/lib/systems/physics/CoordinateMapper.js.map +1 -1
- package/lib/systems/physics/KnockbackPhysics.js.map +1 -1
- package/lib/systems/physics/MovementPhysics.js.map +1 -1
- package/lib/systems/physics/PhysicalReachCalculator.js.map +1 -1
- package/lib/systems/physics/SpeedModifierSystem.js.map +1 -1
- package/lib/systems/trigram/KoreanCulture.js.map +1 -1
- package/lib/systems/trigram/KoreanTechniques.js.map +1 -1
- package/lib/systems/trigram/StanceManager.js.map +1 -1
- package/lib/systems/trigram/TransitionCalculator.js.map +1 -1
- package/lib/systems/trigram/TrigramCalculator.js.map +1 -1
- package/lib/systems/trigram/techniques/DarkOpsTechniques.js.map +1 -1
- package/lib/systems/trigram/techniques/GamTechniques.js.map +1 -1
- package/lib/systems/trigram/techniques/GanTechniques.js.map +1 -1
- package/lib/systems/trigram/techniques/GonTechniques.js.map +1 -1
- package/lib/systems/trigram/techniques/SonTechniques.js.map +1 -1
- package/lib/systems/trigram/techniques/TechniqueConfig.js.map +1 -1
- package/lib/systems/trigram/techniques/index.js.map +1 -1
- package/lib/systems/trigram/types/GonTechniqueExtensions.js.map +1 -1
- package/lib/systems/trigram/types.js.map +1 -1
- package/lib/systems/types.js.map +1 -1
- package/lib/systems/vitalpoint/DamageCalculator.js.map +1 -1
- package/lib/systems/vitalpoint/HitDetection.js.map +1 -1
- package/lib/systems/vitalpoint/KoreanAnatomy.js.map +1 -1
- package/lib/systems/vitalpoint/KoreanVitalPoints.js.map +1 -1
- package/lib/systems/vitalpoint/MeridianVitalPointMapping.js.map +1 -1
- package/lib/types/AccessibilityTypes.js.map +1 -1
- package/lib/types/PhysicsTypes.js.map +1 -1
- package/lib/types/common.js.map +1 -1
- package/lib/types/constants/colors.js.map +1 -1
- package/lib/types/constants/designSystem.js.map +1 -1
- package/lib/types/constants/layout.js.map +1 -1
- package/lib/types/constants/performance.js.map +1 -1
- package/lib/types/constants/typography.js.map +1 -1
- package/lib/types/facial.js.map +1 -1
- package/lib/types/hand-animation.js.map +1 -1
- package/lib/types/injury.js.map +1 -1
- package/lib/types/physics.js.map +1 -1
- package/lib/types/skeletal.js.map +1 -1
- package/lib/types/techniqueId.js.map +1 -1
- package/lib/utils/accessibility.js.map +1 -1
- package/lib/utils/arenaWorldDimensions.js.map +1 -1
- package/lib/utils/assetConfig.js.map +1 -1
- package/lib/utils/characterScaling.js.map +1 -1
- package/lib/utils/colorHelpers.js.map +1 -1
- package/lib/utils/colorUtils.js.map +1 -1
- package/lib/utils/combatReadiness.js.map +1 -1
- package/lib/utils/controlMapping.js.map +1 -1
- package/lib/utils/deviceDetection.js.map +1 -1
- package/lib/utils/effectUtils.js.map +1 -1
- package/lib/utils/fabricTextures.js.map +1 -1
- package/lib/utils/hapticFeedback.js.map +1 -1
- package/lib/utils/haptics.js.map +1 -1
- package/lib/utils/htmlOverlayHelpers.js.map +1 -1
- package/lib/utils/inputSystem.js.map +1 -1
- package/lib/utils/koreanThemeHelpers.js.map +1 -1
- package/lib/utils/math.js.map +1 -1
- package/lib/utils/mobileLayoutHelpers.js.map +1 -1
- package/lib/utils/mobileUIUtils.js.map +1 -1
- package/lib/utils/performance/PerformanceMonitor.js.map +1 -1
- package/lib/utils/performance/PerformanceOverlay3D.js.map +1 -1
- package/lib/utils/performance/usePerformanceMonitor.js.map +1 -1
- package/lib/utils/performanceOptimization.js.map +1 -1
- package/lib/utils/player3DHelpers.js.map +1 -1
- package/lib/utils/playerUtils.js.map +1 -1
- package/lib/utils/responsiveLayout.js.map +1 -1
- package/lib/utils/responsiveLayoutHelpers.js.map +1 -1
- package/lib/utils/responsiveOrientationConstants.js.map +1 -1
- package/lib/utils/safeAreaUtils.js.map +1 -1
- package/lib/utils/sharedPhysicsConfig.js.map +1 -1
- package/lib/utils/skeletonScaling.js.map +1 -1
- package/lib/utils/stanceHelpers.js.map +1 -1
- package/lib/utils/threeObjectPool.js.map +1 -1
- package/lib/utils/visualEffects.js.map +1 -1
- package/package.json +8 -8
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"DecisionTree.js","names":[],"sources":["../../../src/systems/ai/DecisionTree.ts"],"sourcesContent":["/**\n * AI Decision Tree for Korean Martial Arts Combat\n * Strategic decision-making system with multiple tactical options\n *\n * **Korean Philosophy Integration (한국 무술 철학)**:\n * - 지피지기백전불태 (知彼知己百戰不殆) - Know the enemy, know yourself, and victory is certain\n * - 이순응변 (以柔應變) - Adapt with flexibility and flow like water\n * - 급소공격 (急所攻擊) - Strike vital points with precision and timing\n */\n\nimport { getArchetypePhysicalAttributes } from \"@/data/archetypePhysicalAttributes\";\nimport { PlayerState } from \"@/systems/player\";\nimport { TrigramSystem } from \"@/systems/TrigramSystem\";\nimport {\n KOREAN_VITAL_POINTS,\n getVitalPointById,\n} from \"@/systems/vitalpoint/KoreanVitalPoints\";\nimport { PlayerArchetype, Position, TrigramStance } from \"@/types\";\nimport { AI_MOVEMENT_METERS } from \"@/types/physicsConstants\";\nimport type { CounterOpportunity, ExposedLimbType } from \"@/types/physics\";\nimport { DifficultyParameters } from \"./AdaptiveDifficulty\";\nimport { AIPersonality, getArchetypeBehavior } from \"./AIPersonality\";\nimport { enforceArchetypeBehavior } from \"./ArchetypeEnforcer\";\nimport { AIComboSystem } from \"./ComboSystem\";\nimport {\n AIActionType,\n AIDecision,\n CombatContext,\n VulnerabilityContext,\n} from \"./types\";\n\n// Re-export types for backward compatibility\nexport { AIActionType } from \"./types\"; // Enum (value + type)\nexport type { AIDecision, CombatContext, VulnerabilityContext } from \"./types\"; // Type-only\n\n/**\n * Body pivot contributions for reach calculations (meters)\n * \n * These values represent additional reach gained from body mechanics:\n * - Hip rotation and torso lean for kicks\n * - Shoulder offset and torso rotation for punches\n * \n * Heuristically aligned with PhysicalReachCalculator.ts but intentionally\n * simplified for AI decision-making (e.g., omits stance/animation modifiers).\n * \n * **NOTE ON BODY RADIUS**: The body pivot values approximate the body radius\n * effect for AI range calculations. For precise hit detection in CombatSystem,\n * use calculateBodyRadius() which accounts for individual archetype differences.\n * \n * AI decision-making trade-off:\n * - Punch body pivot (shoulder offset + torso rotation): ~0.3-0.35m\n * - Actual body radius (shoulder width * 0.5 / 100): ~0.215-0.27m\n * - Difference: AI slightly overestimates close-range effectiveness\n * - Impact: AI may attempt attacks at marginally longer range than optimal\n * - Benefit: Simplified calculations, acceptable gameplay behavior\n * \n * @korean 신체 회전 도달 거리 증가 (AI 의사결정을 위한 근사치)\n */\nconst BODY_PIVOT_METERS = {\n /** Hip rotation + torso lean for kicks (meters) */\n KICK: 0.25,\n /** Torso rotation for punches (meters) */\n TORSO_ROTATION: 0.1,\n} as const;\n\n/**\n * Average technique extension multipliers\n * \n * Based on analysis of technique baseExtension values:\n * - Punches: 0.9-0.95 (average 0.95)\n * - Kicks: 1.0-1.05 (average 1.05)\n * \n * TODO: Consider implementing technique-specific lookup based on actual\n * technique data for more precise range calculations. Current averages\n * are sufficient for AI decision-making but may need refinement.\n * \n * @korean 기술 연장 배수\n */\nconst TECHNIQUE_EXTENSION = {\n /** Average punch baseExtension multiplier */\n PUNCH: 0.95,\n /** Average kick baseExtension multiplier */\n KICK: 1.05,\n} as const;\n\n/**\n * Distance-based stance preferences for tactical positioning\n *\n * Stances are categorized by optimal combat range based on actual technique\n * reach configurations (baseExtension values from technique definitions):\n *\n * **CLOSE (≤0.6m)** - Grappling/clinch range:\n * - TAE (☱ Lake) - Joint manipulation, grapples (0.9 baseExtension, needs contact)\n * - GAM (☵ Water) - Throws, counters (0.7-0.9 baseExtension)\n * - GON (☷ Earth) - Ground techniques, Ssireum throws (0.9 baseExtension)\n * - GAN (☶ Mountain) - Immovable defense, blocks (0.9 baseExtension)\n *\n * **MID (0.6-1.0m)** - Striking range:\n * - GEON (☰ Heaven) - Direct force, power strikes (0.95-1.05 baseExtension)\n * - LI (☲ Fire) - Precision nerve strikes (0.92-0.95 baseExtension)\n * - SON (☴ Wind) - Continuous pressure, Taekyon (0.95-1.05 baseExtension)\n *\n * **FAR (>1.0m)** - Long range kicks/closing distance:\n * - JIN (☳ Thunder) - Explosive jumping kicks (1.15 baseExtension)\n * - SON (☴ Wind) - Closing footwork pressure\n * - GAN (☶ Mountain) - Patient defensive waiting\n *\n * @korean 거리별 자세 선호도 (기술 도달 거리 기반)\n */\nconst DISTANCE_BASED_STANCES: Record<string, readonly TrigramStance[]> = {\n CLOSE: [\n TrigramStance.TAE, // ☱ Joint manipulation/grapples\n TrigramStance.GAM, // ☵ Throws/counters\n TrigramStance.GON, // ☷ Ground techniques\n TrigramStance.GAN, // ☶ Blocking defense\n ],\n MID: [\n TrigramStance.GEON, // ☰ Power strikes\n TrigramStance.LI, // ☲ Precision strikes\n TrigramStance.SON, // ☴ Continuous pressure\n ],\n FAR: [\n TrigramStance.JIN, // ☳ Explosive jumping kicks\n TrigramStance.SON, // ☴ Closing pressure\n TrigramStance.GAN, // ☶ Patient defensive waiting\n ],\n};\n\n/**\n * Hacker observation phase duration in milliseconds\n *\n * Hacker archetype observes opponents for this duration before attacking,\n * collecting combat data for analysis-based tactics.\n *\n * @korean 해커 관찰 단계 지속 시간 (밀리초)\n */\nconst HACKER_OBSERVATION_DURATION_MS = 10000; // 10 seconds\n\n/**\n * Hacker observation phase actions\n *\n * During the observation phase, Hacker only uses non-aggressive\n * positioning actions to collect combat data.\n *\n * @korean 해커 관찰 단계 행동\n */\nconst HACKER_OBSERVATION_ACTIONS: readonly AIActionType[] = [\n AIActionType.WAIT,\n AIActionType.CIRCLE,\n AIActionType.APPROACH,\n] as const;\n\n/**\n * Assess opponent vulnerability for exploitation tactics\n *\n * Analyzes multiple vulnerability factors to create comprehensive assessment:\n * - Balance states (HELPLESS/VULNERABLE/SHAKEN)\n * - Stamina depletion (< 20%)\n * - Ki depletion (< 10%)\n * - Composite vulnerability score (weighted average)\n *\n * **Korean Philosophy (취약성 평가)**:\n * Traditional Korean martial arts teach reading opponent's weakness:\n * - 기회 포착 (Gihoei Pochak) - Seizing opportunities\n * - 약점 공격 (Yakjeom Gonggyeok) - Exploiting weaknesses\n * - 결정타 (Gyeoljeongta) - Delivering decisive strikes\n *\n * @korean 상대 취약성 평가\n *\n * @param context - Current combat context with opponent state\n * @returns Vulnerability assessment with boolean flags and composite score\n */\nfunction assessVulnerability(context: CombatContext): VulnerabilityContext {\n // Balance-based vulnerability (Issue #enhance-intelligence-operative-ai)\n // Uses balance state enum from context: HELPLESS, VULNERABLE, SHAKEN, READY\n const isHelpless = context.opponentBalance === \"HELPLESS\";\n const isVulnerable =\n context.opponentBalance === \"VULNERABLE\" ||\n context.opponentBalance === \"HELPLESS\";\n const isShaken =\n context.opponentBalance === \"SHAKEN\" ||\n context.opponentBalance === \"VULNERABLE\" ||\n context.opponentBalance === \"HELPLESS\";\n\n // Resource-based vulnerability (Issue #enhance-intelligence-operative-ai)\n // Stamina and ki depletion indicate defensive weakness\n const staminaPercent =\n context.opponentStamina && context.opponentMaxStamina\n ? context.opponentStamina / context.opponentMaxStamina\n : 1.0;\n const kiPercent =\n context.opponentKi && context.opponentMaxKi\n ? context.opponentKi / context.opponentMaxKi\n : 1.0;\n\n const hasLowStamina = staminaPercent < 0.2; // < 20% stamina\n const hasNoKi = kiPercent < 0.1; // < 10% ki\n\n // Composite vulnerability score (0.0-1.0) with weighted factors:\n // - Balance: 40% weight (most critical - physical vulnerability)\n // - Stamina: 30% weight (affects defensive capability)\n // - Ki: 30% weight (affects technique usage)\n let balanceVulnerability = 0.0;\n if (isHelpless) balanceVulnerability = 1.0;\n else if (isVulnerable) balanceVulnerability = 0.7;\n else if (isShaken) balanceVulnerability = 0.5;\n\n const overallVulnerability =\n balanceVulnerability * 0.4 +\n (1 - staminaPercent) * 0.3 +\n (1 - kiPercent) * 0.3;\n\n return {\n isHelpless,\n isVulnerable,\n isShaken,\n hasLowStamina,\n hasNoKi,\n overallVulnerability,\n };\n}\n\n/**\n * AI Decision Tree System\n *\n * **Korean Combat Philosophy (한국 무술 철학)**:\n * This system embodies traditional Korean martial arts principles:\n *\n * - **팔괘 응용** (Trigram Application): Uses Eight Trigram system for stance transitions\n * - **급소 타격** (Vital Point Strikes): Targets anatomical weak points with precision\n * - **상황 판단** (Situational Awareness): Adapts tactics based on combat context\n * - **기술 조합** (Technique Combinations): Chains attacks into flowing combos\n * - **방어 우선** (Defense First): Prioritizes survival and tactical retreat when needed\n * - **취약성 공략** (Vulnerability Exploitation): Exploits defenseless states with precision (Issue #enhance-intelligence-operative-ai)\n */\nexport class AIDecisionTree {\n private lastDecisionTime = 0;\n private decisionCooldown = 50; // 50ms minimum between decisions\n private consecutiveAttacks = 0;\n private lastStanceChange = 0;\n private readonly stanceChangeCooldown = 3000; // 3 seconds\n\n // Psychological warfare tracking (Issue #enhance-intelligence-operative-ai)\n // Tracks cumulative psychological pressure for Intelligence Operative archetype\n private psychologicalPressure = 0; // 0-100: Cumulative psychological pressure\n private lastPressureActionTime = 0; // Timestamp of last pressure-building action\n\n // Systems for advanced decision-making\n private trigramSystem: TrigramSystem;\n private difficultyLevel: number = 0.5; // 0.0-1.0: AI skill level\n private difficultyParams?: DifficultyParameters; // Difficulty parameters for AI behavior\n private currentReactionDelay: number = 50; // Current reaction delay (calculated once per param change)\n\n /**\n * Scaling factor for fatigue override probability calculation.\n * Used to convert fatigue modifier to override chance in non-linear manner.\n * Value of 0.5 provides gradual scaling: 1.2x fatigue → ~10% override, 1.5x → ~25%.\n *\n * @korean 피로도 우선순위 무시 배율\n */\n private static readonly FATIGUE_OVERRIDE_SCALING_FACTOR = 0.5;\n\n constructor() {\n this.trigramSystem = new TrigramSystem();\n }\n\n /**\n * Calculate kick reach for an archetype based on physical attributes.\n * \n * **Physics-First**: Returns reach in METERS based on leg length.\n * Includes realistic kick reach calculation:\n * - Leg length converted from cm to meters\n * - Body pivot contribution (hip rotation + torso lean)\n * - Average baseExtension for kicks (1.05x multiplier)\n *\n * Formula: (legLength/100 + BODY_PIVOT_METERS.KICK) × TECHNIQUE_EXTENSION.KICK\n *\n * @param archetype - Player archetype to calculate reach for\n * @returns Kick reach in meters\n * @korean 발차기 도달 거리 (미터)\n */\n private getKickReachMeters(archetype: PlayerArchetype): number {\n const physical = getArchetypePhysicalAttributes(archetype);\n // Leg length in cm -> meters + body pivot * average kick baseExtension\n return (physical.legLength / 100 + BODY_PIVOT_METERS.KICK) * TECHNIQUE_EXTENSION.KICK;\n }\n\n /**\n * Calculate punch reach for an archetype based on physical attributes.\n * \n * **Physics-First**: Returns reach in METERS based on arm length.\n * Includes realistic punch reach calculation:\n * - Arm length converted from cm to meters\n * - Body pivot (shoulder offset + torso rotation)\n * - Average baseExtension for punches (0.95x multiplier)\n *\n * Formula: (armLength/100 + shoulderOffset + torsoRotation) × TECHNIQUE_EXTENSION.PUNCH\n *\n * @param archetype - Player archetype to calculate reach for\n * @returns Punch reach in meters\n * @korean 주먹 도달 거리 (미터)\n */\n private getPunchReachMeters(archetype: PlayerArchetype): number {\n const physical = getArchetypePhysicalAttributes(archetype);\n // Arm length in cm -> meters + body pivot * average punch baseExtension\n const shoulderOffset = physical.shoulderWidth / 2 / 100; // Convert cm to meters\n const bodyPivot = shoulderOffset + BODY_PIVOT_METERS.TORSO_ROTATION;\n return (physical.armLength / 100 + bodyPivot) * TECHNIQUE_EXTENSION.PUNCH;\n }\n\n /**\n * Calculate maximum combat reach for an archetype based on physical attributes.\n *\n * **Physics-First**: Returns reach in METERS based on leg length.\n * Kicks have the longest reach (~1.0-1.3m with body pivot contribution).\n *\n * Note: This is a heuristic approximation. Actual reach also depends on\n * stance modifiers and animation timing not included in AI range calculations.\n *\n * @param archetype - Player archetype to calculate reach for\n * @returns Maximum combat reach in meters\n * @korean 원형별 최대 도달 거리 (미터)\n */\n private getArchetypeMaxReach(archetype: PlayerArchetype): number {\n return this.getKickReachMeters(archetype);\n }\n\n /**\n * Get close range threshold for an archetype (punching/elbow distance).\n * Based on arm length from physical attributes.\n * \n * Uses the shared punch reach calculation helper for consistency.\n * \n * TODO: Extract to shared utility function with PhysicalReachCalculator.ts\n * to maintain consistency between AI range calculations and actual hit detection.\n * Consider handling elbow techniques separately with different body pivot values.\n *\n * @param archetype - Player archetype\n * @returns Close range threshold in meters\n * @korean 근접 범위 (미터)\n */\n private getArchetypeCloseRange(archetype: PlayerArchetype): number {\n return this.getPunchReachMeters(archetype);\n }\n\n /**\n * Get medium range threshold for an archetype (kicking distance).\n * Based on leg length from physical attributes.\n * \n * Includes realistic kick reach calculation:\n * - Leg length converted from cm to meters \n * - Body pivot contribution (hip rotation + torso lean)\n * - Average baseExtension for kicks\n *\n * @param archetype - Player archetype\n * @returns Medium range threshold in meters\n * @korean 중거리 범위 (미터)\n */\n private getArchetypeMediumRange(archetype: PlayerArchetype): number {\n return this.getKickReachMeters(archetype);\n }\n\n /**\n * Set AI difficulty level for vital point targeting accuracy\n * @param level - 0.0 (beginner) to 1.0 (master)\n */\n setDifficultyLevel(level: number): void {\n this.difficultyLevel = Math.max(0, Math.min(1, level));\n }\n\n /**\n * Set difficulty parameters for AI behavior\n * Affects reaction time, accuracy, decision quality, etc.\n *\n * Calculates a randomized reaction delay (within parameter range) once when\n * parameters change. This provides varied AI timing while maintaining consistent\n * behavior throughout the current parameter set.\n *\n * @korean 난이도 매개변수 설정\n * @param params - Difficulty parameters to apply\n */\n setDifficultyParameters(params: DifficultyParameters): void {\n this.difficultyParams = params;\n // Calculate randomized reaction delay once when params change\n // This provides variety while ensuring consistent timing until next param update\n if (params) {\n this.currentReactionDelay =\n params.reactionTimeMs.min +\n Math.random() * (params.reactionTimeMs.max - params.reactionTimeMs.min);\n }\n }\n\n /**\n * Check if kill mode should be activated based on archetype behavior\n *\n * Kill mode activates when:\n * - Opponent health is low (<30%)\n * - Opponent is in vulnerable balance state (HELPLESS/VULNERABLE)\n *\n * **Korean Philosophy (결정타 모드)**:\n * Each archetype activates kill mode differently based on combat philosophy:\n * - **Musa**: Honor demands finishing the fight decisively\n * - **Amsalja**: Opportunity for instant takedown with precision\n * - **Hacker**: Analytical window for calculated strike\n * - **Jeongbo Yowon**: Strategic opportunity for submission\n * - **Jojik Pokryeokbae**: Pragmatic moment to finish brutally\n *\n * @korean 결정타 모드 활성화 확인\n *\n * @param context - Current combat context\n * @param personality - AI personality archetype\n * @returns True if kill mode should be active\n */\n private isKillModeActive(\n context: CombatContext,\n personality: AIPersonality,\n ): boolean {\n const opponentHealthPercent =\n context.opponentHealth / context.playerMaxHealth;\n const isOpponentVulnerable =\n context.opponentBalance != null &&\n (context.opponentBalance === \"HELPLESS\" ||\n context.opponentBalance === \"VULNERABLE\");\n\n // Different activation thresholds based on archetype philosophy\n let healthThreshold = 0.3; // Default 30%\n\n switch (personality.archetype) {\n case PlayerArchetype.MUSA:\n // Aggressive: Activate kill mode early (honor code)\n healthThreshold = 0.3;\n break;\n case PlayerArchetype.AMSALJA:\n // Precise: Activate when perfect opportunity presents\n healthThreshold = 0.3;\n break;\n case PlayerArchetype.HACKER:\n // Analytical: Calculate optimal finishing window\n healthThreshold = 0.25; // More conservative, waits for clear advantage\n break;\n case PlayerArchetype.JEONGBO_YOWON:\n // Strategic: Balanced approach\n healthThreshold = 0.28;\n break;\n case PlayerArchetype.JOJIK_POKRYEOKBAE:\n // Pragmatic: Opportunistic finishing\n healthThreshold = 0.35; // Earlier activation, dirty fighter mentality\n break;\n }\n\n // Activate kill mode when opponent is low health OR vulnerable\n return opponentHealthPercent < healthThreshold || isOpponentVulnerable;\n }\n\n /**\n * Apply kill mode modifiers to action weights for finishing behavior\n *\n * **Kill Mode Behavior (결정타 행동)**:\n * Each archetype has unique finishing behavior based on combat philosophy:\n * - **Musa**: All-in overwhelming force (2.5x attack, 0x retreat)\n * - **Amsalja**: Instant takedown focus (3.0x technique, feints disabled)\n * - **Hacker**: Analytical precision (2.0x technique, counter focus)\n * - **Jeongbo Yowon**: Strategic control (1.8x technique, balanced approach)\n * - **Jojik Pokryeokbae**: Brutal pragmatism (2.2x attack, dirty tactics)\n *\n * @korean 결정타 모드 가중치 적용\n *\n * @param baseWeights - Base action weight multipliers\n * @param personality - AI personality archetype\n * @param isKillMode - Whether kill mode is active\n * @returns Modified action weights for kill mode\n */\n private applyKillModeModifiers(\n baseWeights: {\n attack: number;\n technique: number;\n defend: number;\n retreat: number;\n },\n personality: AIPersonality,\n isKillMode: boolean,\n ): { attack: number; technique: number; defend: number; retreat: number } {\n if (!isKillMode) {\n return baseWeights;\n }\n\n const modified = { ...baseWeights };\n\n // Apply archetype-specific kill mode behavior\n switch (personality.archetype) {\n case PlayerArchetype.MUSA:\n // Warrior: All-in overwhelming force (honor code)\n modified.attack *= 2.5; // Massive attack priority\n modified.technique *= 2.0; // Prefer powerful techniques\n modified.defend *= 0.2; // Minimal defense\n modified.retreat = 0.0; // No retreat (honor code)\n break;\n\n case PlayerArchetype.AMSALJA:\n // Assassin: Instant takedown focus (precision)\n modified.technique *= 3.0; // Prioritize lethal techniques\n modified.attack *= 1.5; // Quick finishers\n modified.defend *= 0.3; // Reduce defense during kill window\n // Note: retreat remains available for tactical repositioning\n break;\n\n case PlayerArchetype.HACKER:\n // Hacker: Analytical precision (calculated strike)\n modified.technique *= 2.0; // Calculated finishing techniques\n modified.attack *= 1.3; // Measured attacks\n modified.defend *= 0.7; // Maintain defensive awareness\n // Counter-attack focus through higher base defense\n break;\n\n case PlayerArchetype.JEONGBO_YOWON:\n // Intelligence Operative: Strategic control (psychological pressure)\n modified.technique *= 1.8; // Strategic techniques\n modified.attack *= 1.6; // Balanced offensive\n modified.defend *= 0.6; // Moderate defense reduction\n modified.retreat *= 0.5; // Tactical retreat available\n break;\n\n case PlayerArchetype.JOJIK_POKRYEOKBAE:\n // Organized Crime: Brutal pragmatism (dirty fighter)\n modified.attack *= 2.2; // Brutal finishing attacks\n modified.technique *= 1.7; // Dirty techniques\n modified.defend *= 0.4; // Reduced defense (pragmatic risk)\n modified.retreat *= 1.2; // Will retreat if needed (survival instinct)\n break;\n }\n\n return modified;\n }\n\n /**\n * Apply Intelligence Operative (Jeongbo Yowon) vulnerability exploitation\n *\n * Enhances decision weights to exploit opponent's defenseless states with precision:\n * - **HELPLESS (balance === HELPLESS)**: 90% takedown priority - Execute precision takedown\n * - **VULNERABLE (balance === VULNERABLE)**: 70% aggressive attack - Sustained pressure tactics\n * - **SHAKEN (balance === SHAKEN)**: 50% pressure increase - Psychological warfare\n * - **Low Stamina (< 20%)**: 60% exploitation - Force defensive positions\n * - **No Ki (< 10%)**: 50% technique spam - Prevent powerful techniques\n *\n * **Multiplier Stacking Behavior**:\n * When multiple vulnerabilities are present, multipliers stack multiplicatively:\n * - VULNERABLE (2.0x attack) + low stamina (1.5x attack) = 3.0x total attack multiplier\n * - VULNERABLE (1.8x technique) + low ki (1.4x technique) = 2.52x total technique multiplier\n * This creates increasingly aggressive exploitation as opponent becomes more vulnerable.\n * Note: HELPLESS state uses exclusive else-if, so it doesn't stack with VULNERABLE/SHAKEN.\n *\n * **Jeongbo Philosophy (정보요원 전략)**:\n * - Knowledge through observation (관찰을 통한 지식)\n * - Psychological manipulation (심리적 조작)\n * - Precise timing (정확한 타이밍)\n * - Strategic exploitation (전략적 공략)\n *\n * This function provides 3x higher vulnerability exploitation rate than Musa,\n * 2x higher psychological warfare usage than Amsalja, and 5x higher takedown\n * success rate when opponent is HELPLESS.\n *\n * @korean 정보요원 취약성 공략\n *\n * @param weights - Base action weight multipliers\n * @param vulnerability - Vulnerability assessment context\n * @param personality - AI personality archetype\n * @returns Modified action weights for Jeongbo exploitation\n */\n private applyJeongboExploitation(\n weights: {\n attack: number;\n technique: number;\n defend: number;\n feint: number;\n approach: number;\n circle: number;\n },\n vulnerability: VulnerabilityContext,\n personality: AIPersonality,\n ): {\n attack: number;\n technique: number;\n defend: number;\n feint: number;\n approach: number;\n circle: number;\n } {\n // Only Jeongbo gets vulnerability exploitation bonuses\n if (personality.archetype !== PlayerArchetype.JEONGBO_YOWON) {\n return weights;\n }\n\n const modified = { ...weights };\n\n // Check for psychological pressure buildup (Issue #enhance-intelligence-operative-ai Phase 3)\n const pressureStrike = this.shouldExecutePressureStrike(vulnerability);\n if (pressureStrike) {\n // Psychological pressure ≥ 50 + Opponent VULNERABLE = Decisive Strike\n modified.technique *= 3.0; // Execute decisive psychological technique\n modified.attack *= 0.3; // Reduce basic attacks\n return modified; // Override other modifiers for pressure strike\n }\n\n // HELPLESS: Execute precision takedown (90% priority)\n if (vulnerability.isHelpless) {\n modified.technique *= 5.0; // Massive technique priority for Precision Takedown\n modified.attack *= 0.2; // Reduce basic attacks\n modified.defend *= 0.1; // Minimal defense needed\n modified.feint *= 0.1; // No feinting during execution\n }\n // VULNERABLE: Aggressive pressure (70% priority)\n else if (vulnerability.isVulnerable) {\n modified.attack *= 2.0; // Increased attack frequency\n modified.technique *= 1.8; // Tactical Strike priority\n modified.feint *= 0.5; // Less feinting, more action\n modified.defend *= 0.5; // Reduced defense (maintain offensive)\n }\n // SHAKEN: Psychological warfare (50% priority)\n else if (vulnerability.isShaken) {\n modified.feint *= 2.0; // Increase psychological pressure\n modified.circle *= 1.5; // Intimidation tactics (circling)\n modified.technique *= 1.3; // \"Psychological Warfare\" technique\n }\n\n // Low stamina: Relentless pressure (60% priority)\n if (vulnerability.hasLowStamina) {\n modified.attack *= 1.5; // Force stamina drain\n modified.approach *= 1.3; // Close distance to pressure\n }\n\n // No ki: Technique spam (50% priority)\n if (vulnerability.hasNoKi) {\n modified.technique *= 1.4; // Opponent can't use powerful techniques\n modified.attack *= 1.3; // Maintain offensive\n }\n\n return modified;\n }\n\n /**\n * Build psychological pressure through intimidation tactics\n *\n * Intelligence Operative uses feints, circling, and approach/retreat patterns\n * to build cumulative psychological pressure on opponent. When pressure reaches\n * 50+ and opponent is VULNERABLE, triggers decisive strike.\n *\n * **Psychological Tactics (심리전 전술)**:\n * - Feints: +10 pressure (fake attacks create hesitation)\n * - Circling: +5 pressure (predator circling prey)\n * - Approach: +3 pressure (aggressive positioning)\n * - Decay: -3 pressure per second (pressure fades without action)\n *\n * @korean 심리적 압박 증가\n *\n * @param actionType - Type of action taken (FEINT, CIRCLE, APPROACH, etc.)\n * @param now - Current timestamp for decay calculation\n */\n private buildPsychologicalPressure(\n actionType: AIActionType,\n now: number,\n ): void {\n // Apply pressure decay (3 points per second since last pressure action)\n if (this.lastPressureActionTime > 0) {\n const timeSinceLastAction = now - this.lastPressureActionTime;\n const decayAmount = (timeSinceLastAction / 1000) * 3; // 3 points per second\n this.psychologicalPressure = Math.max(\n 0,\n this.psychologicalPressure - decayAmount,\n );\n }\n\n // Build pressure based on action type\n switch (actionType) {\n case AIActionType.FEINT:\n this.psychologicalPressure = Math.min(\n 100,\n this.psychologicalPressure + 10,\n );\n this.lastPressureActionTime = now;\n break;\n case AIActionType.CIRCLE:\n this.psychologicalPressure = Math.min(\n 100,\n this.psychologicalPressure + 5,\n );\n this.lastPressureActionTime = now;\n break;\n case AIActionType.APPROACH:\n this.psychologicalPressure = Math.min(\n 100,\n this.psychologicalPressure + 3,\n );\n this.lastPressureActionTime = now;\n break;\n // Attacks and techniques release pressure (execution phase)\n case AIActionType.ATTACK:\n case AIActionType.TECHNIQUE:\n // Pressure resets after decisive action\n this.psychologicalPressure = Math.max(\n 0,\n this.psychologicalPressure * 0.5,\n );\n break;\n }\n }\n\n /**\n * Check if psychological pressure should trigger decisive strike\n *\n * Jeongbo executes decisive technique when:\n * - Psychological pressure ≥ 50 (sustained intimidation)\n * - Opponent is VULNERABLE or worse (balance < 30)\n * - Returns true to boost technique priority\n *\n * @korean 심리적 압박 결정타 확인\n *\n * @param vulnerability - Vulnerability assessment context\n * @returns True if pressure warrants decisive strike\n */\n private shouldExecutePressureStrike(\n vulnerability: VulnerabilityContext,\n ): boolean {\n return (\n this.psychologicalPressure >= 50 &&\n (vulnerability.isVulnerable || vulnerability.isHelpless)\n );\n }\n\n /**\n * Analyze counter-attack opportunity from opponent's limb exposure.\n *\n * **Korean**: 반격 기회 분석 (Counter Opportunity Analysis)\n *\n * Detects when the opponent has exposed limbs during technique execution,\n * enabling defensive counter-attacks and breaking techniques.\n *\n * This integrates the LimbExposureSystem with AI decision-making by:\n * 1. Detecting exposed limbs from opponent's current technique\n * 2. Calculating vulnerability windows and multipliers\n * 3. Providing counter opportunity data for decision prioritization\n *\n * @param context - Combat context with opponent technique data\n * @returns Enhanced context with counter opportunity analysis, or undefined if no opportunity\n *\n * @remarks\n * Returns undefined if:\n * - Opponent is not executing a technique\n * - Technique has no exposure window defined\n * - Current time is outside the exposure window\n *\n * @korean 반격기회분석\n */\n private analyzeCounterOpportunity(\n context: CombatContext,\n ): {\n readonly counterOpportunity?: CounterOpportunity;\n readonly opponentVulnerability: number;\n } {\n // Use pre-calculated counter opportunity from context\n // (CombatSystem calculates this based on opponent's technique state)\n const counterOpportunity = context.counterOpportunity;\n \n // Calculate vulnerability multiplier from opportunity if present\n const opponentVulnerability = counterOpportunity?.vulnerabilityMultiplier ?? 1.0;\n\n return {\n counterOpportunity,\n opponentVulnerability,\n };\n }\n\n /**\n * Make strategic decision based on combat context\n *\n * Applies difficulty-based reaction time delays if difficulty parameters are set\n */\n makeDecision(\n context: CombatContext,\n personality: AIPersonality,\n comboSystem: AIComboSystem,\n ): AIDecision {\n const now = Date.now();\n\n // Apply difficulty-based reaction time delay (calculated once per param change)\n const reactionDelay = this.difficultyParams\n ? this.currentReactionDelay\n : this.decisionCooldown;\n\n // Respect decision cooldown (use reaction delay if difficulty params available)\n const effectiveCooldown = Math.max(this.decisionCooldown, reactionDelay);\n if (now - this.lastDecisionTime < effectiveCooldown) {\n return {\n action: AIActionType.WAIT,\n priority: 0,\n reason: this.difficultyParams\n ? `Reaction time delay: ${effectiveCooldown.toFixed(0)}ms`\n : \"Decision cooldown active\",\n };\n }\n\n this.lastDecisionTime = now;\n\n // Hacker observation phase (Issue #enforce-distinct-combat-philosophies)\n // Hacker archetype observes for first 10 seconds before attacking (data collection)\n // Skip observation phase during critical situations:\n // - Low health (below tactical retreat threshold)\n // - Kill mode opportunity (opponent < 30% health)\n // - Opponent vulnerable or helpless (exploitable state)\n const healthPercent = context.playerHealth / context.playerMaxHealth;\n const tacticalRetreatThreshold = personality.tacticalRetreatThreshold;\n const isBelowRetreatThreshold = healthPercent < tacticalRetreatThreshold; // Need to retreat\n const opponentMaxHealth =\n context.opponentMaxHealth ?? context.playerMaxHealth; // Use opponent max health if available, fallback to symmetric assumption\n const opponentHealthPercent = context.opponentHealth / opponentMaxHealth;\n const isKillOpportunity = opponentHealthPercent < 0.3; // Kill mode opportunity\n const isOpponentVulnerable =\n context.opponentBalance === \"VULNERABLE\" ||\n context.opponentBalance === \"HELPLESS\"; // Exploitable state\n\n if (\n personality.archetype === PlayerArchetype.HACKER &&\n context.timeInMatch < HACKER_OBSERVATION_DURATION_MS &&\n !isBelowRetreatThreshold && // Skip observation if need to retreat\n !isKillOpportunity && // Skip observation if kill opportunity\n !isOpponentVulnerable // Skip observation if opponent is vulnerable\n ) {\n // During observation phase, Hacker only circles and waits\n // No attacks or techniques until data collection is complete\n const randomObservationAction =\n HACKER_OBSERVATION_ACTIONS[\n Math.floor(Math.random() * HACKER_OBSERVATION_ACTIONS.length)\n ];\n\n return {\n action: randomObservationAction,\n priority: 10, // High priority to ensure observation phase is respected\n reason: `Hacker observation phase: collecting data (${(context.timeInMatch / 1000).toFixed(1)}s / ${HACKER_OBSERVATION_DURATION_MS / 1000}s) (해커 관찰 단계)`,\n };\n }\n\n // Check for kill mode activation (Issue #enhance-ai-aggression)\n const killModeActive = this.isKillModeActive(context, personality);\n\n // Assess opponent vulnerability for exploitation (Issue #enhance-intelligence-operative-ai)\n const vulnerability = assessVulnerability(context);\n\n // Analyze counter-attack opportunities from limb exposure (Limb Exposure System Integration)\n // This detects when opponent's technique execution exposes vulnerable limbs\n const counterAnalysis = this.analyzeCounterOpportunity(context);\n\n // Check for active combo first\n if (comboSystem.isComboActive()) {\n return this.decideComboAction(context, personality);\n }\n\n // Evaluate tactical options in priority order\n const decisions: AIDecision[] = [];\n\n // Get optimal range for this archetype (using context for meters conversion)\n const optimalRange = this.getOptimalRange(personality, context);\n const distance = context.distanceToOpponent;\n\n // 1. Critical health - survival priority\n decisions.push(this.evaluateSurvival(context, personality));\n\n // 2. Limb exposure counter-attack opportunity (highest tactical priority after survival)\n // Evaluates opportunities to exploit opponent's exposed limbs during technique execution\n if (counterAnalysis.counterOpportunity) {\n decisions.push(\n this.evaluateLimbExposureCounter(\n context,\n personality,\n counterAnalysis.counterOpportunity,\n counterAnalysis.opponentVulnerability,\n ),\n );\n }\n\n // 3. Standard counter-attack opportunity (opponent attacking)\n if (context.isOpponentAttacking) {\n decisions.push(\n this.evaluateCounter(context, personality, killModeActive),\n );\n }\n\n // 4. Combo initiation (only if at reasonable distance)\n if (distance < optimalRange * 1.5) {\n decisions.push(\n this.evaluateComboStart(context, personality, comboSystem),\n );\n }\n\n // 5. Stance transition\n decisions.push(this.evaluateStanceChange(context, personality, now));\n\n // 6. Feint attack (only at mid-close range) - reduced priority in kill mode\n if (distance < optimalRange * 1.8 && !killModeActive) {\n decisions.push(this.evaluateFeint(context, personality));\n }\n\n // 7. Distance-based tactics (archetype-aware ranges)\n // Increased multiplier from 1.2 to 2.0 to allow attacks at greater distances\n // Players start at ~1.6m apart, so this ensures AI can attack immediately\n // Using <= to include exact boundary cases (e.g., Jeongbo at 1.6m = 0.8m * 2.0)\n if (distance <= optimalRange * 2.0) {\n // Close to optimal range - use close-range tactics including vital point targeting\n decisions.push(\n this.evaluateCloseRange(context, personality, killModeActive),\n );\n } else if (distance > optimalRange * 2.5) {\n // Too far - need to approach\n decisions.push(\n this.evaluateApproach(context, personality, killModeActive),\n );\n } else {\n // Mid-range (>2.0x && <=2.5x optimal range) - good tactical position\n decisions.push(this.evaluateMidRange(context, personality));\n }\n\n // 8. Defensive positioning (reduced in kill mode)\n if (!killModeActive || personality.archetype !== PlayerArchetype.MUSA) {\n decisions.push(this.evaluateDefense(context, personality));\n }\n\n // Apply Jeongbo vulnerability exploitation (Issue #enhance-intelligence-operative-ai)\n // This provides 3x higher exploitation rate than other archetypes\n let modifiedDecisions = decisions;\n if (personality.archetype === PlayerArchetype.JEONGBO_YOWON) {\n modifiedDecisions = decisions.map((decision) => {\n // Skip survival decisions (preserve self-preservation)\n const SURVIVAL_REASON_KEYWORDS = [\n \"critical health\",\n \"high pain\",\n \"survival retreat\",\n \"emergency retreat\",\n \"위급 상황\",\n \"고통 회피\",\n ];\n const reasonLower = decision.reason.toLowerCase();\n const hasSurvivalKeyword = SURVIVAL_REASON_KEYWORDS.some((keyword) =>\n reasonLower.includes(keyword),\n );\n const isSurvivalRetreat =\n decision.action === AIActionType.RETREAT &&\n (decision.priority === 20 || hasSurvivalKeyword);\n\n if (isSurvivalRetreat) {\n return decision;\n }\n\n // Only apply exploitation to relevant action types\n // Other actions (COUNTER, RETREAT, WAIT, STANCE_CHANGE, COMBO) maintain original priority\n const exploitableActions = [\n AIActionType.ATTACK,\n AIActionType.TECHNIQUE,\n AIActionType.DEFEND,\n AIActionType.FEINT,\n AIActionType.APPROACH,\n AIActionType.CIRCLE,\n ];\n\n if (!exploitableActions.includes(decision.action)) {\n return decision; // Preserve priority for non-exploitable actions\n }\n\n // Calculate base action weights including feint, approach, circle\n const weights = {\n attack: decision.action === AIActionType.ATTACK ? 1.0 : 0.0,\n technique: decision.action === AIActionType.TECHNIQUE ? 1.0 : 0.0,\n defend: decision.action === AIActionType.DEFEND ? 1.0 : 0.0,\n feint: decision.action === AIActionType.FEINT ? 1.0 : 0.0,\n approach: decision.action === AIActionType.APPROACH ? 1.0 : 0.0,\n circle: decision.action === AIActionType.CIRCLE ? 1.0 : 0.0,\n };\n\n // Apply Jeongbo exploitation modifiers\n const modifiedWeights = this.applyJeongboExploitation(\n weights,\n vulnerability,\n personality,\n );\n\n // Adjust priority based on modified weights\n let newPriority = decision.priority;\n if (decision.action === AIActionType.ATTACK) {\n newPriority = decision.priority * modifiedWeights.attack;\n } else if (decision.action === AIActionType.TECHNIQUE) {\n newPriority = decision.priority * modifiedWeights.technique;\n } else if (decision.action === AIActionType.DEFEND) {\n newPriority = decision.priority * modifiedWeights.defend;\n } else if (decision.action === AIActionType.FEINT) {\n newPriority = decision.priority * modifiedWeights.feint;\n } else if (decision.action === AIActionType.APPROACH) {\n newPriority = decision.priority * modifiedWeights.approach;\n } else if (decision.action === AIActionType.CIRCLE) {\n newPriority = decision.priority * modifiedWeights.circle;\n }\n\n return { ...decision, priority: newPriority };\n });\n }\n\n // Apply kill mode modifiers to boost aggression (Issue #enhance-ai-aggression)\n if (killModeActive) {\n // Map decisions to new array with modified priorities\n modifiedDecisions = modifiedDecisions.map((decision) => {\n // CRITICAL: Survival decisions (retreat for self-preservation) should NOT be affected by kill mode\n // Kill mode is about finishing the opponent, not about ignoring the AI's own safety\n // Survival retreats are identified by priority 20 OR reason containing survival keywords\n // Survival retreat detection with English and Korean keywords\n const SURVIVAL_REASON_KEYWORDS = [\n // English survival indicators (lowercase)\n \"critical health\",\n \"high pain\",\n \"survival retreat\",\n \"emergency retreat\",\n // Korean survival indicators\n \"위급 상황\",\n \"고통 회피\",\n ];\n\n const reasonLower = decision.reason.toLowerCase();\n const hasSurvivalKeyword = SURVIVAL_REASON_KEYWORDS.some((keyword) =>\n reasonLower.includes(keyword),\n );\n\n const isSurvivalRetreat =\n decision.action === AIActionType.RETREAT &&\n (decision.priority === 20 || hasSurvivalKeyword);\n\n if (isSurvivalRetreat) {\n // This is a survival retreat decision - preserve its priority\n return decision;\n }\n\n // Calculate base action weights for non-survival decisions\n const weights = {\n attack: decision.action === AIActionType.ATTACK ? 1.0 : 0.0,\n technique: decision.action === AIActionType.TECHNIQUE ? 1.0 : 0.0,\n defend: decision.action === AIActionType.DEFEND ? 1.0 : 0.0,\n retreat: decision.action === AIActionType.RETREAT ? 1.0 : 0.0,\n };\n\n // Apply kill mode modifiers\n const modifiedWeights = this.applyKillModeModifiers(\n weights,\n personality,\n true,\n );\n\n // Adjust priority based on modified weights\n let newPriority = decision.priority;\n if (decision.action === AIActionType.ATTACK) {\n newPriority = decision.priority * modifiedWeights.attack;\n } else if (decision.action === AIActionType.TECHNIQUE) {\n newPriority = decision.priority * modifiedWeights.technique;\n } else if (decision.action === AIActionType.DEFEND) {\n newPriority = decision.priority * modifiedWeights.defend;\n } else if (decision.action === AIActionType.RETREAT) {\n newPriority = decision.priority * modifiedWeights.retreat;\n }\n\n return { ...decision, priority: newPriority };\n });\n\n // Select highest priority decision from modified array\n const bestDecision = modifiedDecisions.reduce((best, current) =>\n current.priority > best.priority ? current : best,\n );\n\n // Build psychological pressure for Jeongbo (Issue #enhance-intelligence-operative-ai Phase 3)\n if (personality.archetype === PlayerArchetype.JEONGBO_YOWON) {\n this.buildPsychologicalPressure(bestDecision.action, now);\n }\n\n // Track consecutive attacks\n if (\n bestDecision.action === AIActionType.ATTACK ||\n bestDecision.action === AIActionType.TECHNIQUE\n ) {\n this.consecutiveAttacks++;\n } else {\n this.consecutiveAttacks = 0;\n }\n\n // Apply archetype behavior enforcement (Issue #enforce-distinct-combat-philosophies)\n // This ensures each archetype has immediately recognizable combat patterns in kill mode\n const enforcedDecision = enforceArchetypeBehavior(\n bestDecision,\n personality.archetype,\n context,\n );\n\n return enforcedDecision;\n }\n\n // Normal mode: Select highest priority decision (may have Jeongbo exploitation applied)\n const bestDecision = modifiedDecisions.reduce((best, current) =>\n current.priority > best.priority ? current : best,\n );\n\n // Build psychological pressure for Jeongbo (Issue #enhance-intelligence-operative-ai Phase 3)\n if (personality.archetype === PlayerArchetype.JEONGBO_YOWON) {\n this.buildPsychologicalPressure(bestDecision.action, now);\n }\n\n // Track consecutive attacks\n if (\n bestDecision.action === AIActionType.ATTACK ||\n bestDecision.action === AIActionType.TECHNIQUE\n ) {\n this.consecutiveAttacks++;\n } else {\n this.consecutiveAttacks = 0;\n }\n\n // Apply archetype behavior enforcement (Issue #enforce-distinct-combat-philosophies)\n // This ensures each archetype has immediately recognizable combat patterns\n const enforcedDecision = enforceArchetypeBehavior(\n bestDecision,\n personality.archetype,\n context,\n );\n\n return enforcedDecision;\n }\n\n /**\n * Evaluate survival tactics when critically low health\n *\n * **Korean Philosophy (생존 전략)**:\n * - Consider both health and pain levels\n * - Archetype affects retreat threshold and behavior\n * - Honor code (Musa) prevents retreat above threshold\n */\n private evaluateSurvival(\n context: CombatContext,\n personality: AIPersonality,\n ): AIDecision {\n const healthPercent = context.playerHealth / context.playerMaxHealth;\n const painLevel = context.recentDamageTaken;\n\n // Get archetype behavior profile\n const behavior = getArchetypeBehavior(personality.archetype);\n\n // Check critical survival condition: low health OR (moderate health + high pain)\n const isCritical = healthPercent < personality.tacticalRetreatThreshold;\n const isHighPain = healthPercent < 0.5 && painLevel > 50;\n\n if (isCritical || isHighPain) {\n // Honor code: Musa never retreats above their threshold (30%)\n if (\n behavior.honorCode &&\n healthPercent > behavior.retreatThreshold / 100\n ) {\n return {\n action: AIActionType.WAIT,\n priority: 0,\n reason: `Honor code prevents retreat: ${(healthPercent * 100).toFixed(1)}% (명예 규범)`,\n };\n }\n\n const retreatVector = this.calculateRetreatPosition(context);\n\n return {\n action: AIActionType.RETREAT,\n targetPosition: retreatVector,\n priority: 20, // Highest priority - must always override kill mode aggression\n reason: isCritical\n ? `Critical health: ${(healthPercent * 100).toFixed(1)}% (위급 상황)`\n : `High pain: ${painLevel.toFixed(0)} (고통 회피)`,\n };\n }\n\n return { action: AIActionType.WAIT, priority: 0, reason: \"Health stable\" };\n }\n\n /**\n * Evaluate counter-attack opportunity\n *\n * **Kill Mode Enhancement (결정타 반격)**:\n * All archetypes enhance counter behavior during kill mode based on philosophy:\n * - **Musa**: Increased counter frequency (honor demands swift response)\n * - **Amsalja**: Enhanced counter timing with precision strikes\n * - **Hacker**: Calculated counter-attacks (analytical opportunity)\n * - **Jeongbo Yowon**: Strategic counters (psychological advantage)\n * - **Jojik Pokryeokbae**: Opportunistic counters (dirty tactics)\n *\n * @param context - Combat context\n * @param personality - AI personality\n * @param killModeActive - Whether kill mode is active\n */\n private evaluateCounter(\n context: CombatContext,\n personality: AIPersonality,\n killModeActive: boolean = false,\n ): AIDecision {\n // Base counter chance affected by defense preference\n let counterChance = personality.defensePreference * 0.8;\n let counterPriority = 8;\n\n // Kill mode: Archetype-specific counter behavior enhancements\n if (killModeActive) {\n switch (personality.archetype) {\n case PlayerArchetype.MUSA:\n // Musa: Honor code demands swift aggressive counter\n counterChance = Math.min(0.95, counterChance + 0.3); // +30% counter chance\n counterPriority = 9; // Highest priority counter\n break;\n\n case PlayerArchetype.AMSALJA:\n // Amsalja: Precision counter-strikes for instant takedown\n counterChance = Math.min(0.9, counterChance + 0.25); // +25% counter chance\n counterPriority = 9; // Highest priority counter\n break;\n\n case PlayerArchetype.HACKER:\n // Hacker: Calculated counter with analytical precision\n counterChance = Math.min(0.85, counterChance + 0.15); // +15% counter chance\n counterPriority = 9; // Enhanced priority for analytical strike\n break;\n\n case PlayerArchetype.JEONGBO_YOWON:\n // Jeongbo Yowon: Strategic counter with psychological pressure\n counterChance = Math.min(0.8, counterChance + 0.2); // +20% counter chance\n counterPriority = 8; // Moderate priority increase\n break;\n\n case PlayerArchetype.JOJIK_POKRYEOKBAE:\n // Jojik: Opportunistic dirty counter\n counterChance = Math.min(0.85, counterChance + 0.25); // +25% counter chance\n counterPriority = 8; // Pragmatic priority\n break;\n }\n }\n\n // Use archetype-based max reach for counter distance check\n const maxReach = this.getArchetypeMaxReach(personality.archetype);\n const shouldCounter =\n Math.random() < counterChance && context.distanceToOpponent < maxReach;\n\n if (shouldCounter) {\n let killModeReason = \"\";\n if (killModeActive) {\n switch (personality.archetype) {\n case PlayerArchetype.MUSA:\n killModeReason = \" - 명예 반격 (honor counter)\";\n break;\n case PlayerArchetype.AMSALJA:\n killModeReason = \" - 정밀 반격 (precision counter)\";\n break;\n case PlayerArchetype.HACKER:\n killModeReason = \" - 분석 반격 (analytical counter)\";\n break;\n case PlayerArchetype.JEONGBO_YOWON:\n killModeReason = \" - 전략 반격 (strategic counter)\";\n break;\n case PlayerArchetype.JOJIK_POKRYEOKBAE:\n killModeReason = \" - 기습 반격 (opportunistic counter)\";\n break;\n }\n }\n\n return {\n action: AIActionType.COUNTER,\n priority: counterPriority,\n reason: `Opponent attacking - counter opportunity${killModeReason}`,\n };\n }\n\n return {\n action: AIActionType.DEFEND,\n priority: 6,\n reason: \"Opponent attacking - defensive stance\",\n };\n }\n\n /**\n * Evaluate counter-attack opportunity from limb exposure.\n *\n * **Korean**: 사지 노출 반격 평가 (Limb Exposure Counter Evaluation)\n *\n * Analyzes opportunities to exploit opponent's exposed limbs during technique\n * execution. This is higher priority than standard counters because it targets\n * specific anatomical vulnerabilities.\n *\n * **Defensive Archetype Priority**:\n * Defensive archetypes (high defensiveness) strongly favor counter-attacks:\n * - **Musa**: Honorable defense with precise counters (defensiveness ~0.7)\n * - **Amsalja**: Patient assassin waiting for openings (defensiveness ~0.6)\n * - **Jeongbo Yowon**: Analytical exploitation of weaknesses (defensiveness ~0.5)\n *\n * **Breaking Techniques**:\n * When allowsBreaking is true, AI can execute joint locks and limb breaks\n * for severe damage and mobility reduction.\n *\n * @param context - Combat context with positioning\n * @param personality - AI personality with archetype and defensiveness\n * @param counterOpportunity - Detected limb exposure opportunity\n * @param opponentVulnerability - Opponent's vulnerability multiplier\n * @returns Counter-attack decision with priority based on archetype\n *\n * @korean 사지노출반격평가\n */\n private evaluateLimbExposureCounter(\n context: CombatContext,\n personality: AIPersonality,\n counterOpportunity: CounterOpportunity,\n opponentVulnerability: number,\n ): AIDecision {\n // Base counter priority starts high (limb exposure is a prime opportunity)\n let counterPriority = 9;\n\n // Defensive archetypes prioritize counter-attacks significantly\n // Musa (defensiveness ~0.7): +2.1 bonus → priority 11+\n // Amsalja (defensiveness ~0.6): +1.8 bonus → priority 10+\n // Jeongbo (defensiveness ~0.5): +1.5 bonus → priority 10+\n const defensivenessBonus = personality.defensePreference * 3;\n counterPriority += defensivenessBonus;\n\n // Breaking opportunities are extremely high value\n if (counterOpportunity.allowsBreaking) {\n counterPriority += 2;\n }\n\n // High vulnerability multipliers increase priority\n if (opponentVulnerability > 2.0) {\n counterPriority += 1; // Severely overextended\n } else if (opponentVulnerability > 1.5) {\n counterPriority += 0.5; // Moderately vulnerable\n }\n\n // Check if AI has sufficient resources to execute counter\n const hasStamina = context.playerStamina > context.playerMaxStamina * 0.2;\n if (!hasStamina) {\n // Low stamina reduces priority but doesn't eliminate opportunity\n counterPriority -= 3;\n }\n\n // Distance check - counter must be executable at current range\n const maxCounterRange = 1.0; // 1 meter max for counter-attacks\n if (context.distanceToOpponent > maxCounterRange) {\n // Too far to execute counter\n return {\n action: AIActionType.WAIT,\n priority: 0,\n reason: `Limb exposed but too far: ${context.distanceToOpponent.toFixed(2)}m > ${maxCounterRange}m`,\n };\n }\n\n // Build descriptive reason with Korean translation\n const exposedLimbKorean = this.translateExposedLimb(\n counterOpportunity.exposedLimb,\n );\n const breakingNote = counterOpportunity.allowsBreaking\n ? \" - 파쇄 가능 (breaking possible)\"\n : \"\";\n const vulnerabilityNote = ` (취약성: ${opponentVulnerability.toFixed(1)}x)`;\n\n return {\n action: AIActionType.COUNTER,\n priority: counterPriority,\n reason: `Exposed limb counter: ${counterOpportunity.exposedLimb} (${exposedLimbKorean})${breakingNote}${vulnerabilityNote}`,\n };\n }\n\n /**\n * Translate exposed limb type to Korean.\n *\n * **Korean**: 노출 사지 번역 (Exposed Limb Translation)\n *\n * @param exposedLimb - English limb type identifier\n * @returns Korean translation\n *\n * @korean 노출사지번역\n */\n private translateExposedLimb(exposedLimb: ExposedLimbType): string {\n const translations: Record<ExposedLimbType, string> = {\n left_arm: \"왼팔\",\n right_arm: \"오른팔\",\n left_elbow: \"왼팔꿈치\",\n right_elbow: \"오른팔꿈치\",\n left_wrist: \"왼손목\",\n right_wrist: \"오른손목\",\n left_leg: \"왼다리\",\n right_leg: \"오른다리\",\n left_knee: \"왼무릎\",\n right_knee: \"오른무릎\",\n left_ankle: \"왼발목\",\n right_ankle: \"오른발목\",\n };\n\n return translations[exposedLimb];\n }\n\n /**\n * Evaluate combo initiation (fix for issue #2529467014)\n */\n private evaluateComboStart(\n context: CombatContext,\n personality: AIPersonality,\n comboSystem: AIComboSystem,\n ): AIDecision {\n // Check if combo system is already active\n if (comboSystem.isComboActive()) {\n return {\n action: AIActionType.WAIT,\n priority: 0,\n reason: \"Combo already active\",\n };\n }\n\n // Don't start combo if already in consecutive attacks\n if (this.consecutiveAttacks > 0) {\n return {\n action: AIActionType.WAIT,\n priority: 0,\n reason: \"Combo cooldown\",\n };\n }\n\n const hasResources =\n context.playerKi > context.playerMaxKi * 0.3 &&\n context.playerStamina > context.playerMaxStamina * 0.3;\n\n // Use archetype-based medium range (leg length) for combo distance check\n const mediumRange = this.getArchetypeMediumRange(personality.archetype);\n const goodDistance = context.distanceToOpponent < mediumRange;\n const comboChance = Math.random() < personality.comboTendency;\n\n if (hasResources && goodDistance && comboChance) {\n return {\n action: AIActionType.COMBO,\n priority: 7,\n reason: \"Initiating combo sequence\",\n };\n }\n\n return {\n action: AIActionType.WAIT,\n priority: 0,\n reason: \"Combo conditions not met\",\n };\n }\n\n /**\n * Select stance based on distance to opponent\n *\n * Chooses optimal stance for current combat range, prioritizing:\n * 1. Overlap between distance-optimal stances and archetype preferred stances\n * 2. Any distance-optimal stance if no overlap exists (expands tactical repertoire)\n * 3. Avoids switching to current stance\n *\n * **Distance Categories**:\n * - CLOSE (≤2 cells / 80px): GEON, JIN, LI, SON - Aggressive close-quarters stances\n * - MID (3-4 cells / 120-160px): GAM, TAE, GAN - Adaptive mid-range stances\n * - FAR (≥5 cells / 200px+): GAN, GON - Defensive distance stances\n *\n * @korean 거리별 자세 선택\n *\n * **Physics-First**: Distance parameter is in METERS.\n *\n * @param distance - Distance to opponent in meters\n * @param preferredStances - Archetype's preferred stances\n * @param currentStance - Current stance (to avoid redundant switches)\n * @returns Optimal stance for distance, or undefined if no valid options\n */\n private selectStanceForDistance(\n distance: number,\n preferredStances: readonly TrigramStance[],\n currentStance: TrigramStance,\n archetype: PlayerArchetype,\n ): TrigramStance | undefined {\n // Determine distance category using archetype-based physical attributes\n // Close = arm length (punching range), Medium = leg length (kicking range)\n const closeRange = this.getArchetypeCloseRange(archetype);\n const mediumRange = this.getArchetypeMediumRange(archetype);\n\n let distanceCategory: string;\n if (distance <= closeRange) {\n distanceCategory = \"CLOSE\";\n } else if (distance <= mediumRange) {\n distanceCategory = \"MID\";\n } else {\n distanceCategory = \"FAR\";\n }\n\n const optimalStances = DISTANCE_BASED_STANCES[distanceCategory];\n\n // Find overlap between optimal stances and preferred stances\n const candidates = optimalStances.filter((s) =>\n preferredStances.includes(s),\n );\n\n // If no overlap, use any optimal stance (expand tactical repertoire)\n const finalCandidates = candidates.length > 0 ? candidates : optimalStances;\n\n // Don't switch to current stance\n const filtered = finalCandidates.filter((s) => s !== currentStance);\n\n if (filtered.length === 0) {\n return undefined;\n }\n\n return filtered[Math.floor(Math.random() * filtered.length)];\n }\n\n /**\n * Evaluate stance change using TrigramSystem and distance-based selection\n *\n * **Korean Philosophy (자세 전환)**:\n * Uses I Ching-based trigram system to find optimal stance transitions.\n * Considers resource costs, counter-stance effectiveness, archetype preferences,\n * and distance-based tactical positioning.\n *\n * **Dynamic Stance Rotation (Issue #dynamic-ai-stance-rotation)**:\n * - Integrates distance-based stance selection for tactical variety\n * - Prioritizes counter-stances for opponent matchup advantage\n * - Expands tactical repertoire beyond archetype preferences when needed\n */\n private evaluateStanceChange(\n context: CombatContext,\n personality: AIPersonality,\n now: number,\n ): AIDecision {\n // Respect stance change cooldown\n if (now - this.lastStanceChange < this.stanceChangeCooldown) {\n return {\n action: AIActionType.WAIT,\n priority: 0,\n reason: \"Stance change on cooldown\",\n };\n }\n\n const behavior = getArchetypeBehavior(personality.archetype);\n\n // Apply stance fatigue modifier (Issue #dynamic-ai-stance-rotation Phase 4)\n // Increases stance switch probability based on time in current stance:\n // - After 10 seconds: +20% increase (1.2x multiplier)\n // - After 20 seconds: +50% increase (1.5x multiplier)\n const timeInStance = Math.max(0, context.stanceFatigue?.timeInStance ?? 0);\n const fatigueModifier = this.getStanceFatigueModifier(timeInStance);\n const adjustedSwitchFrequency = Math.min(\n 0.95,\n personality.stanceSwitchFrequency * fatigueModifier,\n );\n\n // Single random check using fatigue-adjusted frequency to avoid compounding probability\n const shouldChange = Math.random() < adjustedSwitchFrequency;\n if (!shouldChange) {\n return {\n action: AIActionType.WAIT,\n priority: 0,\n reason:\n fatigueModifier > 1.0\n ? `Stance change deferred (fatigue: ${fatigueModifier.toFixed(2)}x, probability: ${(adjustedSwitchFrequency * 100).toFixed(1)}%)`\n : \"No stance change needed\",\n };\n }\n\n // Check if already in a preferred stance - if so, reduce change chance (but not completely)\n // This check only applies outside combat to avoid stance lock during active fighting\n const inPreferredStance = behavior.preferredStances.includes(\n context.playerStance,\n );\n if (\n inPreferredStance &&\n !context.isOpponentAttacking &&\n Math.random() < 0.6\n ) {\n // 60% chance to stay in preferred stance when not under immediate pressure\n // However, fatigue can override this (higher fatigue = more likely to switch anyway)\n // Use non-linear scaling for gradual, predictable override behavior:\n // - At 1.2x fatigue (10s): ~10% override chance\n // - At 1.5x fatigue (20s): ~25% override chance\n // - Caps at 80% to preserve some tactical consideration\n const fatigueOverrideProbability =\n fatigueModifier > 1.0\n ? Math.min(\n 0.8,\n (fatigueModifier - 1.0) *\n AIDecisionTree.FATIGUE_OVERRIDE_SCALING_FACTOR,\n )\n : 0;\n const fatigueOverride =\n fatigueModifier > 1.2 && Math.random() < fatigueOverrideProbability;\n if (!fatigueOverride) {\n return {\n action: AIActionType.WAIT,\n priority: 0,\n reason: \"Already in preferred stance (선호 자세 유지)\",\n };\n }\n }\n\n // Create a minimal PlayerState object with only the properties actually used\n const playerState = {\n currentStance: context.playerStance,\n ki: context.playerKi,\n stamina: context.playerStamina,\n archetype: personality.archetype,\n } as unknown as PlayerState;\n\n // Priority 1: Try distance-based stance selection for tactical variety\n // Lower priority than attacks but competitive with approach/defense\n const distanceStance = this.selectStanceForDistance(\n context.distanceToOpponent,\n behavior.preferredStances,\n context.playerStance,\n personality.archetype,\n );\n\n if (\n distanceStance &&\n this.trigramSystem.canTransitionTo(\n context.playerStance,\n distanceStance,\n playerState,\n )\n ) {\n this.lastStanceChange = now;\n return {\n action: AIActionType.STANCE_CHANGE,\n targetStance: distanceStance,\n priority: 5, // Competitive with approach (4-6) but below attacks (7-10)\n reason: `Distance-optimal stance (거리 최적 자세: ${distanceStance})`,\n };\n }\n\n // Priority 2: Try counter-stance for opponent matchup\n const counterStance = this.trigramSystem.getCounterStance(\n context.opponentStance,\n );\n if (\n counterStance !== context.playerStance &&\n this.trigramSystem.canTransitionTo(\n context.playerStance,\n counterStance,\n playerState,\n )\n ) {\n this.lastStanceChange = now;\n return {\n action: AIActionType.STANCE_CHANGE,\n targetStance: counterStance,\n priority: 5, // Competitive with approach (4-6) but below attacks (7-10)\n reason: `Counter stance to ${context.opponentStance} (상극 대응)`,\n };\n }\n\n // Priority 3: Use TrigramSystem to recommend optimal stance\n const recommendedStance = this.trigramSystem.recommendStance(playerState);\n\n if (\n this.trigramSystem.canTransitionTo(\n context.playerStance,\n recommendedStance,\n playerState,\n )\n ) {\n this.lastStanceChange = now;\n return {\n action: AIActionType.STANCE_CHANGE,\n targetStance: recommendedStance,\n priority: 4, // Lower priority for general optimization\n reason: `Optimal stance transition via TrigramSystem (팔괘 전환)`,\n };\n }\n\n // Priority 4: Try archetype-preferred stance\n const preferredAvailable = behavior.preferredStances.find(\n (stance) =>\n stance !== context.playerStance &&\n this.trigramSystem.canTransitionTo(\n context.playerStance,\n stance,\n playerState,\n ),\n );\n\n if (preferredAvailable) {\n this.lastStanceChange = now;\n return {\n action: AIActionType.STANCE_CHANGE,\n targetStance: preferredAvailable,\n priority: 4, // Lower priority for preferred stance switch\n reason: `Switching to preferred stance (선호 자세 전환: ${preferredAvailable})`,\n };\n }\n\n // No viable stance change available\n return {\n action: AIActionType.WAIT,\n priority: 0,\n reason: \"No viable stance transition\",\n };\n }\n\n /**\n * Evaluate feint attack\n */\n private evaluateFeint(\n context: CombatContext,\n personality: AIPersonality,\n ): AIDecision {\n // Use archetype-based max reach for feint distance check\n const maxReach = this.getArchetypeMaxReach(personality.archetype);\n const shouldFeint =\n Math.random() < personality.feintChance &&\n context.distanceToOpponent < maxReach;\n\n if (shouldFeint) {\n return {\n action: AIActionType.FEINT,\n priority: 4,\n reason: \"Feinting to bait opponent\",\n };\n }\n\n return {\n action: AIActionType.WAIT,\n priority: 0,\n reason: \"No feint opportunity\",\n };\n }\n\n /**\n * Evaluate close range tactics with vital point targeting\n *\n * **Korean Philosophy (급소 공격)**:\n * At close range, AI targets specific vital points based on difficulty level.\n * Higher difficulty = more precise targeting of critical points.\n *\n * **Kill Mode Enhancement (결정타)**:\n * When kill mode is active, AI prioritizes finishing techniques with boosted priority.\n *\n * @param context - Combat context\n * @param personality - AI personality\n * @param killModeActive - Whether kill mode is active (opponent <30% health or vulnerable)\n */\n private evaluateCloseRange(\n context: CombatContext,\n personality: AIPersonality,\n killModeActive: boolean = false,\n ): AIDecision {\n const hasResources = context.playerKi > 10 && context.playerStamina > 15;\n const aggression = personality.aggressionLevel;\n\n // Select vital point target based on difficulty\n const targetVitalPoint = this.selectVitalPointTarget(context, personality);\n\n // Get Korean name for logging if vital point is selected\n const vitalPointName = targetVitalPoint\n ? (getVitalPointById(targetVitalPoint)?.names.korean ?? targetVitalPoint)\n : undefined;\n\n // Kill mode: Prioritize finishing attacks with maximum aggression\n if (killModeActive) {\n const killModeSuffix =\n personality.archetype === PlayerArchetype.MUSA\n ? \" (결정타 - 압도적 공격)\"\n : \" (결정타 - 즉사 기술)\";\n\n if (hasResources) {\n return {\n action: AIActionType.TECHNIQUE,\n targetVitalPoint,\n priority: targetVitalPoint ? 10 : 9, // Maximum priority - finish the fight\n reason: targetVitalPoint\n ? `Kill mode - finishing technique on vital point (급소 결정타: ${vitalPointName})${killModeSuffix}`\n : `Kill mode - finishing technique${killModeSuffix}`,\n };\n } else {\n return {\n action: AIActionType.ATTACK,\n targetVitalPoint,\n priority: targetVitalPoint ? 9 : 8, // Very high priority\n reason: targetVitalPoint\n ? `Kill mode - finishing attack (결정타 급소: ${vitalPointName})${killModeSuffix}`\n : `Kill mode - finishing attack${killModeSuffix}`,\n };\n }\n }\n\n // Normal close range behavior - expert fighters attack aggressively\n // Higher base attack chance for aggressive AI (aggression * 0.95 instead of 0.8)\n // Use single random value to determine action type fairly\n const actionRoll = Math.random();\n const attackThreshold = aggression * 0.95;\n const techniqueThreshold = hasResources ? aggression * 0.5 : 0; // Additional threshold for techniques\n\n if (actionRoll < attackThreshold) {\n return {\n action: AIActionType.ATTACK,\n targetVitalPoint,\n priority: targetVitalPoint ? 8 : 7, // High priority - attacks win over stance changes\n reason: targetVitalPoint\n ? `Close range - vital point attack (급소 타격: ${vitalPointName})`\n : \"Close range - aggressive strike\",\n };\n } else if (\n actionRoll < attackThreshold + techniqueThreshold &&\n hasResources\n ) {\n return {\n action: AIActionType.TECHNIQUE,\n targetVitalPoint,\n priority: targetVitalPoint ? 8 : 7, // High priority for techniques too\n reason: targetVitalPoint\n ? `Close range - technique on vital point (급소 기술: ${vitalPointName})`\n : \"Close range - technique execution\",\n };\n } else {\n return {\n action: AIActionType.DEFEND,\n priority: 4,\n reason: \"Close range - defensive posture (방어 자세)\",\n };\n }\n }\n\n /**\n * Select vital point to target based on difficulty and stance\n *\n * **Korean Philosophy (급소 선택)**:\n * - Beginner AI: Random targeting or no specific target\n * - Intermediate AI: Favors easier vital points\n * - Advanced AI: Targets appropriate points for current stance\n * - Master AI: Targets critical points with high precision\n */\n private selectVitalPointTarget(\n context: CombatContext,\n personality: AIPersonality,\n ): string | undefined {\n // Guard: Ensure vital points are available\n if (KOREAN_VITAL_POINTS.length === 0) {\n return undefined;\n }\n\n // Check if AI attempts vital point targeting based on difficulty\n const targetChance = this.difficultyLevel * personality.aggressionLevel;\n if (Math.random() > targetChance) {\n return undefined; // No specific vital point target\n }\n\n // Filter vital points by effective stance\n const effectivePoints = KOREAN_VITAL_POINTS.filter((point) =>\n point.effectiveStances?.includes(context.playerStance),\n );\n\n if (effectivePoints.length === 0) {\n // Fallback to any vital point\n const randomIndex = Math.floor(\n Math.random() * KOREAN_VITAL_POINTS.length,\n );\n return KOREAN_VITAL_POINTS[randomIndex].id;\n }\n\n // Select based on difficulty level\n if (this.difficultyLevel < 0.3) {\n // Beginner: Random selection from effective points.\n // NOTE: This uses Math.random(), which is not seeded and thus not deterministic.\n // For reproducible AI behavior (e.g., in testing or balancing), consider using a seeded RNG.\n // Also, this \"beginner\" AI still filters by effective points (stance-appropriate), which may be more sophisticated than a true novice.\n // If true beginner behavior is desired, select from all KOREAN_VITAL_POINTS instead.\n const randomIndex = Math.floor(Math.random() * effectivePoints.length);\n return effectivePoints[randomIndex].id;\n } else if (this.difficultyLevel < 0.6) {\n // Intermediate: Prefer easier targets (lower difficulty)\n const easierPoints = effectivePoints.filter(\n (p) => p.targetingDifficulty < 0.7,\n );\n\n if (easierPoints.length > 0) {\n // Sort without mutating original array\n const sortedEasierPoints = [...easierPoints].sort(\n (a, b) => a.targetingDifficulty - b.targetingDifficulty,\n );\n return sortedEasierPoints[0].id;\n }\n return effectivePoints[0].id;\n } else {\n // Advanced/Master: Target high-value critical points\n const criticalPoints = effectivePoints.filter(\n (p) => p.severity === \"critical\" || p.severity === \"major\",\n );\n\n if (criticalPoints.length > 0) {\n // Sort without mutating original array\n const sortedCritical = [...criticalPoints].sort(\n (a, b) => (b.baseDamage ?? 0) - (a.baseDamage ?? 0),\n );\n return sortedCritical[0].id;\n }\n\n // Fallback to highest damage point (guaranteed to exist due to check at line 456)\n const sortedByDamage = [...effectivePoints].sort(\n (a, b) => (b.baseDamage ?? 0) - (a.baseDamage ?? 0),\n );\n return sortedByDamage[0]?.id ?? effectivePoints[0].id;\n }\n }\n\n /**\n * Calculate stance fatigue modifier for increased switching probability\n *\n * Applies time-based modifiers to encourage dynamic stance rotation:\n * - 0-10 seconds: No modifier (1.0x)\n * - 10-20 seconds: +20% increase (1.2x multiplier)\n * - 20+ seconds: +50% increase (1.5x multiplier)\n *\n * This ensures AI doesn't stay locked in one stance for extended periods,\n * promoting the use of all 8 trigram stances throughout combat.\n *\n * **Korean Philosophy (자세 피로도)**:\n * Remaining in one stance too long reduces tactical flexibility and\n * makes the fighter predictable. The Eight Trigram system requires\n * constant adaptation and flow between stances.\n *\n * @korean 자세 피로도 배율 계산\n *\n * @param timeInStance - Time in current stance in milliseconds\n * @returns Stance switch frequency multiplier (1.0 = no change, >1.0 = increased probability)\n */\n private getStanceFatigueModifier(timeInStance: number): number {\n if (timeInStance > 20000) {\n return 1.5; // 50% increase after 20 seconds\n }\n if (timeInStance > 10000) {\n return 1.2; // 20% increase after 10 seconds\n }\n return 1.0; // No modifier for first 10 seconds\n }\n\n /**\n * Get optimal combat range based on AI personality archetype\n *\n * Uses archetype behavior profiles to determine preferred combat distance.\n * **Physics-First**: Returns distance in METERS directly.\n *\n * @param personality - AI personality with archetype behavior\n * @param _context - Combat context (unused, kept for API compatibility)\n * @returns Optimal range in meters\n * @korean 최적 전투 거리 - 원형별 선호 거리 (미터)\n */\n private getOptimalRange(\n personality: AIPersonality,\n _context?: CombatContext,\n ): number {\n // Get archetype behavior profile\n const behavior = getArchetypeBehavior(personality.archetype);\n\n // Convert optimal range from grid cells to meters (1 cell ≈ 0.4m)\n // Physics-first: return directly in meters\n return behavior.optimalRange * 0.4;\n }\n\n /**\n * Evaluate approach tactics with archetype-specific behavior\n *\n * **Korean Philosophy (접근 전략)**:\n * - Musa charges directly (70% direct path)\n * - Amsalja uses flanking movements (40% diagonal approach)\n * - Hacker maintains optimal distance (prefers not to close too much)\n *\n * **Kill Mode Enhancement (결정타 접근)**:\n * All archetypes enhance movement speed in kill mode based on combat philosophy:\n * - **Musa**: Direct charging with leg shifts for maximum speed (40% faster)\n * - **Amsalja**: Swift stepping patterns for rapid positioning (30% faster)\n * - **Hacker**: Calculated approach for optimal strike position (20% faster)\n * - **Jeongbo Yowon**: Strategic positioning for control (25% faster)\n * - **Jojik Pokryeokbae**: Unpredictable rush for brutal finish (35% faster)\n *\n * @param context - Combat context\n * @param personality - AI personality\n * @param killModeActive - Whether kill mode is active\n */\n private evaluateApproach(\n context: CombatContext,\n personality: AIPersonality,\n killModeActive: boolean = false,\n ): AIDecision {\n const optimalRange = this.getOptimalRange(personality, context);\n const distance = context.distanceToOpponent;\n\n // If already at optimal range or closer, lower priority\n if (distance <= optimalRange * 1.2) {\n return {\n action: AIActionType.WAIT,\n priority: 0,\n reason: \"Already at optimal range\",\n };\n }\n\n // Apply archetype-specific movement bias\n let movementBias = this.getArchetypeMovementBias(personality.archetype);\n\n // Kill mode: Enhance movement speed for all archetypes based on philosophy\n if (killModeActive) {\n switch (personality.archetype) {\n case PlayerArchetype.MUSA:\n movementBias *= 1.4; // 40% faster closing speed with leg shifts\n break;\n case PlayerArchetype.AMSALJA:\n movementBias *= 1.3; // 30% faster with stepping patterns\n break;\n case PlayerArchetype.HACKER:\n movementBias *= 1.2; // 20% faster for calculated approach\n break;\n case PlayerArchetype.JEONGBO_YOWON:\n movementBias *= 1.25; // 25% faster for strategic positioning\n break;\n case PlayerArchetype.JOJIK_POKRYEOKBAE:\n movementBias *= 1.35; // 35% faster for unpredictable rush\n break;\n }\n }\n\n let approachPos: Position;\n\n // Archetype-specific approach patterns\n if (personality.archetype === PlayerArchetype.MUSA && Math.random() < 0.7) {\n // Musa: Direct charge 70% of the time (enhanced in kill mode)\n approachPos = this.calculateDirectApproach(context, killModeActive);\n } else if (\n personality.archetype === PlayerArchetype.AMSALJA &&\n Math.random() < 0.4\n ) {\n // Amsalja: Flanking approach 40% of the time (enhanced in kill mode)\n approachPos = this.calculateFlankingApproach(context, killModeActive);\n } else {\n // Default approach with slight randomization\n approachPos = this.calculateApproachPosition(context);\n }\n\n // Calculate priority based on distance from optimal range\n // Very far: priority ~6-7, moderate distance: priority ~5\n let basePriority = 4;\n\n // Kill mode: Increase approach priority for closing distance (archetype-dependent)\n if (killModeActive && distance > optimalRange * 1.5) {\n switch (personality.archetype) {\n case PlayerArchetype.MUSA:\n case PlayerArchetype.JOJIK_POKRYEOKBAE:\n basePriority = 6; // Aggressive approach\n break;\n case PlayerArchetype.AMSALJA:\n basePriority = 6; // Swift approach for takedown\n break;\n case PlayerArchetype.HACKER:\n case PlayerArchetype.JEONGBO_YOWON:\n basePriority = 5; // Calculated/strategic approach\n break;\n }\n }\n\n const distanceRatio = Math.min(2, (distance - optimalRange) / optimalRange);\n const priorityBoost = distanceRatio * movementBias * 0.8;\n const finalPriority = basePriority + priorityBoost;\n\n let killModeReason = \"\";\n if (killModeActive) {\n switch (personality.archetype) {\n case PlayerArchetype.MUSA:\n killModeReason = \" - 돌격 (charging)\";\n break;\n case PlayerArchetype.AMSALJA:\n killModeReason = \" - 신속 접근 (swift approach)\";\n break;\n case PlayerArchetype.HACKER:\n killModeReason = \" - 분석 접근 (calculated approach)\";\n break;\n case PlayerArchetype.JEONGBO_YOWON:\n killModeReason = \" - 전략 접근 (strategic approach)\";\n break;\n case PlayerArchetype.JOJIK_POKRYEOKBAE:\n killModeReason = \" - 돌진 (rush)\";\n break;\n }\n }\n\n return {\n action: AIActionType.APPROACH,\n targetPosition: approachPos,\n priority: Math.min(9, finalPriority), // Allow higher cap in kill mode\n reason: `Moving closer (distance: ${Math.round(\n distance,\n )}, optimal: ${optimalRange})${killModeReason}`,\n };\n }\n\n /**\n * Get archetype-specific movement bias multipliers\n *\n * Applies movement pattern modifiers based on archetype behavior profiles:\n * - Aggressive: High forward pressure (2.0x)\n * - Evasive: Moderate mobility (1.5x)\n * - Analytical: Conservative approach (0.8x-1.0x)\n * - Unpredictable: Variable movement (1.3x)\n *\n * @korean 원형별 이동 성향\n */\n private getArchetypeMovementBias(archetype: PlayerArchetype): number {\n const behavior = getArchetypeBehavior(archetype);\n\n switch (behavior.movementPattern) {\n case \"aggressive\": // Musa - aggressive forward movement\n return 2.0;\n case \"evasive\": // Amsalja - high mobility, flanking preference\n return 1.5;\n case \"analytical\": // Hacker, Jeongbo - calculated approach\n return archetype === PlayerArchetype.HACKER ? 0.8 : 1.0;\n case \"unpredictable\": // Jojik - variable patterns\n return 1.3;\n default:\n return 1.0;\n }\n }\n\n /**\n * Calculate direct approach position (straight line to opponent)\n * Used primarily by Musa archetype for charging attacks\n *\n * **Kill Mode Enhancement (결정타 돌격)**:\n * - Larger step size for faster closing with leg shifts\n * - Aggressive stride pattern for maximum forward momentum\n *\n * **Physics-First**: All calculations in METERS.\n *\n * @param context - Combat context\n * @param killModeActive - Whether kill mode is active\n */\n private calculateDirectApproach(\n context: CombatContext,\n killModeActive: boolean = false,\n ): Position {\n const dx = context.opponentPosition.x - context.playerPosition.x;\n const dy = context.opponentPosition.y - context.playerPosition.y;\n const distance = Math.sqrt(dx * dx + dy * dy);\n\n // All thresholds in meters\n const minDistance = AI_MOVEMENT_METERS.MIN_DISTANCE_THRESHOLD;\n\n // If already very close to the opponent, hold position (avoid erratic movement)\n if (distance < minDistance) {\n return this.clampToArenaBoundsMeters(context.playerPosition, context);\n }\n\n // Step size in meters\n const baseStepSize = AI_MOVEMENT_METERS.STEP_SIZE;\n\n // Kill mode: Enhanced step size for faster charging with leg shifts\n const stepSize = killModeActive\n ? Math.min(baseStepSize * 1.5, distance) // 50% larger steps (leg shift technique)\n : Math.min(baseStepSize, distance);\n\n // Move straight toward opponent with enhanced step size in kill mode\n return this.clampToArenaBoundsMeters(\n {\n x: context.playerPosition.x + (dx / distance) * stepSize,\n y: context.playerPosition.y + (dy / distance) * stepSize,\n },\n context,\n );\n }\n\n /**\n * Calculate flanking approach position (diagonal/side approach)\n * Used primarily by Amsalja archetype for stealth positioning\n *\n * **Kill Mode Enhancement (결정타 측면 공격)**:\n * - Tighter flanking angle for more aggressive positioning\n * - Swift stepping pattern for rapid side movement\n *\n * **Physics-First**: All calculations in METERS.\n *\n * @param context - Combat context\n * @param killModeActive - Whether kill mode is active\n */\n private calculateFlankingApproach(\n context: CombatContext,\n killModeActive: boolean = false,\n ): Position {\n const dx = context.opponentPosition.x - context.playerPosition.x;\n const dy = context.opponentPosition.y - context.playerPosition.y;\n const distance = Math.sqrt(dx * dx + dy * dy);\n\n // All thresholds in meters\n const minDistance = AI_MOVEMENT_METERS.MIN_DISTANCE_THRESHOLD;\n\n // If distance is too small, return player's current position (avoid erratic movement)\n if (distance < minDistance) {\n return this.clampToArenaBoundsMeters(context.playerPosition, context);\n }\n\n // Flank offset in meters\n const baseFlankOffset =\n AI_MOVEMENT_METERS.FLANK_OFFSET_BASE +\n Math.random() * AI_MOVEMENT_METERS.FLANK_OFFSET_RANDOM;\n\n // Kill mode: Tighter flanking for more aggressive positioning\n const flankOffset = killModeActive\n ? baseFlankOffset * 0.7 // 30% closer flank (swift stepping)\n : baseFlankOffset;\n\n const perpX = -dy / distance; // Perpendicular vector\n const perpY = dx / distance;\n const flankSide = Math.random() < 0.5 ? 1 : -1; // Random side\n\n return this.clampToArenaBoundsMeters(\n {\n x: context.opponentPosition.x + perpX * flankOffset * flankSide,\n y: context.opponentPosition.y + perpY * flankOffset * flankSide,\n },\n context,\n );\n }\n\n /**\n * Clamp position to arena boundaries with proper margins\n * Centralizes boundary validation logic for all movement calculations\n *\n * **Physics-First**: Works entirely in METERS using worldWidthMeters/worldDepthMeters.\n * Arena is centered at origin (0,0), so bounds are -halfWidth to +halfWidth.\n *\n * @param position - Position to clamp (in meters)\n * @param context - Combat context with arena dimensions in meters\n */\n private clampToArenaBoundsMeters(\n position: Position,\n context: CombatContext,\n ): Position {\n // Arena is centered at origin, half dimensions define bounds\n const halfWidth = context.arenaBounds.worldWidthMeters / 2;\n const halfDepth = context.arenaBounds.worldDepthMeters / 2;\n\n // Margins in meters for character collision\n const marginX = AI_MOVEMENT_METERS.ARENA_MARGIN_X;\n const marginY = AI_MOVEMENT_METERS.ARENA_MARGIN_Y;\n\n return {\n x: Math.max(\n -halfWidth + marginX,\n Math.min(halfWidth - marginX, position.x),\n ),\n y: Math.max(\n -halfDepth + marginY,\n Math.min(halfDepth - marginY, position.y),\n ),\n };\n }\n\n /**\n * Evaluate mid-range tactics with distance awareness\n *\n * **Korean Philosophy (중거리 전술)**:\n * - Considers optimal range for archetype\n * - Hacker prefers to maintain this range (analytical pattern)\n * - Jeongbo uses strategic timing and analysis\n * - Others may close or open distance based on situation\n */\n private evaluateMidRange(\n context: CombatContext,\n personality: AIPersonality,\n ): AIDecision {\n const hasResources = context.playerKi > context.playerMaxKi * 0.3;\n const optimalRange = this.getOptimalRange(personality, context);\n const distance = context.distanceToOpponent;\n const tacticRoll = Math.random();\n const behavior = getArchetypeBehavior(personality.archetype);\n\n // Threshold for being \"at optimal range\" - 0.5 meters tolerance\n const optimalRangeThreshold = 0.5; // meters\n\n // Archetype-specific mid-range behavior based on movement pattern\n if (\n behavior.movementPattern === \"analytical\" &&\n Math.abs(distance - optimalRange) < optimalRangeThreshold\n ) {\n // Analytical archetypes (Hacker, Jeongbo) at ideal range - maintain position\n const circlePos = this.calculateCirclePosition(context);\n const archetypeName =\n personality.archetype === PlayerArchetype.HACKER ? \"사이버\" : \"정보\";\n return {\n action: AIActionType.CIRCLE,\n targetPosition: circlePos,\n priority: 6,\n reason: `${archetypeName} maintaining optimal mid-range (${archetypeName} 위치 유지)`,\n };\n }\n\n // Too far from optimal range - approach\n if (distance > optimalRange * 1.5) {\n const approachPos = this.calculateApproachPosition(context);\n return {\n action: AIActionType.APPROACH,\n targetPosition: approachPos,\n priority: 5,\n reason: \"Moving to optimal range (최적 거리로 이동)\",\n };\n }\n\n // Too close to optimal range - analytical archetypes create space\n if (\n distance < optimalRange * 0.7 &&\n behavior.movementPattern === \"analytical\"\n ) {\n const retreatPos = this.calculateRetreatPosition(context);\n return {\n action: AIActionType.RETREAT,\n targetPosition: retreatPos,\n priority: 5,\n reason: \"Creating tactical space (거리 확보)\",\n };\n }\n\n // Unpredictable archetype (Jojik) - randomize tactics\n if (behavior.movementPattern === \"unpredictable\") {\n const randomAction =\n tacticRoll < 0.33\n ? \"attack\"\n : tacticRoll < 0.66\n ? \"circle\"\n : \"approach\";\n if (randomAction === \"attack\" && hasResources) {\n return {\n action: AIActionType.TECHNIQUE,\n priority: 5,\n reason: \"Unpredictable attack (예측불가 공격)\",\n };\n } else if (randomAction === \"circle\") {\n const circlePos = this.calculateCirclePosition(context);\n return {\n action: AIActionType.CIRCLE,\n targetPosition: circlePos,\n priority: 4,\n reason: \"Unpredictable movement (예측불가 이동)\",\n };\n }\n }\n\n // At good range - mix of techniques and repositioning\n if (tacticRoll < 0.3 && hasResources) {\n return {\n action: AIActionType.TECHNIQUE,\n priority: 5,\n reason: \"Mid-range technique (중거리 기술)\",\n };\n } else if (tacticRoll < 0.6) {\n const circlePos = this.calculateCirclePosition(context);\n return {\n action: AIActionType.CIRCLE,\n targetPosition: circlePos,\n priority: 4,\n reason: \"Tactical repositioning (전술적 이동)\",\n };\n } else {\n const approachPos = this.calculateApproachPosition(context);\n return {\n action: AIActionType.APPROACH,\n targetPosition: approachPos,\n priority: 4,\n reason: \"Moving to optimal range (최적 거리로 이동)\",\n };\n }\n }\n\n /**\n * Evaluate defensive tactics\n */\n private evaluateDefense(\n context: CombatContext,\n personality: AIPersonality,\n ): AIDecision {\n const shouldDefend =\n Math.random() < personality.defensePreference &&\n context.recentDamageTaken > 20;\n\n if (shouldDefend) {\n return {\n action: AIActionType.DEFEND,\n priority: 6,\n reason: \"Defensive response to damage\",\n };\n }\n\n return {\n action: AIActionType.WAIT,\n priority: 0,\n reason: \"No defensive need\",\n };\n }\n\n /**\n * Decide combo action\n */\n private decideComboAction(\n _context: CombatContext,\n _personality: AIPersonality,\n ): AIDecision {\n return {\n action: AIActionType.COMBO,\n priority: 9,\n reason: \"Continuing active combo\",\n };\n }\n\n /**\n * Calculate retreat position\n *\n * **Physics-First**: All calculations in METERS.\n */\n private calculateRetreatPosition(context: CombatContext): Position {\n const dx = context.playerPosition.x - context.opponentPosition.x;\n const dy = context.playerPosition.y - context.opponentPosition.y;\n const distance = Math.sqrt(dx * dx + dy * dy);\n\n // All thresholds in meters\n const minDistance = AI_MOVEMENT_METERS.MIN_DISTANCE_THRESHOLD;\n // Retreat distance in meters (1.5m)\n const retreatDistance = 1.5;\n\n // If distance is too small, retreat in a default direction (away from center)\n if (distance < minDistance) {\n return this.clampToArenaBoundsMeters(\n {\n x: context.playerPosition.x + retreatDistance,\n y: context.playerPosition.y,\n },\n context,\n );\n }\n\n // Normalize and retreat\n const nx = dx / distance;\n const ny = dy / distance;\n\n return this.clampToArenaBoundsMeters(\n {\n x: context.playerPosition.x + nx * retreatDistance,\n y: context.playerPosition.y + ny * retreatDistance,\n },\n context,\n );\n }\n\n /**\n * Calculate approach position\n *\n * **Physics-First**: All calculations in METERS.\n */\n private calculateApproachPosition(context: CombatContext): Position {\n // Offset in meters (0.8m max horizontal, 0.6m max vertical)\n const offsetX = (Math.random() - 0.5) * 0.8;\n const offsetY = (Math.random() - 0.5) * 0.6;\n\n return this.clampToArenaBoundsMeters(\n {\n x: context.opponentPosition.x + offsetX,\n y: context.opponentPosition.y + offsetY,\n },\n context,\n );\n }\n\n /**\n * Calculate circle position\n *\n * **Physics-First**: All calculations in METERS.\n */\n private calculateCirclePosition(context: CombatContext): Position {\n const angle = Math.atan2(\n context.opponentPosition.y - context.playerPosition.y,\n context.opponentPosition.x - context.playerPosition.x,\n );\n // Circle radius in meters (1.5m base + 0.5m random)\n const circleRadius = 1.5 + Math.random() * 0.5;\n\n return this.clampToArenaBoundsMeters(\n {\n x:\n context.opponentPosition.x +\n Math.cos(angle + Math.PI / 2) * circleRadius,\n y:\n context.opponentPosition.y +\n Math.sin(angle + Math.PI / 2) * circleRadius,\n },\n context,\n );\n }\n\n /**\n * Reset decision state\n */\n reset(): void {\n this.lastDecisionTime = 0;\n this.consecutiveAttacks = 0;\n this.lastStanceChange = 0;\n // Reset psychological pressure tracking (Issue #enhance-intelligence-operative-ai Phase 3)\n this.psychologicalPressure = 0;\n this.lastPressureActionTime = 0;\n }\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"DecisionTree.js","names":[],"sources":["../../../src/systems/ai/DecisionTree.ts"],"sourcesContent":["/**\n * AI Decision Tree for Korean Martial Arts Combat\n * Strategic decision-making system with multiple tactical options\n *\n * **Korean Philosophy Integration (한국 무술 철학)**:\n * - 지피지기백전불태 (知彼知己百戰不殆) - Know the enemy, know yourself, and victory is certain\n * - 이순응변 (以柔應變) - Adapt with flexibility and flow like water\n * - 급소공격 (急所攻擊) - Strike vital points with precision and timing\n */\n\nimport { getArchetypePhysicalAttributes } from \"@/data/archetypePhysicalAttributes\";\nimport { PlayerState } from \"@/systems/player\";\nimport { TrigramSystem } from \"@/systems/TrigramSystem\";\nimport {\n KOREAN_VITAL_POINTS,\n getVitalPointById,\n} from \"@/systems/vitalpoint/KoreanVitalPoints\";\nimport { PlayerArchetype, Position, TrigramStance } from \"@/types\";\nimport { AI_MOVEMENT_METERS } from \"@/types/physicsConstants\";\nimport type { CounterOpportunity, ExposedLimbType } from \"@/types/physics\";\nimport { DifficultyParameters } from \"./AdaptiveDifficulty\";\nimport { AIPersonality, getArchetypeBehavior } from \"./AIPersonality\";\nimport { enforceArchetypeBehavior } from \"./ArchetypeEnforcer\";\nimport { AIComboSystem } from \"./ComboSystem\";\nimport {\n AIActionType,\n AIDecision,\n CombatContext,\n VulnerabilityContext,\n} from \"./types\";\n\n// Re-export types for backward compatibility\nexport { AIActionType } from \"./types\"; // Enum (value + type)\nexport type { AIDecision, CombatContext, VulnerabilityContext } from \"./types\"; // Type-only\n\n/**\n * Body pivot contributions for reach calculations (meters)\n * \n * These values represent additional reach gained from body mechanics:\n * - Hip rotation and torso lean for kicks\n * - Shoulder offset and torso rotation for punches\n * \n * Heuristically aligned with PhysicalReachCalculator.ts but intentionally\n * simplified for AI decision-making (e.g., omits stance/animation modifiers).\n * \n * **NOTE ON BODY RADIUS**: The body pivot values approximate the body radius\n * effect for AI range calculations. For precise hit detection in CombatSystem,\n * use calculateBodyRadius() which accounts for individual archetype differences.\n * \n * AI decision-making trade-off:\n * - Punch body pivot (shoulder offset + torso rotation): ~0.3-0.35m\n * - Actual body radius (shoulder width * 0.5 / 100): ~0.215-0.27m\n * - Difference: AI slightly overestimates close-range effectiveness\n * - Impact: AI may attempt attacks at marginally longer range than optimal\n * - Benefit: Simplified calculations, acceptable gameplay behavior\n * \n * @korean 신체 회전 도달 거리 증가 (AI 의사결정을 위한 근사치)\n */\nconst BODY_PIVOT_METERS = {\n /** Hip rotation + torso lean for kicks (meters) */\n KICK: 0.25,\n /** Torso rotation for punches (meters) */\n TORSO_ROTATION: 0.1,\n} as const;\n\n/**\n * Average technique extension multipliers\n * \n * Based on analysis of technique baseExtension values:\n * - Punches: 0.9-0.95 (average 0.95)\n * - Kicks: 1.0-1.05 (average 1.05)\n * \n * TODO: Consider implementing technique-specific lookup based on actual\n * technique data for more precise range calculations. Current averages\n * are sufficient for AI decision-making but may need refinement.\n * \n * @korean 기술 연장 배수\n */\nconst TECHNIQUE_EXTENSION = {\n /** Average punch baseExtension multiplier */\n PUNCH: 0.95,\n /** Average kick baseExtension multiplier */\n KICK: 1.05,\n} as const;\n\n/**\n * Distance-based stance preferences for tactical positioning\n *\n * Stances are categorized by optimal combat range based on actual technique\n * reach configurations (baseExtension values from technique definitions):\n *\n * **CLOSE (≤0.6m)** - Grappling/clinch range:\n * - TAE (☱ Lake) - Joint manipulation, grapples (0.9 baseExtension, needs contact)\n * - GAM (☵ Water) - Throws, counters (0.7-0.9 baseExtension)\n * - GON (☷ Earth) - Ground techniques, Ssireum throws (0.9 baseExtension)\n * - GAN (☶ Mountain) - Immovable defense, blocks (0.9 baseExtension)\n *\n * **MID (0.6-1.0m)** - Striking range:\n * - GEON (☰ Heaven) - Direct force, power strikes (0.95-1.05 baseExtension)\n * - LI (☲ Fire) - Precision nerve strikes (0.92-0.95 baseExtension)\n * - SON (☴ Wind) - Continuous pressure, Taekyon (0.95-1.05 baseExtension)\n *\n * **FAR (>1.0m)** - Long range kicks/closing distance:\n * - JIN (☳ Thunder) - Explosive jumping kicks (1.15 baseExtension)\n * - SON (☴ Wind) - Closing footwork pressure\n * - GAN (☶ Mountain) - Patient defensive waiting\n *\n * @korean 거리별 자세 선호도 (기술 도달 거리 기반)\n */\nconst DISTANCE_BASED_STANCES: Record<string, readonly TrigramStance[]> = {\n CLOSE: [\n TrigramStance.TAE, // ☱ Joint manipulation/grapples\n TrigramStance.GAM, // ☵ Throws/counters\n TrigramStance.GON, // ☷ Ground techniques\n TrigramStance.GAN, // ☶ Blocking defense\n ],\n MID: [\n TrigramStance.GEON, // ☰ Power strikes\n TrigramStance.LI, // ☲ Precision strikes\n TrigramStance.SON, // ☴ Continuous pressure\n ],\n FAR: [\n TrigramStance.JIN, // ☳ Explosive jumping kicks\n TrigramStance.SON, // ☴ Closing pressure\n TrigramStance.GAN, // ☶ Patient defensive waiting\n ],\n};\n\n/**\n * Hacker observation phase duration in milliseconds\n *\n * Hacker archetype observes opponents for this duration before attacking,\n * collecting combat data for analysis-based tactics.\n *\n * @korean 해커 관찰 단계 지속 시간 (밀리초)\n */\nconst HACKER_OBSERVATION_DURATION_MS = 10000; // 10 seconds\n\n/**\n * Hacker observation phase actions\n *\n * During the observation phase, Hacker only uses non-aggressive\n * positioning actions to collect combat data.\n *\n * @korean 해커 관찰 단계 행동\n */\nconst HACKER_OBSERVATION_ACTIONS: readonly AIActionType[] = [\n AIActionType.WAIT,\n AIActionType.CIRCLE,\n AIActionType.APPROACH,\n] as const;\n\n/**\n * Assess opponent vulnerability for exploitation tactics\n *\n * Analyzes multiple vulnerability factors to create comprehensive assessment:\n * - Balance states (HELPLESS/VULNERABLE/SHAKEN)\n * - Stamina depletion (< 20%)\n * - Ki depletion (< 10%)\n * - Composite vulnerability score (weighted average)\n *\n * **Korean Philosophy (취약성 평가)**:\n * Traditional Korean martial arts teach reading opponent's weakness:\n * - 기회 포착 (Gihoei Pochak) - Seizing opportunities\n * - 약점 공격 (Yakjeom Gonggyeok) - Exploiting weaknesses\n * - 결정타 (Gyeoljeongta) - Delivering decisive strikes\n *\n * @korean 상대 취약성 평가\n *\n * @param context - Current combat context with opponent state\n * @returns Vulnerability assessment with boolean flags and composite score\n */\nfunction assessVulnerability(context: CombatContext): VulnerabilityContext {\n // Balance-based vulnerability (Issue #enhance-intelligence-operative-ai)\n // Uses balance state enum from context: HELPLESS, VULNERABLE, SHAKEN, READY\n const isHelpless = context.opponentBalance === \"HELPLESS\";\n const isVulnerable =\n context.opponentBalance === \"VULNERABLE\" ||\n context.opponentBalance === \"HELPLESS\";\n const isShaken =\n context.opponentBalance === \"SHAKEN\" ||\n context.opponentBalance === \"VULNERABLE\" ||\n context.opponentBalance === \"HELPLESS\";\n\n // Resource-based vulnerability (Issue #enhance-intelligence-operative-ai)\n // Stamina and ki depletion indicate defensive weakness\n const staminaPercent =\n context.opponentStamina && context.opponentMaxStamina\n ? context.opponentStamina / context.opponentMaxStamina\n : 1.0;\n const kiPercent =\n context.opponentKi && context.opponentMaxKi\n ? context.opponentKi / context.opponentMaxKi\n : 1.0;\n\n const hasLowStamina = staminaPercent < 0.2; // < 20% stamina\n const hasNoKi = kiPercent < 0.1; // < 10% ki\n\n // Composite vulnerability score (0.0-1.0) with weighted factors:\n // - Balance: 40% weight (most critical - physical vulnerability)\n // - Stamina: 30% weight (affects defensive capability)\n // - Ki: 30% weight (affects technique usage)\n let balanceVulnerability = 0.0;\n if (isHelpless) balanceVulnerability = 1.0;\n else if (isVulnerable) balanceVulnerability = 0.7;\n else if (isShaken) balanceVulnerability = 0.5;\n\n const overallVulnerability =\n balanceVulnerability * 0.4 +\n (1 - staminaPercent) * 0.3 +\n (1 - kiPercent) * 0.3;\n\n return {\n isHelpless,\n isVulnerable,\n isShaken,\n hasLowStamina,\n hasNoKi,\n overallVulnerability,\n };\n}\n\n/**\n * AI Decision Tree System\n *\n * **Korean Combat Philosophy (한국 무술 철학)**:\n * This system embodies traditional Korean martial arts principles:\n *\n * - **팔괘 응용** (Trigram Application): Uses Eight Trigram system for stance transitions\n * - **급소 타격** (Vital Point Strikes): Targets anatomical weak points with precision\n * - **상황 판단** (Situational Awareness): Adapts tactics based on combat context\n * - **기술 조합** (Technique Combinations): Chains attacks into flowing combos\n * - **방어 우선** (Defense First): Prioritizes survival and tactical retreat when needed\n * - **취약성 공략** (Vulnerability Exploitation): Exploits defenseless states with precision (Issue #enhance-intelligence-operative-ai)\n */\nexport class AIDecisionTree {\n private lastDecisionTime = 0;\n private decisionCooldown = 50; // 50ms minimum between decisions\n private consecutiveAttacks = 0;\n private lastStanceChange = 0;\n private readonly stanceChangeCooldown = 3000; // 3 seconds\n\n // Psychological warfare tracking (Issue #enhance-intelligence-operative-ai)\n // Tracks cumulative psychological pressure for Intelligence Operative archetype\n private psychologicalPressure = 0; // 0-100: Cumulative psychological pressure\n private lastPressureActionTime = 0; // Timestamp of last pressure-building action\n\n // Systems for advanced decision-making\n private trigramSystem: TrigramSystem;\n private difficultyLevel: number = 0.5; // 0.0-1.0: AI skill level\n private difficultyParams?: DifficultyParameters; // Difficulty parameters for AI behavior\n private currentReactionDelay: number = 50; // Current reaction delay (calculated once per param change)\n\n /**\n * Scaling factor for fatigue override probability calculation.\n * Used to convert fatigue modifier to override chance in non-linear manner.\n * Value of 0.5 provides gradual scaling: 1.2x fatigue → ~10% override, 1.5x → ~25%.\n *\n * @korean 피로도 우선순위 무시 배율\n */\n private static readonly FATIGUE_OVERRIDE_SCALING_FACTOR = 0.5;\n\n constructor() {\n this.trigramSystem = new TrigramSystem();\n }\n\n /**\n * Calculate kick reach for an archetype based on physical attributes.\n * \n * **Physics-First**: Returns reach in METERS based on leg length.\n * Includes realistic kick reach calculation:\n * - Leg length converted from cm to meters\n * - Body pivot contribution (hip rotation + torso lean)\n * - Average baseExtension for kicks (1.05x multiplier)\n *\n * Formula: (legLength/100 + BODY_PIVOT_METERS.KICK) × TECHNIQUE_EXTENSION.KICK\n *\n * @param archetype - Player archetype to calculate reach for\n * @returns Kick reach in meters\n * @korean 발차기 도달 거리 (미터)\n */\n private getKickReachMeters(archetype: PlayerArchetype): number {\n const physical = getArchetypePhysicalAttributes(archetype);\n // Leg length in cm -> meters + body pivot * average kick baseExtension\n return (physical.legLength / 100 + BODY_PIVOT_METERS.KICK) * TECHNIQUE_EXTENSION.KICK;\n }\n\n /**\n * Calculate punch reach for an archetype based on physical attributes.\n * \n * **Physics-First**: Returns reach in METERS based on arm length.\n * Includes realistic punch reach calculation:\n * - Arm length converted from cm to meters\n * - Body pivot (shoulder offset + torso rotation)\n * - Average baseExtension for punches (0.95x multiplier)\n *\n * Formula: (armLength/100 + shoulderOffset + torsoRotation) × TECHNIQUE_EXTENSION.PUNCH\n *\n * @param archetype - Player archetype to calculate reach for\n * @returns Punch reach in meters\n * @korean 주먹 도달 거리 (미터)\n */\n private getPunchReachMeters(archetype: PlayerArchetype): number {\n const physical = getArchetypePhysicalAttributes(archetype);\n // Arm length in cm -> meters + body pivot * average punch baseExtension\n const shoulderOffset = physical.shoulderWidth / 2 / 100; // Convert cm to meters\n const bodyPivot = shoulderOffset + BODY_PIVOT_METERS.TORSO_ROTATION;\n return (physical.armLength / 100 + bodyPivot) * TECHNIQUE_EXTENSION.PUNCH;\n }\n\n /**\n * Calculate maximum combat reach for an archetype based on physical attributes.\n *\n * **Physics-First**: Returns reach in METERS based on leg length.\n * Kicks have the longest reach (~1.0-1.3m with body pivot contribution).\n *\n * Note: This is a heuristic approximation. Actual reach also depends on\n * stance modifiers and animation timing not included in AI range calculations.\n *\n * @param archetype - Player archetype to calculate reach for\n * @returns Maximum combat reach in meters\n * @korean 원형별 최대 도달 거리 (미터)\n */\n private getArchetypeMaxReach(archetype: PlayerArchetype): number {\n return this.getKickReachMeters(archetype);\n }\n\n /**\n * Get close range threshold for an archetype (punching/elbow distance).\n * Based on arm length from physical attributes.\n * \n * Uses the shared punch reach calculation helper for consistency.\n * \n * TODO: Extract to shared utility function with PhysicalReachCalculator.ts\n * to maintain consistency between AI range calculations and actual hit detection.\n * Consider handling elbow techniques separately with different body pivot values.\n *\n * @param archetype - Player archetype\n * @returns Close range threshold in meters\n * @korean 근접 범위 (미터)\n */\n private getArchetypeCloseRange(archetype: PlayerArchetype): number {\n return this.getPunchReachMeters(archetype);\n }\n\n /**\n * Get medium range threshold for an archetype (kicking distance).\n * Based on leg length from physical attributes.\n * \n * Includes realistic kick reach calculation:\n * - Leg length converted from cm to meters \n * - Body pivot contribution (hip rotation + torso lean)\n * - Average baseExtension for kicks\n *\n * @param archetype - Player archetype\n * @returns Medium range threshold in meters\n * @korean 중거리 범위 (미터)\n */\n private getArchetypeMediumRange(archetype: PlayerArchetype): number {\n return this.getKickReachMeters(archetype);\n }\n\n /**\n * Set AI difficulty level for vital point targeting accuracy\n * @param level - 0.0 (beginner) to 1.0 (master)\n */\n setDifficultyLevel(level: number): void {\n this.difficultyLevel = Math.max(0, Math.min(1, level));\n }\n\n /**\n * Set difficulty parameters for AI behavior\n * Affects reaction time, accuracy, decision quality, etc.\n *\n * Calculates a randomized reaction delay (within parameter range) once when\n * parameters change. This provides varied AI timing while maintaining consistent\n * behavior throughout the current parameter set.\n *\n * @korean 난이도 매개변수 설정\n * @param params - Difficulty parameters to apply\n */\n setDifficultyParameters(params: DifficultyParameters): void {\n this.difficultyParams = params;\n // Calculate randomized reaction delay once when params change\n // This provides variety while ensuring consistent timing until next param update\n if (params) {\n this.currentReactionDelay =\n params.reactionTimeMs.min +\n Math.random() * (params.reactionTimeMs.max - params.reactionTimeMs.min);\n }\n }\n\n /**\n * Check if kill mode should be activated based on archetype behavior\n *\n * Kill mode activates when:\n * - Opponent health is low (<30%)\n * - Opponent is in vulnerable balance state (HELPLESS/VULNERABLE)\n *\n * **Korean Philosophy (결정타 모드)**:\n * Each archetype activates kill mode differently based on combat philosophy:\n * - **Musa**: Honor demands finishing the fight decisively\n * - **Amsalja**: Opportunity for instant takedown with precision\n * - **Hacker**: Analytical window for calculated strike\n * - **Jeongbo Yowon**: Strategic opportunity for submission\n * - **Jojik Pokryeokbae**: Pragmatic moment to finish brutally\n *\n * @korean 결정타 모드 활성화 확인\n *\n * @param context - Current combat context\n * @param personality - AI personality archetype\n * @returns True if kill mode should be active\n */\n private isKillModeActive(\n context: CombatContext,\n personality: AIPersonality,\n ): boolean {\n const opponentHealthPercent =\n context.opponentHealth / context.playerMaxHealth;\n const isOpponentVulnerable =\n context.opponentBalance != null &&\n (context.opponentBalance === \"HELPLESS\" ||\n context.opponentBalance === \"VULNERABLE\");\n\n // Different activation thresholds based on archetype philosophy\n let healthThreshold = 0.3; // Default 30%\n\n switch (personality.archetype) {\n case PlayerArchetype.MUSA:\n // Aggressive: Activate kill mode early (honor code)\n healthThreshold = 0.3;\n break;\n case PlayerArchetype.AMSALJA:\n // Precise: Activate when perfect opportunity presents\n healthThreshold = 0.3;\n break;\n case PlayerArchetype.HACKER:\n // Analytical: Calculate optimal finishing window\n healthThreshold = 0.25; // More conservative, waits for clear advantage\n break;\n case PlayerArchetype.JEONGBO_YOWON:\n // Strategic: Balanced approach\n healthThreshold = 0.28;\n break;\n case PlayerArchetype.JOJIK_POKRYEOKBAE:\n // Pragmatic: Opportunistic finishing\n healthThreshold = 0.35; // Earlier activation, dirty fighter mentality\n break;\n }\n\n // Activate kill mode when opponent is low health OR vulnerable\n return opponentHealthPercent < healthThreshold || isOpponentVulnerable;\n }\n\n /**\n * Apply kill mode modifiers to action weights for finishing behavior\n *\n * **Kill Mode Behavior (결정타 행동)**:\n * Each archetype has unique finishing behavior based on combat philosophy:\n * - **Musa**: All-in overwhelming force (2.5x attack, 0x retreat)\n * - **Amsalja**: Instant takedown focus (3.0x technique, feints disabled)\n * - **Hacker**: Analytical precision (2.0x technique, counter focus)\n * - **Jeongbo Yowon**: Strategic control (1.8x technique, balanced approach)\n * - **Jojik Pokryeokbae**: Brutal pragmatism (2.2x attack, dirty tactics)\n *\n * @korean 결정타 모드 가중치 적용\n *\n * @param baseWeights - Base action weight multipliers\n * @param personality - AI personality archetype\n * @param isKillMode - Whether kill mode is active\n * @returns Modified action weights for kill mode\n */\n private applyKillModeModifiers(\n baseWeights: {\n attack: number;\n technique: number;\n defend: number;\n retreat: number;\n },\n personality: AIPersonality,\n isKillMode: boolean,\n ): { attack: number; technique: number; defend: number; retreat: number } {\n if (!isKillMode) {\n return baseWeights;\n }\n\n const modified = { ...baseWeights };\n\n // Apply archetype-specific kill mode behavior\n switch (personality.archetype) {\n case PlayerArchetype.MUSA:\n // Warrior: All-in overwhelming force (honor code)\n modified.attack *= 2.5; // Massive attack priority\n modified.technique *= 2.0; // Prefer powerful techniques\n modified.defend *= 0.2; // Minimal defense\n modified.retreat = 0.0; // No retreat (honor code)\n break;\n\n case PlayerArchetype.AMSALJA:\n // Assassin: Instant takedown focus (precision)\n modified.technique *= 3.0; // Prioritize lethal techniques\n modified.attack *= 1.5; // Quick finishers\n modified.defend *= 0.3; // Reduce defense during kill window\n // Note: retreat remains available for tactical repositioning\n break;\n\n case PlayerArchetype.HACKER:\n // Hacker: Analytical precision (calculated strike)\n modified.technique *= 2.0; // Calculated finishing techniques\n modified.attack *= 1.3; // Measured attacks\n modified.defend *= 0.7; // Maintain defensive awareness\n // Counter-attack focus through higher base defense\n break;\n\n case PlayerArchetype.JEONGBO_YOWON:\n // Intelligence Operative: Strategic control (psychological pressure)\n modified.technique *= 1.8; // Strategic techniques\n modified.attack *= 1.6; // Balanced offensive\n modified.defend *= 0.6; // Moderate defense reduction\n modified.retreat *= 0.5; // Tactical retreat available\n break;\n\n case PlayerArchetype.JOJIK_POKRYEOKBAE:\n // Organized Crime: Brutal pragmatism (dirty fighter)\n modified.attack *= 2.2; // Brutal finishing attacks\n modified.technique *= 1.7; // Dirty techniques\n modified.defend *= 0.4; // Reduced defense (pragmatic risk)\n modified.retreat *= 1.2; // Will retreat if needed (survival instinct)\n break;\n }\n\n return modified;\n }\n\n /**\n * Apply Intelligence Operative (Jeongbo Yowon) vulnerability exploitation\n *\n * Enhances decision weights to exploit opponent's defenseless states with precision:\n * - **HELPLESS (balance === HELPLESS)**: 90% takedown priority - Execute precision takedown\n * - **VULNERABLE (balance === VULNERABLE)**: 70% aggressive attack - Sustained pressure tactics\n * - **SHAKEN (balance === SHAKEN)**: 50% pressure increase - Psychological warfare\n * - **Low Stamina (< 20%)**: 60% exploitation - Force defensive positions\n * - **No Ki (< 10%)**: 50% technique spam - Prevent powerful techniques\n *\n * **Multiplier Stacking Behavior**:\n * When multiple vulnerabilities are present, multipliers stack multiplicatively:\n * - VULNERABLE (2.0x attack) + low stamina (1.5x attack) = 3.0x total attack multiplier\n * - VULNERABLE (1.8x technique) + low ki (1.4x technique) = 2.52x total technique multiplier\n * This creates increasingly aggressive exploitation as opponent becomes more vulnerable.\n * Note: HELPLESS state uses exclusive else-if, so it doesn't stack with VULNERABLE/SHAKEN.\n *\n * **Jeongbo Philosophy (정보요원 전략)**:\n * - Knowledge through observation (관찰을 통한 지식)\n * - Psychological manipulation (심리적 조작)\n * - Precise timing (정확한 타이밍)\n * - Strategic exploitation (전략적 공략)\n *\n * This function provides 3x higher vulnerability exploitation rate than Musa,\n * 2x higher psychological warfare usage than Amsalja, and 5x higher takedown\n * success rate when opponent is HELPLESS.\n *\n * @korean 정보요원 취약성 공략\n *\n * @param weights - Base action weight multipliers\n * @param vulnerability - Vulnerability assessment context\n * @param personality - AI personality archetype\n * @returns Modified action weights for Jeongbo exploitation\n */\n private applyJeongboExploitation(\n weights: {\n attack: number;\n technique: number;\n defend: number;\n feint: number;\n approach: number;\n circle: number;\n },\n vulnerability: VulnerabilityContext,\n personality: AIPersonality,\n ): {\n attack: number;\n technique: number;\n defend: number;\n feint: number;\n approach: number;\n circle: number;\n } {\n // Only Jeongbo gets vulnerability exploitation bonuses\n if (personality.archetype !== PlayerArchetype.JEONGBO_YOWON) {\n return weights;\n }\n\n const modified = { ...weights };\n\n // Check for psychological pressure buildup (Issue #enhance-intelligence-operative-ai Phase 3)\n const pressureStrike = this.shouldExecutePressureStrike(vulnerability);\n if (pressureStrike) {\n // Psychological pressure ≥ 50 + Opponent VULNERABLE = Decisive Strike\n modified.technique *= 3.0; // Execute decisive psychological technique\n modified.attack *= 0.3; // Reduce basic attacks\n return modified; // Override other modifiers for pressure strike\n }\n\n // HELPLESS: Execute precision takedown (90% priority)\n if (vulnerability.isHelpless) {\n modified.technique *= 5.0; // Massive technique priority for Precision Takedown\n modified.attack *= 0.2; // Reduce basic attacks\n modified.defend *= 0.1; // Minimal defense needed\n modified.feint *= 0.1; // No feinting during execution\n }\n // VULNERABLE: Aggressive pressure (70% priority)\n else if (vulnerability.isVulnerable) {\n modified.attack *= 2.0; // Increased attack frequency\n modified.technique *= 1.8; // Tactical Strike priority\n modified.feint *= 0.5; // Less feinting, more action\n modified.defend *= 0.5; // Reduced defense (maintain offensive)\n }\n // SHAKEN: Psychological warfare (50% priority)\n else if (vulnerability.isShaken) {\n modified.feint *= 2.0; // Increase psychological pressure\n modified.circle *= 1.5; // Intimidation tactics (circling)\n modified.technique *= 1.3; // \"Psychological Warfare\" technique\n }\n\n // Low stamina: Relentless pressure (60% priority)\n if (vulnerability.hasLowStamina) {\n modified.attack *= 1.5; // Force stamina drain\n modified.approach *= 1.3; // Close distance to pressure\n }\n\n // No ki: Technique spam (50% priority)\n if (vulnerability.hasNoKi) {\n modified.technique *= 1.4; // Opponent can't use powerful techniques\n modified.attack *= 1.3; // Maintain offensive\n }\n\n return modified;\n }\n\n /**\n * Build psychological pressure through intimidation tactics\n *\n * Intelligence Operative uses feints, circling, and approach/retreat patterns\n * to build cumulative psychological pressure on opponent. When pressure reaches\n * 50+ and opponent is VULNERABLE, triggers decisive strike.\n *\n * **Psychological Tactics (심리전 전술)**:\n * - Feints: +10 pressure (fake attacks create hesitation)\n * - Circling: +5 pressure (predator circling prey)\n * - Approach: +3 pressure (aggressive positioning)\n * - Decay: -3 pressure per second (pressure fades without action)\n *\n * @korean 심리적 압박 증가\n *\n * @param actionType - Type of action taken (FEINT, CIRCLE, APPROACH, etc.)\n * @param now - Current timestamp for decay calculation\n */\n private buildPsychologicalPressure(\n actionType: AIActionType,\n now: number,\n ): void {\n // Apply pressure decay (3 points per second since last pressure action)\n if (this.lastPressureActionTime > 0) {\n const timeSinceLastAction = now - this.lastPressureActionTime;\n const decayAmount = (timeSinceLastAction / 1000) * 3; // 3 points per second\n this.psychologicalPressure = Math.max(\n 0,\n this.psychologicalPressure - decayAmount,\n );\n }\n\n // Build pressure based on action type\n switch (actionType) {\n case AIActionType.FEINT:\n this.psychologicalPressure = Math.min(\n 100,\n this.psychologicalPressure + 10,\n );\n this.lastPressureActionTime = now;\n break;\n case AIActionType.CIRCLE:\n this.psychologicalPressure = Math.min(\n 100,\n this.psychologicalPressure + 5,\n );\n this.lastPressureActionTime = now;\n break;\n case AIActionType.APPROACH:\n this.psychologicalPressure = Math.min(\n 100,\n this.psychologicalPressure + 3,\n );\n this.lastPressureActionTime = now;\n break;\n // Attacks and techniques release pressure (execution phase)\n case AIActionType.ATTACK:\n case AIActionType.TECHNIQUE:\n // Pressure resets after decisive action\n this.psychologicalPressure = Math.max(\n 0,\n this.psychologicalPressure * 0.5,\n );\n break;\n }\n }\n\n /**\n * Check if psychological pressure should trigger decisive strike\n *\n * Jeongbo executes decisive technique when:\n * - Psychological pressure ≥ 50 (sustained intimidation)\n * - Opponent is VULNERABLE or worse (balance < 30)\n * - Returns true to boost technique priority\n *\n * @korean 심리적 압박 결정타 확인\n *\n * @param vulnerability - Vulnerability assessment context\n * @returns True if pressure warrants decisive strike\n */\n private shouldExecutePressureStrike(\n vulnerability: VulnerabilityContext,\n ): boolean {\n return (\n this.psychologicalPressure >= 50 &&\n (vulnerability.isVulnerable || vulnerability.isHelpless)\n );\n }\n\n /**\n * Analyze counter-attack opportunity from opponent's limb exposure.\n *\n * **Korean**: 반격 기회 분석 (Counter Opportunity Analysis)\n *\n * Detects when the opponent has exposed limbs during technique execution,\n * enabling defensive counter-attacks and breaking techniques.\n *\n * This integrates the LimbExposureSystem with AI decision-making by:\n * 1. Detecting exposed limbs from opponent's current technique\n * 2. Calculating vulnerability windows and multipliers\n * 3. Providing counter opportunity data for decision prioritization\n *\n * @param context - Combat context with opponent technique data\n * @returns Enhanced context with counter opportunity analysis, or undefined if no opportunity\n *\n * @remarks\n * Returns undefined if:\n * - Opponent is not executing a technique\n * - Technique has no exposure window defined\n * - Current time is outside the exposure window\n *\n * @korean 반격기회분석\n */\n private analyzeCounterOpportunity(\n context: CombatContext,\n ): {\n readonly counterOpportunity?: CounterOpportunity;\n readonly opponentVulnerability: number;\n } {\n // Use pre-calculated counter opportunity from context\n // (CombatSystem calculates this based on opponent's technique state)\n const counterOpportunity = context.counterOpportunity;\n \n // Calculate vulnerability multiplier from opportunity if present\n const opponentVulnerability = counterOpportunity?.vulnerabilityMultiplier ?? 1.0;\n\n return {\n counterOpportunity,\n opponentVulnerability,\n };\n }\n\n /**\n * Make strategic decision based on combat context\n *\n * Applies difficulty-based reaction time delays if difficulty parameters are set\n */\n makeDecision(\n context: CombatContext,\n personality: AIPersonality,\n comboSystem: AIComboSystem,\n ): AIDecision {\n const now = Date.now();\n\n // Apply difficulty-based reaction time delay (calculated once per param change)\n const reactionDelay = this.difficultyParams\n ? this.currentReactionDelay\n : this.decisionCooldown;\n\n // Respect decision cooldown (use reaction delay if difficulty params available)\n const effectiveCooldown = Math.max(this.decisionCooldown, reactionDelay);\n if (now - this.lastDecisionTime < effectiveCooldown) {\n return {\n action: AIActionType.WAIT,\n priority: 0,\n reason: this.difficultyParams\n ? `Reaction time delay: ${effectiveCooldown.toFixed(0)}ms`\n : \"Decision cooldown active\",\n };\n }\n\n this.lastDecisionTime = now;\n\n // Hacker observation phase (Issue #enforce-distinct-combat-philosophies)\n // Hacker archetype observes for first 10 seconds before attacking (data collection)\n // Skip observation phase during critical situations:\n // - Low health (below tactical retreat threshold)\n // - Kill mode opportunity (opponent < 30% health)\n // - Opponent vulnerable or helpless (exploitable state)\n const healthPercent = context.playerHealth / context.playerMaxHealth;\n const tacticalRetreatThreshold = personality.tacticalRetreatThreshold;\n const isBelowRetreatThreshold = healthPercent < tacticalRetreatThreshold; // Need to retreat\n const opponentMaxHealth =\n context.opponentMaxHealth ?? context.playerMaxHealth; // Use opponent max health if available, fallback to symmetric assumption\n const opponentHealthPercent = context.opponentHealth / opponentMaxHealth;\n const isKillOpportunity = opponentHealthPercent < 0.3; // Kill mode opportunity\n const isOpponentVulnerable =\n context.opponentBalance === \"VULNERABLE\" ||\n context.opponentBalance === \"HELPLESS\"; // Exploitable state\n\n if (\n personality.archetype === PlayerArchetype.HACKER &&\n context.timeInMatch < HACKER_OBSERVATION_DURATION_MS &&\n !isBelowRetreatThreshold && // Skip observation if need to retreat\n !isKillOpportunity && // Skip observation if kill opportunity\n !isOpponentVulnerable // Skip observation if opponent is vulnerable\n ) {\n // During observation phase, Hacker only circles and waits\n // No attacks or techniques until data collection is complete\n const randomObservationAction =\n HACKER_OBSERVATION_ACTIONS[\n Math.floor(Math.random() * HACKER_OBSERVATION_ACTIONS.length)\n ];\n\n return {\n action: randomObservationAction,\n priority: 10, // High priority to ensure observation phase is respected\n reason: `Hacker observation phase: collecting data (${(context.timeInMatch / 1000).toFixed(1)}s / ${HACKER_OBSERVATION_DURATION_MS / 1000}s) (해커 관찰 단계)`,\n };\n }\n\n // Check for kill mode activation (Issue #enhance-ai-aggression)\n const killModeActive = this.isKillModeActive(context, personality);\n\n // Assess opponent vulnerability for exploitation (Issue #enhance-intelligence-operative-ai)\n const vulnerability = assessVulnerability(context);\n\n // Analyze counter-attack opportunities from limb exposure (Limb Exposure System Integration)\n // This detects when opponent's technique execution exposes vulnerable limbs\n const counterAnalysis = this.analyzeCounterOpportunity(context);\n\n // Check for active combo first\n if (comboSystem.isComboActive()) {\n return this.decideComboAction(context, personality);\n }\n\n // Evaluate tactical options in priority order\n const decisions: AIDecision[] = [];\n\n // Get optimal range for this archetype (using context for meters conversion)\n const optimalRange = this.getOptimalRange(personality, context);\n const distance = context.distanceToOpponent;\n\n // 1. Critical health - survival priority\n decisions.push(this.evaluateSurvival(context, personality));\n\n // 2. Limb exposure counter-attack opportunity (highest tactical priority after survival)\n // Evaluates opportunities to exploit opponent's exposed limbs during technique execution\n if (counterAnalysis.counterOpportunity) {\n decisions.push(\n this.evaluateLimbExposureCounter(\n context,\n personality,\n counterAnalysis.counterOpportunity,\n counterAnalysis.opponentVulnerability,\n ),\n );\n }\n\n // 3. Standard counter-attack opportunity (opponent attacking)\n if (context.isOpponentAttacking) {\n decisions.push(\n this.evaluateCounter(context, personality, killModeActive),\n );\n }\n\n // 4. Combo initiation (only if at reasonable distance)\n if (distance < optimalRange * 1.5) {\n decisions.push(\n this.evaluateComboStart(context, personality, comboSystem),\n );\n }\n\n // 5. Stance transition\n decisions.push(this.evaluateStanceChange(context, personality, now));\n\n // 6. Feint attack (only at mid-close range) - reduced priority in kill mode\n if (distance < optimalRange * 1.8 && !killModeActive) {\n decisions.push(this.evaluateFeint(context, personality));\n }\n\n // 7. Distance-based tactics (archetype-aware ranges)\n // Increased multiplier from 1.2 to 2.0 to allow attacks at greater distances\n // Players start at ~1.6m apart, so this ensures AI can attack immediately\n // Using <= to include exact boundary cases (e.g., Jeongbo at 1.6m = 0.8m * 2.0)\n if (distance <= optimalRange * 2.0) {\n // Close to optimal range - use close-range tactics including vital point targeting\n decisions.push(\n this.evaluateCloseRange(context, personality, killModeActive),\n );\n } else if (distance > optimalRange * 2.5) {\n // Too far - need to approach\n decisions.push(\n this.evaluateApproach(context, personality, killModeActive),\n );\n } else {\n // Mid-range (>2.0x && <=2.5x optimal range) - good tactical position\n decisions.push(this.evaluateMidRange(context, personality));\n }\n\n // 8. Defensive positioning (reduced in kill mode)\n if (!killModeActive || personality.archetype !== PlayerArchetype.MUSA) {\n decisions.push(this.evaluateDefense(context, personality));\n }\n\n // Apply Jeongbo vulnerability exploitation (Issue #enhance-intelligence-operative-ai)\n // This provides 3x higher exploitation rate than other archetypes\n let modifiedDecisions = decisions;\n if (personality.archetype === PlayerArchetype.JEONGBO_YOWON) {\n modifiedDecisions = decisions.map((decision) => {\n // Skip survival decisions (preserve self-preservation)\n const SURVIVAL_REASON_KEYWORDS = [\n \"critical health\",\n \"high pain\",\n \"survival retreat\",\n \"emergency retreat\",\n \"위급 상황\",\n \"고통 회피\",\n ];\n const reasonLower = decision.reason.toLowerCase();\n const hasSurvivalKeyword = SURVIVAL_REASON_KEYWORDS.some((keyword) =>\n reasonLower.includes(keyword),\n );\n const isSurvivalRetreat =\n decision.action === AIActionType.RETREAT &&\n (decision.priority === 20 || hasSurvivalKeyword);\n\n if (isSurvivalRetreat) {\n return decision;\n }\n\n // Only apply exploitation to relevant action types\n // Other actions (COUNTER, RETREAT, WAIT, STANCE_CHANGE, COMBO) maintain original priority\n const exploitableActions = [\n AIActionType.ATTACK,\n AIActionType.TECHNIQUE,\n AIActionType.DEFEND,\n AIActionType.FEINT,\n AIActionType.APPROACH,\n AIActionType.CIRCLE,\n ];\n\n if (!exploitableActions.includes(decision.action)) {\n return decision; // Preserve priority for non-exploitable actions\n }\n\n // Calculate base action weights including feint, approach, circle\n const weights = {\n attack: decision.action === AIActionType.ATTACK ? 1.0 : 0.0,\n technique: decision.action === AIActionType.TECHNIQUE ? 1.0 : 0.0,\n defend: decision.action === AIActionType.DEFEND ? 1.0 : 0.0,\n feint: decision.action === AIActionType.FEINT ? 1.0 : 0.0,\n approach: decision.action === AIActionType.APPROACH ? 1.0 : 0.0,\n circle: decision.action === AIActionType.CIRCLE ? 1.0 : 0.0,\n };\n\n // Apply Jeongbo exploitation modifiers\n const modifiedWeights = this.applyJeongboExploitation(\n weights,\n vulnerability,\n personality,\n );\n\n // Adjust priority based on modified weights\n let newPriority = decision.priority;\n if (decision.action === AIActionType.ATTACK) {\n newPriority = decision.priority * modifiedWeights.attack;\n } else if (decision.action === AIActionType.TECHNIQUE) {\n newPriority = decision.priority * modifiedWeights.technique;\n } else if (decision.action === AIActionType.DEFEND) {\n newPriority = decision.priority * modifiedWeights.defend;\n } else if (decision.action === AIActionType.FEINT) {\n newPriority = decision.priority * modifiedWeights.feint;\n } else if (decision.action === AIActionType.APPROACH) {\n newPriority = decision.priority * modifiedWeights.approach;\n } else if (decision.action === AIActionType.CIRCLE) {\n newPriority = decision.priority * modifiedWeights.circle;\n }\n\n return { ...decision, priority: newPriority };\n });\n }\n\n // Apply kill mode modifiers to boost aggression (Issue #enhance-ai-aggression)\n if (killModeActive) {\n // Map decisions to new array with modified priorities\n modifiedDecisions = modifiedDecisions.map((decision) => {\n // CRITICAL: Survival decisions (retreat for self-preservation) should NOT be affected by kill mode\n // Kill mode is about finishing the opponent, not about ignoring the AI's own safety\n // Survival retreats are identified by priority 20 OR reason containing survival keywords\n // Survival retreat detection with English and Korean keywords\n const SURVIVAL_REASON_KEYWORDS = [\n // English survival indicators (lowercase)\n \"critical health\",\n \"high pain\",\n \"survival retreat\",\n \"emergency retreat\",\n // Korean survival indicators\n \"위급 상황\",\n \"고통 회피\",\n ];\n\n const reasonLower = decision.reason.toLowerCase();\n const hasSurvivalKeyword = SURVIVAL_REASON_KEYWORDS.some((keyword) =>\n reasonLower.includes(keyword),\n );\n\n const isSurvivalRetreat =\n decision.action === AIActionType.RETREAT &&\n (decision.priority === 20 || hasSurvivalKeyword);\n\n if (isSurvivalRetreat) {\n // This is a survival retreat decision - preserve its priority\n return decision;\n }\n\n // Calculate base action weights for non-survival decisions\n const weights = {\n attack: decision.action === AIActionType.ATTACK ? 1.0 : 0.0,\n technique: decision.action === AIActionType.TECHNIQUE ? 1.0 : 0.0,\n defend: decision.action === AIActionType.DEFEND ? 1.0 : 0.0,\n retreat: decision.action === AIActionType.RETREAT ? 1.0 : 0.0,\n };\n\n // Apply kill mode modifiers\n const modifiedWeights = this.applyKillModeModifiers(\n weights,\n personality,\n true,\n );\n\n // Adjust priority based on modified weights\n let newPriority = decision.priority;\n if (decision.action === AIActionType.ATTACK) {\n newPriority = decision.priority * modifiedWeights.attack;\n } else if (decision.action === AIActionType.TECHNIQUE) {\n newPriority = decision.priority * modifiedWeights.technique;\n } else if (decision.action === AIActionType.DEFEND) {\n newPriority = decision.priority * modifiedWeights.defend;\n } else if (decision.action === AIActionType.RETREAT) {\n newPriority = decision.priority * modifiedWeights.retreat;\n }\n\n return { ...decision, priority: newPriority };\n });\n\n // Select highest priority decision from modified array\n const bestDecision = modifiedDecisions.reduce((best, current) =>\n current.priority > best.priority ? current : best,\n );\n\n // Build psychological pressure for Jeongbo (Issue #enhance-intelligence-operative-ai Phase 3)\n if (personality.archetype === PlayerArchetype.JEONGBO_YOWON) {\n this.buildPsychologicalPressure(bestDecision.action, now);\n }\n\n // Track consecutive attacks\n if (\n bestDecision.action === AIActionType.ATTACK ||\n bestDecision.action === AIActionType.TECHNIQUE\n ) {\n this.consecutiveAttacks++;\n } else {\n this.consecutiveAttacks = 0;\n }\n\n // Apply archetype behavior enforcement (Issue #enforce-distinct-combat-philosophies)\n // This ensures each archetype has immediately recognizable combat patterns in kill mode\n const enforcedDecision = enforceArchetypeBehavior(\n bestDecision,\n personality.archetype,\n context,\n );\n\n return enforcedDecision;\n }\n\n // Normal mode: Select highest priority decision (may have Jeongbo exploitation applied)\n const bestDecision = modifiedDecisions.reduce((best, current) =>\n current.priority > best.priority ? current : best,\n );\n\n // Build psychological pressure for Jeongbo (Issue #enhance-intelligence-operative-ai Phase 3)\n if (personality.archetype === PlayerArchetype.JEONGBO_YOWON) {\n this.buildPsychologicalPressure(bestDecision.action, now);\n }\n\n // Track consecutive attacks\n if (\n bestDecision.action === AIActionType.ATTACK ||\n bestDecision.action === AIActionType.TECHNIQUE\n ) {\n this.consecutiveAttacks++;\n } else {\n this.consecutiveAttacks = 0;\n }\n\n // Apply archetype behavior enforcement (Issue #enforce-distinct-combat-philosophies)\n // This ensures each archetype has immediately recognizable combat patterns\n const enforcedDecision = enforceArchetypeBehavior(\n bestDecision,\n personality.archetype,\n context,\n );\n\n return enforcedDecision;\n }\n\n /**\n * Evaluate survival tactics when critically low health\n *\n * **Korean Philosophy (생존 전략)**:\n * - Consider both health and pain levels\n * - Archetype affects retreat threshold and behavior\n * - Honor code (Musa) prevents retreat above threshold\n */\n private evaluateSurvival(\n context: CombatContext,\n personality: AIPersonality,\n ): AIDecision {\n const healthPercent = context.playerHealth / context.playerMaxHealth;\n const painLevel = context.recentDamageTaken;\n\n // Get archetype behavior profile\n const behavior = getArchetypeBehavior(personality.archetype);\n\n // Check critical survival condition: low health OR (moderate health + high pain)\n const isCritical = healthPercent < personality.tacticalRetreatThreshold;\n const isHighPain = healthPercent < 0.5 && painLevel > 50;\n\n if (isCritical || isHighPain) {\n // Honor code: Musa never retreats above their threshold (30%)\n if (\n behavior.honorCode &&\n healthPercent > behavior.retreatThreshold / 100\n ) {\n return {\n action: AIActionType.WAIT,\n priority: 0,\n reason: `Honor code prevents retreat: ${(healthPercent * 100).toFixed(1)}% (명예 규범)`,\n };\n }\n\n const retreatVector = this.calculateRetreatPosition(context);\n\n return {\n action: AIActionType.RETREAT,\n targetPosition: retreatVector,\n priority: 20, // Highest priority - must always override kill mode aggression\n reason: isCritical\n ? `Critical health: ${(healthPercent * 100).toFixed(1)}% (위급 상황)`\n : `High pain: ${painLevel.toFixed(0)} (고통 회피)`,\n };\n }\n\n return { action: AIActionType.WAIT, priority: 0, reason: \"Health stable\" };\n }\n\n /**\n * Evaluate counter-attack opportunity\n *\n * **Kill Mode Enhancement (결정타 반격)**:\n * All archetypes enhance counter behavior during kill mode based on philosophy:\n * - **Musa**: Increased counter frequency (honor demands swift response)\n * - **Amsalja**: Enhanced counter timing with precision strikes\n * - **Hacker**: Calculated counter-attacks (analytical opportunity)\n * - **Jeongbo Yowon**: Strategic counters (psychological advantage)\n * - **Jojik Pokryeokbae**: Opportunistic counters (dirty tactics)\n *\n * @param context - Combat context\n * @param personality - AI personality\n * @param killModeActive - Whether kill mode is active\n */\n private evaluateCounter(\n context: CombatContext,\n personality: AIPersonality,\n killModeActive: boolean = false,\n ): AIDecision {\n // Base counter chance affected by defense preference\n let counterChance = personality.defensePreference * 0.8;\n let counterPriority = 8;\n\n // Kill mode: Archetype-specific counter behavior enhancements\n if (killModeActive) {\n switch (personality.archetype) {\n case PlayerArchetype.MUSA:\n // Musa: Honor code demands swift aggressive counter\n counterChance = Math.min(0.95, counterChance + 0.3); // +30% counter chance\n counterPriority = 9; // Highest priority counter\n break;\n\n case PlayerArchetype.AMSALJA:\n // Amsalja: Precision counter-strikes for instant takedown\n counterChance = Math.min(0.9, counterChance + 0.25); // +25% counter chance\n counterPriority = 9; // Highest priority counter\n break;\n\n case PlayerArchetype.HACKER:\n // Hacker: Calculated counter with analytical precision\n counterChance = Math.min(0.85, counterChance + 0.15); // +15% counter chance\n counterPriority = 9; // Enhanced priority for analytical strike\n break;\n\n case PlayerArchetype.JEONGBO_YOWON:\n // Jeongbo Yowon: Strategic counter with psychological pressure\n counterChance = Math.min(0.8, counterChance + 0.2); // +20% counter chance\n counterPriority = 8; // Moderate priority increase\n break;\n\n case PlayerArchetype.JOJIK_POKRYEOKBAE:\n // Jojik: Opportunistic dirty counter\n counterChance = Math.min(0.85, counterChance + 0.25); // +25% counter chance\n counterPriority = 8; // Pragmatic priority\n break;\n }\n }\n\n // Use archetype-based max reach for counter distance check\n const maxReach = this.getArchetypeMaxReach(personality.archetype);\n const shouldCounter =\n Math.random() < counterChance && context.distanceToOpponent < maxReach;\n\n if (shouldCounter) {\n let killModeReason = \"\";\n if (killModeActive) {\n switch (personality.archetype) {\n case PlayerArchetype.MUSA:\n killModeReason = \" - 명예 반격 (honor counter)\";\n break;\n case PlayerArchetype.AMSALJA:\n killModeReason = \" - 정밀 반격 (precision counter)\";\n break;\n case PlayerArchetype.HACKER:\n killModeReason = \" - 분석 반격 (analytical counter)\";\n break;\n case PlayerArchetype.JEONGBO_YOWON:\n killModeReason = \" - 전략 반격 (strategic counter)\";\n break;\n case PlayerArchetype.JOJIK_POKRYEOKBAE:\n killModeReason = \" - 기습 반격 (opportunistic counter)\";\n break;\n }\n }\n\n return {\n action: AIActionType.COUNTER,\n priority: counterPriority,\n reason: `Opponent attacking - counter opportunity${killModeReason}`,\n };\n }\n\n return {\n action: AIActionType.DEFEND,\n priority: 6,\n reason: \"Opponent attacking - defensive stance\",\n };\n }\n\n /**\n * Evaluate counter-attack opportunity from limb exposure.\n *\n * **Korean**: 사지 노출 반격 평가 (Limb Exposure Counter Evaluation)\n *\n * Analyzes opportunities to exploit opponent's exposed limbs during technique\n * execution. This is higher priority than standard counters because it targets\n * specific anatomical vulnerabilities.\n *\n * **Defensive Archetype Priority**:\n * Defensive archetypes (high defensiveness) strongly favor counter-attacks:\n * - **Musa**: Honorable defense with precise counters (defensiveness ~0.7)\n * - **Amsalja**: Patient assassin waiting for openings (defensiveness ~0.6)\n * - **Jeongbo Yowon**: Analytical exploitation of weaknesses (defensiveness ~0.5)\n *\n * **Breaking Techniques**:\n * When allowsBreaking is true, AI can execute joint locks and limb breaks\n * for severe damage and mobility reduction.\n *\n * @param context - Combat context with positioning\n * @param personality - AI personality with archetype and defensiveness\n * @param counterOpportunity - Detected limb exposure opportunity\n * @param opponentVulnerability - Opponent's vulnerability multiplier\n * @returns Counter-attack decision with priority based on archetype\n *\n * @korean 사지노출반격평가\n */\n private evaluateLimbExposureCounter(\n context: CombatContext,\n personality: AIPersonality,\n counterOpportunity: CounterOpportunity,\n opponentVulnerability: number,\n ): AIDecision {\n // Base counter priority starts high (limb exposure is a prime opportunity)\n let counterPriority = 9;\n\n // Defensive archetypes prioritize counter-attacks significantly\n // Musa (defensiveness ~0.7): +2.1 bonus → priority 11+\n // Amsalja (defensiveness ~0.6): +1.8 bonus → priority 10+\n // Jeongbo (defensiveness ~0.5): +1.5 bonus → priority 10+\n const defensivenessBonus = personality.defensePreference * 3;\n counterPriority += defensivenessBonus;\n\n // Breaking opportunities are extremely high value\n if (counterOpportunity.allowsBreaking) {\n counterPriority += 2;\n }\n\n // High vulnerability multipliers increase priority\n if (opponentVulnerability > 2.0) {\n counterPriority += 1; // Severely overextended\n } else if (opponentVulnerability > 1.5) {\n counterPriority += 0.5; // Moderately vulnerable\n }\n\n // Check if AI has sufficient resources to execute counter\n const hasStamina = context.playerStamina > context.playerMaxStamina * 0.2;\n if (!hasStamina) {\n // Low stamina reduces priority but doesn't eliminate opportunity\n counterPriority -= 3;\n }\n\n // Distance check - counter must be executable at current range\n const maxCounterRange = 1.0; // 1 meter max for counter-attacks\n if (context.distanceToOpponent > maxCounterRange) {\n // Too far to execute counter\n return {\n action: AIActionType.WAIT,\n priority: 0,\n reason: `Limb exposed but too far: ${context.distanceToOpponent.toFixed(2)}m > ${maxCounterRange}m`,\n };\n }\n\n // Build descriptive reason with Korean translation\n const exposedLimbKorean = this.translateExposedLimb(\n counterOpportunity.exposedLimb,\n );\n const breakingNote = counterOpportunity.allowsBreaking\n ? \" - 파쇄 가능 (breaking possible)\"\n : \"\";\n const vulnerabilityNote = ` (취약성: ${opponentVulnerability.toFixed(1)}x)`;\n\n return {\n action: AIActionType.COUNTER,\n priority: counterPriority,\n reason: `Exposed limb counter: ${counterOpportunity.exposedLimb} (${exposedLimbKorean})${breakingNote}${vulnerabilityNote}`,\n };\n }\n\n /**\n * Translate exposed limb type to Korean.\n *\n * **Korean**: 노출 사지 번역 (Exposed Limb Translation)\n *\n * @param exposedLimb - English limb type identifier\n * @returns Korean translation\n *\n * @korean 노출사지번역\n */\n private translateExposedLimb(exposedLimb: ExposedLimbType): string {\n const translations: Record<ExposedLimbType, string> = {\n left_arm: \"왼팔\",\n right_arm: \"오른팔\",\n left_elbow: \"왼팔꿈치\",\n right_elbow: \"오른팔꿈치\",\n left_wrist: \"왼손목\",\n right_wrist: \"오른손목\",\n left_leg: \"왼다리\",\n right_leg: \"오른다리\",\n left_knee: \"왼무릎\",\n right_knee: \"오른무릎\",\n left_ankle: \"왼발목\",\n right_ankle: \"오른발목\",\n };\n\n return translations[exposedLimb];\n }\n\n /**\n * Evaluate combo initiation (fix for issue #2529467014)\n */\n private evaluateComboStart(\n context: CombatContext,\n personality: AIPersonality,\n comboSystem: AIComboSystem,\n ): AIDecision {\n // Check if combo system is already active\n if (comboSystem.isComboActive()) {\n return {\n action: AIActionType.WAIT,\n priority: 0,\n reason: \"Combo already active\",\n };\n }\n\n // Don't start combo if already in consecutive attacks\n if (this.consecutiveAttacks > 0) {\n return {\n action: AIActionType.WAIT,\n priority: 0,\n reason: \"Combo cooldown\",\n };\n }\n\n const hasResources =\n context.playerKi > context.playerMaxKi * 0.3 &&\n context.playerStamina > context.playerMaxStamina * 0.3;\n\n // Use archetype-based medium range (leg length) for combo distance check\n const mediumRange = this.getArchetypeMediumRange(personality.archetype);\n const goodDistance = context.distanceToOpponent < mediumRange;\n const comboChance = Math.random() < personality.comboTendency;\n\n if (hasResources && goodDistance && comboChance) {\n return {\n action: AIActionType.COMBO,\n priority: 7,\n reason: \"Initiating combo sequence\",\n };\n }\n\n return {\n action: AIActionType.WAIT,\n priority: 0,\n reason: \"Combo conditions not met\",\n };\n }\n\n /**\n * Select stance based on distance to opponent\n *\n * Chooses optimal stance for current combat range, prioritizing:\n * 1. Overlap between distance-optimal stances and archetype preferred stances\n * 2. Any distance-optimal stance if no overlap exists (expands tactical repertoire)\n * 3. Avoids switching to current stance\n *\n * **Distance Categories**:\n * - CLOSE (≤2 cells / 80px): GEON, JIN, LI, SON - Aggressive close-quarters stances\n * - MID (3-4 cells / 120-160px): GAM, TAE, GAN - Adaptive mid-range stances\n * - FAR (≥5 cells / 200px+): GAN, GON - Defensive distance stances\n *\n * @korean 거리별 자세 선택\n *\n * **Physics-First**: Distance parameter is in METERS.\n *\n * @param distance - Distance to opponent in meters\n * @param preferredStances - Archetype's preferred stances\n * @param currentStance - Current stance (to avoid redundant switches)\n * @returns Optimal stance for distance, or undefined if no valid options\n */\n private selectStanceForDistance(\n distance: number,\n preferredStances: readonly TrigramStance[],\n currentStance: TrigramStance,\n archetype: PlayerArchetype,\n ): TrigramStance | undefined {\n // Determine distance category using archetype-based physical attributes\n // Close = arm length (punching range), Medium = leg length (kicking range)\n const closeRange = this.getArchetypeCloseRange(archetype);\n const mediumRange = this.getArchetypeMediumRange(archetype);\n\n let distanceCategory: string;\n if (distance <= closeRange) {\n distanceCategory = \"CLOSE\";\n } else if (distance <= mediumRange) {\n distanceCategory = \"MID\";\n } else {\n distanceCategory = \"FAR\";\n }\n\n const optimalStances = DISTANCE_BASED_STANCES[distanceCategory];\n\n // Find overlap between optimal stances and preferred stances\n const candidates = optimalStances.filter((s) =>\n preferredStances.includes(s),\n );\n\n // If no overlap, use any optimal stance (expand tactical repertoire)\n const finalCandidates = candidates.length > 0 ? candidates : optimalStances;\n\n // Don't switch to current stance\n const filtered = finalCandidates.filter((s) => s !== currentStance);\n\n if (filtered.length === 0) {\n return undefined;\n }\n\n return filtered[Math.floor(Math.random() * filtered.length)];\n }\n\n /**\n * Evaluate stance change using TrigramSystem and distance-based selection\n *\n * **Korean Philosophy (자세 전환)**:\n * Uses I Ching-based trigram system to find optimal stance transitions.\n * Considers resource costs, counter-stance effectiveness, archetype preferences,\n * and distance-based tactical positioning.\n *\n * **Dynamic Stance Rotation (Issue #dynamic-ai-stance-rotation)**:\n * - Integrates distance-based stance selection for tactical variety\n * - Prioritizes counter-stances for opponent matchup advantage\n * - Expands tactical repertoire beyond archetype preferences when needed\n */\n private evaluateStanceChange(\n context: CombatContext,\n personality: AIPersonality,\n now: number,\n ): AIDecision {\n // Respect stance change cooldown\n if (now - this.lastStanceChange < this.stanceChangeCooldown) {\n return {\n action: AIActionType.WAIT,\n priority: 0,\n reason: \"Stance change on cooldown\",\n };\n }\n\n const behavior = getArchetypeBehavior(personality.archetype);\n\n // Apply stance fatigue modifier (Issue #dynamic-ai-stance-rotation Phase 4)\n // Increases stance switch probability based on time in current stance:\n // - After 10 seconds: +20% increase (1.2x multiplier)\n // - After 20 seconds: +50% increase (1.5x multiplier)\n const timeInStance = Math.max(0, context.stanceFatigue?.timeInStance ?? 0);\n const fatigueModifier = this.getStanceFatigueModifier(timeInStance);\n const adjustedSwitchFrequency = Math.min(\n 0.95,\n personality.stanceSwitchFrequency * fatigueModifier,\n );\n\n // Single random check using fatigue-adjusted frequency to avoid compounding probability\n const shouldChange = Math.random() < adjustedSwitchFrequency;\n if (!shouldChange) {\n return {\n action: AIActionType.WAIT,\n priority: 0,\n reason:\n fatigueModifier > 1.0\n ? `Stance change deferred (fatigue: ${fatigueModifier.toFixed(2)}x, probability: ${(adjustedSwitchFrequency * 100).toFixed(1)}%)`\n : \"No stance change needed\",\n };\n }\n\n // Check if already in a preferred stance - if so, reduce change chance (but not completely)\n // This check only applies outside combat to avoid stance lock during active fighting\n const inPreferredStance = behavior.preferredStances.includes(\n context.playerStance,\n );\n if (\n inPreferredStance &&\n !context.isOpponentAttacking &&\n Math.random() < 0.6\n ) {\n // 60% chance to stay in preferred stance when not under immediate pressure\n // However, fatigue can override this (higher fatigue = more likely to switch anyway)\n // Use non-linear scaling for gradual, predictable override behavior:\n // - At 1.2x fatigue (10s): ~10% override chance\n // - At 1.5x fatigue (20s): ~25% override chance\n // - Caps at 80% to preserve some tactical consideration\n const fatigueOverrideProbability =\n fatigueModifier > 1.0\n ? Math.min(\n 0.8,\n (fatigueModifier - 1.0) *\n AIDecisionTree.FATIGUE_OVERRIDE_SCALING_FACTOR,\n )\n : 0;\n const fatigueOverride =\n fatigueModifier > 1.2 && Math.random() < fatigueOverrideProbability;\n if (!fatigueOverride) {\n return {\n action: AIActionType.WAIT,\n priority: 0,\n reason: \"Already in preferred stance (선호 자세 유지)\",\n };\n }\n }\n\n // Create a minimal PlayerState object with only the properties actually used\n const playerState = {\n currentStance: context.playerStance,\n ki: context.playerKi,\n stamina: context.playerStamina,\n archetype: personality.archetype,\n } as unknown as PlayerState;\n\n // Priority 1: Try distance-based stance selection for tactical variety\n // Lower priority than attacks but competitive with approach/defense\n const distanceStance = this.selectStanceForDistance(\n context.distanceToOpponent,\n behavior.preferredStances,\n context.playerStance,\n personality.archetype,\n );\n\n if (\n distanceStance &&\n this.trigramSystem.canTransitionTo(\n context.playerStance,\n distanceStance,\n playerState,\n )\n ) {\n this.lastStanceChange = now;\n return {\n action: AIActionType.STANCE_CHANGE,\n targetStance: distanceStance,\n priority: 5, // Competitive with approach (4-6) but below attacks (7-10)\n reason: `Distance-optimal stance (거리 최적 자세: ${distanceStance})`,\n };\n }\n\n // Priority 2: Try counter-stance for opponent matchup\n const counterStance = this.trigramSystem.getCounterStance(\n context.opponentStance,\n );\n if (\n counterStance !== context.playerStance &&\n this.trigramSystem.canTransitionTo(\n context.playerStance,\n counterStance,\n playerState,\n )\n ) {\n this.lastStanceChange = now;\n return {\n action: AIActionType.STANCE_CHANGE,\n targetStance: counterStance,\n priority: 5, // Competitive with approach (4-6) but below attacks (7-10)\n reason: `Counter stance to ${context.opponentStance} (상극 대응)`,\n };\n }\n\n // Priority 3: Use TrigramSystem to recommend optimal stance\n const recommendedStance = this.trigramSystem.recommendStance(playerState);\n\n if (\n this.trigramSystem.canTransitionTo(\n context.playerStance,\n recommendedStance,\n playerState,\n )\n ) {\n this.lastStanceChange = now;\n return {\n action: AIActionType.STANCE_CHANGE,\n targetStance: recommendedStance,\n priority: 4, // Lower priority for general optimization\n reason: `Optimal stance transition via TrigramSystem (팔괘 전환)`,\n };\n }\n\n // Priority 4: Try archetype-preferred stance\n const preferredAvailable = behavior.preferredStances.find(\n (stance) =>\n stance !== context.playerStance &&\n this.trigramSystem.canTransitionTo(\n context.playerStance,\n stance,\n playerState,\n ),\n );\n\n if (preferredAvailable) {\n this.lastStanceChange = now;\n return {\n action: AIActionType.STANCE_CHANGE,\n targetStance: preferredAvailable,\n priority: 4, // Lower priority for preferred stance switch\n reason: `Switching to preferred stance (선호 자세 전환: ${preferredAvailable})`,\n };\n }\n\n // No viable stance change available\n return {\n action: AIActionType.WAIT,\n priority: 0,\n reason: \"No viable stance transition\",\n };\n }\n\n /**\n * Evaluate feint attack\n */\n private evaluateFeint(\n context: CombatContext,\n personality: AIPersonality,\n ): AIDecision {\n // Use archetype-based max reach for feint distance check\n const maxReach = this.getArchetypeMaxReach(personality.archetype);\n const shouldFeint =\n Math.random() < personality.feintChance &&\n context.distanceToOpponent < maxReach;\n\n if (shouldFeint) {\n return {\n action: AIActionType.FEINT,\n priority: 4,\n reason: \"Feinting to bait opponent\",\n };\n }\n\n return {\n action: AIActionType.WAIT,\n priority: 0,\n reason: \"No feint opportunity\",\n };\n }\n\n /**\n * Evaluate close range tactics with vital point targeting\n *\n * **Korean Philosophy (급소 공격)**:\n * At close range, AI targets specific vital points based on difficulty level.\n * Higher difficulty = more precise targeting of critical points.\n *\n * **Kill Mode Enhancement (결정타)**:\n * When kill mode is active, AI prioritizes finishing techniques with boosted priority.\n *\n * @param context - Combat context\n * @param personality - AI personality\n * @param killModeActive - Whether kill mode is active (opponent <30% health or vulnerable)\n */\n private evaluateCloseRange(\n context: CombatContext,\n personality: AIPersonality,\n killModeActive: boolean = false,\n ): AIDecision {\n const hasResources = context.playerKi > 10 && context.playerStamina > 15;\n const aggression = personality.aggressionLevel;\n\n // Select vital point target based on difficulty\n const targetVitalPoint = this.selectVitalPointTarget(context, personality);\n\n // Get Korean name for logging if vital point is selected\n const vitalPointName = targetVitalPoint\n ? (getVitalPointById(targetVitalPoint)?.names.korean ?? targetVitalPoint)\n : undefined;\n\n // Kill mode: Prioritize finishing attacks with maximum aggression\n if (killModeActive) {\n const killModeSuffix =\n personality.archetype === PlayerArchetype.MUSA\n ? \" (결정타 - 압도적 공격)\"\n : \" (결정타 - 즉사 기술)\";\n\n if (hasResources) {\n return {\n action: AIActionType.TECHNIQUE,\n targetVitalPoint,\n priority: targetVitalPoint ? 10 : 9, // Maximum priority - finish the fight\n reason: targetVitalPoint\n ? `Kill mode - finishing technique on vital point (급소 결정타: ${vitalPointName})${killModeSuffix}`\n : `Kill mode - finishing technique${killModeSuffix}`,\n };\n } else {\n return {\n action: AIActionType.ATTACK,\n targetVitalPoint,\n priority: targetVitalPoint ? 9 : 8, // Very high priority\n reason: targetVitalPoint\n ? `Kill mode - finishing attack (결정타 급소: ${vitalPointName})${killModeSuffix}`\n : `Kill mode - finishing attack${killModeSuffix}`,\n };\n }\n }\n\n // Normal close range behavior - expert fighters attack aggressively\n // Higher base attack chance for aggressive AI (aggression * 0.95 instead of 0.8)\n // Use single random value to determine action type fairly\n const actionRoll = Math.random();\n const attackThreshold = aggression * 0.95;\n const techniqueThreshold = hasResources ? aggression * 0.5 : 0; // Additional threshold for techniques\n\n if (actionRoll < attackThreshold) {\n return {\n action: AIActionType.ATTACK,\n targetVitalPoint,\n priority: targetVitalPoint ? 8 : 7, // High priority - attacks win over stance changes\n reason: targetVitalPoint\n ? `Close range - vital point attack (급소 타격: ${vitalPointName})`\n : \"Close range - aggressive strike\",\n };\n } else if (\n actionRoll < attackThreshold + techniqueThreshold &&\n hasResources\n ) {\n return {\n action: AIActionType.TECHNIQUE,\n targetVitalPoint,\n priority: targetVitalPoint ? 8 : 7, // High priority for techniques too\n reason: targetVitalPoint\n ? `Close range - technique on vital point (급소 기술: ${vitalPointName})`\n : \"Close range - technique execution\",\n };\n } else {\n return {\n action: AIActionType.DEFEND,\n priority: 4,\n reason: \"Close range - defensive posture (방어 자세)\",\n };\n }\n }\n\n /**\n * Select vital point to target based on difficulty and stance\n *\n * **Korean Philosophy (급소 선택)**:\n * - Beginner AI: Random targeting or no specific target\n * - Intermediate AI: Favors easier vital points\n * - Advanced AI: Targets appropriate points for current stance\n * - Master AI: Targets critical points with high precision\n */\n private selectVitalPointTarget(\n context: CombatContext,\n personality: AIPersonality,\n ): string | undefined {\n // Guard: Ensure vital points are available\n if (KOREAN_VITAL_POINTS.length === 0) {\n return undefined;\n }\n\n // Check if AI attempts vital point targeting based on difficulty\n const targetChance = this.difficultyLevel * personality.aggressionLevel;\n if (Math.random() > targetChance) {\n return undefined; // No specific vital point target\n }\n\n // Filter vital points by effective stance\n const effectivePoints = KOREAN_VITAL_POINTS.filter((point) =>\n point.effectiveStances?.includes(context.playerStance),\n );\n\n if (effectivePoints.length === 0) {\n // Fallback to any vital point\n const randomIndex = Math.floor(\n Math.random() * KOREAN_VITAL_POINTS.length,\n );\n return KOREAN_VITAL_POINTS[randomIndex].id;\n }\n\n // Select based on difficulty level\n if (this.difficultyLevel < 0.3) {\n // Beginner: Random selection from effective points.\n // NOTE: This uses Math.random(), which is not seeded and thus not deterministic.\n // For reproducible AI behavior (e.g., in testing or balancing), consider using a seeded RNG.\n // Also, this \"beginner\" AI still filters by effective points (stance-appropriate), which may be more sophisticated than a true novice.\n // If true beginner behavior is desired, select from all KOREAN_VITAL_POINTS instead.\n const randomIndex = Math.floor(Math.random() * effectivePoints.length);\n return effectivePoints[randomIndex].id;\n } else if (this.difficultyLevel < 0.6) {\n // Intermediate: Prefer easier targets (lower difficulty)\n const easierPoints = effectivePoints.filter(\n (p) => p.targetingDifficulty < 0.7,\n );\n\n if (easierPoints.length > 0) {\n // Sort without mutating original array\n const sortedEasierPoints = [...easierPoints].sort(\n (a, b) => a.targetingDifficulty - b.targetingDifficulty,\n );\n return sortedEasierPoints[0].id;\n }\n return effectivePoints[0].id;\n } else {\n // Advanced/Master: Target high-value critical points\n const criticalPoints = effectivePoints.filter(\n (p) => p.severity === \"critical\" || p.severity === \"major\",\n );\n\n if (criticalPoints.length > 0) {\n // Sort without mutating original array\n const sortedCritical = [...criticalPoints].sort(\n (a, b) => (b.baseDamage ?? 0) - (a.baseDamage ?? 0),\n );\n return sortedCritical[0].id;\n }\n\n // Fallback to highest damage point (guaranteed to exist due to check at line 456)\n const sortedByDamage = [...effectivePoints].sort(\n (a, b) => (b.baseDamage ?? 0) - (a.baseDamage ?? 0),\n );\n return sortedByDamage[0]?.id ?? effectivePoints[0].id;\n }\n }\n\n /**\n * Calculate stance fatigue modifier for increased switching probability\n *\n * Applies time-based modifiers to encourage dynamic stance rotation:\n * - 0-10 seconds: No modifier (1.0x)\n * - 10-20 seconds: +20% increase (1.2x multiplier)\n * - 20+ seconds: +50% increase (1.5x multiplier)\n *\n * This ensures AI doesn't stay locked in one stance for extended periods,\n * promoting the use of all 8 trigram stances throughout combat.\n *\n * **Korean Philosophy (자세 피로도)**:\n * Remaining in one stance too long reduces tactical flexibility and\n * makes the fighter predictable. The Eight Trigram system requires\n * constant adaptation and flow between stances.\n *\n * @korean 자세 피로도 배율 계산\n *\n * @param timeInStance - Time in current stance in milliseconds\n * @returns Stance switch frequency multiplier (1.0 = no change, >1.0 = increased probability)\n */\n private getStanceFatigueModifier(timeInStance: number): number {\n if (timeInStance > 20000) {\n return 1.5; // 50% increase after 20 seconds\n }\n if (timeInStance > 10000) {\n return 1.2; // 20% increase after 10 seconds\n }\n return 1.0; // No modifier for first 10 seconds\n }\n\n /**\n * Get optimal combat range based on AI personality archetype\n *\n * Uses archetype behavior profiles to determine preferred combat distance.\n * **Physics-First**: Returns distance in METERS directly.\n *\n * @param personality - AI personality with archetype behavior\n * @param _context - Combat context (unused, kept for API compatibility)\n * @returns Optimal range in meters\n * @korean 최적 전투 거리 - 원형별 선호 거리 (미터)\n */\n private getOptimalRange(\n personality: AIPersonality,\n _context?: CombatContext,\n ): number {\n // Get archetype behavior profile\n const behavior = getArchetypeBehavior(personality.archetype);\n\n // Convert optimal range from grid cells to meters (1 cell ≈ 0.4m)\n // Physics-first: return directly in meters\n return behavior.optimalRange * 0.4;\n }\n\n /**\n * Evaluate approach tactics with archetype-specific behavior\n *\n * **Korean Philosophy (접근 전략)**:\n * - Musa charges directly (70% direct path)\n * - Amsalja uses flanking movements (40% diagonal approach)\n * - Hacker maintains optimal distance (prefers not to close too much)\n *\n * **Kill Mode Enhancement (결정타 접근)**:\n * All archetypes enhance movement speed in kill mode based on combat philosophy:\n * - **Musa**: Direct charging with leg shifts for maximum speed (40% faster)\n * - **Amsalja**: Swift stepping patterns for rapid positioning (30% faster)\n * - **Hacker**: Calculated approach for optimal strike position (20% faster)\n * - **Jeongbo Yowon**: Strategic positioning for control (25% faster)\n * - **Jojik Pokryeokbae**: Unpredictable rush for brutal finish (35% faster)\n *\n * @param context - Combat context\n * @param personality - AI personality\n * @param killModeActive - Whether kill mode is active\n */\n private evaluateApproach(\n context: CombatContext,\n personality: AIPersonality,\n killModeActive: boolean = false,\n ): AIDecision {\n const optimalRange = this.getOptimalRange(personality, context);\n const distance = context.distanceToOpponent;\n\n // If already at optimal range or closer, lower priority\n if (distance <= optimalRange * 1.2) {\n return {\n action: AIActionType.WAIT,\n priority: 0,\n reason: \"Already at optimal range\",\n };\n }\n\n // Apply archetype-specific movement bias\n let movementBias = this.getArchetypeMovementBias(personality.archetype);\n\n // Kill mode: Enhance movement speed for all archetypes based on philosophy\n if (killModeActive) {\n switch (personality.archetype) {\n case PlayerArchetype.MUSA:\n movementBias *= 1.4; // 40% faster closing speed with leg shifts\n break;\n case PlayerArchetype.AMSALJA:\n movementBias *= 1.3; // 30% faster with stepping patterns\n break;\n case PlayerArchetype.HACKER:\n movementBias *= 1.2; // 20% faster for calculated approach\n break;\n case PlayerArchetype.JEONGBO_YOWON:\n movementBias *= 1.25; // 25% faster for strategic positioning\n break;\n case PlayerArchetype.JOJIK_POKRYEOKBAE:\n movementBias *= 1.35; // 35% faster for unpredictable rush\n break;\n }\n }\n\n let approachPos: Position;\n\n // Archetype-specific approach patterns\n if (personality.archetype === PlayerArchetype.MUSA && Math.random() < 0.7) {\n // Musa: Direct charge 70% of the time (enhanced in kill mode)\n approachPos = this.calculateDirectApproach(context, killModeActive);\n } else if (\n personality.archetype === PlayerArchetype.AMSALJA &&\n Math.random() < 0.4\n ) {\n // Amsalja: Flanking approach 40% of the time (enhanced in kill mode)\n approachPos = this.calculateFlankingApproach(context, killModeActive);\n } else {\n // Default approach with slight randomization\n approachPos = this.calculateApproachPosition(context);\n }\n\n // Calculate priority based on distance from optimal range\n // Very far: priority ~6-7, moderate distance: priority ~5\n let basePriority = 4;\n\n // Kill mode: Increase approach priority for closing distance (archetype-dependent)\n if (killModeActive && distance > optimalRange * 1.5) {\n switch (personality.archetype) {\n case PlayerArchetype.MUSA:\n case PlayerArchetype.JOJIK_POKRYEOKBAE:\n basePriority = 6; // Aggressive approach\n break;\n case PlayerArchetype.AMSALJA:\n basePriority = 6; // Swift approach for takedown\n break;\n case PlayerArchetype.HACKER:\n case PlayerArchetype.JEONGBO_YOWON:\n basePriority = 5; // Calculated/strategic approach\n break;\n }\n }\n\n const distanceRatio = Math.min(2, (distance - optimalRange) / optimalRange);\n const priorityBoost = distanceRatio * movementBias * 0.8;\n const finalPriority = basePriority + priorityBoost;\n\n let killModeReason = \"\";\n if (killModeActive) {\n switch (personality.archetype) {\n case PlayerArchetype.MUSA:\n killModeReason = \" - 돌격 (charging)\";\n break;\n case PlayerArchetype.AMSALJA:\n killModeReason = \" - 신속 접근 (swift approach)\";\n break;\n case PlayerArchetype.HACKER:\n killModeReason = \" - 분석 접근 (calculated approach)\";\n break;\n case PlayerArchetype.JEONGBO_YOWON:\n killModeReason = \" - 전략 접근 (strategic approach)\";\n break;\n case PlayerArchetype.JOJIK_POKRYEOKBAE:\n killModeReason = \" - 돌진 (rush)\";\n break;\n }\n }\n\n return {\n action: AIActionType.APPROACH,\n targetPosition: approachPos,\n priority: Math.min(9, finalPriority), // Allow higher cap in kill mode\n reason: `Moving closer (distance: ${Math.round(\n distance,\n )}, optimal: ${optimalRange})${killModeReason}`,\n };\n }\n\n /**\n * Get archetype-specific movement bias multipliers\n *\n * Applies movement pattern modifiers based on archetype behavior profiles:\n * - Aggressive: High forward pressure (2.0x)\n * - Evasive: Moderate mobility (1.5x)\n * - Analytical: Conservative approach (0.8x-1.0x)\n * - Unpredictable: Variable movement (1.3x)\n *\n * @korean 원형별 이동 성향\n */\n private getArchetypeMovementBias(archetype: PlayerArchetype): number {\n const behavior = getArchetypeBehavior(archetype);\n\n switch (behavior.movementPattern) {\n case \"aggressive\": // Musa - aggressive forward movement\n return 2.0;\n case \"evasive\": // Amsalja - high mobility, flanking preference\n return 1.5;\n case \"analytical\": // Hacker, Jeongbo - calculated approach\n return archetype === PlayerArchetype.HACKER ? 0.8 : 1.0;\n case \"unpredictable\": // Jojik - variable patterns\n return 1.3;\n default:\n return 1.0;\n }\n }\n\n /**\n * Calculate direct approach position (straight line to opponent)\n * Used primarily by Musa archetype for charging attacks\n *\n * **Kill Mode Enhancement (결정타 돌격)**:\n * - Larger step size for faster closing with leg shifts\n * - Aggressive stride pattern for maximum forward momentum\n *\n * **Physics-First**: All calculations in METERS.\n *\n * @param context - Combat context\n * @param killModeActive - Whether kill mode is active\n */\n private calculateDirectApproach(\n context: CombatContext,\n killModeActive: boolean = false,\n ): Position {\n const dx = context.opponentPosition.x - context.playerPosition.x;\n const dy = context.opponentPosition.y - context.playerPosition.y;\n const distance = Math.sqrt(dx * dx + dy * dy);\n\n // All thresholds in meters\n const minDistance = AI_MOVEMENT_METERS.MIN_DISTANCE_THRESHOLD;\n\n // If already very close to the opponent, hold position (avoid erratic movement)\n if (distance < minDistance) {\n return this.clampToArenaBoundsMeters(context.playerPosition, context);\n }\n\n // Step size in meters\n const baseStepSize = AI_MOVEMENT_METERS.STEP_SIZE;\n\n // Kill mode: Enhanced step size for faster charging with leg shifts\n const stepSize = killModeActive\n ? Math.min(baseStepSize * 1.5, distance) // 50% larger steps (leg shift technique)\n : Math.min(baseStepSize, distance);\n\n // Move straight toward opponent with enhanced step size in kill mode\n return this.clampToArenaBoundsMeters(\n {\n x: context.playerPosition.x + (dx / distance) * stepSize,\n y: context.playerPosition.y + (dy / distance) * stepSize,\n },\n context,\n );\n }\n\n /**\n * Calculate flanking approach position (diagonal/side approach)\n * Used primarily by Amsalja archetype for stealth positioning\n *\n * **Kill Mode Enhancement (결정타 측면 공격)**:\n * - Tighter flanking angle for more aggressive positioning\n * - Swift stepping pattern for rapid side movement\n *\n * **Physics-First**: All calculations in METERS.\n *\n * @param context - Combat context\n * @param killModeActive - Whether kill mode is active\n */\n private calculateFlankingApproach(\n context: CombatContext,\n killModeActive: boolean = false,\n ): Position {\n const dx = context.opponentPosition.x - context.playerPosition.x;\n const dy = context.opponentPosition.y - context.playerPosition.y;\n const distance = Math.sqrt(dx * dx + dy * dy);\n\n // All thresholds in meters\n const minDistance = AI_MOVEMENT_METERS.MIN_DISTANCE_THRESHOLD;\n\n // If distance is too small, return player's current position (avoid erratic movement)\n if (distance < minDistance) {\n return this.clampToArenaBoundsMeters(context.playerPosition, context);\n }\n\n // Flank offset in meters\n const baseFlankOffset =\n AI_MOVEMENT_METERS.FLANK_OFFSET_BASE +\n Math.random() * AI_MOVEMENT_METERS.FLANK_OFFSET_RANDOM;\n\n // Kill mode: Tighter flanking for more aggressive positioning\n const flankOffset = killModeActive\n ? baseFlankOffset * 0.7 // 30% closer flank (swift stepping)\n : baseFlankOffset;\n\n const perpX = -dy / distance; // Perpendicular vector\n const perpY = dx / distance;\n const flankSide = Math.random() < 0.5 ? 1 : -1; // Random side\n\n return this.clampToArenaBoundsMeters(\n {\n x: context.opponentPosition.x + perpX * flankOffset * flankSide,\n y: context.opponentPosition.y + perpY * flankOffset * flankSide,\n },\n context,\n );\n }\n\n /**\n * Clamp position to arena boundaries with proper margins\n * Centralizes boundary validation logic for all movement calculations\n *\n * **Physics-First**: Works entirely in METERS using worldWidthMeters/worldDepthMeters.\n * Arena is centered at origin (0,0), so bounds are -halfWidth to +halfWidth.\n *\n * @param position - Position to clamp (in meters)\n * @param context - Combat context with arena dimensions in meters\n */\n private clampToArenaBoundsMeters(\n position: Position,\n context: CombatContext,\n ): Position {\n // Arena is centered at origin, half dimensions define bounds\n const halfWidth = context.arenaBounds.worldWidthMeters / 2;\n const halfDepth = context.arenaBounds.worldDepthMeters / 2;\n\n // Margins in meters for character collision\n const marginX = AI_MOVEMENT_METERS.ARENA_MARGIN_X;\n const marginY = AI_MOVEMENT_METERS.ARENA_MARGIN_Y;\n\n return {\n x: Math.max(\n -halfWidth + marginX,\n Math.min(halfWidth - marginX, position.x),\n ),\n y: Math.max(\n -halfDepth + marginY,\n Math.min(halfDepth - marginY, position.y),\n ),\n };\n }\n\n /**\n * Evaluate mid-range tactics with distance awareness\n *\n * **Korean Philosophy (중거리 전술)**:\n * - Considers optimal range for archetype\n * - Hacker prefers to maintain this range (analytical pattern)\n * - Jeongbo uses strategic timing and analysis\n * - Others may close or open distance based on situation\n */\n private evaluateMidRange(\n context: CombatContext,\n personality: AIPersonality,\n ): AIDecision {\n const hasResources = context.playerKi > context.playerMaxKi * 0.3;\n const optimalRange = this.getOptimalRange(personality, context);\n const distance = context.distanceToOpponent;\n const tacticRoll = Math.random();\n const behavior = getArchetypeBehavior(personality.archetype);\n\n // Threshold for being \"at optimal range\" - 0.5 meters tolerance\n const optimalRangeThreshold = 0.5; // meters\n\n // Archetype-specific mid-range behavior based on movement pattern\n if (\n behavior.movementPattern === \"analytical\" &&\n Math.abs(distance - optimalRange) < optimalRangeThreshold\n ) {\n // Analytical archetypes (Hacker, Jeongbo) at ideal range - maintain position\n const circlePos = this.calculateCirclePosition(context);\n const archetypeName =\n personality.archetype === PlayerArchetype.HACKER ? \"사이버\" : \"정보\";\n return {\n action: AIActionType.CIRCLE,\n targetPosition: circlePos,\n priority: 6,\n reason: `${archetypeName} maintaining optimal mid-range (${archetypeName} 위치 유지)`,\n };\n }\n\n // Too far from optimal range - approach\n if (distance > optimalRange * 1.5) {\n const approachPos = this.calculateApproachPosition(context);\n return {\n action: AIActionType.APPROACH,\n targetPosition: approachPos,\n priority: 5,\n reason: \"Moving to optimal range (최적 거리로 이동)\",\n };\n }\n\n // Too close to optimal range - analytical archetypes create space\n if (\n distance < optimalRange * 0.7 &&\n behavior.movementPattern === \"analytical\"\n ) {\n const retreatPos = this.calculateRetreatPosition(context);\n return {\n action: AIActionType.RETREAT,\n targetPosition: retreatPos,\n priority: 5,\n reason: \"Creating tactical space (거리 확보)\",\n };\n }\n\n // Unpredictable archetype (Jojik) - randomize tactics\n if (behavior.movementPattern === \"unpredictable\") {\n const randomAction =\n tacticRoll < 0.33\n ? \"attack\"\n : tacticRoll < 0.66\n ? \"circle\"\n : \"approach\";\n if (randomAction === \"attack\" && hasResources) {\n return {\n action: AIActionType.TECHNIQUE,\n priority: 5,\n reason: \"Unpredictable attack (예측불가 공격)\",\n };\n } else if (randomAction === \"circle\") {\n const circlePos = this.calculateCirclePosition(context);\n return {\n action: AIActionType.CIRCLE,\n targetPosition: circlePos,\n priority: 4,\n reason: \"Unpredictable movement (예측불가 이동)\",\n };\n }\n }\n\n // At good range - mix of techniques and repositioning\n if (tacticRoll < 0.3 && hasResources) {\n return {\n action: AIActionType.TECHNIQUE,\n priority: 5,\n reason: \"Mid-range technique (중거리 기술)\",\n };\n } else if (tacticRoll < 0.6) {\n const circlePos = this.calculateCirclePosition(context);\n return {\n action: AIActionType.CIRCLE,\n targetPosition: circlePos,\n priority: 4,\n reason: \"Tactical repositioning (전술적 이동)\",\n };\n } else {\n const approachPos = this.calculateApproachPosition(context);\n return {\n action: AIActionType.APPROACH,\n targetPosition: approachPos,\n priority: 4,\n reason: \"Moving to optimal range (최적 거리로 이동)\",\n };\n }\n }\n\n /**\n * Evaluate defensive tactics\n */\n private evaluateDefense(\n context: CombatContext,\n personality: AIPersonality,\n ): AIDecision {\n const shouldDefend =\n Math.random() < personality.defensePreference &&\n context.recentDamageTaken > 20;\n\n if (shouldDefend) {\n return {\n action: AIActionType.DEFEND,\n priority: 6,\n reason: \"Defensive response to damage\",\n };\n }\n\n return {\n action: AIActionType.WAIT,\n priority: 0,\n reason: \"No defensive need\",\n };\n }\n\n /**\n * Decide combo action\n */\n private decideComboAction(\n _context: CombatContext,\n _personality: AIPersonality,\n ): AIDecision {\n return {\n action: AIActionType.COMBO,\n priority: 9,\n reason: \"Continuing active combo\",\n };\n }\n\n /**\n * Calculate retreat position\n *\n * **Physics-First**: All calculations in METERS.\n */\n private calculateRetreatPosition(context: CombatContext): Position {\n const dx = context.playerPosition.x - context.opponentPosition.x;\n const dy = context.playerPosition.y - context.opponentPosition.y;\n const distance = Math.sqrt(dx * dx + dy * dy);\n\n // All thresholds in meters\n const minDistance = AI_MOVEMENT_METERS.MIN_DISTANCE_THRESHOLD;\n // Retreat distance in meters (1.5m)\n const retreatDistance = 1.5;\n\n // If distance is too small, retreat in a default direction (away from center)\n if (distance < minDistance) {\n return this.clampToArenaBoundsMeters(\n {\n x: context.playerPosition.x + retreatDistance,\n y: context.playerPosition.y,\n },\n context,\n );\n }\n\n // Normalize and retreat\n const nx = dx / distance;\n const ny = dy / distance;\n\n return this.clampToArenaBoundsMeters(\n {\n x: context.playerPosition.x + nx * retreatDistance,\n y: context.playerPosition.y + ny * retreatDistance,\n },\n context,\n );\n }\n\n /**\n * Calculate approach position\n *\n * **Physics-First**: All calculations in METERS.\n */\n private calculateApproachPosition(context: CombatContext): Position {\n // Offset in meters (0.8m max horizontal, 0.6m max vertical)\n const offsetX = (Math.random() - 0.5) * 0.8;\n const offsetY = (Math.random() - 0.5) * 0.6;\n\n return this.clampToArenaBoundsMeters(\n {\n x: context.opponentPosition.x + offsetX,\n y: context.opponentPosition.y + offsetY,\n },\n context,\n );\n }\n\n /**\n * Calculate circle position\n *\n * **Physics-First**: All calculations in METERS.\n */\n private calculateCirclePosition(context: CombatContext): Position {\n const angle = Math.atan2(\n context.opponentPosition.y - context.playerPosition.y,\n context.opponentPosition.x - context.playerPosition.x,\n );\n // Circle radius in meters (1.5m base + 0.5m random)\n const circleRadius = 1.5 + Math.random() * 0.5;\n\n return this.clampToArenaBoundsMeters(\n {\n x:\n context.opponentPosition.x +\n Math.cos(angle + Math.PI / 2) * circleRadius,\n y:\n context.opponentPosition.y +\n Math.sin(angle + Math.PI / 2) * circleRadius,\n },\n context,\n );\n }\n\n /**\n * Reset decision state\n */\n reset(): void {\n this.lastDecisionTime = 0;\n this.consecutiveAttacks = 0;\n this.lastStanceChange = 0;\n // Reset psychological pressure tracking (Issue #enhance-intelligence-operative-ai Phase 3)\n this.psychologicalPressure = 0;\n this.lastPressureActionTime = 0;\n }\n}\n"],"mappings":"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"}
|