blacktrigram 0.7.47 → 0.7.49
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.d.ts.map +1 -1
- package/lib/components/screens/combat/CombatScreen3D.js +29 -25
- 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.d.ts.map +1 -1
- package/lib/components/screens/combat/components/hud/CombatBottomHUD.js +2 -2
- 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 +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.d.ts.map +1 -1
- package/lib/components/screens/combat/components/hud/CombatTopHUD.js +2 -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.d.ts.map +1 -1
- package/lib/components/screens/combat/helpers/AnimationUpdater.js +4 -2
- 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.d.ts.map +1 -1
- package/lib/components/screens/combat/hooks/useCombatLayout.js +11 -5
- 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 +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 +1 -1
- package/lib/components/screens/philosophy/PhilosophyScreen3D.js.map +1 -1
- package/lib/components/screens/training/TrainingScreen3D.d.ts.map +1 -1
- package/lib/components/screens/training/TrainingScreen3D.js +3 -11
- 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.d.ts.map +1 -1
- package/lib/components/screens/training/components/hud/TrainingBottomHUD.js +2 -2
- 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.d.ts +1 -0
- package/lib/components/screens/training/hooks/useTrainingActions.d.ts.map +1 -1
- package/lib/components/screens/training/hooks/useTrainingActions.js +6 -4
- package/lib/components/screens/training/hooks/useTrainingActions.js.map +1 -1
- package/lib/components/screens/training/hooks/useTrainingLayout.d.ts.map +1 -1
- package/lib/components/screens/training/hooks/useTrainingLayout.js +11 -5
- 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.d.ts.map +1 -1
- package/lib/components/shared/three/models/SkeletalPlayer3D.js +7 -5
- 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.d.ts.map +1 -1
- package/lib/hooks/useHUDLayout.js +3 -2
- 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.d.ts.map +1 -1
- package/lib/hooks/useSkeletalAnimation.js +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 +16 -16
- 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.map +1 -1
- package/lib/systems/animation/builders/KickPhaseApplicator.d.ts +6 -0
- package/lib/systems/animation/builders/KickPhaseApplicator.d.ts.map +1 -1
- package/lib/systems/animation/builders/KickPhaseApplicator.js +16 -9
- package/lib/systems/animation/builders/KickPhaseApplicator.js.map +1 -1
- package/lib/systems/animation/builders/KoreanGuardPositions.d.ts +4 -4
- package/lib/systems/animation/builders/KoreanGuardPositions.js.map +1 -1
- package/lib/systems/animation/builders/MartialArtsAnimationBuilder.d.ts +1 -1
- package/lib/systems/animation/builders/MartialArtsAnimationBuilder.d.ts.map +1 -1
- package/lib/systems/animation/builders/MartialArtsAnimationBuilder.js +5 -5
- package/lib/systems/animation/builders/MartialArtsAnimationBuilder.js.map +1 -1
- package/lib/systems/animation/builders/MartialArtsConstants.d.ts +112 -71
- package/lib/systems/animation/builders/MartialArtsConstants.d.ts.map +1 -1
- package/lib/systems/animation/builders/MartialArtsConstants.js +113 -72
- 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/AttackAnimations.js.map +1 -1
- package/lib/systems/animation/catalogs/BasicAnimations.js.map +1 -1
- package/lib/systems/animation/catalogs/ComboAnimations.js.map +1 -1
- package/lib/systems/animation/catalogs/DarkOpsAnimations.js.map +1 -1
- package/lib/systems/animation/catalogs/DefensiveAnimations.js.map +1 -1
- package/lib/systems/animation/catalogs/ElbowKneeAnimations.js.map +1 -1
- package/lib/systems/animation/catalogs/EnhancedAttackAnimations.js.map +1 -1
- package/lib/systems/animation/catalogs/EnhancedElbowKneeAnimations.js.map +1 -1
- package/lib/systems/animation/catalogs/FootworkSkeletalAnimations.js.map +1 -1
- package/lib/systems/animation/catalogs/GamRedirectionAnimations.js.map +1 -1
- package/lib/systems/animation/catalogs/GamStanceAnimations.js +21 -0
- package/lib/systems/animation/catalogs/GamStanceAnimations.js.map +1 -0
- package/lib/systems/animation/catalogs/GamTechniqueAnimations.js +34 -2
- package/lib/systems/animation/catalogs/GamTechniqueAnimations.js.map +1 -1
- package/lib/systems/animation/catalogs/GanStanceAnimations.js.map +1 -1
- package/lib/systems/animation/catalogs/GanTechniqueAnimations.js.map +1 -1
- package/lib/systems/animation/catalogs/GeonStanceAnimations.js.map +1 -1
- package/lib/systems/animation/catalogs/GonTechniqueAnimations.d.ts +9 -0
- package/lib/systems/animation/catalogs/GonTechniqueAnimations.d.ts.map +1 -1
- package/lib/systems/animation/catalogs/GonTechniqueAnimations.js +288 -0
- package/lib/systems/animation/catalogs/GonTechniqueAnimations.js.map +1 -0
- package/lib/systems/animation/catalogs/GrapplingAnimations.js.map +1 -1
- package/lib/systems/animation/catalogs/JinStanceAnimations.js.map +1 -1
- package/lib/systems/animation/catalogs/JinTechniqueAnimations.js.map +1 -1
- package/lib/systems/animation/catalogs/KickAnimations.d.ts +2 -2
- package/lib/systems/animation/catalogs/KickAnimations.js +2 -2
- package/lib/systems/animation/catalogs/KickAnimations.js.map +1 -1
- package/lib/systems/animation/catalogs/LiStanceAnimations.js +14 -1
- package/lib/systems/animation/catalogs/LiStanceAnimations.js.map +1 -1
- package/lib/systems/animation/catalogs/LiTechniqueAnimations.js.map +1 -1
- package/lib/systems/animation/catalogs/MovementAnimations.js.map +1 -1
- package/lib/systems/animation/catalogs/PunchAnimations.d.ts +1 -1
- package/lib/systems/animation/catalogs/PunchAnimations.js +1 -1
- package/lib/systems/animation/catalogs/PunchAnimations.js.map +1 -1
- package/lib/systems/animation/catalogs/RecoveryAnimations.js.map +1 -1
- package/lib/systems/animation/catalogs/SonStanceAnimations.js.map +1 -1
- package/lib/systems/animation/catalogs/SonTechniqueAnimations.js.map +1 -1
- package/lib/systems/animation/catalogs/SpecializedPunchAnimations.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.d.ts +6 -6
- package/lib/systems/animation/catalogs/StanceGuardPoses.js +36 -36
- 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/catalogs/TaeJointLockAnimations.js.map +1 -1
- package/lib/systems/animation/catalogs/TaeStanceAnimations.js.map +1 -1
- package/lib/systems/animation/constants/AnatomicalLimits.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 +15 -15
- package/lib/systems/animation/core/AnimationPriority.js.map +1 -1
- package/lib/systems/animation/core/AnimationRegistry.d.ts +30 -0
- package/lib/systems/animation/core/AnimationRegistry.d.ts.map +1 -1
- package/lib/systems/animation/core/AnimationRegistry.js +74 -12
- package/lib/systems/animation/core/AnimationRegistry.js.map +1 -1
- package/lib/systems/animation/core/AnimationStateMachine.js +16 -16
- package/lib/systems/animation/core/AnimationStateMachine.js.map +1 -1
- package/lib/systems/animation/core/AnimationTransitions.d.ts.map +1 -1
- package/lib/systems/animation/core/AnimationTransitions.js +34 -0
- 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/index.d.ts +1 -1
- package/lib/systems/animation/core/index.d.ts.map +1 -1
- package/lib/systems/animation/core/types.d.ts +24 -0
- package/lib/systems/animation/core/types.d.ts.map +1 -1
- package/lib/systems/animation/core/types.js +27 -11
- 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 +19 -19
- 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 +19 -19
- package/lib/systems/combat/BalanceSystem.js.map +1 -1
- package/lib/systems/combat/BreakingStatusEffects.js.map +1 -1
- package/lib/systems/combat/CombatStateSystem.js +17 -17
- package/lib/systems/combat/CombatStateSystem.js.map +1 -1
- package/lib/systems/combat/ConsciousnessSystem.js +24 -24
- 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 +21 -21
- 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 +6 -6
- 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/GeonTechniques.js.map +1 -1
- package/lib/systems/trigram/techniques/GonTechniques.js.map +1 -1
- package/lib/systems/trigram/techniques/JinTechniques.js.map +1 -1
- package/lib/systems/trigram/techniques/LiTechniques.js.map +1 -1
- package/lib/systems/trigram/techniques/SonTechniques.js.map +1 -1
- package/lib/systems/trigram/techniques/TaeTechniques.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/systems/vitalpoint/VitalPointsData.js.map +1 -1
- package/lib/types/AccessibilityTypes.js.map +1 -1
- package/lib/types/LayoutTypes.js.map +1 -1
- package/lib/types/PhysicsTypes.js.map +1 -1
- package/lib/types/common.js.map +1 -1
- package/lib/types/constants/animations.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/index.js.map +1 -1
- package/lib/types/constants/layout.d.ts +21 -0
- package/lib/types/constants/layout.d.ts.map +1 -1
- package/lib/types/constants/layout.js +22 -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/constants/ui.js.map +1 -1
- package/lib/types/facial.js +19 -19
- 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/muscle.js.map +1 -1
- package/lib/types/physics.js.map +1 -1
- package/lib/types/physicsConstants.js.map +1 -1
- package/lib/types/player-visual.d.ts +1 -1
- package/lib/types/player-visual.d.ts.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 +6 -7
- 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.d.ts +7 -0
- package/lib/utils/responsiveLayoutHelpers.d.ts.map +1 -1
- package/lib/utils/responsiveLayoutHelpers.js +16 -2
- 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 +7 -7
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"AdaptiveDifficulty.js","names":[],"sources":["../../../src/systems/ai/AdaptiveDifficulty.ts"],"sourcesContent":["/**\n * Adaptive Difficulty System for Korean Martial Arts Combat\n * Tracks player skill metrics and adjusts AI difficulty dynamically\n */\n\nimport { AIPersonality } from \"./AIPersonality\";\n\n/**\n * Player skill metrics tracked for adaptive difficulty\n */\nexport interface PlayerSkillMetrics {\n averageAccuracy: number; // 0.0-1.0: Hit rate\n comboCount: number; // Total combos executed\n perfectBlocks: number; // Perfect timing blocks\n reactionTime: number; // Average reaction time in ms\n vitalPointHits: number; // Successful vital point strikes\n stanceTransitions: number; // Effective stance changes\n damageEfficiency: number; // 0.0-1.0: Damage dealt vs taken ratio\n matchesPlayed: number; // Total matches for scaling\n}\n\n/**\n * Difficulty tier levels (5 tiers for adaptive system)\n */\nexport enum DifficultyTier {\n BEGINNER = 1,\n NOVICE = 2,\n INTERMEDIATE = 3,\n ADVANCED = 4,\n EXPERT = 5,\n}\n\n/**\n * Difficulty parameters that control AI behavior\n * Applied dynamically based on player skill level\n * \n * @korean 난이도 매개변수 - 플레이어 실력에 따라 AI 행동 제어\n */\nexport interface DifficultyParameters {\n /** AI reaction time range in milliseconds */\n readonly reactionTimeMs: { readonly min: number; readonly max: number };\n /** Accuracy for vital point strikes (0.0-1.0) */\n readonly vitalPointAccuracy: number;\n /** Accuracy for basic attacks (0.0-1.0) */\n readonly basicAttackAccuracy: number;\n /** Block timing window in milliseconds (smaller = harder to block) */\n readonly blockTimingWindow: number;\n /** Decision quality affects technique selection optimality (0.0-1.0) */\n readonly decisionQuality: number;\n /** Aggression modifier multiplier (0.5-2.0) */\n readonly aggressionModifier: number;\n /** Chance to attempt combo sequences (0.0-1.0) */\n readonly comboChance: number;\n}\n\n/**\n * Difficulty parameter sets for each skill tier\n * Defines AI behavior characteristics at each difficulty level\n * \n * @korean 각 난이도 단계별 매개변수 설정\n */\nexport const DIFFICULTY_PARAMETERS: Record<DifficultyTier, DifficultyParameters> = {\n [DifficultyTier.BEGINNER]: {\n reactionTimeMs: { min: 800, max: 1200 },\n vitalPointAccuracy: 0.40,\n basicAttackAccuracy: 0.70,\n blockTimingWindow: 150,\n decisionQuality: 0.50,\n aggressionModifier: 0.7,\n comboChance: 0.20,\n },\n [DifficultyTier.NOVICE]: {\n reactionTimeMs: { min: 500, max: 800 },\n vitalPointAccuracy: 0.55,\n basicAttackAccuracy: 0.78,\n blockTimingWindow: 120,\n decisionQuality: 0.65,\n aggressionModifier: 0.9,\n comboChance: 0.35,\n },\n [DifficultyTier.INTERMEDIATE]: {\n reactionTimeMs: { min: 300, max: 500 },\n vitalPointAccuracy: 0.65,\n basicAttackAccuracy: 0.85,\n blockTimingWindow: 90,\n decisionQuality: 0.75,\n aggressionModifier: 1.1,\n comboChance: 0.50,\n },\n [DifficultyTier.ADVANCED]: {\n reactionTimeMs: { min: 150, max: 300 },\n vitalPointAccuracy: 0.75,\n basicAttackAccuracy: 0.90,\n blockTimingWindow: 70,\n decisionQuality: 0.85,\n aggressionModifier: 1.3,\n comboChance: 0.60,\n },\n [DifficultyTier.EXPERT]: {\n reactionTimeMs: { min: 50, max: 150 },\n vitalPointAccuracy: 0.85,\n basicAttackAccuracy: 0.95,\n blockTimingWindow: 50,\n decisionQuality: 0.95,\n aggressionModifier: 1.5,\n comboChance: 0.70,\n },\n};\n\n/**\n * Map skill score (0.0-1.0) to difficulty tier\n * Uses fixed thresholds for consistent tier assignment\n * \n * @korean 실력 점수를 난이도 단계로 변환\n * \n * @param score - Player skill score (0.0-1.0)\n * @returns Corresponding difficulty tier\n */\nexport function skillScoreToTier(score: number): DifficultyTier {\n if (score < 0.2) return DifficultyTier.BEGINNER;\n if (score < 0.4) return DifficultyTier.NOVICE;\n if (score < 0.6) return DifficultyTier.INTERMEDIATE;\n if (score < 0.8) return DifficultyTier.ADVANCED;\n return DifficultyTier.EXPERT;\n}\n\n/**\n * Linear interpolation helper\n * @param a - Start value\n * @param b - End value\n * @param t - Interpolation factor (0.0-1.0)\n * @returns Interpolated value\n */\nfunction lerp(a: number, b: number, t: number): number {\n return a + (b - a) * Math.max(0, Math.min(1, t));\n}\n\n/**\n * Interpolate between two difficulty parameter sets\n * Used for smooth difficulty transitions over time\n * \n * @korean 난이도 매개변수 간 부드러운 전환\n * \n * @param from - Starting difficulty parameters\n * @param to - Target difficulty parameters\n * @param progress - Interpolation progress (0.0-1.0)\n * @returns Interpolated difficulty parameters\n */\nexport function interpolateDifficultyParameters(\n from: DifficultyParameters,\n to: DifficultyParameters,\n progress: number\n): DifficultyParameters {\n const t = Math.max(0, Math.min(1, progress));\n \n return {\n reactionTimeMs: {\n min: lerp(from.reactionTimeMs.min, to.reactionTimeMs.min, t),\n max: lerp(from.reactionTimeMs.max, to.reactionTimeMs.max, t),\n },\n vitalPointAccuracy: lerp(from.vitalPointAccuracy, to.vitalPointAccuracy, t),\n basicAttackAccuracy: lerp(from.basicAttackAccuracy, to.basicAttackAccuracy, t),\n blockTimingWindow: lerp(from.blockTimingWindow, to.blockTimingWindow, t),\n decisionQuality: lerp(from.decisionQuality, to.decisionQuality, t),\n aggressionModifier: lerp(from.aggressionModifier, to.aggressionModifier, t),\n comboChance: lerp(from.comboChance, to.comboChance, t),\n };\n}\n\n/**\n * Adaptive Difficulty System\n */\nexport class AdaptiveDifficulty {\n private playerSkillMetrics: PlayerSkillMetrics;\n private readonly skillDecay = 0.95; // Gradual skill decay between matches\n private readonly learningRate = 0.1; // How quickly to adapt\n\n constructor() {\n this.playerSkillMetrics = {\n averageAccuracy: 0.5,\n comboCount: 0,\n perfectBlocks: 0,\n reactionTime: 800,\n vitalPointHits: 0,\n stanceTransitions: 0,\n damageEfficiency: 0.5,\n matchesPlayed: 0,\n };\n }\n\n /**\n * Update player skill metrics based on match performance\n */\n updateSkillMetrics(matchData: {\n hitsLanded: number;\n totalAttacks: number;\n combosExecuted: number;\n perfectBlockCount: number;\n avgReactionTimeMs: number;\n vitalPointsHit: number;\n effectiveStanceChanges: number;\n damageDealt: number;\n damageTaken: number;\n }): void {\n const { metrics } = this;\n\n // Update accuracy with learning rate\n const matchAccuracy =\n matchData.totalAttacks > 0\n ? matchData.hitsLanded / matchData.totalAttacks\n : 0.5;\n metrics.averageAccuracy =\n metrics.averageAccuracy * (1 - this.learningRate) +\n matchAccuracy * this.learningRate;\n\n // Update combo count\n metrics.comboCount += matchData.combosExecuted;\n\n // Update perfect blocks\n metrics.perfectBlocks += matchData.perfectBlockCount;\n\n // Update reaction time\n if (matchData.avgReactionTimeMs > 0) {\n metrics.reactionTime =\n metrics.reactionTime * (1 - this.learningRate) +\n matchData.avgReactionTimeMs * this.learningRate;\n }\n\n // Update vital point hits\n metrics.vitalPointHits += matchData.vitalPointsHit;\n\n // Update stance transitions\n metrics.stanceTransitions += matchData.effectiveStanceChanges;\n\n // Update damage efficiency\n const matchEfficiency =\n matchData.damageTaken > 0\n ? Math.min(1, matchData.damageDealt / matchData.damageTaken)\n : matchData.damageDealt > 0\n ? 1.0 // Perfect defense with damage dealt\n : 0.5; // No damage on either side\n metrics.damageEfficiency =\n metrics.damageEfficiency * (1 - this.learningRate) +\n matchEfficiency * this.learningRate;\n\n // Increment matches played\n metrics.matchesPlayed += 1;\n\n // Apply skill decay to prevent over-adjustment\n this.applySkillDecay();\n }\n\n /**\n * Apply gradual skill decay to prevent over-adjustment\n */\n private applySkillDecay(): void {\n const { metrics } = this;\n metrics.averageAccuracy =\n metrics.averageAccuracy * this.skillDecay +\n 0.5 * (1 - this.skillDecay);\n metrics.damageEfficiency =\n metrics.damageEfficiency * this.skillDecay +\n 0.5 * (1 - this.skillDecay);\n }\n\n /**\n * Calculate overall player skill level (0.0 - 1.0)\n */\n calculatePlayerSkill(): number {\n const { metrics } = this;\n\n // Weight different skill components\n const accuracyScore = metrics.averageAccuracy * 0.3;\n const comboScore = Math.min(1, metrics.comboCount / 20) * 0.2;\n const blockScore = Math.min(1, metrics.perfectBlocks / 10) * 0.2;\n const reactionScore = Math.max(0, 1 - metrics.reactionTime / 1000) * 0.15;\n const vitalScore = Math.min(1, metrics.vitalPointHits / 15) * 0.15;\n\n return accuracyScore + comboScore + blockScore + reactionScore + vitalScore;\n }\n\n /**\n * Get current difficulty tier based on skill level\n */\n getDifficultyTier(): DifficultyTier {\n const skillLevel = this.calculatePlayerSkill();\n return skillScoreToTier(skillLevel);\n }\n\n /**\n * Get difficulty parameters for current skill tier\n * Returns the appropriate DifficultyParameters based on player skill\n * \n * @korean 현재 실력 단계에 맞는 난이도 매개변수 반환\n */\n getDifficultyParameters(): DifficultyParameters {\n const tier = this.getDifficultyTier();\n return DIFFICULTY_PARAMETERS[tier];\n }\n\n /**\n * Adjust AI personality based on player skill\n */\n adjustAIPersonality(personality: AIPersonality): AIPersonality {\n const skillLevel = this.calculatePlayerSkill();\n const tier = this.getDifficultyTier();\n\n // Scale factors based on difficulty tier\n const aggressionScale = 1 + tier * 0.1; // +10% per tier\n const feintScale = 1 + tier * 0.15; // +15% per tier\n const comboScale = 1 + tier * 0.12; // +12% per tier\n const stanceScale = 1 + tier * 0.08; // +8% per tier\n\n return {\n ...personality,\n aggressionLevel: Math.min(\n 0.95,\n personality.aggressionLevel * aggressionScale\n ),\n feintChance: Math.min(0.6, personality.feintChance * feintScale),\n comboTendency: Math.min(0.85, personality.comboTendency * comboScale),\n stanceSwitchFrequency: Math.min(\n 0.9,\n personality.stanceSwitchFrequency * stanceScale\n ),\n // Adjust retreat threshold - better players face more aggressive AI\n tacticalRetreatThreshold: Math.max(\n 0.1,\n personality.tacticalRetreatThreshold * (1 - skillLevel * 0.3)\n ),\n };\n }\n\n /**\n * Get skill metrics\n */\n getMetrics(): Readonly<PlayerSkillMetrics> {\n return { ...this.playerSkillMetrics };\n }\n\n /**\n * Reset skill metrics\n */\n reset(): void {\n this.playerSkillMetrics = {\n averageAccuracy: 0.5,\n comboCount: 0,\n perfectBlocks: 0,\n reactionTime: 800,\n vitalPointHits: 0,\n stanceTransitions: 0,\n damageEfficiency: 0.5,\n matchesPlayed: 0,\n };\n }\n\n /**\n * Get difficulty adjustment recommendation\n */\n getDifficultyRecommendation(): {\n tier: DifficultyTier;\n tierName: string;\n skillLevel: number;\n shouldIncrease: boolean;\n message: string;\n } {\n const skillLevel = this.calculatePlayerSkill();\n const tier = this.getDifficultyTier();\n const shouldIncrease = skillLevel > 0.7 && tier < DifficultyTier.EXPERT;\n\n const tierNames: Record<DifficultyTier, string> = {\n [DifficultyTier.BEGINNER]: \"Beginner (초보)\",\n [DifficultyTier.NOVICE]: \"Novice (입문)\",\n [DifficultyTier.INTERMEDIATE]: \"Intermediate (중급)\",\n [DifficultyTier.ADVANCED]: \"Advanced (고급)\",\n [DifficultyTier.EXPERT]: \"Expert (전문)\",\n };\n\n let message: string;\n if (shouldIncrease) {\n message = \"Player shows mastery - increasing difficulty\";\n } else if (skillLevel < 0.3) {\n message = \"Player struggling - maintaining current difficulty\";\n } else {\n message = \"Player performing well - difficulty appropriate\";\n }\n\n return {\n tier,\n tierName: tierNames[tier],\n skillLevel,\n shouldIncrease,\n message,\n };\n }\n\n /**\n * Export metrics for persistence\n */\n exportMetrics(): string {\n return JSON.stringify(this.playerSkillMetrics);\n }\n\n /**\n * Import metrics from persistence\n */\n importMetrics(data: string): boolean {\n try {\n const metrics = JSON.parse(data) as PlayerSkillMetrics;\n this.playerSkillMetrics = metrics;\n return true;\n } catch {\n return false;\n }\n }\n\n /**\n * Get reference to metrics (for internal use)\n */\n private get metrics(): PlayerSkillMetrics {\n return this.playerSkillMetrics;\n }\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"AdaptiveDifficulty.js","names":[],"sources":["../../../src/systems/ai/AdaptiveDifficulty.ts"],"sourcesContent":["/**\n * Adaptive Difficulty System for Korean Martial Arts Combat\n * Tracks player skill metrics and adjusts AI difficulty dynamically\n */\n\nimport { AIPersonality } from \"./AIPersonality\";\n\n/**\n * Player skill metrics tracked for adaptive difficulty\n */\nexport interface PlayerSkillMetrics {\n averageAccuracy: number; // 0.0-1.0: Hit rate\n comboCount: number; // Total combos executed\n perfectBlocks: number; // Perfect timing blocks\n reactionTime: number; // Average reaction time in ms\n vitalPointHits: number; // Successful vital point strikes\n stanceTransitions: number; // Effective stance changes\n damageEfficiency: number; // 0.0-1.0: Damage dealt vs taken ratio\n matchesPlayed: number; // Total matches for scaling\n}\n\n/**\n * Difficulty tier levels (5 tiers for adaptive system)\n */\nexport enum DifficultyTier {\n BEGINNER = 1,\n NOVICE = 2,\n INTERMEDIATE = 3,\n ADVANCED = 4,\n EXPERT = 5,\n}\n\n/**\n * Difficulty parameters that control AI behavior\n * Applied dynamically based on player skill level\n * \n * @korean 난이도 매개변수 - 플레이어 실력에 따라 AI 행동 제어\n */\nexport interface DifficultyParameters {\n /** AI reaction time range in milliseconds */\n readonly reactionTimeMs: { readonly min: number; readonly max: number };\n /** Accuracy for vital point strikes (0.0-1.0) */\n readonly vitalPointAccuracy: number;\n /** Accuracy for basic attacks (0.0-1.0) */\n readonly basicAttackAccuracy: number;\n /** Block timing window in milliseconds (smaller = harder to block) */\n readonly blockTimingWindow: number;\n /** Decision quality affects technique selection optimality (0.0-1.0) */\n readonly decisionQuality: number;\n /** Aggression modifier multiplier (0.5-2.0) */\n readonly aggressionModifier: number;\n /** Chance to attempt combo sequences (0.0-1.0) */\n readonly comboChance: number;\n}\n\n/**\n * Difficulty parameter sets for each skill tier\n * Defines AI behavior characteristics at each difficulty level\n * \n * @korean 각 난이도 단계별 매개변수 설정\n */\nexport const DIFFICULTY_PARAMETERS: Record<DifficultyTier, DifficultyParameters> = {\n [DifficultyTier.BEGINNER]: {\n reactionTimeMs: { min: 800, max: 1200 },\n vitalPointAccuracy: 0.40,\n basicAttackAccuracy: 0.70,\n blockTimingWindow: 150,\n decisionQuality: 0.50,\n aggressionModifier: 0.7,\n comboChance: 0.20,\n },\n [DifficultyTier.NOVICE]: {\n reactionTimeMs: { min: 500, max: 800 },\n vitalPointAccuracy: 0.55,\n basicAttackAccuracy: 0.78,\n blockTimingWindow: 120,\n decisionQuality: 0.65,\n aggressionModifier: 0.9,\n comboChance: 0.35,\n },\n [DifficultyTier.INTERMEDIATE]: {\n reactionTimeMs: { min: 300, max: 500 },\n vitalPointAccuracy: 0.65,\n basicAttackAccuracy: 0.85,\n blockTimingWindow: 90,\n decisionQuality: 0.75,\n aggressionModifier: 1.1,\n comboChance: 0.50,\n },\n [DifficultyTier.ADVANCED]: {\n reactionTimeMs: { min: 150, max: 300 },\n vitalPointAccuracy: 0.75,\n basicAttackAccuracy: 0.90,\n blockTimingWindow: 70,\n decisionQuality: 0.85,\n aggressionModifier: 1.3,\n comboChance: 0.60,\n },\n [DifficultyTier.EXPERT]: {\n reactionTimeMs: { min: 50, max: 150 },\n vitalPointAccuracy: 0.85,\n basicAttackAccuracy: 0.95,\n blockTimingWindow: 50,\n decisionQuality: 0.95,\n aggressionModifier: 1.5,\n comboChance: 0.70,\n },\n};\n\n/**\n * Map skill score (0.0-1.0) to difficulty tier\n * Uses fixed thresholds for consistent tier assignment\n * \n * @korean 실력 점수를 난이도 단계로 변환\n * \n * @param score - Player skill score (0.0-1.0)\n * @returns Corresponding difficulty tier\n */\nexport function skillScoreToTier(score: number): DifficultyTier {\n if (score < 0.2) return DifficultyTier.BEGINNER;\n if (score < 0.4) return DifficultyTier.NOVICE;\n if (score < 0.6) return DifficultyTier.INTERMEDIATE;\n if (score < 0.8) return DifficultyTier.ADVANCED;\n return DifficultyTier.EXPERT;\n}\n\n/**\n * Linear interpolation helper\n * @param a - Start value\n * @param b - End value\n * @param t - Interpolation factor (0.0-1.0)\n * @returns Interpolated value\n */\nfunction lerp(a: number, b: number, t: number): number {\n return a + (b - a) * Math.max(0, Math.min(1, t));\n}\n\n/**\n * Interpolate between two difficulty parameter sets\n * Used for smooth difficulty transitions over time\n * \n * @korean 난이도 매개변수 간 부드러운 전환\n * \n * @param from - Starting difficulty parameters\n * @param to - Target difficulty parameters\n * @param progress - Interpolation progress (0.0-1.0)\n * @returns Interpolated difficulty parameters\n */\nexport function interpolateDifficultyParameters(\n from: DifficultyParameters,\n to: DifficultyParameters,\n progress: number\n): DifficultyParameters {\n const t = Math.max(0, Math.min(1, progress));\n \n return {\n reactionTimeMs: {\n min: lerp(from.reactionTimeMs.min, to.reactionTimeMs.min, t),\n max: lerp(from.reactionTimeMs.max, to.reactionTimeMs.max, t),\n },\n vitalPointAccuracy: lerp(from.vitalPointAccuracy, to.vitalPointAccuracy, t),\n basicAttackAccuracy: lerp(from.basicAttackAccuracy, to.basicAttackAccuracy, t),\n blockTimingWindow: lerp(from.blockTimingWindow, to.blockTimingWindow, t),\n decisionQuality: lerp(from.decisionQuality, to.decisionQuality, t),\n aggressionModifier: lerp(from.aggressionModifier, to.aggressionModifier, t),\n comboChance: lerp(from.comboChance, to.comboChance, t),\n };\n}\n\n/**\n * Adaptive Difficulty System\n */\nexport class AdaptiveDifficulty {\n private playerSkillMetrics: PlayerSkillMetrics;\n private readonly skillDecay = 0.95; // Gradual skill decay between matches\n private readonly learningRate = 0.1; // How quickly to adapt\n\n constructor() {\n this.playerSkillMetrics = {\n averageAccuracy: 0.5,\n comboCount: 0,\n perfectBlocks: 0,\n reactionTime: 800,\n vitalPointHits: 0,\n stanceTransitions: 0,\n damageEfficiency: 0.5,\n matchesPlayed: 0,\n };\n }\n\n /**\n * Update player skill metrics based on match performance\n */\n updateSkillMetrics(matchData: {\n hitsLanded: number;\n totalAttacks: number;\n combosExecuted: number;\n perfectBlockCount: number;\n avgReactionTimeMs: number;\n vitalPointsHit: number;\n effectiveStanceChanges: number;\n damageDealt: number;\n damageTaken: number;\n }): void {\n const { metrics } = this;\n\n // Update accuracy with learning rate\n const matchAccuracy =\n matchData.totalAttacks > 0\n ? matchData.hitsLanded / matchData.totalAttacks\n : 0.5;\n metrics.averageAccuracy =\n metrics.averageAccuracy * (1 - this.learningRate) +\n matchAccuracy * this.learningRate;\n\n // Update combo count\n metrics.comboCount += matchData.combosExecuted;\n\n // Update perfect blocks\n metrics.perfectBlocks += matchData.perfectBlockCount;\n\n // Update reaction time\n if (matchData.avgReactionTimeMs > 0) {\n metrics.reactionTime =\n metrics.reactionTime * (1 - this.learningRate) +\n matchData.avgReactionTimeMs * this.learningRate;\n }\n\n // Update vital point hits\n metrics.vitalPointHits += matchData.vitalPointsHit;\n\n // Update stance transitions\n metrics.stanceTransitions += matchData.effectiveStanceChanges;\n\n // Update damage efficiency\n const matchEfficiency =\n matchData.damageTaken > 0\n ? Math.min(1, matchData.damageDealt / matchData.damageTaken)\n : matchData.damageDealt > 0\n ? 1.0 // Perfect defense with damage dealt\n : 0.5; // No damage on either side\n metrics.damageEfficiency =\n metrics.damageEfficiency * (1 - this.learningRate) +\n matchEfficiency * this.learningRate;\n\n // Increment matches played\n metrics.matchesPlayed += 1;\n\n // Apply skill decay to prevent over-adjustment\n this.applySkillDecay();\n }\n\n /**\n * Apply gradual skill decay to prevent over-adjustment\n */\n private applySkillDecay(): void {\n const { metrics } = this;\n metrics.averageAccuracy =\n metrics.averageAccuracy * this.skillDecay +\n 0.5 * (1 - this.skillDecay);\n metrics.damageEfficiency =\n metrics.damageEfficiency * this.skillDecay +\n 0.5 * (1 - this.skillDecay);\n }\n\n /**\n * Calculate overall player skill level (0.0 - 1.0)\n */\n calculatePlayerSkill(): number {\n const { metrics } = this;\n\n // Weight different skill components\n const accuracyScore = metrics.averageAccuracy * 0.3;\n const comboScore = Math.min(1, metrics.comboCount / 20) * 0.2;\n const blockScore = Math.min(1, metrics.perfectBlocks / 10) * 0.2;\n const reactionScore = Math.max(0, 1 - metrics.reactionTime / 1000) * 0.15;\n const vitalScore = Math.min(1, metrics.vitalPointHits / 15) * 0.15;\n\n return accuracyScore + comboScore + blockScore + reactionScore + vitalScore;\n }\n\n /**\n * Get current difficulty tier based on skill level\n */\n getDifficultyTier(): DifficultyTier {\n const skillLevel = this.calculatePlayerSkill();\n return skillScoreToTier(skillLevel);\n }\n\n /**\n * Get difficulty parameters for current skill tier\n * Returns the appropriate DifficultyParameters based on player skill\n * \n * @korean 현재 실력 단계에 맞는 난이도 매개변수 반환\n */\n getDifficultyParameters(): DifficultyParameters {\n const tier = this.getDifficultyTier();\n return DIFFICULTY_PARAMETERS[tier];\n }\n\n /**\n * Adjust AI personality based on player skill\n */\n adjustAIPersonality(personality: AIPersonality): AIPersonality {\n const skillLevel = this.calculatePlayerSkill();\n const tier = this.getDifficultyTier();\n\n // Scale factors based on difficulty tier\n const aggressionScale = 1 + tier * 0.1; // +10% per tier\n const feintScale = 1 + tier * 0.15; // +15% per tier\n const comboScale = 1 + tier * 0.12; // +12% per tier\n const stanceScale = 1 + tier * 0.08; // +8% per tier\n\n return {\n ...personality,\n aggressionLevel: Math.min(\n 0.95,\n personality.aggressionLevel * aggressionScale\n ),\n feintChance: Math.min(0.6, personality.feintChance * feintScale),\n comboTendency: Math.min(0.85, personality.comboTendency * comboScale),\n stanceSwitchFrequency: Math.min(\n 0.9,\n personality.stanceSwitchFrequency * stanceScale\n ),\n // Adjust retreat threshold - better players face more aggressive AI\n tacticalRetreatThreshold: Math.max(\n 0.1,\n personality.tacticalRetreatThreshold * (1 - skillLevel * 0.3)\n ),\n };\n }\n\n /**\n * Get skill metrics\n */\n getMetrics(): Readonly<PlayerSkillMetrics> {\n return { ...this.playerSkillMetrics };\n }\n\n /**\n * Reset skill metrics\n */\n reset(): void {\n this.playerSkillMetrics = {\n averageAccuracy: 0.5,\n comboCount: 0,\n perfectBlocks: 0,\n reactionTime: 800,\n vitalPointHits: 0,\n stanceTransitions: 0,\n damageEfficiency: 0.5,\n matchesPlayed: 0,\n };\n }\n\n /**\n * Get difficulty adjustment recommendation\n */\n getDifficultyRecommendation(): {\n tier: DifficultyTier;\n tierName: string;\n skillLevel: number;\n shouldIncrease: boolean;\n message: string;\n } {\n const skillLevel = this.calculatePlayerSkill();\n const tier = this.getDifficultyTier();\n const shouldIncrease = skillLevel > 0.7 && tier < DifficultyTier.EXPERT;\n\n const tierNames: Record<DifficultyTier, string> = {\n [DifficultyTier.BEGINNER]: \"Beginner (초보)\",\n [DifficultyTier.NOVICE]: \"Novice (입문)\",\n [DifficultyTier.INTERMEDIATE]: \"Intermediate (중급)\",\n [DifficultyTier.ADVANCED]: \"Advanced (고급)\",\n [DifficultyTier.EXPERT]: \"Expert (전문)\",\n };\n\n let message: string;\n if (shouldIncrease) {\n message = \"Player shows mastery - increasing difficulty\";\n } else if (skillLevel < 0.3) {\n message = \"Player struggling - maintaining current difficulty\";\n } else {\n message = \"Player performing well - difficulty appropriate\";\n }\n\n return {\n tier,\n tierName: tierNames[tier],\n skillLevel,\n shouldIncrease,\n message,\n };\n }\n\n /**\n * Export metrics for persistence\n */\n exportMetrics(): string {\n return JSON.stringify(this.playerSkillMetrics);\n }\n\n /**\n * Import metrics from persistence\n */\n importMetrics(data: string): boolean {\n try {\n const metrics = JSON.parse(data) as PlayerSkillMetrics;\n this.playerSkillMetrics = metrics;\n return true;\n } catch {\n return false;\n }\n }\n\n /**\n * Get reference to metrics (for internal use)\n */\n private get metrics(): PlayerSkillMetrics {\n return this.playerSkillMetrics;\n }\n}\n"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ArchetypeEnforcer.js","names":[],"sources":["../../../src/systems/ai/ArchetypeEnforcer.ts"],"sourcesContent":["/**\n * Archetype Behavior Enforcement System\n * \n * Centralizes archetype-specific combat philosophy enforcement to ensure\n * each of the 5 player archetypes has immediately recognizable and distinct\n * AI behavior patterns.\n * \n * **Korean Philosophy Integration (한국 무술 철학)**:\n * - 무사 (Musa): 명예 (Honor) - Direct, honorable combat\n * - 암살자 (Amsalja): 은밀 (Stealth) - Silent, precise takedowns\n * - 해커 (Hacker): 분석 (Analysis) - Tech-enhanced tactical combat\n * - 정보요원 (Jeongbo): 심리전 (Psychological Warfare) - Strategic manipulation\n * - 조직폭력배 (Jojik): 생존 (Survival) - Ruthless, unpredictable tactics\n * \n * @module systems/ai/ArchetypeEnforcer\n * @category AI Combat\n * @korean 원형 행동 강화 시스템\n */\n\nimport { PlayerArchetype } from \"@/types\";\nimport { AIDecision, AIActionType, CombatContext } from \"./types\";\n\n/**\n * Archetype enforcement rules defining distinct combat behaviors\n * \n * Each archetype has:\n * - Preferred actions (used most frequently)\n * - Prohibited actions (never used due to philosophy)\n * - Signature move (iconic technique with specific trigger condition)\n * - Action frequencies (target distribution percentages)\n * \n * **Note on Action Frequencies**:\n * These values represent **target frequencies/proportions** for each action type\n * rather than a single normalized probability distribution. Multiple actions can have\n * high target frequencies because they can occur in overlapping situations:\n * - ATTACK (70%) and TECHNIQUE (70%) both represent aggressive behavior\n * - STANCE_CHANGE (80%) occurs frequently but doesn't prevent other actions\n * - The frequencies serve as **behavioral targets** to guide AI personality,\n * and are not required to sum to 100% across all actions\n * \n * @korean 원형 강화 규칙\n */\nexport interface ArchetypeEnforcementRules {\n /** Actions this archetype uses most frequently */\n readonly preferredActions: readonly AIActionType[];\n \n /** Actions this archetype never uses (philosophy conflicts) */\n readonly prohibitedActions: readonly AIActionType[];\n \n /** Signature technique ID for this archetype */\n readonly signatureMove: string;\n \n /** Condition function to trigger signature move */\n readonly signatureCondition: (context: CombatContext) => boolean;\n \n /** Target frequency distribution for each action type (0.0-1.0) */\n readonly actionFrequencies: Readonly<Record<AIActionType, number>>;\n}\n\n/**\n * Archetype enforcement rules for all 5 player archetypes\n * \n * Defines distinct combat philosophies with measurable behavior patterns\n * that make each archetype immediately identifiable through AI actions.\n * \n * @korean 5대 원형 강화 규칙\n */\nexport const ARCHETYPE_ENFORCEMENT: Record<PlayerArchetype, ArchetypeEnforcementRules> = {\n [PlayerArchetype.MUSA]: {\n preferredActions: [AIActionType.ATTACK, AIActionType.APPROACH, AIActionType.TECHNIQUE],\n prohibitedActions: [AIActionType.FEINT], // Honor code: No deception\n signatureMove: \"musa_mountain_breaker\",\n signatureCondition: (context) => {\n /**\n * Musa (무사) signature move condition: Mountain Breaker when opponent <40% health\n * \n * NOTE: Uses opponentMaxHealth if available, otherwise assumes symmetric max-health\n * pools (context.playerMaxHealth === opponentMaxHealth). If asymmetric max health\n * exists, CombatContext provides opponentMaxHealth for accurate calculation.\n */\n const maxHealth = context.opponentMaxHealth ?? context.playerMaxHealth;\n if (maxHealth <= 0) {\n // Defensive guard: avoid divide-by-zero / invalid percentages\n return false;\n }\n const opponentHealthPercent = context.opponentHealth / maxHealth;\n return opponentHealthPercent < 0.40; // Finishing move when opponent <40% health\n },\n actionFrequencies: {\n /**\n * Musa (무사) Action Frequencies - Honor Code Warrior\n * \n * These frequencies represent behavioral targets (not strict probability distributions).\n * Multiple actions can have high frequencies as they represent overlapping aggressive behavior.\n * \n * **Honor Code Rules**:\n * - FEINT: 0% (prohibited - no deception)\n * - RETREAT: 2% (minimal - only at critical health <10%, handled by evaluateSurvival)\n * - DEFEND: 10% (minimal - offense-focused philosophy)\n * \n * The 2% retreat frequency applies only when health drops below the honor threshold (~5-10%),\n * enforced by evaluateSurvival logic. Above this threshold, Musa never retreats.\n */\n [AIActionType.ATTACK]: 0.70, // 70% aggressive attacks\n [AIActionType.DEFEND]: 0.10, // 10% defense (honor code: minimal defense)\n [AIActionType.RETREAT]: 0.02, // 2% retreat (only at critical health <10%)\n [AIActionType.APPROACH]: 0.15, // 15% approach\n [AIActionType.FEINT]: 0.00, // 0% feints (prohibited by honor code)\n [AIActionType.TECHNIQUE]: 0.70, // 70% techniques (overlaps with attack)\n [AIActionType.COUNTER]: 0.10, // 10% counters\n [AIActionType.STANCE_CHANGE]: 0.10, // 10% stance changes\n [AIActionType.CIRCLE]: 0.05, // 5% circling\n [AIActionType.WAIT]: 0.03, // 3% wait\n [AIActionType.COMBO]: 0.05, // 5% combos\n },\n },\n\n [PlayerArchetype.AMSALJA]: {\n preferredActions: [AIActionType.TECHNIQUE, AIActionType.CIRCLE, AIActionType.COUNTER],\n prohibitedActions: [], // No prohibitions (pragmatic assassin)\n signatureMove: \"amsalja_silent_death\",\n signatureCondition: (context) => {\n // Execute Silent Death when opponent is VULNERABLE or worse\n return (\n context.opponentBalance === \"VULNERABLE\" ||\n context.opponentBalance === \"HELPLESS\"\n );\n },\n actionFrequencies: {\n [AIActionType.TECHNIQUE]: 0.60, // 60% techniques (vital point priority)\n [AIActionType.CIRCLE]: 0.20, // 20% circling (stealth positioning to flanks/rear)\n [AIActionType.ATTACK]: 0.15, // 15% basic attacks\n [AIActionType.RETREAT]: 0.05, // 5% tactical retreats (at 60% health threshold)\n [AIActionType.DEFEND]: 0.10, // 10% defense\n [AIActionType.APPROACH]: 0.10, // 10% approach\n [AIActionType.FEINT]: 0.10, // 10% feints\n [AIActionType.COUNTER]: 0.20, // 20% counters\n [AIActionType.STANCE_CHANGE]: 0.15, // 15% stance changes\n [AIActionType.WAIT]: 0.05, // 5% wait\n [AIActionType.COMBO]: 0.10, // 10% combos\n },\n },\n\n [PlayerArchetype.HACKER]: {\n preferredActions: [AIActionType.WAIT, AIActionType.TECHNIQUE, AIActionType.CIRCLE],\n prohibitedActions: [], // Analytical, uses all tactics\n signatureMove: \"hacker_system_crash\",\n signatureCondition: (context) => {\n // Execute System Crash after 20 seconds of observation (data collection phase)\n return context.timeInMatch > 20000;\n },\n actionFrequencies: {\n [AIActionType.WAIT]: 0.25, // 25% observation (data collection)\n [AIActionType.TECHNIQUE]: 0.40, // 40% tech-assisted strikes\n [AIActionType.CIRCLE]: 0.15, // 15% repositioning (maintain mid-range)\n [AIActionType.ATTACK]: 0.15, // 15% basic attacks\n [AIActionType.DEFEND]: 0.05, // 5% defense\n [AIActionType.APPROACH]: 0.05, // 5% approach (prefers mid-range)\n [AIActionType.RETREAT]: 0.10, // 10% retreat (maintain optimal distance)\n [AIActionType.FEINT]: 0.05, // 5% feints\n [AIActionType.COUNTER]: 0.15, // 15% counters\n [AIActionType.STANCE_CHANGE]: 0.10, // 10% stance changes\n [AIActionType.COMBO]: 0.05, // 5% combos\n },\n },\n\n [PlayerArchetype.JEONGBO_YOWON]: {\n preferredActions: [AIActionType.FEINT, AIActionType.COUNTER, AIActionType.TECHNIQUE],\n prohibitedActions: [], // Psychological warfare, uses all tactics\n signatureMove: \"jeongbo_precision_takedown\",\n signatureCondition: (context) => {\n // Execute Precision Takedown when opponent is HELPLESS (implemented in Issue #1186)\n return context.opponentBalance === \"HELPLESS\";\n },\n actionFrequencies: {\n [AIActionType.FEINT]: 0.30, // 30% psychological pressure (feints)\n [AIActionType.TECHNIQUE]: 0.35, // 35% precision strikes\n [AIActionType.COUNTER]: 0.20, // 20% counters\n [AIActionType.WAIT]: 0.10, // 10% observation (strategic timing)\n [AIActionType.ATTACK]: 0.05, // 5% basic attacks\n [AIActionType.DEFEND]: 0.10, // 10% defense\n [AIActionType.APPROACH]: 0.10, // 10% approach\n [AIActionType.RETREAT]: 0.05, // 5% tactical retreats\n [AIActionType.CIRCLE]: 0.15, // 15% circling (intimidation)\n [AIActionType.STANCE_CHANGE]: 0.10, // 10% stance changes\n [AIActionType.COMBO]: 0.05, // 5% combos\n },\n },\n\n [PlayerArchetype.JOJIK_POKRYEOKBAE]: {\n preferredActions: [AIActionType.ATTACK, AIActionType.TECHNIQUE, AIActionType.STANCE_CHANGE],\n prohibitedActions: [], // No rules, survival at any cost\n signatureMove: \"jojik_improvised_weapon\",\n signatureCondition: (context) => {\n /**\n * Jojik (조직폭력배) signature move: Improvised Weapon when desperate (<30% health)\n * \n * This condition uses playerHealth (the AI's health) to trigger desperate tactics.\n */\n const maxHealth = context.playerMaxHealth;\n if (maxHealth <= 0) {\n // Defensive guard: avoid divide-by-zero\n return false;\n }\n const playerHealthPercent = context.playerHealth / maxHealth;\n return playerHealthPercent < 0.30; // Desperate improvised weapon when health <30%\n },\n actionFrequencies: {\n /**\n * Jojik (조직폭력배) Action Frequencies - Chaotic Survivor\n * \n * These frequencies represent behavioral targets for Jojik's unpredictable fighting style.\n * \n * **Key Behavioral Characteristics**:\n * - STANCE_CHANGE: 80% (extremely high for unpredictability)\n * This means that in approximately 80% of decision cycles where a stance change\n * is a valid option, the AI will evaluate and strongly favor a stance change,\n * creating the erratic, chaotic movement pattern that defines Jojik's combat style.\n * \n * - RETREAT: 5% base, but survival instinct triggers tactical retreat at 70% health\n * (handled by evaluateSurvival logic with personality.tacticalRetreatThreshold)\n * \n * The high stance change frequency is the primary mechanism for Jojik's unpredictable behavior.\n */\n [AIActionType.ATTACK]: 0.50, // 50% chaotic attacks\n [AIActionType.TECHNIQUE]: 0.25, // 25% dirty techniques\n [AIActionType.STANCE_CHANGE]: 0.80, // 80% unpredictable stance changes (defines chaotic behavior)\n [AIActionType.RETREAT]: 0.05, // 5% pragmatic retreats (survival instinct at 70% health via threshold)\n [AIActionType.FEINT]: 0.05, // 5% deceptive moves\n [AIActionType.DEFEND]: 0.10, // 10% defense\n [AIActionType.APPROACH]: 0.15, // 15% approach\n [AIActionType.COUNTER]: 0.10, // 10% counters\n [AIActionType.CIRCLE]: 0.10, // 10% circling\n [AIActionType.WAIT]: 0.03, // 3% wait\n [AIActionType.COMBO]: 0.10, // 10% combos\n },\n },\n};\n\n/**\n * Enforce archetype-specific behavior on AI decision\n * \n * Applies philosophy-based restrictions and redirects to enforce distinct\n * combat patterns for each archetype. This ensures players can identify\n * which archetype they're fighting based on behavior alone.\n * \n * **Enforcement Logic**:\n * 1. Check for prohibited actions → Replace with preferred action\n * 2. Check for signature move condition → Override with signature move\n * 3. Return decision (possibly modified)\n * \n * @korean 원형 행동 강화\n * \n * @param decision - Original AI decision from DecisionTree\n * @param archetype - Player archetype to enforce\n * @param context - Current combat context\n * @returns Modified decision enforcing archetype philosophy\n */\nexport function enforceArchetypeBehavior(\n decision: AIDecision,\n archetype: PlayerArchetype,\n context: CombatContext\n): AIDecision {\n const rules = ARCHETYPE_ENFORCEMENT[archetype];\n\n // Check for prohibited actions\n if (rules.prohibitedActions.includes(decision.action)) {\n // Replace with preferred action (random selection from preferred set)\n // Defensive guard: Ensure preferredActions is not empty\n if (rules.preferredActions.length === 0) {\n console.warn(\n `[ArchetypeEnforcer] ${archetype} has empty preferredActions array. Using original decision with warning.`\n );\n return {\n ...decision,\n reason: `Archetype enforcement: ${archetype} prohibited action ${decision.action}, but no alternatives available`,\n };\n }\n\n const alternativeAction = rules.preferredActions[\n Math.floor(Math.random() * rules.preferredActions.length)\n ];\n\n return {\n ...decision,\n action: alternativeAction,\n reason: `Archetype enforcement: ${archetype} does not use ${decision.action} (원형 강화: ${decision.action} 사용 불가)`,\n };\n }\n\n // Check for signature move condition\n if (rules.signatureCondition(context)) {\n // Override with signature move\n return {\n action: AIActionType.TECHNIQUE,\n targetVitalPoint: undefined, // Let technique selection handle vital point targeting\n priority: 10, // Maximum priority\n reason: `Signature move: ${rules.signatureMove} for ${archetype} (서명 기술: ${rules.signatureMove})`,\n };\n }\n\n // No enforcement needed - return original decision\n return decision;\n}\n\n/**\n * Get signature move ID for archetype\n * \n * Returns the technique ID of this archetype's signature move.\n * Used for technique selection in useAICombat.ts.\n * \n * @korean 서명 기술 ID 가져오기\n * \n * @param archetype - Player archetype\n * @returns Signature technique ID\n */\nexport function getSignatureMove(archetype: PlayerArchetype): string {\n return ARCHETYPE_ENFORCEMENT[archetype].signatureMove;\n}\n\n/**\n * Check if signature move condition is met\n * \n * Evaluates whether the archetype should execute its signature move\n * based on current combat context.\n * \n * @korean 서명 기술 조건 확인\n * \n * @param archetype - Player archetype\n * @param context - Current combat context\n * @returns True if signature move should be executed\n */\nexport function shouldExecuteSignatureMove(\n archetype: PlayerArchetype,\n context: CombatContext\n): boolean {\n const rules = ARCHETYPE_ENFORCEMENT[archetype];\n return rules.signatureCondition(context);\n}\n\n/**\n * Get action frequency target for archetype\n * \n * Returns the target frequency (0.0-1.0) for a specific action type\n * based on archetype combat philosophy.\n * \n * @korean 행동 빈도 목표 가져오기\n * \n * @param archetype - Player archetype\n * @param actionType - AI action type\n * @returns Target frequency for this action (0.0-1.0)\n */\nexport function getActionFrequency(\n archetype: PlayerArchetype,\n actionType: AIActionType\n): number {\n return ARCHETYPE_ENFORCEMENT[archetype].actionFrequencies[actionType] ?? 0.0;\n}\n\n/**\n * Check if action is prohibited for archetype\n * \n * Returns true if the archetype's philosophy prohibits using this action.\n * \n * @korean 금지된 행동 확인\n * \n * @param archetype - Player archetype\n * @param actionType - AI action type to check\n * @returns True if action is prohibited\n */\nexport function isActionProhibited(\n archetype: PlayerArchetype,\n actionType: AIActionType\n): boolean {\n return ARCHETYPE_ENFORCEMENT[archetype].prohibitedActions.includes(actionType);\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAmEA,IAAa,wBAA4E;EACtF,gBAAgB,OAAO;EACtB,kBAAkB;GAAC,aAAa;GAAQ,aAAa;GAAU,aAAa;GAAU;EACtF,mBAAmB,CAAC,aAAa,MAAM;EACvC,eAAe;EACf,qBAAqB,YAAY;;;;;;;;GAQ/B,MAAM,YAAY,QAAQ,qBAAqB,QAAQ;GACvD,IAAI,aAAa,GAEf,OAAO;GAGT,OAD8B,QAAQ,iBAAiB,YACxB;;EAEjC,mBAAmB;;;;;;;;;;;;;;;IAehB,aAAa,SAAS;IACtB,aAAa,SAAS;IACtB,aAAa,UAAU;IACvB,aAAa,WAAW;IACxB,aAAa,QAAQ;IACrB,aAAa,YAAY;IACzB,aAAa,UAAU;IACvB,aAAa,gBAAgB;IAC7B,aAAa,SAAS;IACtB,aAAa,OAAO;IACpB,aAAa,QAAQ;GACvB;EACF;EAEA,gBAAgB,UAAU;EACzB,kBAAkB;GAAC,aAAa;GAAW,aAAa;GAAQ,aAAa;GAAQ;EACrF,mBAAmB,EAAE;EACrB,eAAe;EACf,qBAAqB,YAAY;GAE/B,OACE,QAAQ,oBAAoB,gBAC5B,QAAQ,oBAAoB;;EAGhC,mBAAmB;IAChB,aAAa,YAAY;IACzB,aAAa,SAAS;IACtB,aAAa,SAAS;IACtB,aAAa,UAAU;IACvB,aAAa,SAAS;IACtB,aAAa,WAAW;IACxB,aAAa,QAAQ;IACrB,aAAa,UAAU;IACvB,aAAa,gBAAgB;IAC7B,aAAa,OAAO;IACpB,aAAa,QAAQ;GACvB;EACF;EAEA,gBAAgB,SAAS;EACxB,kBAAkB;GAAC,aAAa;GAAM,aAAa;GAAW,aAAa;GAAO;EAClF,mBAAmB,EAAE;EACrB,eAAe;EACf,qBAAqB,YAAY;GAE/B,OAAO,QAAQ,cAAc;;EAE/B,mBAAmB;IAChB,aAAa,OAAO;IACpB,aAAa,YAAY;IACzB,aAAa,SAAS;IACtB,aAAa,SAAS;IACtB,aAAa,SAAS;IACtB,aAAa,WAAW;IACxB,aAAa,UAAU;IACvB,aAAa,QAAQ;IACrB,aAAa,UAAU;IACvB,aAAa,gBAAgB;IAC7B,aAAa,QAAQ;GACvB;EACF;EAEA,gBAAgB,gBAAgB;EAC/B,kBAAkB;GAAC,aAAa;GAAO,aAAa;GAAS,aAAa;GAAU;EACpF,mBAAmB,EAAE;EACrB,eAAe;EACf,qBAAqB,YAAY;GAE/B,OAAO,QAAQ,oBAAoB;;EAErC,mBAAmB;IAChB,aAAa,QAAQ;IACrB,aAAa,YAAY;IACzB,aAAa,UAAU;IACvB,aAAa,OAAO;IACpB,aAAa,SAAS;IACtB,aAAa,SAAS;IACtB,aAAa,WAAW;IACxB,aAAa,UAAU;IACvB,aAAa,SAAS;IACtB,aAAa,gBAAgB;IAC7B,aAAa,QAAQ;GACvB;EACF;EAEA,gBAAgB,oBAAoB;EACnC,kBAAkB;GAAC,aAAa;GAAQ,aAAa;GAAW,aAAa;GAAc;EAC3F,mBAAmB,EAAE;EACrB,eAAe;EACf,qBAAqB,YAAY;;;;;;GAM/B,MAAM,YAAY,QAAQ;GAC1B,IAAI,aAAa,GAEf,OAAO;GAGT,OAD4B,QAAQ,eAAe,YACtB;;EAE/B,mBAAmB;;;;;;;;;;;;;;;;;IAiBhB,aAAa,SAAS;IACtB,aAAa,YAAY;IACzB,aAAa,gBAAgB;IAC7B,aAAa,UAAU;IACvB,aAAa,QAAQ;IACrB,aAAa,SAAS;IACtB,aAAa,WAAW;IACxB,aAAa,UAAU;IACvB,aAAa,SAAS;IACtB,aAAa,OAAO;IACpB,aAAa,QAAQ;GACvB;EACF;CACF;;;;;;;;;;;;;;;;;;;;AAqBD,SAAgB,yBACd,UACA,WACA,SACY;CACZ,MAAM,QAAQ,sBAAsB;CAGpC,IAAI,MAAM,kBAAkB,SAAS,SAAS,OAAO,EAAE;EAGrD,IAAI,MAAM,iBAAiB,WAAW,GAAG;GACvC,QAAQ,KACN,uBAAuB,UAAU,0EAClC;GACD,OAAO;IACL,GAAG;IACH,QAAQ,0BAA0B,UAAU,qBAAqB,SAAS,OAAO;IAClF;;EAGH,MAAM,oBAAoB,MAAM,iBAC9B,KAAK,MAAM,KAAK,QAAQ,GAAG,MAAM,iBAAiB,OAAO;EAG3D,OAAO;GACL,GAAG;GACH,QAAQ;GACR,QAAQ,0BAA0B,UAAU,gBAAgB,SAAS,OAAO,WAAW,SAAS,OAAO;GACxG;;CAIH,IAAI,MAAM,mBAAmB,QAAQ,EAEnC,OAAO;EACL,QAAQ,aAAa;EACrB,kBAAkB,KAAA;EAClB,UAAU;EACV,QAAQ,mBAAmB,MAAM,cAAc,OAAO,UAAU,WAAW,MAAM,cAAc;EAChG;CAIH,OAAO;;;;;;;;;;;;;AAcT,SAAgB,iBAAiB,WAAoC;CACnE,OAAO,sBAAsB,WAAW;;;;;;;;;;;;;;AAe1C,SAAgB,2BACd,WACA,SACS;CAET,OADc,sBAAsB,WACvB,mBAAmB,QAAQ;;;;;;;;;;;;;;AAe1C,SAAgB,mBACd,WACA,YACQ;CACR,OAAO,sBAAsB,WAAW,kBAAkB,eAAe;;;;;;;;;;;;;AAc3E,SAAgB,mBACd,WACA,YACS;CACT,OAAO,sBAAsB,WAAW,kBAAkB,SAAS,WAAW"}
|
|
1
|
+
{"version":3,"file":"ArchetypeEnforcer.js","names":[],"sources":["../../../src/systems/ai/ArchetypeEnforcer.ts"],"sourcesContent":["/**\n * Archetype Behavior Enforcement System\n * \n * Centralizes archetype-specific combat philosophy enforcement to ensure\n * each of the 5 player archetypes has immediately recognizable and distinct\n * AI behavior patterns.\n * \n * **Korean Philosophy Integration (한국 무술 철학)**:\n * - 무사 (Musa): 명예 (Honor) - Direct, honorable combat\n * - 암살자 (Amsalja): 은밀 (Stealth) - Silent, precise takedowns\n * - 해커 (Hacker): 분석 (Analysis) - Tech-enhanced tactical combat\n * - 정보요원 (Jeongbo): 심리전 (Psychological Warfare) - Strategic manipulation\n * - 조직폭력배 (Jojik): 생존 (Survival) - Ruthless, unpredictable tactics\n * \n * @module systems/ai/ArchetypeEnforcer\n * @category AI Combat\n * @korean 원형 행동 강화 시스템\n */\n\nimport { PlayerArchetype } from \"@/types\";\nimport { AIDecision, AIActionType, CombatContext } from \"./types\";\n\n/**\n * Archetype enforcement rules defining distinct combat behaviors\n * \n * Each archetype has:\n * - Preferred actions (used most frequently)\n * - Prohibited actions (never used due to philosophy)\n * - Signature move (iconic technique with specific trigger condition)\n * - Action frequencies (target distribution percentages)\n * \n * **Note on Action Frequencies**:\n * These values represent **target frequencies/proportions** for each action type\n * rather than a single normalized probability distribution. Multiple actions can have\n * high target frequencies because they can occur in overlapping situations:\n * - ATTACK (70%) and TECHNIQUE (70%) both represent aggressive behavior\n * - STANCE_CHANGE (80%) occurs frequently but doesn't prevent other actions\n * - The frequencies serve as **behavioral targets** to guide AI personality,\n * and are not required to sum to 100% across all actions\n * \n * @korean 원형 강화 규칙\n */\nexport interface ArchetypeEnforcementRules {\n /** Actions this archetype uses most frequently */\n readonly preferredActions: readonly AIActionType[];\n \n /** Actions this archetype never uses (philosophy conflicts) */\n readonly prohibitedActions: readonly AIActionType[];\n \n /** Signature technique ID for this archetype */\n readonly signatureMove: string;\n \n /** Condition function to trigger signature move */\n readonly signatureCondition: (context: CombatContext) => boolean;\n \n /** Target frequency distribution for each action type (0.0-1.0) */\n readonly actionFrequencies: Readonly<Record<AIActionType, number>>;\n}\n\n/**\n * Archetype enforcement rules for all 5 player archetypes\n * \n * Defines distinct combat philosophies with measurable behavior patterns\n * that make each archetype immediately identifiable through AI actions.\n * \n * @korean 5대 원형 강화 규칙\n */\nexport const ARCHETYPE_ENFORCEMENT: Record<PlayerArchetype, ArchetypeEnforcementRules> = {\n [PlayerArchetype.MUSA]: {\n preferredActions: [AIActionType.ATTACK, AIActionType.APPROACH, AIActionType.TECHNIQUE],\n prohibitedActions: [AIActionType.FEINT], // Honor code: No deception\n signatureMove: \"musa_mountain_breaker\",\n signatureCondition: (context) => {\n /**\n * Musa (무사) signature move condition: Mountain Breaker when opponent <40% health\n * \n * NOTE: Uses opponentMaxHealth if available, otherwise assumes symmetric max-health\n * pools (context.playerMaxHealth === opponentMaxHealth). If asymmetric max health\n * exists, CombatContext provides opponentMaxHealth for accurate calculation.\n */\n const maxHealth = context.opponentMaxHealth ?? context.playerMaxHealth;\n if (maxHealth <= 0) {\n // Defensive guard: avoid divide-by-zero / invalid percentages\n return false;\n }\n const opponentHealthPercent = context.opponentHealth / maxHealth;\n return opponentHealthPercent < 0.40; // Finishing move when opponent <40% health\n },\n actionFrequencies: {\n /**\n * Musa (무사) Action Frequencies - Honor Code Warrior\n * \n * These frequencies represent behavioral targets (not strict probability distributions).\n * Multiple actions can have high frequencies as they represent overlapping aggressive behavior.\n * \n * **Honor Code Rules**:\n * - FEINT: 0% (prohibited - no deception)\n * - RETREAT: 2% (minimal - only at critical health <10%, handled by evaluateSurvival)\n * - DEFEND: 10% (minimal - offense-focused philosophy)\n * \n * The 2% retreat frequency applies only when health drops below the honor threshold (~5-10%),\n * enforced by evaluateSurvival logic. Above this threshold, Musa never retreats.\n */\n [AIActionType.ATTACK]: 0.70, // 70% aggressive attacks\n [AIActionType.DEFEND]: 0.10, // 10% defense (honor code: minimal defense)\n [AIActionType.RETREAT]: 0.02, // 2% retreat (only at critical health <10%)\n [AIActionType.APPROACH]: 0.15, // 15% approach\n [AIActionType.FEINT]: 0.00, // 0% feints (prohibited by honor code)\n [AIActionType.TECHNIQUE]: 0.70, // 70% techniques (overlaps with attack)\n [AIActionType.COUNTER]: 0.10, // 10% counters\n [AIActionType.STANCE_CHANGE]: 0.10, // 10% stance changes\n [AIActionType.CIRCLE]: 0.05, // 5% circling\n [AIActionType.WAIT]: 0.03, // 3% wait\n [AIActionType.COMBO]: 0.05, // 5% combos\n },\n },\n\n [PlayerArchetype.AMSALJA]: {\n preferredActions: [AIActionType.TECHNIQUE, AIActionType.CIRCLE, AIActionType.COUNTER],\n prohibitedActions: [], // No prohibitions (pragmatic assassin)\n signatureMove: \"amsalja_silent_death\",\n signatureCondition: (context) => {\n // Execute Silent Death when opponent is VULNERABLE or worse\n return (\n context.opponentBalance === \"VULNERABLE\" ||\n context.opponentBalance === \"HELPLESS\"\n );\n },\n actionFrequencies: {\n [AIActionType.TECHNIQUE]: 0.60, // 60% techniques (vital point priority)\n [AIActionType.CIRCLE]: 0.20, // 20% circling (stealth positioning to flanks/rear)\n [AIActionType.ATTACK]: 0.15, // 15% basic attacks\n [AIActionType.RETREAT]: 0.05, // 5% tactical retreats (at 60% health threshold)\n [AIActionType.DEFEND]: 0.10, // 10% defense\n [AIActionType.APPROACH]: 0.10, // 10% approach\n [AIActionType.FEINT]: 0.10, // 10% feints\n [AIActionType.COUNTER]: 0.20, // 20% counters\n [AIActionType.STANCE_CHANGE]: 0.15, // 15% stance changes\n [AIActionType.WAIT]: 0.05, // 5% wait\n [AIActionType.COMBO]: 0.10, // 10% combos\n },\n },\n\n [PlayerArchetype.HACKER]: {\n preferredActions: [AIActionType.WAIT, AIActionType.TECHNIQUE, AIActionType.CIRCLE],\n prohibitedActions: [], // Analytical, uses all tactics\n signatureMove: \"hacker_system_crash\",\n signatureCondition: (context) => {\n // Execute System Crash after 20 seconds of observation (data collection phase)\n return context.timeInMatch > 20000;\n },\n actionFrequencies: {\n [AIActionType.WAIT]: 0.25, // 25% observation (data collection)\n [AIActionType.TECHNIQUE]: 0.40, // 40% tech-assisted strikes\n [AIActionType.CIRCLE]: 0.15, // 15% repositioning (maintain mid-range)\n [AIActionType.ATTACK]: 0.15, // 15% basic attacks\n [AIActionType.DEFEND]: 0.05, // 5% defense\n [AIActionType.APPROACH]: 0.05, // 5% approach (prefers mid-range)\n [AIActionType.RETREAT]: 0.10, // 10% retreat (maintain optimal distance)\n [AIActionType.FEINT]: 0.05, // 5% feints\n [AIActionType.COUNTER]: 0.15, // 15% counters\n [AIActionType.STANCE_CHANGE]: 0.10, // 10% stance changes\n [AIActionType.COMBO]: 0.05, // 5% combos\n },\n },\n\n [PlayerArchetype.JEONGBO_YOWON]: {\n preferredActions: [AIActionType.FEINT, AIActionType.COUNTER, AIActionType.TECHNIQUE],\n prohibitedActions: [], // Psychological warfare, uses all tactics\n signatureMove: \"jeongbo_precision_takedown\",\n signatureCondition: (context) => {\n // Execute Precision Takedown when opponent is HELPLESS (implemented in Issue #1186)\n return context.opponentBalance === \"HELPLESS\";\n },\n actionFrequencies: {\n [AIActionType.FEINT]: 0.30, // 30% psychological pressure (feints)\n [AIActionType.TECHNIQUE]: 0.35, // 35% precision strikes\n [AIActionType.COUNTER]: 0.20, // 20% counters\n [AIActionType.WAIT]: 0.10, // 10% observation (strategic timing)\n [AIActionType.ATTACK]: 0.05, // 5% basic attacks\n [AIActionType.DEFEND]: 0.10, // 10% defense\n [AIActionType.APPROACH]: 0.10, // 10% approach\n [AIActionType.RETREAT]: 0.05, // 5% tactical retreats\n [AIActionType.CIRCLE]: 0.15, // 15% circling (intimidation)\n [AIActionType.STANCE_CHANGE]: 0.10, // 10% stance changes\n [AIActionType.COMBO]: 0.05, // 5% combos\n },\n },\n\n [PlayerArchetype.JOJIK_POKRYEOKBAE]: {\n preferredActions: [AIActionType.ATTACK, AIActionType.TECHNIQUE, AIActionType.STANCE_CHANGE],\n prohibitedActions: [], // No rules, survival at any cost\n signatureMove: \"jojik_improvised_weapon\",\n signatureCondition: (context) => {\n /**\n * Jojik (조직폭력배) signature move: Improvised Weapon when desperate (<30% health)\n * \n * This condition uses playerHealth (the AI's health) to trigger desperate tactics.\n */\n const maxHealth = context.playerMaxHealth;\n if (maxHealth <= 0) {\n // Defensive guard: avoid divide-by-zero\n return false;\n }\n const playerHealthPercent = context.playerHealth / maxHealth;\n return playerHealthPercent < 0.30; // Desperate improvised weapon when health <30%\n },\n actionFrequencies: {\n /**\n * Jojik (조직폭력배) Action Frequencies - Chaotic Survivor\n * \n * These frequencies represent behavioral targets for Jojik's unpredictable fighting style.\n * \n * **Key Behavioral Characteristics**:\n * - STANCE_CHANGE: 80% (extremely high for unpredictability)\n * This means that in approximately 80% of decision cycles where a stance change\n * is a valid option, the AI will evaluate and strongly favor a stance change,\n * creating the erratic, chaotic movement pattern that defines Jojik's combat style.\n * \n * - RETREAT: 5% base, but survival instinct triggers tactical retreat at 70% health\n * (handled by evaluateSurvival logic with personality.tacticalRetreatThreshold)\n * \n * The high stance change frequency is the primary mechanism for Jojik's unpredictable behavior.\n */\n [AIActionType.ATTACK]: 0.50, // 50% chaotic attacks\n [AIActionType.TECHNIQUE]: 0.25, // 25% dirty techniques\n [AIActionType.STANCE_CHANGE]: 0.80, // 80% unpredictable stance changes (defines chaotic behavior)\n [AIActionType.RETREAT]: 0.05, // 5% pragmatic retreats (survival instinct at 70% health via threshold)\n [AIActionType.FEINT]: 0.05, // 5% deceptive moves\n [AIActionType.DEFEND]: 0.10, // 10% defense\n [AIActionType.APPROACH]: 0.15, // 15% approach\n [AIActionType.COUNTER]: 0.10, // 10% counters\n [AIActionType.CIRCLE]: 0.10, // 10% circling\n [AIActionType.WAIT]: 0.03, // 3% wait\n [AIActionType.COMBO]: 0.10, // 10% combos\n },\n },\n};\n\n/**\n * Enforce archetype-specific behavior on AI decision\n * \n * Applies philosophy-based restrictions and redirects to enforce distinct\n * combat patterns for each archetype. This ensures players can identify\n * which archetype they're fighting based on behavior alone.\n * \n * **Enforcement Logic**:\n * 1. Check for prohibited actions → Replace with preferred action\n * 2. Check for signature move condition → Override with signature move\n * 3. Return decision (possibly modified)\n * \n * @korean 원형 행동 강화\n * \n * @param decision - Original AI decision from DecisionTree\n * @param archetype - Player archetype to enforce\n * @param context - Current combat context\n * @returns Modified decision enforcing archetype philosophy\n */\nexport function enforceArchetypeBehavior(\n decision: AIDecision,\n archetype: PlayerArchetype,\n context: CombatContext\n): AIDecision {\n const rules = ARCHETYPE_ENFORCEMENT[archetype];\n\n // Check for prohibited actions\n if (rules.prohibitedActions.includes(decision.action)) {\n // Replace with preferred action (random selection from preferred set)\n // Defensive guard: Ensure preferredActions is not empty\n if (rules.preferredActions.length === 0) {\n console.warn(\n `[ArchetypeEnforcer] ${archetype} has empty preferredActions array. Using original decision with warning.`\n );\n return {\n ...decision,\n reason: `Archetype enforcement: ${archetype} prohibited action ${decision.action}, but no alternatives available`,\n };\n }\n\n const alternativeAction = rules.preferredActions[\n Math.floor(Math.random() * rules.preferredActions.length)\n ];\n\n return {\n ...decision,\n action: alternativeAction,\n reason: `Archetype enforcement: ${archetype} does not use ${decision.action} (원형 강화: ${decision.action} 사용 불가)`,\n };\n }\n\n // Check for signature move condition\n if (rules.signatureCondition(context)) {\n // Override with signature move\n return {\n action: AIActionType.TECHNIQUE,\n targetVitalPoint: undefined, // Let technique selection handle vital point targeting\n priority: 10, // Maximum priority\n reason: `Signature move: ${rules.signatureMove} for ${archetype} (서명 기술: ${rules.signatureMove})`,\n };\n }\n\n // No enforcement needed - return original decision\n return decision;\n}\n\n/**\n * Get signature move ID for archetype\n * \n * Returns the technique ID of this archetype's signature move.\n * Used for technique selection in useAICombat.ts.\n * \n * @korean 서명 기술 ID 가져오기\n * \n * @param archetype - Player archetype\n * @returns Signature technique ID\n */\nexport function getSignatureMove(archetype: PlayerArchetype): string {\n return ARCHETYPE_ENFORCEMENT[archetype].signatureMove;\n}\n\n/**\n * Check if signature move condition is met\n * \n * Evaluates whether the archetype should execute its signature move\n * based on current combat context.\n * \n * @korean 서명 기술 조건 확인\n * \n * @param archetype - Player archetype\n * @param context - Current combat context\n * @returns True if signature move should be executed\n */\nexport function shouldExecuteSignatureMove(\n archetype: PlayerArchetype,\n context: CombatContext\n): boolean {\n const rules = ARCHETYPE_ENFORCEMENT[archetype];\n return rules.signatureCondition(context);\n}\n\n/**\n * Get action frequency target for archetype\n * \n * Returns the target frequency (0.0-1.0) for a specific action type\n * based on archetype combat philosophy.\n * \n * @korean 행동 빈도 목표 가져오기\n * \n * @param archetype - Player archetype\n * @param actionType - AI action type\n * @returns Target frequency for this action (0.0-1.0)\n */\nexport function getActionFrequency(\n archetype: PlayerArchetype,\n actionType: AIActionType\n): number {\n return ARCHETYPE_ENFORCEMENT[archetype].actionFrequencies[actionType] ?? 0.0;\n}\n\n/**\n * Check if action is prohibited for archetype\n * \n * Returns true if the archetype's philosophy prohibits using this action.\n * \n * @korean 금지된 행동 확인\n * \n * @param archetype - Player archetype\n * @param actionType - AI action type to check\n * @returns True if action is prohibited\n */\nexport function isActionProhibited(\n archetype: PlayerArchetype,\n actionType: AIActionType\n): boolean {\n return ARCHETYPE_ENFORCEMENT[archetype].prohibitedActions.includes(actionType);\n}\n"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ComboSystem.js","names":[],"sources":["../../../src/systems/ai/ComboSystem.ts"],"sourcesContent":["/**\n * AI Combo System for Korean Martial Arts\n * Manages multi-hit combo sequences based on trigram stances and archetype signature combos\n */\n\nimport { PlayerState } from \"@/systems/player\";\nimport { TRIGRAM_TECHNIQUES } from \"@/systems/trigram\";\nimport { KoreanTechnique } from \"@/systems/vitalpoint\";\nimport { PlayerArchetype, TrigramStance } from \"@/types\";\nimport { AIPersonality } from \"./AIPersonality\";\n\n/**\n * Combo sequence for a specific trigram stance\n */\nexport interface ComboSequence {\n readonly stanceId: TrigramStance;\n readonly techniques: readonly KoreanTechnique[];\n readonly minDistance: number;\n readonly maxDistance: number;\n readonly requiredKi: number;\n readonly requiredStamina: number;\n readonly name: {\n readonly korean: string;\n readonly english: string;\n };\n}\n\n/**\n * Archetype signature combo sequence (2-3 technique chains)\n *\n * These are special technique sequences that represent each archetype's\n * fighting philosophy and preferred combat patterns.\n *\n * @korean 원형 대표 연계 기술\n */\nexport interface ArchetypeComboSequence {\n readonly archetype: PlayerArchetype;\n readonly techniqueIds: readonly string[]; // 2-3 technique IDs in sequence\n readonly name: {\n readonly korean: string;\n readonly english: string;\n };\n readonly description: {\n readonly korean: string;\n readonly english: string;\n };\n}\n\n/**\n * Archetype Signature Combos\n *\n * Each archetype has 2 signature combo sequences that reflect their combat philosophy.\n * These combos are prioritized by AI for authentic fighting style representation.\n *\n * Combo patterns:\n * - Musa: Power amplification and defense-to-offense transitions\n * - Amsalja: Setup-to-execution nerve strike sequences\n * - Hacker: Analysis-to-burst and stun-to-shutdown patterns\n * - Jeongbo: Weaken-to-exploit and counter-to-finish sequences\n * - Jojik: Chaos-to-brutality and aggression-to-desperation patterns\n *\n * @korean 원형 대표 연계 기술 정의\n */\nexport const ARCHETYPE_SIGNATURE_COMBOS: Record<\n PlayerArchetype,\n readonly ArchetypeComboSequence[]\n> = {\n [PlayerArchetype.MUSA]: [\n {\n archetype: PlayerArchetype.MUSA,\n techniqueIds: [\"musa_thunder_strike\", \"musa_dragon_fist\"],\n name: {\n korean: \"천둥용권\",\n english: \"Thunder Dragon Combo\",\n },\n description: {\n korean: \"천둥벽력으로 적을 흔들고 용권으로 관통합니다\",\n english:\n \"Shake enemy with Thunder Strike, pierce through with Dragon Fist\",\n },\n },\n {\n archetype: PlayerArchetype.MUSA,\n techniqueIds: [\"musa_iron_defense\", \"musa_mountain_breaker\"],\n name: {\n korean: \"철벽파산\",\n english: \"Iron Mountain Break\",\n },\n description: {\n korean: \"철벽방어로 적의 공격을 막고 파산격으로 반격합니다\",\n english: \"Block with Iron Defense, counter with Mountain Breaker\",\n },\n },\n ],\n\n [PlayerArchetype.AMSALJA]: [\n {\n archetype: PlayerArchetype.AMSALJA,\n techniqueIds: [\"amsalja_shadow_strike\", \"amsalja_silent_death\"],\n name: {\n korean: \"암영무음\",\n english: \"Shadow Silent Death\",\n },\n description: {\n korean: \"암영격으로 신경을 교란하고 무음살로 마무리합니다\",\n english: \"Disrupt nerves with Shadow Strike, finish with Silent Death\",\n },\n },\n {\n archetype: PlayerArchetype.AMSALJA,\n techniqueIds: [\"amsalja_nerve_strike\", \"amsalja_deadly_precision\"],\n name: {\n korean: \"신경정밀\",\n english: \"Nerve Precision Combo\",\n },\n description: {\n korean: \"신경타로 마비시키고 치명정밀로 급소를 공격합니다\",\n english:\n \"Paralyze with Nerve Strike, target vitals with Deadly Precision\",\n },\n },\n ],\n\n [PlayerArchetype.HACKER]: [\n {\n archetype: PlayerArchetype.HACKER,\n techniqueIds: [\"hacker_data_strike\", \"hacker_cyber_overdrive\"],\n name: {\n korean: \"데이터가속\",\n english: \"Data Overdrive Burst\",\n },\n description: {\n korean:\n \"데이터 타격으로 분석하고 사이버 가속으로 폭발적 공격을 수행합니다\",\n english: \"Analyze with Data Strike, burst with Cyber Overdrive\",\n },\n },\n {\n archetype: PlayerArchetype.HACKER,\n techniqueIds: [\"hacker_electric_shock\", \"hacker_system_crash\"],\n name: {\n korean: \"전격크래시\",\n english: \"Electric System Crash\",\n },\n description: {\n korean: \"전격으로 기절시키고 시스템 크래시로 무력화합니다\",\n english: \"Stun with Electric Shock, disable with System Crash\",\n },\n },\n ],\n\n [PlayerArchetype.JEONGBO_YOWON]: [\n {\n archetype: PlayerArchetype.JEONGBO_YOWON,\n techniqueIds: [\n \"jeongbo_tactical_strike\",\n \"jeongbo_psychological_warfare\",\n ],\n name: {\n korean: \"전술심리\",\n english: \"Tactical Psychology Combo\",\n },\n description: {\n korean: \"전술타격으로 약점을 노출시키고 심리전으로 정신을 교란합니다\",\n english:\n \"Expose weakness with Tactical Strike, disrupt with Psychological Warfare\",\n },\n },\n {\n archetype: PlayerArchetype.JEONGBO_YOWON,\n techniqueIds: [\n \"jeongbo_counter_intelligence\",\n \"jeongbo_intelligence_strike\",\n ],\n name: {\n korean: \"역정보타격\",\n english: \"Counter Intelligence Strike\",\n },\n description: {\n korean: \"역정보공작으로 반격하고 정보타격으로 완벽하게 마무리합니다\",\n english:\n \"Counter with Counter Intelligence, finish with Intelligence Strike\",\n },\n },\n ],\n\n [PlayerArchetype.JOJIK_POKRYEOKBAE]: [\n {\n archetype: PlayerArchetype.JOJIK_POKRYEOKBAE,\n techniqueIds: [\"jojik_street_brawl\", \"jojik_brutal_takedown\"],\n name: {\n korean: \"거리잔혹\",\n english: \"Street Brutality Combo\",\n },\n description: {\n korean: \"거리싸움으로 혼란을 주고 잔혹제압으로 무자비하게 쓰러뜨립니다\",\n english:\n \"Create chaos with Street Brawl, brutalize with Brutal Takedown\",\n },\n },\n {\n archetype: PlayerArchetype.JOJIK_POKRYEOKBAE,\n techniqueIds: [\"jojik_improvised_weapon\", \"jojik_ruthless_assault\"],\n name: {\n korean: \"즉석무자비\",\n english: \"Improvised Ruthless Assault\",\n },\n description: {\n korean: \"즉석무기로 공격하고 무자비공격으로 자비 없이 마무리합니다\",\n english:\n \"Attack with Improvised Weapon, finish ruthlessly with Ruthless Assault\",\n },\n },\n ],\n};\n\n/**\n * Get next technique in archetype signature combo\n *\n * Checks if current technique is part of a signature combo sequence,\n * and returns the next technique in that sequence if found.\n *\n * @korean 원형 대표 연계 기술의 다음 기술 가져오기\n *\n * @param currentTechniqueId - Current technique ID just executed\n * @param archetype - Player archetype\n * @returns Next technique ID in combo sequence, or undefined if no combo\n */\nexport function getNextComboTechnique(\n currentTechniqueId: string,\n archetype: PlayerArchetype,\n): string | undefined {\n const combos = ARCHETYPE_SIGNATURE_COMBOS[archetype];\n\n if (!combos) {\n return undefined;\n }\n\n for (const combo of combos) {\n const currentIndex = combo.techniqueIds.indexOf(currentTechniqueId);\n\n // Found current technique in this combo\n if (currentIndex !== -1 && currentIndex < combo.techniqueIds.length - 1) {\n // Return next technique in sequence\n return combo.techniqueIds[currentIndex + 1];\n }\n }\n\n return undefined; // No combo continuation found\n}\n\n/**\n * AI Combo System manages combo execution and decision-making\n */\nexport class AIComboSystem {\n private comboSequences: Map<TrigramStance, ComboSequence[]>;\n private currentCombo: KoreanTechnique[] = [];\n private comboProgress = 0;\n private lastComboTime = 0;\n private readonly comboTimeout = 2000; // 2 seconds to continue combo\n\n constructor() {\n this.comboSequences = this.initializeComboSequences();\n }\n\n /**\n * Initialize combo sequences for each trigram stance\n */\n private initializeComboSequences(): Map<TrigramStance, ComboSequence[]> {\n const sequences = new Map<TrigramStance, ComboSequence[]>();\n\n // Geon (Heaven) - Direct force combos\n sequences.set(TrigramStance.GEON, [\n {\n stanceId: TrigramStance.GEON,\n techniques: this.getTechniqueSequence(TrigramStance.GEON, 3),\n minDistance: 80,\n maxDistance: 140,\n requiredKi: 30,\n requiredStamina: 40,\n name: {\n korean: \"천둥벽력 연타\",\n english: \"Thunder Strike Combo\",\n },\n },\n ]);\n\n // Tae (Lake) - Fluid manipulation combos\n sequences.set(TrigramStance.TAE, [\n {\n stanceId: TrigramStance.TAE,\n techniques: this.getTechniqueSequence(TrigramStance.TAE, 3),\n minDistance: 70,\n maxDistance: 130,\n requiredKi: 25,\n requiredStamina: 35,\n name: {\n korean: \"유수연타\",\n english: \"Flowing Water Combo\",\n },\n },\n ]);\n\n // Li (Fire) - Precision strike combos\n sequences.set(TrigramStance.LI, [\n {\n stanceId: TrigramStance.LI,\n techniques: this.getTechniqueSequence(TrigramStance.LI, 3),\n minDistance: 90,\n maxDistance: 150,\n requiredKi: 35,\n requiredStamina: 30,\n name: {\n korean: \"화염연격\",\n english: \"Flame Strike Series\",\n },\n },\n ]);\n\n // Jin (Thunder) - Explosive combos\n sequences.set(TrigramStance.JIN, [\n {\n stanceId: TrigramStance.JIN,\n techniques: this.getTechniqueSequence(TrigramStance.JIN, 3),\n minDistance: 80,\n maxDistance: 140,\n requiredKi: 40,\n requiredStamina: 45,\n name: {\n korean: \"벽력난타\",\n english: \"Lightning Barrage\",\n },\n },\n ]);\n\n // Son (Wind) - Continuous pressure combos\n sequences.set(TrigramStance.SON, [\n {\n stanceId: TrigramStance.SON,\n techniques: this.getTechniqueSequence(TrigramStance.SON, 3),\n minDistance: 70,\n maxDistance: 130,\n requiredKi: 28,\n requiredStamina: 38,\n name: {\n korean: \"선풍연쇄\",\n english: \"Whirlwind Chain\",\n },\n },\n ]);\n\n // Gam (Water) - Adaptive flow combos\n sequences.set(TrigramStance.GAM, [\n {\n stanceId: TrigramStance.GAM,\n techniques: this.getTechniqueSequence(TrigramStance.GAM, 3),\n minDistance: 75,\n maxDistance: 135,\n requiredKi: 30,\n requiredStamina: 35,\n name: {\n korean: \"수류연환\",\n english: \"Water Flow Sequence\",\n },\n },\n ]);\n\n // Gan (Mountain) - Defensive counter combos\n sequences.set(TrigramStance.GAN, [\n {\n stanceId: TrigramStance.GAN,\n techniques: this.getTechniqueSequence(TrigramStance.GAN, 3),\n minDistance: 70,\n maxDistance: 130,\n requiredKi: 25,\n requiredStamina: 40,\n name: {\n korean: \"반석반격\",\n english: \"Mountain Counter\",\n },\n },\n ]);\n\n // Gon (Earth) - Grounding combos\n sequences.set(TrigramStance.GON, [\n {\n stanceId: TrigramStance.GON,\n techniques: this.getTechniqueSequence(TrigramStance.GON, 3),\n minDistance: 60,\n maxDistance: 120,\n requiredKi: 30,\n requiredStamina: 50,\n name: {\n korean: \"대지낙타\",\n english: \"Earth Slam Combo\",\n },\n },\n ]);\n\n return sequences;\n }\n\n /**\n * Get technique sequence for a stance\n */\n private getTechniqueSequence(\n stance: TrigramStance,\n count: number,\n ): readonly KoreanTechnique[] {\n const techniques = TRIGRAM_TECHNIQUES[stance] ?? [];\n\n // Warn if not enough techniques for full combo (issue #2529727989)\n if (techniques.length < count) {\n console.warn(\n `[AIComboSystem] Not enough techniques defined for stance '${stance}'. Requested ${count}, but only ${techniques.length} available. Returning partial combo.`,\n );\n }\n\n // Return only available techniques\n return techniques.slice(0, count);\n }\n\n /**\n * Calculate distance between two players\n */\n private getDistance(player: PlayerState, opponent: PlayerState): number {\n const dx = player.position.x - opponent.position.x;\n const dy = player.position.y - opponent.position.y;\n return Math.sqrt(dx * dx + dy * dy);\n }\n\n /**\n * Check if AI should continue current combo\n *\n * Enhanced with:\n * - Opponent balance state checking (SHAKEN/VULNERABLE)\n * - Combo length limit (max 3 techniques)\n * - Stamina threshold (>20 for combo continuation)\n *\n * @korean 연계 기술 계속 여부 판단\n */\n shouldContinueCombo(\n player: PlayerState,\n opponent: PlayerState,\n personality: AIPersonality,\n ): boolean {\n const now = Date.now();\n\n // No active combo - check if combo exists\n if (this.currentCombo.length === 0) {\n return false;\n }\n\n // Combo timed out\n if (now - this.lastComboTime > this.comboTimeout) {\n this.resetCombo();\n return false;\n }\n\n // Combo completed\n if (this.comboProgress >= this.currentCombo.length) {\n this.resetCombo();\n return false;\n }\n\n // Combo length limit: max 3 techniques\n if (this.comboProgress >= 3) {\n this.resetCombo();\n return false;\n }\n\n // Korean martial arts philosophy: flow like water\n const distance = this.getDistance(player, opponent);\n const distanceOk = distance < 120;\n\n // Stamina threshold for combo continuation (>20)\n const hasResources = player.ki >= 10 && player.stamina > 20;\n\n // Balance thresholds for vulnerability detection\n // Balance is a numeric value (0-100) representing stability\n // Values below 30 indicate SHAKEN/VULNERABLE states in combat\n const VULNERABLE_BALANCE_THRESHOLD = 30;\n\n // Check opponent balance state - continue combo if opponent is vulnerable\n // Using boolean OR (||) intentionally here - these are boolean conditions, not nullish coalescing\n const isStunned = opponent.isStunned ?? false;\n const isLowHealth =\n (opponent.health ?? 0) < (opponent.maxHealth ?? 100) * 0.3;\n const isLowBalance =\n (opponent.balance ?? 100) < VULNERABLE_BALANCE_THRESHOLD;\n // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing -- intentional boolean OR for vulnerability check\n const opponentVulnerable = isLowBalance || isStunned || isLowHealth;\n\n // Higher chance to continue if opponent is vulnerable\n const baseChance = personality.comboTendency;\n const vulnerabilityBonus = opponentVulnerable ? 0.3 : 0;\n const finalContinueChance = Math.min(0.95, baseChance + vulnerabilityBonus);\n const randomChance = Math.random() < finalContinueChance;\n\n return distanceOk && hasResources && randomChance;\n }\n\n /**\n * Start a new combo sequence\n */\n startCombo(\n player: PlayerState,\n opponent: PlayerState,\n personality: AIPersonality,\n ): boolean {\n // Check if suitable for combo\n const distance = this.getDistance(player, opponent);\n const sequences = this.comboSequences.get(player.currentStance);\n\n if (!sequences || sequences.length === 0) {\n return false;\n }\n\n // Find suitable combo for current situation\n const suitableCombo = sequences.find(\n (seq) =>\n distance >= seq.minDistance &&\n distance <= seq.maxDistance &&\n player.ki >= seq.requiredKi &&\n player.stamina >= seq.requiredStamina,\n );\n\n if (!suitableCombo) {\n return false;\n }\n\n // Random chance based on personality\n if (Math.random() > personality.comboTendency) {\n return false;\n }\n\n // Start combo\n this.currentCombo = [...suitableCombo.techniques];\n this.comboProgress = 0;\n this.lastComboTime = Date.now();\n return true;\n }\n\n /**\n * Get next technique in combo\n */\n getNextComboTechnique(): KoreanTechnique | null {\n if (\n this.comboProgress >= this.currentCombo.length ||\n this.currentCombo.length === 0\n ) {\n return null;\n }\n\n const technique = this.currentCombo[this.comboProgress];\n this.comboProgress++;\n this.lastComboTime = Date.now();\n return technique;\n }\n\n /**\n * Reset current combo\n */\n resetCombo(): void {\n this.currentCombo = [];\n this.comboProgress = 0;\n this.lastComboTime = 0;\n }\n\n /**\n * Check if combo is active (fix for issue #2529727999)\n */\n isComboActive(): boolean {\n return (\n this.currentCombo.length > 0 &&\n this.comboProgress >= 0 &&\n this.comboProgress < this.currentCombo.length &&\n Date.now() - this.lastComboTime < this.comboTimeout\n );\n }\n\n /**\n * Get combo progress information\n */\n getComboInfo(): {\n active: boolean;\n progress: number;\n total: number;\n percentage: number;\n } {\n return {\n active: this.isComboActive(),\n progress: this.comboProgress,\n total: this.currentCombo.length,\n percentage:\n this.currentCombo.length > 0\n ? (this.comboProgress / this.currentCombo.length) * 100\n : 0,\n };\n }\n\n /**\n * Get available combos for stance\n */\n getAvailableCombos(stance: TrigramStance): readonly ComboSequence[] {\n return this.comboSequences.get(stance) ?? [];\n }\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"ComboSystem.js","names":[],"sources":["../../../src/systems/ai/ComboSystem.ts"],"sourcesContent":["/**\n * AI Combo System for Korean Martial Arts\n * Manages multi-hit combo sequences based on trigram stances and archetype signature combos\n */\n\nimport { PlayerState } from \"@/systems/player\";\nimport { TRIGRAM_TECHNIQUES } from \"@/systems/trigram\";\nimport { KoreanTechnique } from \"@/systems/vitalpoint\";\nimport { PlayerArchetype, TrigramStance } from \"@/types\";\nimport { AIPersonality } from \"./AIPersonality\";\n\n/**\n * Combo sequence for a specific trigram stance\n */\nexport interface ComboSequence {\n readonly stanceId: TrigramStance;\n readonly techniques: readonly KoreanTechnique[];\n readonly minDistance: number;\n readonly maxDistance: number;\n readonly requiredKi: number;\n readonly requiredStamina: number;\n readonly name: {\n readonly korean: string;\n readonly english: string;\n };\n}\n\n/**\n * Archetype signature combo sequence (2-3 technique chains)\n *\n * These are special technique sequences that represent each archetype's\n * fighting philosophy and preferred combat patterns.\n *\n * @korean 원형 대표 연계 기술\n */\nexport interface ArchetypeComboSequence {\n readonly archetype: PlayerArchetype;\n readonly techniqueIds: readonly string[]; // 2-3 technique IDs in sequence\n readonly name: {\n readonly korean: string;\n readonly english: string;\n };\n readonly description: {\n readonly korean: string;\n readonly english: string;\n };\n}\n\n/**\n * Archetype Signature Combos\n *\n * Each archetype has 2 signature combo sequences that reflect their combat philosophy.\n * These combos are prioritized by AI for authentic fighting style representation.\n *\n * Combo patterns:\n * - Musa: Power amplification and defense-to-offense transitions\n * - Amsalja: Setup-to-execution nerve strike sequences\n * - Hacker: Analysis-to-burst and stun-to-shutdown patterns\n * - Jeongbo: Weaken-to-exploit and counter-to-finish sequences\n * - Jojik: Chaos-to-brutality and aggression-to-desperation patterns\n *\n * @korean 원형 대표 연계 기술 정의\n */\nexport const ARCHETYPE_SIGNATURE_COMBOS: Record<\n PlayerArchetype,\n readonly ArchetypeComboSequence[]\n> = {\n [PlayerArchetype.MUSA]: [\n {\n archetype: PlayerArchetype.MUSA,\n techniqueIds: [\"musa_thunder_strike\", \"musa_dragon_fist\"],\n name: {\n korean: \"천둥용권\",\n english: \"Thunder Dragon Combo\",\n },\n description: {\n korean: \"천둥벽력으로 적을 흔들고 용권으로 관통합니다\",\n english:\n \"Shake enemy with Thunder Strike, pierce through with Dragon Fist\",\n },\n },\n {\n archetype: PlayerArchetype.MUSA,\n techniqueIds: [\"musa_iron_defense\", \"musa_mountain_breaker\"],\n name: {\n korean: \"철벽파산\",\n english: \"Iron Mountain Break\",\n },\n description: {\n korean: \"철벽방어로 적의 공격을 막고 파산격으로 반격합니다\",\n english: \"Block with Iron Defense, counter with Mountain Breaker\",\n },\n },\n ],\n\n [PlayerArchetype.AMSALJA]: [\n {\n archetype: PlayerArchetype.AMSALJA,\n techniqueIds: [\"amsalja_shadow_strike\", \"amsalja_silent_death\"],\n name: {\n korean: \"암영무음\",\n english: \"Shadow Silent Death\",\n },\n description: {\n korean: \"암영격으로 신경을 교란하고 무음살로 마무리합니다\",\n english: \"Disrupt nerves with Shadow Strike, finish with Silent Death\",\n },\n },\n {\n archetype: PlayerArchetype.AMSALJA,\n techniqueIds: [\"amsalja_nerve_strike\", \"amsalja_deadly_precision\"],\n name: {\n korean: \"신경정밀\",\n english: \"Nerve Precision Combo\",\n },\n description: {\n korean: \"신경타로 마비시키고 치명정밀로 급소를 공격합니다\",\n english:\n \"Paralyze with Nerve Strike, target vitals with Deadly Precision\",\n },\n },\n ],\n\n [PlayerArchetype.HACKER]: [\n {\n archetype: PlayerArchetype.HACKER,\n techniqueIds: [\"hacker_data_strike\", \"hacker_cyber_overdrive\"],\n name: {\n korean: \"데이터가속\",\n english: \"Data Overdrive Burst\",\n },\n description: {\n korean:\n \"데이터 타격으로 분석하고 사이버 가속으로 폭발적 공격을 수행합니다\",\n english: \"Analyze with Data Strike, burst with Cyber Overdrive\",\n },\n },\n {\n archetype: PlayerArchetype.HACKER,\n techniqueIds: [\"hacker_electric_shock\", \"hacker_system_crash\"],\n name: {\n korean: \"전격크래시\",\n english: \"Electric System Crash\",\n },\n description: {\n korean: \"전격으로 기절시키고 시스템 크래시로 무력화합니다\",\n english: \"Stun with Electric Shock, disable with System Crash\",\n },\n },\n ],\n\n [PlayerArchetype.JEONGBO_YOWON]: [\n {\n archetype: PlayerArchetype.JEONGBO_YOWON,\n techniqueIds: [\n \"jeongbo_tactical_strike\",\n \"jeongbo_psychological_warfare\",\n ],\n name: {\n korean: \"전술심리\",\n english: \"Tactical Psychology Combo\",\n },\n description: {\n korean: \"전술타격으로 약점을 노출시키고 심리전으로 정신을 교란합니다\",\n english:\n \"Expose weakness with Tactical Strike, disrupt with Psychological Warfare\",\n },\n },\n {\n archetype: PlayerArchetype.JEONGBO_YOWON,\n techniqueIds: [\n \"jeongbo_counter_intelligence\",\n \"jeongbo_intelligence_strike\",\n ],\n name: {\n korean: \"역정보타격\",\n english: \"Counter Intelligence Strike\",\n },\n description: {\n korean: \"역정보공작으로 반격하고 정보타격으로 완벽하게 마무리합니다\",\n english:\n \"Counter with Counter Intelligence, finish with Intelligence Strike\",\n },\n },\n ],\n\n [PlayerArchetype.JOJIK_POKRYEOKBAE]: [\n {\n archetype: PlayerArchetype.JOJIK_POKRYEOKBAE,\n techniqueIds: [\"jojik_street_brawl\", \"jojik_brutal_takedown\"],\n name: {\n korean: \"거리잔혹\",\n english: \"Street Brutality Combo\",\n },\n description: {\n korean: \"거리싸움으로 혼란을 주고 잔혹제압으로 무자비하게 쓰러뜨립니다\",\n english:\n \"Create chaos with Street Brawl, brutalize with Brutal Takedown\",\n },\n },\n {\n archetype: PlayerArchetype.JOJIK_POKRYEOKBAE,\n techniqueIds: [\"jojik_improvised_weapon\", \"jojik_ruthless_assault\"],\n name: {\n korean: \"즉석무자비\",\n english: \"Improvised Ruthless Assault\",\n },\n description: {\n korean: \"즉석무기로 공격하고 무자비공격으로 자비 없이 마무리합니다\",\n english:\n \"Attack with Improvised Weapon, finish ruthlessly with Ruthless Assault\",\n },\n },\n ],\n};\n\n/**\n * Get next technique in archetype signature combo\n *\n * Checks if current technique is part of a signature combo sequence,\n * and returns the next technique in that sequence if found.\n *\n * @korean 원형 대표 연계 기술의 다음 기술 가져오기\n *\n * @param currentTechniqueId - Current technique ID just executed\n * @param archetype - Player archetype\n * @returns Next technique ID in combo sequence, or undefined if no combo\n */\nexport function getNextComboTechnique(\n currentTechniqueId: string,\n archetype: PlayerArchetype,\n): string | undefined {\n const combos = ARCHETYPE_SIGNATURE_COMBOS[archetype];\n\n if (!combos) {\n return undefined;\n }\n\n for (const combo of combos) {\n const currentIndex = combo.techniqueIds.indexOf(currentTechniqueId);\n\n // Found current technique in this combo\n if (currentIndex !== -1 && currentIndex < combo.techniqueIds.length - 1) {\n // Return next technique in sequence\n return combo.techniqueIds[currentIndex + 1];\n }\n }\n\n return undefined; // No combo continuation found\n}\n\n/**\n * AI Combo System manages combo execution and decision-making\n */\nexport class AIComboSystem {\n private comboSequences: Map<TrigramStance, ComboSequence[]>;\n private currentCombo: KoreanTechnique[] = [];\n private comboProgress = 0;\n private lastComboTime = 0;\n private readonly comboTimeout = 2000; // 2 seconds to continue combo\n\n constructor() {\n this.comboSequences = this.initializeComboSequences();\n }\n\n /**\n * Initialize combo sequences for each trigram stance\n */\n private initializeComboSequences(): Map<TrigramStance, ComboSequence[]> {\n const sequences = new Map<TrigramStance, ComboSequence[]>();\n\n // Geon (Heaven) - Direct force combos\n sequences.set(TrigramStance.GEON, [\n {\n stanceId: TrigramStance.GEON,\n techniques: this.getTechniqueSequence(TrigramStance.GEON, 3),\n minDistance: 80,\n maxDistance: 140,\n requiredKi: 30,\n requiredStamina: 40,\n name: {\n korean: \"천둥벽력 연타\",\n english: \"Thunder Strike Combo\",\n },\n },\n ]);\n\n // Tae (Lake) - Fluid manipulation combos\n sequences.set(TrigramStance.TAE, [\n {\n stanceId: TrigramStance.TAE,\n techniques: this.getTechniqueSequence(TrigramStance.TAE, 3),\n minDistance: 70,\n maxDistance: 130,\n requiredKi: 25,\n requiredStamina: 35,\n name: {\n korean: \"유수연타\",\n english: \"Flowing Water Combo\",\n },\n },\n ]);\n\n // Li (Fire) - Precision strike combos\n sequences.set(TrigramStance.LI, [\n {\n stanceId: TrigramStance.LI,\n techniques: this.getTechniqueSequence(TrigramStance.LI, 3),\n minDistance: 90,\n maxDistance: 150,\n requiredKi: 35,\n requiredStamina: 30,\n name: {\n korean: \"화염연격\",\n english: \"Flame Strike Series\",\n },\n },\n ]);\n\n // Jin (Thunder) - Explosive combos\n sequences.set(TrigramStance.JIN, [\n {\n stanceId: TrigramStance.JIN,\n techniques: this.getTechniqueSequence(TrigramStance.JIN, 3),\n minDistance: 80,\n maxDistance: 140,\n requiredKi: 40,\n requiredStamina: 45,\n name: {\n korean: \"벽력난타\",\n english: \"Lightning Barrage\",\n },\n },\n ]);\n\n // Son (Wind) - Continuous pressure combos\n sequences.set(TrigramStance.SON, [\n {\n stanceId: TrigramStance.SON,\n techniques: this.getTechniqueSequence(TrigramStance.SON, 3),\n minDistance: 70,\n maxDistance: 130,\n requiredKi: 28,\n requiredStamina: 38,\n name: {\n korean: \"선풍연쇄\",\n english: \"Whirlwind Chain\",\n },\n },\n ]);\n\n // Gam (Water) - Adaptive flow combos\n sequences.set(TrigramStance.GAM, [\n {\n stanceId: TrigramStance.GAM,\n techniques: this.getTechniqueSequence(TrigramStance.GAM, 3),\n minDistance: 75,\n maxDistance: 135,\n requiredKi: 30,\n requiredStamina: 35,\n name: {\n korean: \"수류연환\",\n english: \"Water Flow Sequence\",\n },\n },\n ]);\n\n // Gan (Mountain) - Defensive counter combos\n sequences.set(TrigramStance.GAN, [\n {\n stanceId: TrigramStance.GAN,\n techniques: this.getTechniqueSequence(TrigramStance.GAN, 3),\n minDistance: 70,\n maxDistance: 130,\n requiredKi: 25,\n requiredStamina: 40,\n name: {\n korean: \"반석반격\",\n english: \"Mountain Counter\",\n },\n },\n ]);\n\n // Gon (Earth) - Grounding combos\n sequences.set(TrigramStance.GON, [\n {\n stanceId: TrigramStance.GON,\n techniques: this.getTechniqueSequence(TrigramStance.GON, 3),\n minDistance: 60,\n maxDistance: 120,\n requiredKi: 30,\n requiredStamina: 50,\n name: {\n korean: \"대지낙타\",\n english: \"Earth Slam Combo\",\n },\n },\n ]);\n\n return sequences;\n }\n\n /**\n * Get technique sequence for a stance\n */\n private getTechniqueSequence(\n stance: TrigramStance,\n count: number,\n ): readonly KoreanTechnique[] {\n const techniques = TRIGRAM_TECHNIQUES[stance] ?? [];\n\n // Warn if not enough techniques for full combo (issue #2529727989)\n if (techniques.length < count) {\n console.warn(\n `[AIComboSystem] Not enough techniques defined for stance '${stance}'. Requested ${count}, but only ${techniques.length} available. Returning partial combo.`,\n );\n }\n\n // Return only available techniques\n return techniques.slice(0, count);\n }\n\n /**\n * Calculate distance between two players\n */\n private getDistance(player: PlayerState, opponent: PlayerState): number {\n const dx = player.position.x - opponent.position.x;\n const dy = player.position.y - opponent.position.y;\n return Math.sqrt(dx * dx + dy * dy);\n }\n\n /**\n * Check if AI should continue current combo\n *\n * Enhanced with:\n * - Opponent balance state checking (SHAKEN/VULNERABLE)\n * - Combo length limit (max 3 techniques)\n * - Stamina threshold (>20 for combo continuation)\n *\n * @korean 연계 기술 계속 여부 판단\n */\n shouldContinueCombo(\n player: PlayerState,\n opponent: PlayerState,\n personality: AIPersonality,\n ): boolean {\n const now = Date.now();\n\n // No active combo - check if combo exists\n if (this.currentCombo.length === 0) {\n return false;\n }\n\n // Combo timed out\n if (now - this.lastComboTime > this.comboTimeout) {\n this.resetCombo();\n return false;\n }\n\n // Combo completed\n if (this.comboProgress >= this.currentCombo.length) {\n this.resetCombo();\n return false;\n }\n\n // Combo length limit: max 3 techniques\n if (this.comboProgress >= 3) {\n this.resetCombo();\n return false;\n }\n\n // Korean martial arts philosophy: flow like water\n const distance = this.getDistance(player, opponent);\n const distanceOk = distance < 120;\n\n // Stamina threshold for combo continuation (>20)\n const hasResources = player.ki >= 10 && player.stamina > 20;\n\n // Balance thresholds for vulnerability detection\n // Balance is a numeric value (0-100) representing stability\n // Values below 30 indicate SHAKEN/VULNERABLE states in combat\n const VULNERABLE_BALANCE_THRESHOLD = 30;\n\n // Check opponent balance state - continue combo if opponent is vulnerable\n // Using boolean OR (||) intentionally here - these are boolean conditions, not nullish coalescing\n const isStunned = opponent.isStunned ?? false;\n const isLowHealth =\n (opponent.health ?? 0) < (opponent.maxHealth ?? 100) * 0.3;\n const isLowBalance =\n (opponent.balance ?? 100) < VULNERABLE_BALANCE_THRESHOLD;\n // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing -- intentional boolean OR for vulnerability check\n const opponentVulnerable = isLowBalance || isStunned || isLowHealth;\n\n // Higher chance to continue if opponent is vulnerable\n const baseChance = personality.comboTendency;\n const vulnerabilityBonus = opponentVulnerable ? 0.3 : 0;\n const finalContinueChance = Math.min(0.95, baseChance + vulnerabilityBonus);\n const randomChance = Math.random() < finalContinueChance;\n\n return distanceOk && hasResources && randomChance;\n }\n\n /**\n * Start a new combo sequence\n */\n startCombo(\n player: PlayerState,\n opponent: PlayerState,\n personality: AIPersonality,\n ): boolean {\n // Check if suitable for combo\n const distance = this.getDistance(player, opponent);\n const sequences = this.comboSequences.get(player.currentStance);\n\n if (!sequences || sequences.length === 0) {\n return false;\n }\n\n // Find suitable combo for current situation\n const suitableCombo = sequences.find(\n (seq) =>\n distance >= seq.minDistance &&\n distance <= seq.maxDistance &&\n player.ki >= seq.requiredKi &&\n player.stamina >= seq.requiredStamina,\n );\n\n if (!suitableCombo) {\n return false;\n }\n\n // Random chance based on personality\n if (Math.random() > personality.comboTendency) {\n return false;\n }\n\n // Start combo\n this.currentCombo = [...suitableCombo.techniques];\n this.comboProgress = 0;\n this.lastComboTime = Date.now();\n return true;\n }\n\n /**\n * Get next technique in combo\n */\n getNextComboTechnique(): KoreanTechnique | null {\n if (\n this.comboProgress >= this.currentCombo.length ||\n this.currentCombo.length === 0\n ) {\n return null;\n }\n\n const technique = this.currentCombo[this.comboProgress];\n this.comboProgress++;\n this.lastComboTime = Date.now();\n return technique;\n }\n\n /**\n * Reset current combo\n */\n resetCombo(): void {\n this.currentCombo = [];\n this.comboProgress = 0;\n this.lastComboTime = 0;\n }\n\n /**\n * Check if combo is active (fix for issue #2529727999)\n */\n isComboActive(): boolean {\n return (\n this.currentCombo.length > 0 &&\n this.comboProgress >= 0 &&\n this.comboProgress < this.currentCombo.length &&\n Date.now() - this.lastComboTime < this.comboTimeout\n );\n }\n\n /**\n * Get combo progress information\n */\n getComboInfo(): {\n active: boolean;\n progress: number;\n total: number;\n percentage: number;\n } {\n return {\n active: this.isComboActive(),\n progress: this.comboProgress,\n total: this.currentCombo.length,\n percentage:\n this.currentCombo.length > 0\n ? (this.comboProgress / this.currentCombo.length) * 100\n : 0,\n };\n }\n\n /**\n * Get available combos for stance\n */\n getAvailableCombos(stance: TrigramStance): readonly ComboSequence[] {\n return this.comboSequences.get(stance) ?? [];\n }\n}\n"],"mappings":"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"}
|