blacktrigram 0.7.39 → 0.7.40
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/App2.js.map +1 -1
- package/lib/audio/AudioAssetLoader.js.map +1 -1
- package/lib/audio/AudioAssetRegistry.js.map +1 -1
- package/lib/audio/AudioCache.js.map +1 -1
- package/lib/audio/AudioManager.js.map +1 -1
- package/lib/audio/AudioMonitor.js.map +1 -1
- package/lib/audio/AudioPool.js.map +1 -1
- package/lib/audio/AudioProvider.js.map +1 -1
- package/lib/audio/AudioUtils.js.map +1 -1
- package/lib/audio/BoneImpactAudioMap.js.map +1 -1
- package/lib/audio/VariantSelector.js.map +1 -1
- package/lib/audio/types.js.map +1 -1
- package/lib/components/screens/combat/CombatScreen3D.js.map +1 -1
- package/lib/components/screens/combat/components/controls/CombatButtons.js.map +1 -1
- package/lib/components/screens/combat/components/controls/CombatControlsPanel.js.map +1 -1
- package/lib/components/screens/combat/components/controls/ControlsGuide.js.map +1 -1
- package/lib/components/screens/combat/components/controls/KeyboardHints.js.map +1 -1
- package/lib/components/screens/combat/components/controls/PauseMenu.js.map +1 -1
- package/lib/components/screens/combat/components/controls/PauseMenuButton.js.map +1 -1
- package/lib/components/screens/combat/components/controls/QuickSettings.js.map +1 -1
- package/lib/components/screens/combat/components/effects/BloodDecals3D.js.map +1 -1
- package/lib/components/screens/combat/components/effects/BloodLossOverlayHtml.js.map +1 -1
- package/lib/components/screens/combat/components/effects/BloodParticles3D.js.map +1 -1
- package/lib/components/screens/combat/components/effects/BloodViscosity3D.js.map +1 -1
- package/lib/components/screens/combat/components/effects/CombatParticleEffects3D.js.map +1 -1
- package/lib/components/screens/combat/components/effects/ConsciousnessBlur.js.map +1 -1
- package/lib/components/screens/combat/components/effects/InternalDamage3D.js.map +1 -1
- package/lib/components/screens/combat/components/effects/PainVignette.js.map +1 -1
- package/lib/components/screens/combat/components/effects/ParticleAudio3D.js.map +1 -1
- package/lib/components/screens/combat/components/effects/TraumaOverlay3D.js.map +1 -1
- package/lib/components/screens/combat/components/feedback/MatchCountdown.js.map +1 -1
- package/lib/components/screens/combat/components/feedback/RoundAnnouncementOverlayHtml.js.map +1 -1
- package/lib/components/screens/combat/components/feedback/RoundDisplayStatus.js.map +1 -1
- package/lib/components/screens/combat/components/feedback/RoundStartAnnouncementOverlayHtml.js.map +1 -1
- package/lib/components/screens/combat/components/hud/CombatBottomHUD.js.map +1 -1
- package/lib/components/screens/combat/components/hud/CombatLeftHUD.js.map +1 -1
- package/lib/components/screens/combat/components/hud/CombatPortraitStatusStrip.js.map +1 -1
- package/lib/components/screens/combat/components/hud/CombatRightHUD.js.map +1 -1
- package/lib/components/screens/combat/components/hud/CombatTopHUD.js.map +1 -1
- package/lib/components/screens/combat/components/hud/DifficultyIndicator.js.map +1 -1
- package/lib/components/screens/combat/components/hud/FPSMonitor.js.map +1 -1
- package/lib/components/screens/combat/components/hud/MobileControlsWrapper.js.map +1 -1
- package/lib/components/screens/combat/components/hud/PlayerStateOverlayHtml.js.map +1 -1
- package/lib/components/screens/combat/components/indicators/BalanceIndicator.js.map +1 -1
- package/lib/components/screens/combat/components/indicators/InputBufferDisplay.js.map +1 -1
- package/lib/components/screens/combat/components/indicators/StaminaWarning.js.map +1 -1
- package/lib/components/screens/combat/components/indicators/TechniqueNameDisplay.js.map +1 -1
- package/lib/components/screens/combat/helpers/AnimationUpdater.js.map +1 -1
- package/lib/components/screens/combat/helpers/combatHelpers.js.map +1 -1
- package/lib/components/screens/combat/hooks/useAICombat.js.map +1 -1
- package/lib/components/screens/combat/hooks/useCombatActions.js.map +1 -1
- package/lib/components/screens/combat/hooks/useCombatAttackMovement.js.map +1 -1
- package/lib/components/screens/combat/hooks/useCombatAudio.js.map +1 -1
- package/lib/components/screens/combat/hooks/useCombatLayout.js.map +1 -1
- package/lib/components/screens/combat/hooks/useCombatState.js.map +1 -1
- package/lib/components/screens/controls/ControlsScreen3D.js.map +1 -1
- package/lib/components/screens/controls/components/ControlBindingsOverlayHtml.js.map +1 -1
- package/lib/components/screens/controls/components/ControlCategoryTabsOverlayHtml.js.map +1 -1
- package/lib/components/screens/controls/components/GamepadVisualization3D.js.map +1 -1
- package/lib/components/screens/controls/components/InteractiveControlDemoOverlayHtml.js.map +1 -1
- package/lib/components/screens/controls/components/Key3D.js.map +1 -1
- package/lib/components/screens/controls/components/VisualKeyboard3D.js.map +1 -1
- package/lib/components/screens/controls/constants/ControlsConstants.js.map +1 -1
- package/lib/components/screens/controls/hooks/useControlsState.js.map +1 -1
- package/lib/components/screens/endscreen/EndScreen3D.js.map +1 -1
- package/lib/components/screens/endscreen/components/DefeatAnimation3D.js.map +1 -1
- package/lib/components/screens/endscreen/components/MatchStatisticsDisplayOverlayHtml.js.map +1 -1
- package/lib/components/screens/endscreen/components/NavigationButtonsOverlayHtml.js.map +1 -1
- package/lib/components/screens/endscreen/components/PerformanceBreakdownOverlayHtml.js.map +1 -1
- package/lib/components/screens/endscreen/components/PerformanceRatingOverlayHtml.js.map +1 -1
- package/lib/components/screens/endscreen/components/VictoryAnimation3D.js.map +1 -1
- package/lib/components/screens/endscreen/components/WinnerDisplayOverlayHtml.js.map +1 -1
- package/lib/components/screens/intro/IntroScreen3D.js +1 -1
- package/lib/components/screens/intro/IntroScreen3D.js.map +1 -1
- package/lib/components/screens/intro/components/AbilityListOverlayHtml.js.map +1 -1
- package/lib/components/screens/intro/components/ArchetypeCardGridOverlayHtml.js.map +1 -1
- package/lib/components/screens/intro/components/ArchetypeCardOverlayHtml.js.map +1 -1
- package/lib/components/screens/intro/components/ArchetypeDisplayOverlayHtml.js.map +1 -1
- package/lib/components/screens/intro/components/EnhancedArchetypeDisplayOverlayHtml.js.map +1 -1
- package/lib/components/screens/intro/components/MenuButtonsOverlayHtml.js.map +1 -1
- package/lib/components/screens/intro/components/MenuSectionOverlayHtml.js.map +1 -1
- package/lib/components/screens/intro/components/StatBarOverlayHtml.js.map +1 -1
- package/lib/components/screens/philosophy/PhilosophyScreen3D.js.map +1 -1
- package/lib/components/screens/training/TrainingScreen3D.js.map +1 -1
- package/lib/components/screens/training/components/AnatomyControlsOverlayHtml.js.map +1 -1
- package/lib/components/screens/training/components/AnatomyOverlay3D.js.map +1 -1
- package/lib/components/screens/training/components/FootPlacementMarkers3D.js.map +1 -1
- package/lib/components/screens/training/components/FootworkDrillsOverlayHtml.js.map +1 -1
- package/lib/components/screens/training/components/HitFeedbackEffect3D.js.map +1 -1
- package/lib/components/screens/training/components/TrainingButtonsOverlayHtml.js.map +1 -1
- package/lib/components/screens/training/components/TrainingControlsOverlayHtml.js.map +1 -1
- package/lib/components/screens/training/components/TrainingDummy3D.js.map +1 -1
- package/lib/components/screens/training/components/TrainingFeedbackOverlayHtml.js.map +1 -1
- package/lib/components/screens/training/components/TrainingModeSelectorOverlayHtml.js.map +1 -1
- package/lib/components/screens/training/components/TrainingStatsOverlayHtml.js.map +1 -1
- package/lib/components/screens/training/components/VitalPointMarker3D.js.map +1 -1
- package/lib/components/screens/training/components/VitalPointTrainingOverlayHtml.js.map +1 -1
- package/lib/components/screens/training/components/hud/TrainingBottomHUD.js.map +1 -1
- package/lib/components/screens/training/components/hud/TrainingLeftHUD.js.map +1 -1
- package/lib/components/screens/training/components/hud/TrainingRightHUD.js.map +1 -1
- package/lib/components/screens/training/components/hud/TrainingTopHUD.js.map +1 -1
- package/lib/components/screens/training/hooks/useAttackMovement.js.map +1 -1
- package/lib/components/screens/training/hooks/useTrainingActions.js.map +1 -1
- package/lib/components/screens/training/hooks/useTrainingLayout.js.map +1 -1
- package/lib/components/screens/training/hooks/useTrainingState.js.map +1 -1
- package/lib/components/shared/base/BaseButton.js.map +1 -1
- package/lib/components/shared/base/BaseButtonOverlayHtml.js.map +1 -1
- package/lib/components/shared/base/BasePanel.js.map +1 -1
- package/lib/components/shared/base/BaseText.js.map +1 -1
- package/lib/components/shared/base/useKoreanTheme.js.map +1 -1
- package/lib/components/shared/debug/PerformanceDebugOverlayHtml.js.map +1 -1
- package/lib/components/shared/mobile/ActionButtons.js.map +1 -1
- package/lib/components/shared/mobile/GestureRecognizerPure.js.map +1 -1
- package/lib/components/shared/mobile/HapticController.js.map +1 -1
- package/lib/components/shared/mobile/MobileControlsPure.js.map +1 -1
- package/lib/components/shared/mobile/StanceWheelPure.js.map +1 -1
- package/lib/components/shared/mobile/TouchOptimizer.js.map +1 -1
- package/lib/components/shared/mobile/VirtualDPad.js.map +1 -1
- package/lib/components/shared/three/anatomy/BodySurface.js.map +1 -1
- package/lib/components/shared/three/anatomy/BoneAttachedMuscles.js.map +1 -1
- package/lib/components/shared/three/anatomy/BoneClothing.js.map +1 -1
- package/lib/components/shared/three/anatomy/BoneRenderer.js.map +1 -1
- package/lib/components/shared/three/anatomy/Face3D.js.map +1 -1
- package/lib/components/shared/three/anatomy/Foot3D.js.map +1 -1
- package/lib/components/shared/three/anatomy/Hand3D.js.map +1 -1
- package/lib/components/shared/three/effects/ActionFeedback.js.map +1 -1
- package/lib/components/shared/three/effects/DamageNumbers.js.map +1 -1
- package/lib/components/shared/three/effects/HitEffects3D.js.map +1 -1
- package/lib/components/shared/three/effects/PlayerStateIndicators.js.map +1 -1
- package/lib/components/shared/three/effects/StanceSymbol3D.js.map +1 -1
- package/lib/components/shared/three/effects/StanceTransitionEffect.js.map +1 -1
- package/lib/components/shared/three/effects/VitalPointMarkers3D.js.map +1 -1
- package/lib/components/shared/three/indicators/ElementalColorSystem.js.map +1 -1
- package/lib/components/shared/three/indicators/GuardIndicator.js.map +1 -1
- package/lib/components/shared/three/indicators/HapticFeedback.js.map +1 -1
- package/lib/components/shared/three/indicators/StanceChangeIndicator.js.map +1 -1
- package/lib/components/shared/three/models/Player3DWithTransitions.js.map +1 -1
- package/lib/components/shared/three/models/SkeletalPlayer3D.js.map +1 -1
- package/lib/components/shared/three/optimization/AdaptiveQuality.js.map +1 -1
- package/lib/components/shared/three/scene/AtmosphericParticles3D.js.map +1 -1
- package/lib/components/shared/three/scene/BackgroundScene3D.js.map +1 -1
- package/lib/components/shared/three/scene/CombatArena3D.js.map +1 -1
- package/lib/components/shared/three/scene/KoreanSignage3D.js.map +1 -1
- package/lib/components/shared/three/ui/ArchetypeCard.js.map +1 -1
- package/lib/components/shared/three/ui/BodyPartHealthDisplay.js.map +1 -1
- package/lib/components/shared/three/ui/BreathingIndicator2.js.map +1 -1
- package/lib/components/shared/three/ui/CombatReadinessBar.js.map +1 -1
- package/lib/components/shared/three/ui/ComboCounter.js.map +1 -1
- package/lib/components/shared/three/ui/HealthBar.js.map +1 -1
- package/lib/components/shared/three/ui/KoreanButton.js.map +1 -1
- package/lib/components/shared/three/ui/KoreanPanel.js.map +1 -1
- package/lib/components/shared/three/ui/KoreanText.js.map +1 -1
- package/lib/components/shared/three/ui/MenuList.js.map +1 -1
- package/lib/components/shared/three/ui/PlayerHUD.js.map +1 -1
- package/lib/components/shared/three/ui/ProgressBar.js.map +1 -1
- package/lib/components/shared/three/ui/SpeedIndicatorHUD.js.map +1 -1
- package/lib/components/shared/three/ui/StaminaBar.js.map +1 -1
- package/lib/components/shared/three/ui/TechniqueBar.js.map +1 -1
- package/lib/components/shared/three/ui/TechniqueCard.js.map +1 -1
- package/lib/components/shared/three/ui/VitalPointOverlayControlsHtml.js.map +1 -1
- package/lib/components/shared/ui/BackButton.js.map +1 -1
- package/lib/components/shared/ui/BaseHUDContainer.js.map +1 -1
- package/lib/components/shared/ui/CombatTimer.js.map +1 -1
- package/lib/components/shared/ui/ErrorModal.js.map +1 -1
- package/lib/components/shared/ui/LoadingState.js.map +1 -1
- package/lib/components/shared/ui/SplashScreen.js +2 -2
- package/lib/components/shared/ui/SplashScreen.js.map +1 -1
- package/lib/components/shared/ui/VitalPointOverlayControlsPure.js.map +1 -1
- package/lib/components/shared/ui/VolumeControl.js.map +1 -1
- package/lib/components/shared/ui/shared/ConfirmDialog.js.map +1 -1
- package/lib/components/ui/combat/BalanceIndicatorOverlayHtml.js.map +1 -1
- package/lib/constants/bodyDimensions.js.map +1 -1
- package/lib/constants/bodyRenderingConstants.js.map +1 -1
- package/lib/data/archetypeClothing.js.map +1 -1
- package/lib/data/archetypePhysicalAttributes.js.map +1 -1
- package/lib/data/techniqueMappings.js.map +1 -1
- package/lib/data/techniques.js.map +1 -1
- package/lib/hooks/useActionFeedback.js.map +1 -1
- package/lib/hooks/useBalanceAnimations.js.map +1 -1
- package/lib/hooks/useCombatTimer.js.map +1 -1
- package/lib/hooks/useDebounce.js.map +1 -1
- package/lib/hooks/useHUDLayout.js.map +1 -1
- package/lib/hooks/useHandPoseTransitions.js.map +1 -1
- package/lib/hooks/useKeyboardControls.js.map +1 -1
- package/lib/hooks/useMatchCountdown.js.map +1 -1
- package/lib/hooks/useMuscleActivation.js.map +1 -1
- package/lib/hooks/usePauseMenu.js.map +1 -1
- package/lib/hooks/usePlayerAnimation.js.map +1 -1
- package/lib/hooks/useResponsiveLayout.js.map +1 -1
- package/lib/hooks/useRoundTransition.js.map +1 -1
- package/lib/hooks/useSkeletalAnimation.js.map +1 -1
- package/lib/hooks/useTechniqueSelection.js.map +1 -1
- package/lib/hooks/useThrottle.js.map +1 -1
- package/lib/hooks/useTouchControls.js.map +1 -1
- package/lib/hooks/useWebGLContextLossHandler.js.map +1 -1
- package/lib/hooks/useWindowSize.js.map +1 -1
- package/lib/systems/CombatSystem.js.map +1 -1
- package/lib/systems/EffectCalculator.js.map +1 -1
- package/lib/systems/LayoutSystem.js.map +1 -1
- package/lib/systems/PlayerEffectManager.js.map +1 -1
- package/lib/systems/ResponsiveScaling.js.map +1 -1
- package/lib/systems/TrigramSystem.js.map +1 -1
- package/lib/systems/VitalPointSystem.js.map +1 -1
- package/lib/systems/ai/AIPersonality.js.map +1 -1
- package/lib/systems/ai/AdaptiveDifficulty.js.map +1 -1
- package/lib/systems/ai/ArchetypeEnforcer.js.map +1 -1
- package/lib/systems/ai/ComboSystem.js.map +1 -1
- package/lib/systems/ai/DecisionTree.js.map +1 -1
- package/lib/systems/ai/TrainingAI.js.map +1 -1
- package/lib/systems/ai/types.js.map +1 -1
- package/lib/systems/animation/builders/AnimationBuilder.js.map +1 -1
- package/lib/systems/animation/builders/HandPoseApplicator.js.map +1 -1
- package/lib/systems/animation/builders/HandPoses.js.map +1 -1
- package/lib/systems/animation/builders/KeyframeConfig.js.map +1 -1
- package/lib/systems/animation/builders/KeyframeInterpolation.js +3 -90
- package/lib/systems/animation/builders/KeyframeInterpolation.js.map +1 -1
- package/lib/systems/animation/builders/KickPhaseApplicator.js.map +1 -1
- package/lib/systems/animation/builders/KoreanGuardPositions.js.map +1 -1
- package/lib/systems/animation/builders/MartialArtsAnimationBuilder.js.map +1 -1
- package/lib/systems/animation/builders/MartialArtsConstants.js.map +1 -1
- package/lib/systems/animation/builders/MartialPoseApplicator.js.map +1 -1
- package/lib/systems/animation/builders/PunchPhaseApplicator.js.map +1 -1
- package/lib/systems/animation/builders/SkeletonRig.js.map +1 -1
- package/lib/systems/animation/builders/TrigramGuardApplicator.js.map +1 -1
- package/lib/systems/animation/catalogs/DefensiveAnimations.js.map +1 -1
- package/lib/systems/animation/catalogs/FootworkSkeletalAnimations.js.map +1 -1
- package/lib/systems/animation/catalogs/RecoveryAnimations.js.map +1 -1
- package/lib/systems/animation/catalogs/StanceAnimations.js.map +1 -1
- package/lib/systems/animation/catalogs/StanceAttackAnimations.js.map +1 -1
- package/lib/systems/animation/catalogs/StanceGuardPoses.js.map +1 -1
- package/lib/systems/animation/catalogs/StanceIdleAnimations.js.map +1 -1
- package/lib/systems/animation/catalogs/StanceLocomotionAnimations.js.map +1 -1
- package/lib/systems/animation/catalogs/StepSkeletalAnimations.js.map +1 -1
- package/lib/systems/animation/core/AnimationHitTiming.js.map +1 -1
- package/lib/systems/animation/core/AnimationOptimizations.js.map +1 -1
- package/lib/systems/animation/core/AnimationPriority.js.map +1 -1
- package/lib/systems/animation/core/AnimationRegistry.js.map +1 -1
- package/lib/systems/animation/core/AnimationStateMachine.js.map +1 -1
- package/lib/systems/animation/core/AnimationTransitions.js.map +1 -1
- package/lib/systems/animation/core/LateralityTransform.js.map +1 -1
- package/lib/systems/animation/core/RecoveryPhaseEnhancer.js.map +1 -1
- package/lib/systems/animation/core/TechniqueAnimationMapper.js.map +1 -1
- package/lib/systems/animation/core/TechniqueAnimationMapping.js.map +1 -1
- package/lib/systems/animation/core/types.js.map +1 -1
- package/lib/systems/animation/systems/AdvancedJointMovements.js.map +1 -1
- package/lib/systems/animation/systems/BodyFacingSystem.js.map +1 -1
- package/lib/systems/animation/systems/FacialExpressions.js.map +1 -1
- package/lib/systems/animation/systems/FallAnimations.js.map +1 -1
- package/lib/systems/animation/systems/MuscleActivation.js.map +1 -1
- package/lib/systems/bodypart/BodyPartDamageIntegration.js.map +1 -1
- package/lib/systems/bodypart/BodyPartHealthSystem.js.map +1 -1
- package/lib/systems/bodypart/BodyPartPositionMapping.js.map +1 -1
- package/lib/systems/bodypart/CombatInjuryIntegration.js.map +1 -1
- package/lib/systems/bodypart/InjuryIntegration.js.map +1 -1
- package/lib/systems/bodypart/InjuryTracker.js.map +1 -1
- package/lib/systems/bodypart/MovementPenaltySystem.js.map +1 -1
- package/lib/systems/bodypart/PlayerInjuryTrackingManager.js.map +1 -1
- package/lib/systems/bodypart/types.js.map +1 -1
- package/lib/systems/breathing/BreathingDisruptionSystem.js.map +1 -1
- package/lib/systems/breathing/feedback.js.map +1 -1
- package/lib/systems/breathing/integration.js.map +1 -1
- package/lib/systems/combat/BalanceSystem.js.map +1 -1
- package/lib/systems/combat/BreakingStatusEffects.js.map +1 -1
- package/lib/systems/combat/CombatStateSystem.js.map +1 -1
- package/lib/systems/combat/ConsciousnessSystem.js.map +1 -1
- package/lib/systems/combat/FallIntegration.js.map +1 -1
- package/lib/systems/combat/GrappleSystem.js.map +1 -1
- package/lib/systems/combat/LimbExposureSystem.js.map +1 -1
- package/lib/systems/combat/PainResponseSystem.js.map +1 -1
- package/lib/systems/combat/TrainingCombatSystem.js.map +1 -1
- package/lib/systems/combat/painConsciousnessUtils.js.map +1 -1
- package/lib/systems/combat/typeGuards.js.map +1 -1
- package/lib/systems/effects.js.map +1 -1
- package/lib/systems/game.js.map +1 -1
- package/lib/systems/movement/InjuryMovementModifier.js.map +1 -1
- package/lib/systems/movement/helpers/AccelerationUpdater.js.map +1 -1
- package/lib/systems/movement/helpers/accelerationUtils.js.map +1 -1
- package/lib/systems/movement/integration.js.map +1 -1
- package/lib/systems/physics/AttackMovementPhysics.js.map +1 -1
- package/lib/systems/physics/CollisionDetection.js.map +1 -1
- package/lib/systems/physics/CoordinateMapper.js.map +1 -1
- package/lib/systems/physics/KnockbackPhysics.js.map +1 -1
- package/lib/systems/physics/MovementPhysics.js.map +1 -1
- package/lib/systems/physics/PhysicalReachCalculator.js.map +1 -1
- package/lib/systems/physics/SpeedModifierSystem.js.map +1 -1
- package/lib/systems/trigram/KoreanCulture.js.map +1 -1
- package/lib/systems/trigram/KoreanTechniques.js.map +1 -1
- package/lib/systems/trigram/StanceManager.js.map +1 -1
- package/lib/systems/trigram/TransitionCalculator.js.map +1 -1
- package/lib/systems/trigram/TrigramCalculator.js.map +1 -1
- package/lib/systems/trigram/techniques/DarkOpsTechniques.js.map +1 -1
- package/lib/systems/trigram/techniques/GamTechniques.js.map +1 -1
- package/lib/systems/trigram/techniques/GanTechniques.js.map +1 -1
- package/lib/systems/trigram/techniques/GonTechniques.js.map +1 -1
- package/lib/systems/trigram/techniques/SonTechniques.js.map +1 -1
- package/lib/systems/trigram/techniques/TechniqueConfig.js.map +1 -1
- package/lib/systems/trigram/techniques/index.js.map +1 -1
- package/lib/systems/trigram/types/GonTechniqueExtensions.js.map +1 -1
- package/lib/systems/trigram/types.js.map +1 -1
- package/lib/systems/types.js.map +1 -1
- package/lib/systems/vitalpoint/DamageCalculator.js.map +1 -1
- package/lib/systems/vitalpoint/HitDetection.js.map +1 -1
- package/lib/systems/vitalpoint/KoreanAnatomy.js.map +1 -1
- package/lib/systems/vitalpoint/KoreanVitalPoints.js.map +1 -1
- package/lib/systems/vitalpoint/MeridianVitalPointMapping.js.map +1 -1
- package/lib/types/AccessibilityTypes.js.map +1 -1
- package/lib/types/PhysicsTypes.js.map +1 -1
- package/lib/types/common.js.map +1 -1
- package/lib/types/constants/colors.js.map +1 -1
- package/lib/types/constants/designSystem.js.map +1 -1
- package/lib/types/constants/layout.js.map +1 -1
- package/lib/types/constants/performance.js.map +1 -1
- package/lib/types/constants/typography.js.map +1 -1
- package/lib/types/facial.js.map +1 -1
- package/lib/types/hand-animation.js.map +1 -1
- package/lib/types/injury.js.map +1 -1
- package/lib/types/physics.js.map +1 -1
- package/lib/types/skeletal.js.map +1 -1
- package/lib/types/techniqueId.js.map +1 -1
- package/lib/utils/accessibility.js.map +1 -1
- package/lib/utils/arenaWorldDimensions.js.map +1 -1
- package/lib/utils/assetConfig.js.map +1 -1
- package/lib/utils/characterScaling.js.map +1 -1
- package/lib/utils/colorHelpers.js.map +1 -1
- package/lib/utils/colorUtils.js.map +1 -1
- package/lib/utils/combatReadiness.js.map +1 -1
- package/lib/utils/controlMapping.js.map +1 -1
- package/lib/utils/deviceDetection.js.map +1 -1
- package/lib/utils/effectUtils.js.map +1 -1
- package/lib/utils/fabricTextures.js.map +1 -1
- package/lib/utils/hapticFeedback.js.map +1 -1
- package/lib/utils/haptics.js.map +1 -1
- package/lib/utils/htmlOverlayHelpers.js.map +1 -1
- package/lib/utils/inputSystem.js.map +1 -1
- package/lib/utils/koreanThemeHelpers.js.map +1 -1
- package/lib/utils/math.js.map +1 -1
- package/lib/utils/mobileLayoutHelpers.js.map +1 -1
- package/lib/utils/mobileUIUtils.js.map +1 -1
- package/lib/utils/performance/PerformanceMonitor.js.map +1 -1
- package/lib/utils/performance/PerformanceOverlay3D.js.map +1 -1
- package/lib/utils/performance/usePerformanceMonitor.js.map +1 -1
- package/lib/utils/performanceOptimization.js.map +1 -1
- package/lib/utils/player3DHelpers.js.map +1 -1
- package/lib/utils/playerUtils.js.map +1 -1
- package/lib/utils/responsiveLayout.js.map +1 -1
- package/lib/utils/responsiveLayoutHelpers.js.map +1 -1
- package/lib/utils/responsiveOrientationConstants.js.map +1 -1
- package/lib/utils/safeAreaUtils.js.map +1 -1
- package/lib/utils/sharedPhysicsConfig.js.map +1 -1
- package/lib/utils/skeletonScaling.js.map +1 -1
- package/lib/utils/stanceHelpers.js.map +1 -1
- package/lib/utils/threeObjectPool.js.map +1 -1
- package/lib/utils/visualEffects.js.map +1 -1
- package/package.json +5 -5
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"AIPersonality.js","names":[],"sources":["../../../src/systems/ai/AIPersonality.ts"],"sourcesContent":["/**\n * AI Personality System for Korean Martial Arts Combat\n * Defines behavioral archetypes that guide AI decision-making\n */\n\nimport { PlayerArchetype, TrigramStance } from \"@/types\";\n\n/**\n * Movement pattern types for archetype behavior\n *\n * @korean 이동 패턴 타입\n */\nexport type MovementPattern =\n | \"aggressive\"\n | \"evasive\"\n | \"analytical\"\n | \"unpredictable\";\n\n/**\n * Vital target priority for archetype-specific combat strategies\n *\n * @korean 급소 우선순위\n */\nexport type VitalTargetPriority =\n | \"health\"\n | \"pain\"\n | \"consciousness\"\n | \"balanced\";\n\n/**\n * Technique category types for archetype preferences\n *\n * @korean 기술 범주\n */\nexport type TechniqueCategory =\n | \"joint_manipulation\"\n | \"bone_strikes\"\n | \"nerve_strikes\"\n | \"silent_takedowns\"\n | \"anatomical_analysis\"\n | \"calculated_strikes\"\n | \"psychological_pressure\"\n | \"submission_induction\"\n | \"dirty_techniques\"\n | \"environmental_usage\";\n\n/**\n * Archetype-specific behavior profile\n *\n * Defines combat preferences, movement patterns, and tactical decision-making\n * unique to each of the 5 player archetypes.\n *\n * @korean 원형별 행동 프로필\n */\nexport interface ArchetypeBehavior {\n /** Preferred trigram stances for this archetype */\n readonly preferredStances: readonly TrigramStance[];\n /** Optimal combat range in grid cells (1 cell = ~40px) */\n readonly optimalRange: number;\n /** Health percentage threshold to trigger retreat behavior */\n readonly retreatThreshold: number;\n /** Technique categories this archetype favors */\n readonly techniqueSelectionBias: readonly TechniqueCategory[];\n /** Movement pattern characteristic of this archetype */\n readonly movementPattern: MovementPattern;\n /** Whether archetype follows honor code (affects retreat behavior) */\n readonly honorCode: boolean;\n /** Priority system for vital point targeting */\n readonly vitalTargetPriority: VitalTargetPriority;\n}\n\n/**\n * AI personality profile defining combat behavior\n */\nexport interface AIPersonality {\n readonly name: string;\n readonly koreanName: string;\n readonly archetype: PlayerArchetype;\n readonly aggressionLevel: number; // 0.0-1.0: How often AI attacks\n readonly defensePreference: number; // 0.0-1.0: Tendency to block/counter\n readonly comboTendency: number; // 0.0-1.0: Likelihood to continue combos\n readonly stanceSwitchFrequency: number; // 0.0-1.0: How often changes stance\n readonly feintChance: number; // 0.0-1.0: Probability of fake attacks\n readonly tacticalRetreatThreshold: number; // Health % to retreat\n readonly favoredStances: readonly TrigramStance[];\n readonly description: {\n readonly korean: string;\n readonly english: string;\n };\n}\n\n/**\n * Archetype-specific behavior profiles\n *\n * Maps each of the 5 player archetypes to their unique combat behaviors,\n * movement patterns, and tactical preferences based on Korean martial arts\n * traditions and game design philosophy.\n *\n * @korean 원형별 행동 프로필\n */\nexport const ARCHETYPE_BEHAVIORS: Record<PlayerArchetype, ArchetypeBehavior> = {\n [PlayerArchetype.MUSA]: {\n preferredStances: [\n TrigramStance.GEON,\n TrigramStance.JIN,\n TrigramStance.GAN,\n ], // Heaven, Thunder, Mountain\n optimalRange: 1, // Close quarters (1 cell = ~40px)\n retreatThreshold: 5, // Enhanced: fights to near-death (honor code)\n techniqueSelectionBias: [\"joint_manipulation\", \"bone_strikes\"],\n movementPattern: \"aggressive\",\n honorCode: true, // Never retreats above threshold\n vitalTargetPriority: \"balanced\",\n },\n [PlayerArchetype.AMSALJA]: {\n preferredStances: [TrigramStance.SON, TrigramStance.GAM], // Wind, Water\n optimalRange: 1, // Stealth melee (1 cell)\n retreatThreshold: 20, // Enhanced: tactical retreat, not cowardice\n techniqueSelectionBias: [\"nerve_strikes\", \"silent_takedowns\"],\n movementPattern: \"evasive\",\n honorCode: false,\n vitalTargetPriority: \"consciousness\",\n },\n [PlayerArchetype.HACKER]: {\n preferredStances: [TrigramStance.LI, TrigramStance.TAE], // Fire, Lake\n optimalRange: 3, // Mid-range analysis (3 cells = ~120px)\n retreatThreshold: 50,\n techniqueSelectionBias: [\"anatomical_analysis\", \"calculated_strikes\"],\n movementPattern: \"analytical\",\n honorCode: false,\n vitalTargetPriority: \"balanced\",\n },\n [PlayerArchetype.JEONGBO_YOWON]: {\n preferredStances: [TrigramStance.GAN, TrigramStance.GON], // Mountain, Earth\n optimalRange: 2, // Tactical mid-range (2 cells = ~80px)\n retreatThreshold: 40,\n techniqueSelectionBias: [\"psychological_pressure\", \"submission_induction\"],\n movementPattern: \"analytical\",\n honorCode: false,\n vitalTargetPriority: \"pain\",\n },\n [PlayerArchetype.JOJIK_POKRYEOKBAE]: {\n preferredStances: [TrigramStance.JIN, TrigramStance.GAM], // Thunder, Water (adaptable)\n optimalRange: 1, // Close brutal combat (1 cell)\n retreatThreshold: 70, // Retreats pragmatically\n techniqueSelectionBias: [\"dirty_techniques\", \"environmental_usage\"],\n movementPattern: \"unpredictable\",\n honorCode: false,\n vitalTargetPriority: \"health\",\n },\n};\n\n/**\n * Five AI personality archetypes inspired by Korean martial arts philosophy\n */\nexport const AI_PERSONALITIES: Record<string, AIPersonality> = {\n /**\n * 맹공자 (Maenggongja) - Fierce Attacker\n * Aggressive pressure fighter using Musa archetype\n *\n * **Enhanced Aggression (Issue #enhance-ai-aggression)**:\n * - Increased aggression: 0.85 → 0.95 (overwhelming force)\n * - Reduced defense: 0.2 → 0.1 (all-in offensive)\n * - Increased combo tendency: 0.7 → 0.8 (sustained pressure)\n * - Reduced retreat threshold: 0.15 → 0.05 (honor code: fights to near-death)\n *\n * **Dynamic Stance Rotation (Issue #dynamic-ai-stance-rotation)**:\n * - Increased stance switch frequency: 0.3 → 0.5 (more tactical flexibility)\n */\n AGGRESSIVE_STRIKER: {\n name: \"Aggressive Striker\",\n koreanName: \"맹공자\",\n archetype: PlayerArchetype.MUSA,\n aggressionLevel: 0.95, // Enhanced from 0.85\n defensePreference: 0.1, // Reduced from 0.2\n comboTendency: 0.8, // Increased from 0.7\n stanceSwitchFrequency: 0.15, // Reduced: Musa focuses on overwhelming force, not stance dancing\n feintChance: 0.15,\n tacticalRetreatThreshold: 0.05, // Reduced from 0.15\n favoredStances: [\n TrigramStance.GEON, // Heaven - Direct force\n TrigramStance.JIN, // Thunder - Explosive power\n TrigramStance.LI, // Fire - Precision strikes\n ],\n description: {\n korean: \"정면 돌파를 선호하는 공격적인 전사\",\n english: \"Aggressive warrior who prefers frontal assault\",\n },\n },\n\n /**\n * 기술가 (Gisulga) - Technical Master\n * Precision fighter using Amsalja archetype\n *\n * **Enhanced Aggression (Issue #enhance-ai-aggression)**:\n * - Increased aggression: 0.5 → 0.85 (instant takedown focus)\n * - Reduced defense: 0.6 → 0.3 (opportunistic aggression)\n * - Increased combo tendency: 0.4 → 0.6 (lethal sequences)\n * - Reduced retreat threshold: 0.35 → 0.20 (tactical retreat, not cowardice)\n *\n * **Dynamic Stance Rotation (Issue #dynamic-ai-stance-rotation)**:\n * - Increased stance switch frequency: 0.7 → 0.85 (highly adaptive)\n */\n TECHNICAL_MASTER: {\n name: \"Technical Master\",\n koreanName: \"기술가\",\n archetype: PlayerArchetype.AMSALJA,\n aggressionLevel: 0.95, // Enhanced for fatal precision strikes\n defensePreference: 0.2, // Reduced: assassins attack, don't defend\n comboTendency: 0.7, // Increased for lethal combinations\n stanceSwitchFrequency: 0.2, // Reduced: precision strikers commit to their stance\n feintChance: 0.35,\n tacticalRetreatThreshold: 0.15, // Reduced: assassins press the attack\n favoredStances: [\n TrigramStance.SON, // Wind - Continuous pressure\n TrigramStance.GAM, // Water - Flow and adaptation\n TrigramStance.TAE, // Lake - Fluid manipulation\n ],\n description: {\n korean: \"정밀한 기술로 약점을 노리는 달인\",\n english: \"Master who targets weaknesses with precise techniques\",\n },\n },\n\n /**\n * 균형 잡힌 자 (Gyunhyeong Jabin-ja) - Balanced Fighter\n * All-around fighter using Jeongbo Yowon archetype\n *\n * **Dynamic Stance Rotation (Issue #dynamic-ai-stance-rotation)**:\n * - Increased stance switch frequency: 0.5 → 0.7 (strategic switching)\n */\n BALANCED_FIGHTER: {\n name: \"Balanced Fighter\",\n koreanName: \"균형 잡힌 자\",\n archetype: PlayerArchetype.JEONGBO_YOWON,\n aggressionLevel: 0.75, // Enhanced: intelligence operatives are decisive\n defensePreference: 0.4, // Slightly reduced for more aggression\n comboTendency: 0.6, // Increased for calculated sequences\n stanceSwitchFrequency: 0.25, // Reduced: strategic, not reactive\n feintChance: 0.3, // Increased: psychological warfare\n tacticalRetreatThreshold: 0.2, // Slightly reduced\n favoredStances: [\n TrigramStance.GEON, // Heaven\n TrigramStance.GAM, // Water\n TrigramStance.GAN, // Mountain\n TrigramStance.GON, // Earth\n ],\n description: {\n korean: \"공격과 방어의 조화를 추구하는 전략가\",\n english: \"Strategist seeking harmony between offense and defense\",\n },\n },\n\n /**\n * 방어의 달인 (Bangeo-ui Dallin) - Defensive Specialist\n * Counter-attack focused using Hacker archetype\n *\n * **Dynamic Stance Rotation (Issue #dynamic-ai-stance-rotation)**:\n * - Increased stance switch frequency: 0.4 → 0.6 (analytical adaptation)\n */\n DEFENSIVE_SPECIALIST: {\n name: \"Defensive Specialist\",\n koreanName: \"방어의 달인\",\n archetype: PlayerArchetype.HACKER,\n aggressionLevel: 0.55, // Enhanced: hackers exploit vulnerabilities aggressively\n defensePreference: 0.6, // Reduced: still defensive but not passive\n comboTendency: 0.5, // Increased for analytical attack chains\n stanceSwitchFrequency: 0.2, // Reduced: analytical fighters don't fidget\n feintChance: 0.45, // Increased: data-driven misdirection\n tacticalRetreatThreshold: 0.3, // Reduced: hackers are more confident\n favoredStances: [\n TrigramStance.GAN, // Mountain - Defensive mastery\n TrigramStance.GON, // Earth - Grounding\n TrigramStance.GAM, // Water - Adaptation\n ],\n description: {\n korean: \"방어에서 반격의 기회를 찾는 전문가\",\n english: \"Expert who finds counter-attack opportunities through defense\",\n },\n },\n\n /**\n * 혼돈의 전사 (Hondon-ui Jeonsa) - Chaos Warrior\n * Unpredictable fighter using Jojik Pokryeokbae archetype\n *\n * **Dynamic Stance Rotation (Issue #dynamic-ai-stance-rotation)**:\n * - Increased stance switch frequency: 0.8 → 0.95 (unpredictable chaos)\n */\n CHAOS_WARRIOR: {\n name: \"Chaos Warrior\",\n koreanName: \"혼돈의 전사\",\n archetype: PlayerArchetype.JOJIK_POKRYEOKBAE,\n aggressionLevel: 0.9, // Enhanced: brutal pragmatists attack relentlessly\n defensePreference: 0.2, // Reduced: dirty fighters don't retreat\n comboTendency: 0.7, // Increased for vicious combinations\n stanceSwitchFrequency: 0.3, // Reduced: chaos is in attacks, not stance dancing\n feintChance: 0.5,\n tacticalRetreatThreshold: 0.05, // Reduced: fights to the bitter end\n favoredStances: [\n TrigramStance.LI, // Fire - Unpredictable\n TrigramStance.SON, // Wind - Constant motion\n TrigramStance.JIN, // Thunder - Explosive\n TrigramStance.TAE, // Lake - Fluid\n ],\n description: {\n korean: \"예측 불가능한 패턴으로 상대를 혼란시키는 전사\",\n english: \"Warrior who confuses opponents with unpredictable patterns\",\n },\n },\n};\n\n/**\n * Get a random AI personality\n */\nexport function getRandomPersonality(): AIPersonality {\n const personalities = Object.values(AI_PERSONALITIES);\n return personalities[Math.floor(Math.random() * personalities.length)];\n}\n\n/**\n * Get personality by archetype\n */\nexport function getPersonalityByArchetype(\n archetype: PlayerArchetype,\n): AIPersonality {\n const personality = Object.values(AI_PERSONALITIES).find(\n (p) => p.archetype === archetype,\n );\n return personality ?? AI_PERSONALITIES.BALANCED_FIGHTER;\n}\n\n/**\n * Get personality by name key\n */\nexport function getPersonalityByName(name: string): AIPersonality {\n return AI_PERSONALITIES[name] ?? AI_PERSONALITIES.BALANCED_FIGHTER;\n}\n\n/**\n * List all available personalities\n */\nexport function getAllPersonalities(): readonly AIPersonality[] {\n return Object.values(AI_PERSONALITIES);\n}\n\n/**\n * Get archetype-specific behavior profile\n *\n * Retrieves the unique combat behavior configuration for a given archetype,\n * including movement patterns, optimal ranges, and tactical preferences.\n *\n * @param archetype - Player archetype to get behavior for\n * @returns Archetype behavior profile\n *\n * @korean 원형별 행동 프로필 가져오기\n */\nexport function getArchetypeBehavior(\n archetype: PlayerArchetype,\n): ArchetypeBehavior {\n return ARCHETYPE_BEHAVIORS[archetype];\n}\n\n/**\n * Check if archetype follows honor code\n *\n * Honor code affects retreat behavior - honor-bound archetypes like Musa\n * will not retreat above their health threshold.\n *\n * @param archetype - Player archetype to check\n * @returns True if archetype follows honor code\n *\n * @korean 명예 규범 확인\n */\nexport function followsHonorCode(archetype: PlayerArchetype): boolean {\n return ARCHETYPE_BEHAVIORS[archetype].honorCode;\n}\n\n/**\n * Get optimal combat range for archetype\n *\n * Returns the preferred distance in grid cells (1 cell = ~40px) where\n * the archetype is most effective in combat.\n *\n * @param archetype - Player archetype\n * @returns Optimal range in grid cells\n *\n * @korean 최적 전투 거리 가져오기\n */\nexport function getOptimalRange(archetype: PlayerArchetype): number {\n return ARCHETYPE_BEHAVIORS[archetype].optimalRange;\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"AIPersonality.js","names":[],"sources":["../../../src/systems/ai/AIPersonality.ts"],"sourcesContent":["/**\n * AI Personality System for Korean Martial Arts Combat\n * Defines behavioral archetypes that guide AI decision-making\n */\n\nimport { PlayerArchetype, TrigramStance } from \"@/types\";\n\n/**\n * Movement pattern types for archetype behavior\n *\n * @korean 이동 패턴 타입\n */\nexport type MovementPattern =\n | \"aggressive\"\n | \"evasive\"\n | \"analytical\"\n | \"unpredictable\";\n\n/**\n * Vital target priority for archetype-specific combat strategies\n *\n * @korean 급소 우선순위\n */\nexport type VitalTargetPriority =\n | \"health\"\n | \"pain\"\n | \"consciousness\"\n | \"balanced\";\n\n/**\n * Technique category types for archetype preferences\n *\n * @korean 기술 범주\n */\nexport type TechniqueCategory =\n | \"joint_manipulation\"\n | \"bone_strikes\"\n | \"nerve_strikes\"\n | \"silent_takedowns\"\n | \"anatomical_analysis\"\n | \"calculated_strikes\"\n | \"psychological_pressure\"\n | \"submission_induction\"\n | \"dirty_techniques\"\n | \"environmental_usage\";\n\n/**\n * Archetype-specific behavior profile\n *\n * Defines combat preferences, movement patterns, and tactical decision-making\n * unique to each of the 5 player archetypes.\n *\n * @korean 원형별 행동 프로필\n */\nexport interface ArchetypeBehavior {\n /** Preferred trigram stances for this archetype */\n readonly preferredStances: readonly TrigramStance[];\n /** Optimal combat range in grid cells (1 cell = ~40px) */\n readonly optimalRange: number;\n /** Health percentage threshold to trigger retreat behavior */\n readonly retreatThreshold: number;\n /** Technique categories this archetype favors */\n readonly techniqueSelectionBias: readonly TechniqueCategory[];\n /** Movement pattern characteristic of this archetype */\n readonly movementPattern: MovementPattern;\n /** Whether archetype follows honor code (affects retreat behavior) */\n readonly honorCode: boolean;\n /** Priority system for vital point targeting */\n readonly vitalTargetPriority: VitalTargetPriority;\n}\n\n/**\n * AI personality profile defining combat behavior\n */\nexport interface AIPersonality {\n readonly name: string;\n readonly koreanName: string;\n readonly archetype: PlayerArchetype;\n readonly aggressionLevel: number; // 0.0-1.0: How often AI attacks\n readonly defensePreference: number; // 0.0-1.0: Tendency to block/counter\n readonly comboTendency: number; // 0.0-1.0: Likelihood to continue combos\n readonly stanceSwitchFrequency: number; // 0.0-1.0: How often changes stance\n readonly feintChance: number; // 0.0-1.0: Probability of fake attacks\n readonly tacticalRetreatThreshold: number; // Health % to retreat\n readonly favoredStances: readonly TrigramStance[];\n readonly description: {\n readonly korean: string;\n readonly english: string;\n };\n}\n\n/**\n * Archetype-specific behavior profiles\n *\n * Maps each of the 5 player archetypes to their unique combat behaviors,\n * movement patterns, and tactical preferences based on Korean martial arts\n * traditions and game design philosophy.\n *\n * @korean 원형별 행동 프로필\n */\nexport const ARCHETYPE_BEHAVIORS: Record<PlayerArchetype, ArchetypeBehavior> = {\n [PlayerArchetype.MUSA]: {\n preferredStances: [\n TrigramStance.GEON,\n TrigramStance.JIN,\n TrigramStance.GAN,\n ], // Heaven, Thunder, Mountain\n optimalRange: 1, // Close quarters (1 cell = ~40px)\n retreatThreshold: 5, // Enhanced: fights to near-death (honor code)\n techniqueSelectionBias: [\"joint_manipulation\", \"bone_strikes\"],\n movementPattern: \"aggressive\",\n honorCode: true, // Never retreats above threshold\n vitalTargetPriority: \"balanced\",\n },\n [PlayerArchetype.AMSALJA]: {\n preferredStances: [TrigramStance.SON, TrigramStance.GAM], // Wind, Water\n optimalRange: 1, // Stealth melee (1 cell)\n retreatThreshold: 20, // Enhanced: tactical retreat, not cowardice\n techniqueSelectionBias: [\"nerve_strikes\", \"silent_takedowns\"],\n movementPattern: \"evasive\",\n honorCode: false,\n vitalTargetPriority: \"consciousness\",\n },\n [PlayerArchetype.HACKER]: {\n preferredStances: [TrigramStance.LI, TrigramStance.TAE], // Fire, Lake\n optimalRange: 3, // Mid-range analysis (3 cells = ~120px)\n retreatThreshold: 50,\n techniqueSelectionBias: [\"anatomical_analysis\", \"calculated_strikes\"],\n movementPattern: \"analytical\",\n honorCode: false,\n vitalTargetPriority: \"balanced\",\n },\n [PlayerArchetype.JEONGBO_YOWON]: {\n preferredStances: [TrigramStance.GAN, TrigramStance.GON], // Mountain, Earth\n optimalRange: 2, // Tactical mid-range (2 cells = ~80px)\n retreatThreshold: 40,\n techniqueSelectionBias: [\"psychological_pressure\", \"submission_induction\"],\n movementPattern: \"analytical\",\n honorCode: false,\n vitalTargetPriority: \"pain\",\n },\n [PlayerArchetype.JOJIK_POKRYEOKBAE]: {\n preferredStances: [TrigramStance.JIN, TrigramStance.GAM], // Thunder, Water (adaptable)\n optimalRange: 1, // Close brutal combat (1 cell)\n retreatThreshold: 70, // Retreats pragmatically\n techniqueSelectionBias: [\"dirty_techniques\", \"environmental_usage\"],\n movementPattern: \"unpredictable\",\n honorCode: false,\n vitalTargetPriority: \"health\",\n },\n};\n\n/**\n * Five AI personality archetypes inspired by Korean martial arts philosophy\n */\nexport const AI_PERSONALITIES: Record<string, AIPersonality> = {\n /**\n * 맹공자 (Maenggongja) - Fierce Attacker\n * Aggressive pressure fighter using Musa archetype\n *\n * **Enhanced Aggression (Issue #enhance-ai-aggression)**:\n * - Increased aggression: 0.85 → 0.95 (overwhelming force)\n * - Reduced defense: 0.2 → 0.1 (all-in offensive)\n * - Increased combo tendency: 0.7 → 0.8 (sustained pressure)\n * - Reduced retreat threshold: 0.15 → 0.05 (honor code: fights to near-death)\n *\n * **Dynamic Stance Rotation (Issue #dynamic-ai-stance-rotation)**:\n * - Increased stance switch frequency: 0.3 → 0.5 (more tactical flexibility)\n */\n AGGRESSIVE_STRIKER: {\n name: \"Aggressive Striker\",\n koreanName: \"맹공자\",\n archetype: PlayerArchetype.MUSA,\n aggressionLevel: 0.95, // Enhanced from 0.85\n defensePreference: 0.1, // Reduced from 0.2\n comboTendency: 0.8, // Increased from 0.7\n stanceSwitchFrequency: 0.15, // Reduced: Musa focuses on overwhelming force, not stance dancing\n feintChance: 0.15,\n tacticalRetreatThreshold: 0.05, // Reduced from 0.15\n favoredStances: [\n TrigramStance.GEON, // Heaven - Direct force\n TrigramStance.JIN, // Thunder - Explosive power\n TrigramStance.LI, // Fire - Precision strikes\n ],\n description: {\n korean: \"정면 돌파를 선호하는 공격적인 전사\",\n english: \"Aggressive warrior who prefers frontal assault\",\n },\n },\n\n /**\n * 기술가 (Gisulga) - Technical Master\n * Precision fighter using Amsalja archetype\n *\n * **Enhanced Aggression (Issue #enhance-ai-aggression)**:\n * - Increased aggression: 0.5 → 0.85 (instant takedown focus)\n * - Reduced defense: 0.6 → 0.3 (opportunistic aggression)\n * - Increased combo tendency: 0.4 → 0.6 (lethal sequences)\n * - Reduced retreat threshold: 0.35 → 0.20 (tactical retreat, not cowardice)\n *\n * **Dynamic Stance Rotation (Issue #dynamic-ai-stance-rotation)**:\n * - Increased stance switch frequency: 0.7 → 0.85 (highly adaptive)\n */\n TECHNICAL_MASTER: {\n name: \"Technical Master\",\n koreanName: \"기술가\",\n archetype: PlayerArchetype.AMSALJA,\n aggressionLevel: 0.95, // Enhanced for fatal precision strikes\n defensePreference: 0.2, // Reduced: assassins attack, don't defend\n comboTendency: 0.7, // Increased for lethal combinations\n stanceSwitchFrequency: 0.2, // Reduced: precision strikers commit to their stance\n feintChance: 0.35,\n tacticalRetreatThreshold: 0.15, // Reduced: assassins press the attack\n favoredStances: [\n TrigramStance.SON, // Wind - Continuous pressure\n TrigramStance.GAM, // Water - Flow and adaptation\n TrigramStance.TAE, // Lake - Fluid manipulation\n ],\n description: {\n korean: \"정밀한 기술로 약점을 노리는 달인\",\n english: \"Master who targets weaknesses with precise techniques\",\n },\n },\n\n /**\n * 균형 잡힌 자 (Gyunhyeong Jabin-ja) - Balanced Fighter\n * All-around fighter using Jeongbo Yowon archetype\n *\n * **Dynamic Stance Rotation (Issue #dynamic-ai-stance-rotation)**:\n * - Increased stance switch frequency: 0.5 → 0.7 (strategic switching)\n */\n BALANCED_FIGHTER: {\n name: \"Balanced Fighter\",\n koreanName: \"균형 잡힌 자\",\n archetype: PlayerArchetype.JEONGBO_YOWON,\n aggressionLevel: 0.75, // Enhanced: intelligence operatives are decisive\n defensePreference: 0.4, // Slightly reduced for more aggression\n comboTendency: 0.6, // Increased for calculated sequences\n stanceSwitchFrequency: 0.25, // Reduced: strategic, not reactive\n feintChance: 0.3, // Increased: psychological warfare\n tacticalRetreatThreshold: 0.2, // Slightly reduced\n favoredStances: [\n TrigramStance.GEON, // Heaven\n TrigramStance.GAM, // Water\n TrigramStance.GAN, // Mountain\n TrigramStance.GON, // Earth\n ],\n description: {\n korean: \"공격과 방어의 조화를 추구하는 전략가\",\n english: \"Strategist seeking harmony between offense and defense\",\n },\n },\n\n /**\n * 방어의 달인 (Bangeo-ui Dallin) - Defensive Specialist\n * Counter-attack focused using Hacker archetype\n *\n * **Dynamic Stance Rotation (Issue #dynamic-ai-stance-rotation)**:\n * - Increased stance switch frequency: 0.4 → 0.6 (analytical adaptation)\n */\n DEFENSIVE_SPECIALIST: {\n name: \"Defensive Specialist\",\n koreanName: \"방어의 달인\",\n archetype: PlayerArchetype.HACKER,\n aggressionLevel: 0.55, // Enhanced: hackers exploit vulnerabilities aggressively\n defensePreference: 0.6, // Reduced: still defensive but not passive\n comboTendency: 0.5, // Increased for analytical attack chains\n stanceSwitchFrequency: 0.2, // Reduced: analytical fighters don't fidget\n feintChance: 0.45, // Increased: data-driven misdirection\n tacticalRetreatThreshold: 0.3, // Reduced: hackers are more confident\n favoredStances: [\n TrigramStance.GAN, // Mountain - Defensive mastery\n TrigramStance.GON, // Earth - Grounding\n TrigramStance.GAM, // Water - Adaptation\n ],\n description: {\n korean: \"방어에서 반격의 기회를 찾는 전문가\",\n english: \"Expert who finds counter-attack opportunities through defense\",\n },\n },\n\n /**\n * 혼돈의 전사 (Hondon-ui Jeonsa) - Chaos Warrior\n * Unpredictable fighter using Jojik Pokryeokbae archetype\n *\n * **Dynamic Stance Rotation (Issue #dynamic-ai-stance-rotation)**:\n * - Increased stance switch frequency: 0.8 → 0.95 (unpredictable chaos)\n */\n CHAOS_WARRIOR: {\n name: \"Chaos Warrior\",\n koreanName: \"혼돈의 전사\",\n archetype: PlayerArchetype.JOJIK_POKRYEOKBAE,\n aggressionLevel: 0.9, // Enhanced: brutal pragmatists attack relentlessly\n defensePreference: 0.2, // Reduced: dirty fighters don't retreat\n comboTendency: 0.7, // Increased for vicious combinations\n stanceSwitchFrequency: 0.3, // Reduced: chaos is in attacks, not stance dancing\n feintChance: 0.5,\n tacticalRetreatThreshold: 0.05, // Reduced: fights to the bitter end\n favoredStances: [\n TrigramStance.LI, // Fire - Unpredictable\n TrigramStance.SON, // Wind - Constant motion\n TrigramStance.JIN, // Thunder - Explosive\n TrigramStance.TAE, // Lake - Fluid\n ],\n description: {\n korean: \"예측 불가능한 패턴으로 상대를 혼란시키는 전사\",\n english: \"Warrior who confuses opponents with unpredictable patterns\",\n },\n },\n};\n\n/**\n * Get a random AI personality\n */\nexport function getRandomPersonality(): AIPersonality {\n const personalities = Object.values(AI_PERSONALITIES);\n return personalities[Math.floor(Math.random() * personalities.length)];\n}\n\n/**\n * Get personality by archetype\n */\nexport function getPersonalityByArchetype(\n archetype: PlayerArchetype,\n): AIPersonality {\n const personality = Object.values(AI_PERSONALITIES).find(\n (p) => p.archetype === archetype,\n );\n return personality ?? AI_PERSONALITIES.BALANCED_FIGHTER;\n}\n\n/**\n * Get personality by name key\n */\nexport function getPersonalityByName(name: string): AIPersonality {\n return AI_PERSONALITIES[name] ?? AI_PERSONALITIES.BALANCED_FIGHTER;\n}\n\n/**\n * List all available personalities\n */\nexport function getAllPersonalities(): readonly AIPersonality[] {\n return Object.values(AI_PERSONALITIES);\n}\n\n/**\n * Get archetype-specific behavior profile\n *\n * Retrieves the unique combat behavior configuration for a given archetype,\n * including movement patterns, optimal ranges, and tactical preferences.\n *\n * @param archetype - Player archetype to get behavior for\n * @returns Archetype behavior profile\n *\n * @korean 원형별 행동 프로필 가져오기\n */\nexport function getArchetypeBehavior(\n archetype: PlayerArchetype,\n): ArchetypeBehavior {\n return ARCHETYPE_BEHAVIORS[archetype];\n}\n\n/**\n * Check if archetype follows honor code\n *\n * Honor code affects retreat behavior - honor-bound archetypes like Musa\n * will not retreat above their health threshold.\n *\n * @param archetype - Player archetype to check\n * @returns True if archetype follows honor code\n *\n * @korean 명예 규범 확인\n */\nexport function followsHonorCode(archetype: PlayerArchetype): boolean {\n return ARCHETYPE_BEHAVIORS[archetype].honorCode;\n}\n\n/**\n * Get optimal combat range for archetype\n *\n * Returns the preferred distance in grid cells (1 cell = ~40px) where\n * the archetype is most effective in combat.\n *\n * @param archetype - Player archetype\n * @returns Optimal range in grid cells\n *\n * @korean 최적 전투 거리 가져오기\n */\nexport function getOptimalRange(archetype: PlayerArchetype): number {\n return ARCHETYPE_BEHAVIORS[archetype].optimalRange;\n}\n"],"mappings":"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"}
|
|
@@ -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":"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"}
|
|
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 +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":";;;;;;;;;;;;;;;;;;AA+DA,IAAa,6BAGT;EACD,gBAAgB,OAAO,CACtB;EACE,WAAW,gBAAgB;EAC3B,cAAc,CAAC,uBAAuB,mBAAmB;EACzD,MAAM;GACJ,QAAQ;GACR,SAAS;GACV;EACD,aAAa;GACX,QAAQ;GACR,SACE;GACH;EACF,EACD;EACE,WAAW,gBAAgB;EAC3B,cAAc,CAAC,qBAAqB,wBAAwB;EAC5D,MAAM;GACJ,QAAQ;GACR,SAAS;GACV;EACD,aAAa;GACX,QAAQ;GACR,SAAS;GACV;EACF,CACF;EAEA,gBAAgB,UAAU,CACzB;EACE,WAAW,gBAAgB;EAC3B,cAAc,CAAC,yBAAyB,uBAAuB;EAC/D,MAAM;GACJ,QAAQ;GACR,SAAS;GACV;EACD,aAAa;GACX,QAAQ;GACR,SAAS;GACV;EACF,EACD;EACE,WAAW,gBAAgB;EAC3B,cAAc,CAAC,wBAAwB,2BAA2B;EAClE,MAAM;GACJ,QAAQ;GACR,SAAS;GACV;EACD,aAAa;GACX,QAAQ;GACR,SACE;GACH;EACF,CACF;EAEA,gBAAgB,SAAS,CACxB;EACE,WAAW,gBAAgB;EAC3B,cAAc,CAAC,sBAAsB,yBAAyB;EAC9D,MAAM;GACJ,QAAQ;GACR,SAAS;GACV;EACD,aAAa;GACX,QACE;GACF,SAAS;GACV;EACF,EACD;EACE,WAAW,gBAAgB;EAC3B,cAAc,CAAC,yBAAyB,sBAAsB;EAC9D,MAAM;GACJ,QAAQ;GACR,SAAS;GACV;EACD,aAAa;GACX,QAAQ;GACR,SAAS;GACV;EACF,CACF;EAEA,gBAAgB,gBAAgB,CAC/B;EACE,WAAW,gBAAgB;EAC3B,cAAc,CACZ,2BACA,gCACD;EACD,MAAM;GACJ,QAAQ;GACR,SAAS;GACV;EACD,aAAa;GACX,QAAQ;GACR,SACE;GACH;EACF,EACD;EACE,WAAW,gBAAgB;EAC3B,cAAc,CACZ,gCACA,8BACD;EACD,MAAM;GACJ,QAAQ;GACR,SAAS;GACV;EACD,aAAa;GACX,QAAQ;GACR,SACE;GACH;EACF,CACF;EAEA,gBAAgB,oBAAoB,CACnC;EACE,WAAW,gBAAgB;EAC3B,cAAc,CAAC,sBAAsB,wBAAwB;EAC7D,MAAM;GACJ,QAAQ;GACR,SAAS;GACV;EACD,aAAa;GACX,QAAQ;GACR,SACE;GACH;EACF,EACD;EACE,WAAW,gBAAgB;EAC3B,cAAc,CAAC,2BAA2B,yBAAyB;EACnE,MAAM;GACJ,QAAQ;GACR,SAAS;GACV;EACD,aAAa;GACX,QAAQ;GACR,SACE;GACH;EACF,CACF;CACF;;;;;;;;;;;;;AAcD,SAAgB,sBACd,oBACA,WACoB;CACpB,MAAM,SAAS,2BAA2B;CAE1C,IAAI,CAAC,QACH;CAGF,KAAK,MAAM,SAAS,QAAQ;EAC1B,MAAM,eAAe,MAAM,aAAa,QAAQ,mBAAmB;EAGnE,IAAI,iBAAiB,MAAM,eAAe,MAAM,aAAa,SAAS,GAEpE,OAAO,MAAM,aAAa,eAAe;;;;;;AAU/C,IAAa,gBAAb,MAA2B;CACzB;CACA,eAA0C,EAAE;CAC5C,gBAAwB;CACxB,gBAAwB;CACxB,eAAgC;CAEhC,cAAc;EACZ,KAAK,iBAAiB,KAAK,0BAA0B;;;;;CAMvD,2BAAwE;EACtE,MAAM,4BAAY,IAAI,KAAqC;EAG3D,UAAU,IAAI,cAAc,MAAM,CAChC;GACE,UAAU,cAAc;GACxB,YAAY,KAAK,qBAAqB,cAAc,MAAM,EAAE;GAC5D,aAAa;GACb,aAAa;GACb,YAAY;GACZ,iBAAiB;GACjB,MAAM;IACJ,QAAQ;IACR,SAAS;IACV;GACF,CACF,CAAC;EAGF,UAAU,IAAI,cAAc,KAAK,CAC/B;GACE,UAAU,cAAc;GACxB,YAAY,KAAK,qBAAqB,cAAc,KAAK,EAAE;GAC3D,aAAa;GACb,aAAa;GACb,YAAY;GACZ,iBAAiB;GACjB,MAAM;IACJ,QAAQ;IACR,SAAS;IACV;GACF,CACF,CAAC;EAGF,UAAU,IAAI,cAAc,IAAI,CAC9B;GACE,UAAU,cAAc;GACxB,YAAY,KAAK,qBAAqB,cAAc,IAAI,EAAE;GAC1D,aAAa;GACb,aAAa;GACb,YAAY;GACZ,iBAAiB;GACjB,MAAM;IACJ,QAAQ;IACR,SAAS;IACV;GACF,CACF,CAAC;EAGF,UAAU,IAAI,cAAc,KAAK,CAC/B;GACE,UAAU,cAAc;GACxB,YAAY,KAAK,qBAAqB,cAAc,KAAK,EAAE;GAC3D,aAAa;GACb,aAAa;GACb,YAAY;GACZ,iBAAiB;GACjB,MAAM;IACJ,QAAQ;IACR,SAAS;IACV;GACF,CACF,CAAC;EAGF,UAAU,IAAI,cAAc,KAAK,CAC/B;GACE,UAAU,cAAc;GACxB,YAAY,KAAK,qBAAqB,cAAc,KAAK,EAAE;GAC3D,aAAa;GACb,aAAa;GACb,YAAY;GACZ,iBAAiB;GACjB,MAAM;IACJ,QAAQ;IACR,SAAS;IACV;GACF,CACF,CAAC;EAGF,UAAU,IAAI,cAAc,KAAK,CAC/B;GACE,UAAU,cAAc;GACxB,YAAY,KAAK,qBAAqB,cAAc,KAAK,EAAE;GAC3D,aAAa;GACb,aAAa;GACb,YAAY;GACZ,iBAAiB;GACjB,MAAM;IACJ,QAAQ;IACR,SAAS;IACV;GACF,CACF,CAAC;EAGF,UAAU,IAAI,cAAc,KAAK,CAC/B;GACE,UAAU,cAAc;GACxB,YAAY,KAAK,qBAAqB,cAAc,KAAK,EAAE;GAC3D,aAAa;GACb,aAAa;GACb,YAAY;GACZ,iBAAiB;GACjB,MAAM;IACJ,QAAQ;IACR,SAAS;IACV;GACF,CACF,CAAC;EAGF,UAAU,IAAI,cAAc,KAAK,CAC/B;GACE,UAAU,cAAc;GACxB,YAAY,KAAK,qBAAqB,cAAc,KAAK,EAAE;GAC3D,aAAa;GACb,aAAa;GACb,YAAY;GACZ,iBAAiB;GACjB,MAAM;IACJ,QAAQ;IACR,SAAS;IACV;GACF,CACF,CAAC;EAEF,OAAO;;;;;CAMT,qBACE,QACA,OAC4B;EAC5B,MAAM,aAAa,mBAAmB,WAAW,EAAE;EAGnD,IAAI,WAAW,SAAS,OACtB,QAAQ,KACN,6DAA6D,OAAO,eAAe,MAAM,aAAa,WAAW,OAAO,sCACzH;EAIH,OAAO,WAAW,MAAM,GAAG,MAAM;;;;;CAMnC,YAAoB,QAAqB,UAA+B;EACtE,MAAM,KAAK,OAAO,SAAS,IAAI,SAAS,SAAS;EACjD,MAAM,KAAK,OAAO,SAAS,IAAI,SAAS,SAAS;EACjD,OAAO,KAAK,KAAK,KAAK,KAAK,KAAK,GAAG;;;;;;;;;;;;CAarC,oBACE,QACA,UACA,aACS;EACT,MAAM,MAAM,KAAK,KAAK;EAGtB,IAAI,KAAK,aAAa,WAAW,GAC/B,OAAO;EAIT,IAAI,MAAM,KAAK,gBAAgB,KAAK,cAAc;GAChD,KAAK,YAAY;GACjB,OAAO;;EAIT,IAAI,KAAK,iBAAiB,KAAK,aAAa,QAAQ;GAClD,KAAK,YAAY;GACjB,OAAO;;EAIT,IAAI,KAAK,iBAAiB,GAAG;GAC3B,KAAK,YAAY;GACjB,OAAO;;EAKT,MAAM,aADW,KAAK,YAAY,QAAQ,SACvB,GAAW;EAG9B,MAAM,eAAe,OAAO,MAAM,MAAM,OAAO,UAAU;EAKzD,MAAM,+BAA+B;EAIrC,MAAM,YAAY,SAAS,aAAa;EACxC,MAAM,eACH,SAAS,UAAU,MAAM,SAAS,aAAa,OAAO;EAIzD,MAAM,sBAFH,SAAS,WAAW,OAAO,gCAEa,aAAa;EAGxD,MAAM,aAAa,YAAY;EAE/B,MAAM,sBAAsB,KAAK,IAAI,KAAM,cADhB,qBAAqB,KAAM,GACqB;EAG3E,OAAO,cAAc,gBAFA,KAAK,QAAQ,GAAG;;;;;CAQvC,WACE,QACA,UACA,aACS;EAET,MAAM,WAAW,KAAK,YAAY,QAAQ,SAAS;EACnD,MAAM,YAAY,KAAK,eAAe,IAAI,OAAO,cAAc;EAE/D,IAAI,CAAC,aAAa,UAAU,WAAW,GACrC,OAAO;EAIT,MAAM,gBAAgB,UAAU,MAC7B,QACC,YAAY,IAAI,eAChB,YAAY,IAAI,eAChB,OAAO,MAAM,IAAI,cACjB,OAAO,WAAW,IAAI,gBACzB;EAED,IAAI,CAAC,eACH,OAAO;EAIT,IAAI,KAAK,QAAQ,GAAG,YAAY,eAC9B,OAAO;EAIT,KAAK,eAAe,CAAC,GAAG,cAAc,WAAW;EACjD,KAAK,gBAAgB;EACrB,KAAK,gBAAgB,KAAK,KAAK;EAC/B,OAAO;;;;;CAMT,wBAAgD;EAC9C,IACE,KAAK,iBAAiB,KAAK,aAAa,UACxC,KAAK,aAAa,WAAW,GAE7B,OAAO;EAGT,MAAM,YAAY,KAAK,aAAa,KAAK;EACzC,KAAK;EACL,KAAK,gBAAgB,KAAK,KAAK;EAC/B,OAAO;;;;;CAMT,aAAmB;EACjB,KAAK,eAAe,EAAE;EACtB,KAAK,gBAAgB;EACrB,KAAK,gBAAgB;;;;;CAMvB,gBAAyB;EACvB,OACE,KAAK,aAAa,SAAS,KAC3B,KAAK,iBAAiB,KACtB,KAAK,gBAAgB,KAAK,aAAa,UACvC,KAAK,KAAK,GAAG,KAAK,gBAAgB,KAAK;;;;;CAO3C,eAKE;EACA,OAAO;GACL,QAAQ,KAAK,eAAe;GAC5B,UAAU,KAAK;GACf,OAAO,KAAK,aAAa;GACzB,YACE,KAAK,aAAa,SAAS,IACtB,KAAK,gBAAgB,KAAK,aAAa,SAAU,MAClD;GACP;;;;;CAMH,mBAAmB,QAAiD;EAClE,OAAO,KAAK,eAAe,IAAI,OAAO,IAAI,EAAE"}
|