blacktrigram 0.7.39 → 0.7.41
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/App2.js.map +1 -1
- package/lib/audio/AudioAssetLoader.js.map +1 -1
- package/lib/audio/AudioAssetRegistry.js.map +1 -1
- package/lib/audio/AudioCache.js.map +1 -1
- package/lib/audio/AudioManager.js.map +1 -1
- package/lib/audio/AudioMonitor.js.map +1 -1
- package/lib/audio/AudioPool.js.map +1 -1
- package/lib/audio/AudioProvider.js.map +1 -1
- package/lib/audio/AudioUtils.js.map +1 -1
- package/lib/audio/BoneImpactAudioMap.js.map +1 -1
- package/lib/audio/VariantSelector.js.map +1 -1
- package/lib/audio/types.js.map +1 -1
- package/lib/components/screens/combat/CombatScreen3D.js.map +1 -1
- package/lib/components/screens/combat/components/controls/CombatButtons.js.map +1 -1
- package/lib/components/screens/combat/components/controls/CombatControlsPanel.js.map +1 -1
- package/lib/components/screens/combat/components/controls/ControlsGuide.js.map +1 -1
- package/lib/components/screens/combat/components/controls/KeyboardHints.js.map +1 -1
- package/lib/components/screens/combat/components/controls/PauseMenu.js.map +1 -1
- package/lib/components/screens/combat/components/controls/PauseMenuButton.js.map +1 -1
- package/lib/components/screens/combat/components/controls/QuickSettings.js.map +1 -1
- package/lib/components/screens/combat/components/effects/BloodDecals3D.js.map +1 -1
- package/lib/components/screens/combat/components/effects/BloodLossOverlayHtml.js.map +1 -1
- package/lib/components/screens/combat/components/effects/BloodParticles3D.js.map +1 -1
- package/lib/components/screens/combat/components/effects/BloodViscosity3D.js.map +1 -1
- package/lib/components/screens/combat/components/effects/CombatParticleEffects3D.js.map +1 -1
- package/lib/components/screens/combat/components/effects/ConsciousnessBlur.js.map +1 -1
- package/lib/components/screens/combat/components/effects/InternalDamage3D.js.map +1 -1
- package/lib/components/screens/combat/components/effects/PainVignette.js.map +1 -1
- package/lib/components/screens/combat/components/effects/ParticleAudio3D.js.map +1 -1
- package/lib/components/screens/combat/components/effects/TraumaOverlay3D.js.map +1 -1
- package/lib/components/screens/combat/components/feedback/MatchCountdown.js.map +1 -1
- package/lib/components/screens/combat/components/feedback/RoundAnnouncementOverlayHtml.js.map +1 -1
- package/lib/components/screens/combat/components/feedback/RoundDisplayStatus.js.map +1 -1
- package/lib/components/screens/combat/components/feedback/RoundStartAnnouncementOverlayHtml.js.map +1 -1
- package/lib/components/screens/combat/components/hud/CombatBottomHUD.js.map +1 -1
- package/lib/components/screens/combat/components/hud/CombatLeftHUD.js.map +1 -1
- package/lib/components/screens/combat/components/hud/CombatPortraitStatusStrip.js.map +1 -1
- package/lib/components/screens/combat/components/hud/CombatRightHUD.js.map +1 -1
- package/lib/components/screens/combat/components/hud/CombatTopHUD.js.map +1 -1
- package/lib/components/screens/combat/components/hud/DifficultyIndicator.js.map +1 -1
- package/lib/components/screens/combat/components/hud/FPSMonitor.js.map +1 -1
- package/lib/components/screens/combat/components/hud/MobileControlsWrapper.js.map +1 -1
- package/lib/components/screens/combat/components/hud/PlayerStateOverlayHtml.js.map +1 -1
- package/lib/components/screens/combat/components/indicators/BalanceIndicator.js.map +1 -1
- package/lib/components/screens/combat/components/indicators/InputBufferDisplay.js.map +1 -1
- package/lib/components/screens/combat/components/indicators/StaminaWarning.js.map +1 -1
- package/lib/components/screens/combat/components/indicators/TechniqueNameDisplay.js.map +1 -1
- package/lib/components/screens/combat/helpers/AnimationUpdater.js.map +1 -1
- package/lib/components/screens/combat/helpers/combatHelpers.js.map +1 -1
- package/lib/components/screens/combat/hooks/useAICombat.js.map +1 -1
- package/lib/components/screens/combat/hooks/useCombatActions.js.map +1 -1
- package/lib/components/screens/combat/hooks/useCombatAttackMovement.js.map +1 -1
- package/lib/components/screens/combat/hooks/useCombatAudio.js.map +1 -1
- package/lib/components/screens/combat/hooks/useCombatLayout.js.map +1 -1
- package/lib/components/screens/combat/hooks/useCombatState.js.map +1 -1
- package/lib/components/screens/controls/ControlsScreen3D.js.map +1 -1
- package/lib/components/screens/controls/components/ControlBindingsOverlayHtml.js.map +1 -1
- package/lib/components/screens/controls/components/ControlCategoryTabsOverlayHtml.js.map +1 -1
- package/lib/components/screens/controls/components/GamepadVisualization3D.js.map +1 -1
- package/lib/components/screens/controls/components/InteractiveControlDemoOverlayHtml.js.map +1 -1
- package/lib/components/screens/controls/components/Key3D.js.map +1 -1
- package/lib/components/screens/controls/components/VisualKeyboard3D.js.map +1 -1
- package/lib/components/screens/controls/constants/ControlsConstants.js.map +1 -1
- package/lib/components/screens/controls/hooks/useControlsState.js.map +1 -1
- package/lib/components/screens/endscreen/EndScreen3D.js.map +1 -1
- package/lib/components/screens/endscreen/components/DefeatAnimation3D.js.map +1 -1
- package/lib/components/screens/endscreen/components/MatchStatisticsDisplayOverlayHtml.js.map +1 -1
- package/lib/components/screens/endscreen/components/NavigationButtonsOverlayHtml.js.map +1 -1
- package/lib/components/screens/endscreen/components/PerformanceBreakdownOverlayHtml.js.map +1 -1
- package/lib/components/screens/endscreen/components/PerformanceRatingOverlayHtml.js.map +1 -1
- package/lib/components/screens/endscreen/components/VictoryAnimation3D.js.map +1 -1
- package/lib/components/screens/endscreen/components/WinnerDisplayOverlayHtml.js.map +1 -1
- package/lib/components/screens/intro/IntroScreen3D.js +1 -1
- package/lib/components/screens/intro/IntroScreen3D.js.map +1 -1
- package/lib/components/screens/intro/components/AbilityListOverlayHtml.js.map +1 -1
- package/lib/components/screens/intro/components/ArchetypeCardGridOverlayHtml.js.map +1 -1
- package/lib/components/screens/intro/components/ArchetypeCardOverlayHtml.js.map +1 -1
- package/lib/components/screens/intro/components/ArchetypeDisplayOverlayHtml.js.map +1 -1
- package/lib/components/screens/intro/components/EnhancedArchetypeDisplayOverlayHtml.js.map +1 -1
- package/lib/components/screens/intro/components/MenuButtonsOverlayHtml.js.map +1 -1
- package/lib/components/screens/intro/components/MenuSectionOverlayHtml.js.map +1 -1
- package/lib/components/screens/intro/components/StatBarOverlayHtml.js.map +1 -1
- package/lib/components/screens/philosophy/PhilosophyScreen3D.js.map +1 -1
- package/lib/components/screens/training/TrainingScreen3D.js.map +1 -1
- package/lib/components/screens/training/components/AnatomyControlsOverlayHtml.js.map +1 -1
- package/lib/components/screens/training/components/AnatomyOverlay3D.js.map +1 -1
- package/lib/components/screens/training/components/FootPlacementMarkers3D.js.map +1 -1
- package/lib/components/screens/training/components/FootworkDrillsOverlayHtml.js.map +1 -1
- package/lib/components/screens/training/components/HitFeedbackEffect3D.js.map +1 -1
- package/lib/components/screens/training/components/TrainingButtonsOverlayHtml.js.map +1 -1
- package/lib/components/screens/training/components/TrainingControlsOverlayHtml.js.map +1 -1
- package/lib/components/screens/training/components/TrainingDummy3D.js.map +1 -1
- package/lib/components/screens/training/components/TrainingFeedbackOverlayHtml.js.map +1 -1
- package/lib/components/screens/training/components/TrainingModeSelectorOverlayHtml.js.map +1 -1
- package/lib/components/screens/training/components/TrainingStatsOverlayHtml.js.map +1 -1
- package/lib/components/screens/training/components/VitalPointMarker3D.js.map +1 -1
- package/lib/components/screens/training/components/VitalPointTrainingOverlayHtml.js.map +1 -1
- package/lib/components/screens/training/components/hud/TrainingBottomHUD.js.map +1 -1
- package/lib/components/screens/training/components/hud/TrainingLeftHUD.js.map +1 -1
- package/lib/components/screens/training/components/hud/TrainingRightHUD.js.map +1 -1
- package/lib/components/screens/training/components/hud/TrainingTopHUD.js.map +1 -1
- package/lib/components/screens/training/hooks/useAttackMovement.js.map +1 -1
- package/lib/components/screens/training/hooks/useTrainingActions.js.map +1 -1
- package/lib/components/screens/training/hooks/useTrainingLayout.js.map +1 -1
- package/lib/components/screens/training/hooks/useTrainingState.js.map +1 -1
- package/lib/components/shared/base/BaseButton.js.map +1 -1
- package/lib/components/shared/base/BaseButtonOverlayHtml.js.map +1 -1
- package/lib/components/shared/base/BasePanel.js.map +1 -1
- package/lib/components/shared/base/BaseText.js.map +1 -1
- package/lib/components/shared/base/useKoreanTheme.js.map +1 -1
- package/lib/components/shared/debug/PerformanceDebugOverlayHtml.js.map +1 -1
- package/lib/components/shared/mobile/ActionButtons.js.map +1 -1
- package/lib/components/shared/mobile/GestureRecognizerPure.js.map +1 -1
- package/lib/components/shared/mobile/HapticController.js.map +1 -1
- package/lib/components/shared/mobile/MobileControlsPure.js.map +1 -1
- package/lib/components/shared/mobile/StanceWheelPure.js.map +1 -1
- package/lib/components/shared/mobile/TouchOptimizer.js.map +1 -1
- package/lib/components/shared/mobile/VirtualDPad.js.map +1 -1
- package/lib/components/shared/three/anatomy/BodySurface.js.map +1 -1
- package/lib/components/shared/three/anatomy/BoneAttachedMuscles.js.map +1 -1
- package/lib/components/shared/three/anatomy/BoneClothing.js.map +1 -1
- package/lib/components/shared/three/anatomy/BoneRenderer.js.map +1 -1
- package/lib/components/shared/three/anatomy/Face3D.js.map +1 -1
- package/lib/components/shared/three/anatomy/Foot3D.js.map +1 -1
- package/lib/components/shared/three/anatomy/Hand3D.js.map +1 -1
- package/lib/components/shared/three/effects/ActionFeedback.js.map +1 -1
- package/lib/components/shared/three/effects/DamageNumbers.js.map +1 -1
- package/lib/components/shared/three/effects/HitEffects3D.js.map +1 -1
- package/lib/components/shared/three/effects/PlayerStateIndicators.js.map +1 -1
- package/lib/components/shared/three/effects/StanceSymbol3D.js.map +1 -1
- package/lib/components/shared/three/effects/StanceTransitionEffect.js.map +1 -1
- package/lib/components/shared/three/effects/VitalPointMarkers3D.js.map +1 -1
- package/lib/components/shared/three/indicators/ElementalColorSystem.js.map +1 -1
- package/lib/components/shared/three/indicators/GuardIndicator.js.map +1 -1
- package/lib/components/shared/three/indicators/HapticFeedback.js.map +1 -1
- package/lib/components/shared/three/indicators/StanceChangeIndicator.js.map +1 -1
- package/lib/components/shared/three/models/Player3DWithTransitions.js.map +1 -1
- package/lib/components/shared/three/models/SkeletalPlayer3D.js.map +1 -1
- package/lib/components/shared/three/optimization/AdaptiveQuality.js.map +1 -1
- package/lib/components/shared/three/scene/AtmosphericParticles3D.js.map +1 -1
- package/lib/components/shared/three/scene/BackgroundScene3D.js.map +1 -1
- package/lib/components/shared/three/scene/CombatArena3D.js.map +1 -1
- package/lib/components/shared/three/scene/KoreanSignage3D.js.map +1 -1
- package/lib/components/shared/three/ui/ArchetypeCard.js.map +1 -1
- package/lib/components/shared/three/ui/BodyPartHealthDisplay.js.map +1 -1
- package/lib/components/shared/three/ui/BreathingIndicator2.js.map +1 -1
- package/lib/components/shared/three/ui/CombatReadinessBar.js.map +1 -1
- package/lib/components/shared/three/ui/ComboCounter.js.map +1 -1
- package/lib/components/shared/three/ui/HealthBar.js.map +1 -1
- package/lib/components/shared/three/ui/KoreanButton.js.map +1 -1
- package/lib/components/shared/three/ui/KoreanPanel.js.map +1 -1
- package/lib/components/shared/three/ui/KoreanText.js.map +1 -1
- package/lib/components/shared/three/ui/MenuList.js.map +1 -1
- package/lib/components/shared/three/ui/PlayerHUD.js.map +1 -1
- package/lib/components/shared/three/ui/ProgressBar.js.map +1 -1
- package/lib/components/shared/three/ui/SpeedIndicatorHUD.js.map +1 -1
- package/lib/components/shared/three/ui/StaminaBar.js.map +1 -1
- package/lib/components/shared/three/ui/TechniqueBar.js.map +1 -1
- package/lib/components/shared/three/ui/TechniqueCard.js.map +1 -1
- package/lib/components/shared/three/ui/VitalPointOverlayControlsHtml.js.map +1 -1
- package/lib/components/shared/ui/BackButton.js.map +1 -1
- package/lib/components/shared/ui/BaseHUDContainer.js.map +1 -1
- package/lib/components/shared/ui/CombatTimer.js.map +1 -1
- package/lib/components/shared/ui/ErrorModal.js.map +1 -1
- package/lib/components/shared/ui/LoadingState.js.map +1 -1
- package/lib/components/shared/ui/SplashScreen.js +2 -2
- package/lib/components/shared/ui/SplashScreen.js.map +1 -1
- package/lib/components/shared/ui/VitalPointOverlayControlsPure.js.map +1 -1
- package/lib/components/shared/ui/VolumeControl.js.map +1 -1
- package/lib/components/shared/ui/shared/ConfirmDialog.js.map +1 -1
- package/lib/components/ui/combat/BalanceIndicatorOverlayHtml.js.map +1 -1
- package/lib/constants/bodyDimensions.js.map +1 -1
- package/lib/constants/bodyRenderingConstants.js.map +1 -1
- package/lib/data/archetypeClothing.js.map +1 -1
- package/lib/data/archetypePhysicalAttributes.js.map +1 -1
- package/lib/data/techniqueMappings.js.map +1 -1
- package/lib/data/techniques.js.map +1 -1
- package/lib/hooks/useActionFeedback.js.map +1 -1
- package/lib/hooks/useBalanceAnimations.js.map +1 -1
- package/lib/hooks/useCombatTimer.js.map +1 -1
- package/lib/hooks/useDebounce.js.map +1 -1
- package/lib/hooks/useHUDLayout.js.map +1 -1
- package/lib/hooks/useHandPoseTransitions.js.map +1 -1
- package/lib/hooks/useKeyboardControls.js.map +1 -1
- package/lib/hooks/useMatchCountdown.js.map +1 -1
- package/lib/hooks/useMuscleActivation.js.map +1 -1
- package/lib/hooks/usePauseMenu.js.map +1 -1
- package/lib/hooks/usePlayerAnimation.js.map +1 -1
- package/lib/hooks/useResponsiveLayout.js.map +1 -1
- package/lib/hooks/useRoundTransition.js.map +1 -1
- package/lib/hooks/useSkeletalAnimation.js.map +1 -1
- package/lib/hooks/useTechniqueSelection.js.map +1 -1
- package/lib/hooks/useThrottle.js.map +1 -1
- package/lib/hooks/useTouchControls.js.map +1 -1
- package/lib/hooks/useWebGLContextLossHandler.js.map +1 -1
- package/lib/hooks/useWindowSize.js.map +1 -1
- package/lib/systems/CombatSystem.js.map +1 -1
- package/lib/systems/EffectCalculator.js.map +1 -1
- package/lib/systems/LayoutSystem.js.map +1 -1
- package/lib/systems/PlayerEffectManager.js.map +1 -1
- package/lib/systems/ResponsiveScaling.js.map +1 -1
- package/lib/systems/TrigramSystem.js.map +1 -1
- package/lib/systems/VitalPointSystem.js.map +1 -1
- package/lib/systems/ai/AIPersonality.js.map +1 -1
- package/lib/systems/ai/AdaptiveDifficulty.js.map +1 -1
- package/lib/systems/ai/ArchetypeEnforcer.js.map +1 -1
- package/lib/systems/ai/ComboSystem.js.map +1 -1
- package/lib/systems/ai/DecisionTree.js.map +1 -1
- package/lib/systems/ai/TrainingAI.js.map +1 -1
- package/lib/systems/ai/types.js.map +1 -1
- package/lib/systems/animation/builders/AnimationBuilder.js.map +1 -1
- package/lib/systems/animation/builders/HandPoseApplicator.js.map +1 -1
- package/lib/systems/animation/builders/HandPoses.js.map +1 -1
- package/lib/systems/animation/builders/KeyframeConfig.js.map +1 -1
- package/lib/systems/animation/builders/KeyframeInterpolation.js +3 -90
- package/lib/systems/animation/builders/KeyframeInterpolation.js.map +1 -1
- package/lib/systems/animation/builders/KickPhaseApplicator.js.map +1 -1
- package/lib/systems/animation/builders/KoreanGuardPositions.js.map +1 -1
- package/lib/systems/animation/builders/MartialArtsAnimationBuilder.js.map +1 -1
- package/lib/systems/animation/builders/MartialArtsConstants.js.map +1 -1
- package/lib/systems/animation/builders/MartialPoseApplicator.js.map +1 -1
- package/lib/systems/animation/builders/PunchPhaseApplicator.js.map +1 -1
- package/lib/systems/animation/builders/SkeletonRig.js.map +1 -1
- package/lib/systems/animation/builders/TrigramGuardApplicator.js.map +1 -1
- package/lib/systems/animation/catalogs/DefensiveAnimations.js.map +1 -1
- package/lib/systems/animation/catalogs/FootworkSkeletalAnimations.js.map +1 -1
- package/lib/systems/animation/catalogs/RecoveryAnimations.js.map +1 -1
- package/lib/systems/animation/catalogs/StanceAnimations.js.map +1 -1
- package/lib/systems/animation/catalogs/StanceAttackAnimations.js.map +1 -1
- package/lib/systems/animation/catalogs/StanceGuardPoses.js.map +1 -1
- package/lib/systems/animation/catalogs/StanceIdleAnimations.js.map +1 -1
- package/lib/systems/animation/catalogs/StanceLocomotionAnimations.js.map +1 -1
- package/lib/systems/animation/catalogs/StepSkeletalAnimations.js.map +1 -1
- package/lib/systems/animation/core/AnimationHitTiming.js.map +1 -1
- package/lib/systems/animation/core/AnimationOptimizations.js.map +1 -1
- package/lib/systems/animation/core/AnimationPriority.js.map +1 -1
- package/lib/systems/animation/core/AnimationRegistry.js.map +1 -1
- package/lib/systems/animation/core/AnimationStateMachine.js.map +1 -1
- package/lib/systems/animation/core/AnimationTransitions.js.map +1 -1
- package/lib/systems/animation/core/LateralityTransform.js.map +1 -1
- package/lib/systems/animation/core/RecoveryPhaseEnhancer.js.map +1 -1
- package/lib/systems/animation/core/TechniqueAnimationMapper.js.map +1 -1
- package/lib/systems/animation/core/TechniqueAnimationMapping.js.map +1 -1
- package/lib/systems/animation/core/types.js.map +1 -1
- package/lib/systems/animation/systems/AdvancedJointMovements.js.map +1 -1
- package/lib/systems/animation/systems/BodyFacingSystem.js.map +1 -1
- package/lib/systems/animation/systems/FacialExpressions.js.map +1 -1
- package/lib/systems/animation/systems/FallAnimations.js.map +1 -1
- package/lib/systems/animation/systems/MuscleActivation.js.map +1 -1
- package/lib/systems/bodypart/BodyPartDamageIntegration.js.map +1 -1
- package/lib/systems/bodypart/BodyPartHealthSystem.js.map +1 -1
- package/lib/systems/bodypart/BodyPartPositionMapping.js.map +1 -1
- package/lib/systems/bodypart/CombatInjuryIntegration.js.map +1 -1
- package/lib/systems/bodypart/InjuryIntegration.js.map +1 -1
- package/lib/systems/bodypart/InjuryTracker.js.map +1 -1
- package/lib/systems/bodypart/MovementPenaltySystem.js.map +1 -1
- package/lib/systems/bodypart/PlayerInjuryTrackingManager.js.map +1 -1
- package/lib/systems/bodypart/types.js.map +1 -1
- package/lib/systems/breathing/BreathingDisruptionSystem.js.map +1 -1
- package/lib/systems/breathing/feedback.js.map +1 -1
- package/lib/systems/breathing/integration.js.map +1 -1
- package/lib/systems/combat/BalanceSystem.js.map +1 -1
- package/lib/systems/combat/BreakingStatusEffects.js.map +1 -1
- package/lib/systems/combat/CombatStateSystem.js.map +1 -1
- package/lib/systems/combat/ConsciousnessSystem.js.map +1 -1
- package/lib/systems/combat/FallIntegration.js.map +1 -1
- package/lib/systems/combat/GrappleSystem.js.map +1 -1
- package/lib/systems/combat/LimbExposureSystem.js.map +1 -1
- package/lib/systems/combat/PainResponseSystem.js.map +1 -1
- package/lib/systems/combat/TrainingCombatSystem.js.map +1 -1
- package/lib/systems/combat/painConsciousnessUtils.js.map +1 -1
- package/lib/systems/combat/typeGuards.js.map +1 -1
- package/lib/systems/effects.js.map +1 -1
- package/lib/systems/game.js.map +1 -1
- package/lib/systems/movement/InjuryMovementModifier.js.map +1 -1
- package/lib/systems/movement/helpers/AccelerationUpdater.js.map +1 -1
- package/lib/systems/movement/helpers/accelerationUtils.js.map +1 -1
- package/lib/systems/movement/integration.js.map +1 -1
- package/lib/systems/physics/AttackMovementPhysics.js.map +1 -1
- package/lib/systems/physics/CollisionDetection.js.map +1 -1
- package/lib/systems/physics/CoordinateMapper.js.map +1 -1
- package/lib/systems/physics/KnockbackPhysics.js.map +1 -1
- package/lib/systems/physics/MovementPhysics.js.map +1 -1
- package/lib/systems/physics/PhysicalReachCalculator.js.map +1 -1
- package/lib/systems/physics/SpeedModifierSystem.js.map +1 -1
- package/lib/systems/trigram/KoreanCulture.js.map +1 -1
- package/lib/systems/trigram/KoreanTechniques.js.map +1 -1
- package/lib/systems/trigram/StanceManager.js.map +1 -1
- package/lib/systems/trigram/TransitionCalculator.js.map +1 -1
- package/lib/systems/trigram/TrigramCalculator.js.map +1 -1
- package/lib/systems/trigram/techniques/DarkOpsTechniques.js.map +1 -1
- package/lib/systems/trigram/techniques/GamTechniques.js.map +1 -1
- package/lib/systems/trigram/techniques/GanTechniques.js.map +1 -1
- package/lib/systems/trigram/techniques/GonTechniques.js.map +1 -1
- package/lib/systems/trigram/techniques/SonTechniques.js.map +1 -1
- package/lib/systems/trigram/techniques/TechniqueConfig.js.map +1 -1
- package/lib/systems/trigram/techniques/index.js.map +1 -1
- package/lib/systems/trigram/types/GonTechniqueExtensions.js.map +1 -1
- package/lib/systems/trigram/types.js.map +1 -1
- package/lib/systems/types.js.map +1 -1
- package/lib/systems/vitalpoint/DamageCalculator.js.map +1 -1
- package/lib/systems/vitalpoint/HitDetection.js.map +1 -1
- package/lib/systems/vitalpoint/KoreanAnatomy.js.map +1 -1
- package/lib/systems/vitalpoint/KoreanVitalPoints.js.map +1 -1
- package/lib/systems/vitalpoint/MeridianVitalPointMapping.js.map +1 -1
- package/lib/types/AccessibilityTypes.js.map +1 -1
- package/lib/types/PhysicsTypes.js.map +1 -1
- package/lib/types/common.js.map +1 -1
- package/lib/types/constants/colors.js.map +1 -1
- package/lib/types/constants/designSystem.js.map +1 -1
- package/lib/types/constants/layout.js.map +1 -1
- package/lib/types/constants/performance.js.map +1 -1
- package/lib/types/constants/typography.js.map +1 -1
- package/lib/types/facial.js.map +1 -1
- package/lib/types/hand-animation.js.map +1 -1
- package/lib/types/injury.js.map +1 -1
- package/lib/types/physics.js.map +1 -1
- package/lib/types/skeletal.js.map +1 -1
- package/lib/types/techniqueId.js.map +1 -1
- package/lib/utils/accessibility.js.map +1 -1
- package/lib/utils/arenaWorldDimensions.js.map +1 -1
- package/lib/utils/assetConfig.js.map +1 -1
- package/lib/utils/characterScaling.js.map +1 -1
- package/lib/utils/colorHelpers.js.map +1 -1
- package/lib/utils/colorUtils.js.map +1 -1
- package/lib/utils/combatReadiness.js.map +1 -1
- package/lib/utils/controlMapping.js.map +1 -1
- package/lib/utils/deviceDetection.js.map +1 -1
- package/lib/utils/effectUtils.js.map +1 -1
- package/lib/utils/fabricTextures.js.map +1 -1
- package/lib/utils/hapticFeedback.js.map +1 -1
- package/lib/utils/haptics.js.map +1 -1
- package/lib/utils/htmlOverlayHelpers.js.map +1 -1
- package/lib/utils/inputSystem.js.map +1 -1
- package/lib/utils/koreanThemeHelpers.js.map +1 -1
- package/lib/utils/math.js.map +1 -1
- package/lib/utils/mobileLayoutHelpers.js.map +1 -1
- package/lib/utils/mobileUIUtils.js.map +1 -1
- package/lib/utils/performance/PerformanceMonitor.js.map +1 -1
- package/lib/utils/performance/PerformanceOverlay3D.js.map +1 -1
- package/lib/utils/performance/usePerformanceMonitor.js.map +1 -1
- package/lib/utils/performanceOptimization.js.map +1 -1
- package/lib/utils/player3DHelpers.js.map +1 -1
- package/lib/utils/playerUtils.js.map +1 -1
- package/lib/utils/responsiveLayout.js.map +1 -1
- package/lib/utils/responsiveLayoutHelpers.js.map +1 -1
- package/lib/utils/responsiveOrientationConstants.js.map +1 -1
- package/lib/utils/safeAreaUtils.js.map +1 -1
- package/lib/utils/sharedPhysicsConfig.js.map +1 -1
- package/lib/utils/skeletonScaling.js.map +1 -1
- package/lib/utils/stanceHelpers.js.map +1 -1
- package/lib/utils/threeObjectPool.js.map +1 -1
- package/lib/utils/visualEffects.js.map +1 -1
- package/package.json +8 -8
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"AnimationOptimizations.js","names":[],"sources":["../../../../src/systems/animation/core/AnimationOptimizations.ts"],"sourcesContent":["/**\n * Optimized Animation System with Caching and Batch Processing\n *\n * High-performance animation pipeline optimizations:\n * - Keyframe caching to avoid redundant interpolations\n * - Batch bone transformation updates\n * - Dirty flag optimization\n * - Precomputed interpolation curves\n *\n * Target: Reduce animation overhead from ~8ms to <5ms per frame\n *\n * @module systems/animation/AnimationOptimizations\n * @category Animation System\n * @korean 애니메이션최적화\n */\n\nimport type {\n AnimationKeyframe,\n SkeletalAnimation,\n SkeletalRig,\n} from \"@/types/skeletal\";\nimport { ThreeObjectPools } from \"@/utils/threeObjectPool\";\nimport * as THREE from \"three\";\n\n/**\n * Cached keyframe with interpolated values\n * Reduces redundant interpolation calculations\n *\n * @korean 캐시된키프레임\n */\ninterface CachedKeyframe {\n /** Original keyframe */\n readonly keyframe: AnimationKeyframe;\n /** Cache timestamp (for invalidation) */\n readonly timestamp: number;\n /** Interpolated bone rotations (Map to avoid allocations) */\n readonly rotations: Map<string, THREE.Euler>;\n /** Interpolated bone positions (if animated) */\n readonly positions: Map<string, THREE.Vector3>;\n}\n\n/**\n * Animation cache entry\n *\n * @korean 애니메이션캐시항목\n */\ninterface AnimationCache {\n /** Animation being cached */\n readonly animation: SkeletalAnimation;\n /** Cached keyframes by time */\n readonly keyframes: Map<number, CachedKeyframe>;\n /** Last access time (for LRU eviction) */\n lastAccessTime: number;\n}\n\n/**\n * Animation Cache Manager\n *\n * Caches interpolated keyframes to avoid redundant calculations.\n * Uses LRU eviction when cache is full.\n *\n * @korean 애니메이션캐시관리자\n */\nclass AnimationCacheManager {\n private cache = new Map<string, AnimationCache>();\n private readonly maxCacheSize: number;\n private readonly maxKeyframesPerAnimation: number;\n\n constructor(maxCacheSize = 50, maxKeyframesPerAnimation = 200) {\n this.maxCacheSize = maxCacheSize;\n this.maxKeyframesPerAnimation = maxKeyframesPerAnimation;\n }\n\n /**\n * Get cached keyframe or create new entry\n *\n * @param animationId - Animation identifier\n * @param animation - Animation data (reserved for future cache invalidation logic)\n * @param time - Current time\n * @returns Cached keyframe or null if not cached\n */\n get(\n animationId: string,\n animation: SkeletalAnimation,\n time: number,\n ): CachedKeyframe | null {\n // Mark parameter as used for future extensibility (e.g., cache invalidation)\n void animation;\n const entry = this.cache.get(animationId);\n if (!entry) {\n return null;\n }\n\n // Update access time for LRU\n entry.lastAccessTime = performance.now();\n\n // Round time to nearest 0.01s for cache hits (100 possible values per second)\n const roundedTime = Math.round(time * 100) / 100;\n return entry.keyframes.get(roundedTime) ?? null;\n }\n\n /**\n * Cache an interpolated keyframe\n *\n * @param animationId - Animation identifier\n * @param animation - Animation data\n * @param time - Current time\n * @param keyframe - Interpolated keyframe to cache\n */\n set(\n animationId: string,\n animation: SkeletalAnimation,\n time: number,\n keyframe: AnimationKeyframe,\n ): void {\n let entry = this.cache.get(animationId);\n\n if (!entry) {\n // Evict oldest entry if cache is full\n if (this.cache.size >= this.maxCacheSize) {\n this.evictLRU();\n }\n\n entry = {\n animation,\n keyframes: new Map(),\n lastAccessTime: performance.now(),\n };\n this.cache.set(animationId, entry);\n }\n\n // Round time for consistent cache keys\n const roundedTime = Math.round(time * 100) / 100;\n\n // Clone keyframe data for caching (avoid reference issues)\n const cachedRotations = new Map<string, THREE.Euler>();\n keyframe.boneRotations.forEach((rotation, boneName) => {\n cachedRotations.set(boneName, rotation.clone());\n });\n\n const cachedPositions = new Map<string, THREE.Vector3>();\n if (keyframe.bonePositions && keyframe.bonePositions.size > 0) {\n keyframe.bonePositions.forEach((position, boneName) => {\n cachedPositions.set(boneName, position.clone());\n });\n }\n\n entry.keyframes.set(roundedTime, {\n keyframe,\n timestamp: performance.now(),\n rotations: cachedRotations,\n positions: cachedPositions,\n });\n\n // Evict oldest keyframes if this animation has too many cached\n if (entry.keyframes.size > this.maxKeyframesPerAnimation) {\n this.evictOldestKeyframes(\n entry,\n entry.keyframes.size - this.maxKeyframesPerAnimation,\n );\n }\n }\n\n /**\n * Evict oldest keyframes from an animation cache entry\n * @param entry - Animation cache entry\n * @param count - Number of keyframes to evict\n */\n private evictOldestKeyframes(entry: AnimationCache, count: number): void {\n const keyframeTimes = Array.from(entry.keyframes.entries())\n .sort((a, b) => a[1].timestamp - b[1].timestamp)\n .slice(0, count)\n .map(([time]) => time);\n\n keyframeTimes.forEach((time) => entry.keyframes.delete(time));\n }\n\n /**\n * Evict least recently used cache entry\n */\n private evictLRU(): void {\n let oldestKey: string | null = null;\n let oldestTime = Infinity;\n\n this.cache.forEach((entry, key) => {\n if (entry.lastAccessTime < oldestTime) {\n oldestTime = entry.lastAccessTime;\n oldestKey = key;\n }\n });\n\n if (oldestKey) {\n this.cache.delete(oldestKey);\n }\n }\n\n /**\n * Clear all cached keyframes\n */\n clear(): void {\n this.cache.clear();\n }\n\n /**\n * Get cache statistics\n */\n getStats(): {\n totalAnimations: number;\n totalKeyframes: number;\n cacheSize: number;\n } {\n let totalKeyframes = 0;\n this.cache.forEach((entry) => {\n totalKeyframes += entry.keyframes.size;\n });\n\n return {\n totalAnimations: this.cache.size,\n totalKeyframes,\n cacheSize: this.maxCacheSize,\n };\n }\n}\n\n/**\n * Global animation cache instance\n *\n * @korean 전역애니메이션캐시\n */\nexport const animationCache = new AnimationCacheManager(50);\n\n/**\n * Batch update multiple bones with dirty flag optimization\n *\n * Only updates bones that have changed rotations/positions.\n * Uses object pooling to avoid allocations.\n *\n * @param rig - Skeletal rig to update\n * @param keyframe - Keyframe with bone transforms\n * @param dirtyBones - Set of bone names that changed (optional, updates all if not provided)\n *\n * @korean 배치뼈업데이트\n */\nexport function batchUpdateBones(\n rig: SkeletalRig,\n keyframe: AnimationKeyframe,\n dirtyBones?: Set<string>,\n): void {\n const bonesToUpdate = dirtyBones ?? new Set(keyframe.boneRotations.keys());\n\n bonesToUpdate.forEach((boneName) => {\n const bone = rig.bones.get(boneName);\n if (!bone) {\n return;\n }\n\n // Update rotation\n const rotation = keyframe.boneRotations.get(boneName);\n if (rotation) {\n bone.rotation.copy(rotation);\n }\n\n // Update position (if animated) - apply as offset from rest position\n const position = keyframe.bonePositions?.get(boneName);\n if (position) {\n // Position values in animations are offsets from rest position, not absolute values\n bone.position.copy(bone.restPosition).add(position);\n }\n });\n}\n\n/**\n * Precompute and cache animation interpolation\n *\n * Generates cached keyframes at regular intervals for smooth playback.\n * Call this during asset loading or idle time.\n *\n * @param animationId - Animation identifier\n * @param animation - Animation to precompute\n * @param sampleRate - Samples per second (default: 60fps = 60 samples/s)\n *\n * @korean 애니메이션사전계산\n */\nexport function precomputeAnimation(\n animationId: string,\n animation: SkeletalAnimation,\n sampleRate = 60,\n): void {\n const duration = animation.duration;\n const step = 1 / sampleRate;\n\n for (let t = 0; t <= duration; t += step) {\n // This will populate the cache\n const keyframe = interpolateKeyframeCached(animationId, animation, t);\n if (keyframe) {\n animationCache.set(animationId, animation, t, keyframe);\n }\n }\n}\n\n/**\n * Interpolate keyframe with caching\n *\n * Checks cache before performing interpolation.\n * Automatically caches result for future use.\n *\n * @param animationId - Animation identifier\n * @param animation - Animation data\n * @param time - Current time\n * @returns Interpolated keyframe\n *\n * @korean 캐시된키프레임보간\n */\nexport function interpolateKeyframeCached(\n animationId: string,\n animation: SkeletalAnimation,\n time: number,\n): AnimationKeyframe | null {\n // Check cache first\n const cached = animationCache.get(animationId, animation, time);\n if (cached) {\n return cached.keyframe;\n }\n\n // Find surrounding keyframes\n const keyframes = animation.keyframes;\n if (keyframes.length === 0) {\n return null;\n }\n\n // Find previous and next keyframes\n let prevKeyframe = keyframes[0];\n let nextKeyframe = keyframes[keyframes.length - 1];\n\n for (let i = 0; i < keyframes.length - 1; i++) {\n if (time >= keyframes[i].time && time <= keyframes[i + 1].time) {\n prevKeyframe = keyframes[i];\n nextKeyframe = keyframes[i + 1];\n break;\n }\n }\n\n // If at exact keyframe, return it directly\n if (Math.abs(time - prevKeyframe.time) < 0.001) {\n animationCache.set(animationId, animation, time, prevKeyframe);\n return prevKeyframe;\n }\n\n // Calculate interpolation factor\n const timeDiff = nextKeyframe.time - prevKeyframe.time;\n const t = timeDiff > 0 ? (time - prevKeyframe.time) / timeDiff : 0;\n\n // Interpolate rotations using object pool\n const interpolatedRotations = new Map<string, THREE.Euler>();\n\n prevKeyframe.boneRotations.forEach((prevRot, boneName) => {\n const nextRot = nextKeyframe.boneRotations.get(boneName);\n if (nextRot) {\n // Use pooled quaternions for slerp interpolation\n const prevQuat = ThreeObjectPools.quaternion.acquire();\n const nextQuat = ThreeObjectPools.quaternion.acquire();\n const resultQuat = ThreeObjectPools.quaternion.acquire();\n\n prevQuat.setFromEuler(prevRot);\n nextQuat.setFromEuler(nextRot);\n resultQuat.slerpQuaternions(prevQuat, nextQuat, t);\n\n // Use pooled Euler, then clone for storage to avoid pooled object reuse issues\n const tempEuler = ThreeObjectPools.euler.acquire();\n tempEuler.setFromQuaternion(resultQuat);\n interpolatedRotations.set(boneName, tempEuler.clone());\n\n // Release pooled objects\n ThreeObjectPools.quaternion.release(prevQuat);\n ThreeObjectPools.quaternion.release(nextQuat);\n ThreeObjectPools.quaternion.release(resultQuat);\n ThreeObjectPools.euler.release(tempEuler);\n } else {\n interpolatedRotations.set(boneName, prevRot.clone());\n }\n });\n\n // Interpolate positions if present\n const interpolatedPositions = new Map<string, THREE.Vector3>();\n if (\n prevKeyframe.bonePositions &&\n prevKeyframe.bonePositions.size > 0 &&\n nextKeyframe.bonePositions &&\n nextKeyframe.bonePositions.size > 0\n ) {\n prevKeyframe.bonePositions.forEach((prevPos, boneName) => {\n const nextPos = nextKeyframe.bonePositions?.get(boneName);\n if (nextPos) {\n // Use pooled Vector3, then clone for storage to avoid pooled object reuse issues\n const tempVec = ThreeObjectPools.vector3.acquire();\n tempVec.lerpVectors(prevPos, nextPos, t);\n interpolatedPositions.set(boneName, tempVec.clone());\n ThreeObjectPools.vector3.release(tempVec);\n }\n });\n }\n\n // ═══════════════════════════════════════════════════════════════════════════\n // ANATOMY STATE INTERPOLATION (해부학 상태 보간)\n // ═══════════════════════════════════════════════════════════════════════════\n\n // For anatomy state, we use discrete values (no interpolation - nearest keyframe wins)\n // At t < 0.5 use prevKeyframe, at t >= 0.5 use nextKeyframe\n const useNextAnatomy = t >= 0.5;\n const anatomySource = useNextAnatomy ? nextKeyframe : prevKeyframe;\n\n // Interpolate muscle activations if present\n let interpolatedMuscles: Map<string, number> | undefined;\n if (prevKeyframe.muscleActivations || nextKeyframe.muscleActivations) {\n interpolatedMuscles = new Map();\n const prevMuscles = prevKeyframe.muscleActivations ?? new Map();\n const nextMuscles = nextKeyframe.muscleActivations ?? new Map();\n\n // Collect all muscle groups from both keyframes\n const allMuscles = new Set([...prevMuscles.keys(), ...nextMuscles.keys()]);\n allMuscles.forEach((muscle) => {\n const prevVal = prevMuscles.get(muscle) ?? 0;\n const nextVal = nextMuscles.get(muscle) ?? 0;\n // Linear interpolation for muscle tension\n interpolatedMuscles?.set(muscle, prevVal + (nextVal - prevVal) * t);\n });\n }\n\n const interpolatedKeyframe: AnimationKeyframe = {\n time,\n boneRotations: interpolatedRotations,\n bonePositions: interpolatedPositions,\n easing: prevKeyframe.easing,\n // Anatomy state (discrete, from nearest keyframe)\n leftHandPose: anatomySource.leftHandPose,\n rightHandPose: anatomySource.rightHandPose,\n leftHandHighlightMode: anatomySource.leftHandHighlightMode,\n rightHandHighlightMode: anatomySource.rightHandHighlightMode,\n leftFootHighlight: anatomySource.leftFootHighlight,\n rightFootHighlight: anatomySource.rightFootHighlight,\n facialExpression: anatomySource.facialExpression,\n // Muscle activations (interpolated)\n muscleActivations: interpolatedMuscles,\n };\n\n // Cache for future use\n animationCache.set(animationId, animation, time, interpolatedKeyframe);\n\n return interpolatedKeyframe;\n}\n\n/**\n * Batch transform multiple bones in a single operation\n *\n * Applies transformations to all bones efficiently using temporary objects.\n * Reduces per-bone overhead by batching operations.\n *\n * @param rig - Skeletal rig\n * @param transforms - Map of bone names to transforms\n *\n * @korean 배치변환\n */\nexport function batchTransformBones(\n rig: SkeletalRig,\n transforms: Map<string, { rotation?: THREE.Euler; position?: THREE.Vector3 }>,\n): void {\n transforms.forEach((transform, boneName) => {\n const bone = rig.bones.get(boneName);\n if (!bone) {\n return;\n }\n\n if (transform.rotation) {\n bone.rotation.copy(transform.rotation);\n }\n\n if (transform.position) {\n bone.position.copy(transform.position);\n }\n });\n}\n\n/**\n * Calculate dirty bones between two keyframes\n *\n * Identifies which bones have changed between keyframes for dirty flag optimization.\n * Only changed bones need to be updated.\n *\n * @param prevKeyframe - Previous keyframe\n * @param nextKeyframe - Next keyframe\n * @param threshold - Minimum rotation difference in radians (default: 0.01)\n * @returns Set of bone names that changed\n *\n * @korean 변경된뼈계산\n */\nexport function calculateDirtyBones(\n prevKeyframe: AnimationKeyframe,\n nextKeyframe: AnimationKeyframe,\n threshold = 0.01,\n): Set<string> {\n const dirtyBones = new Set<string>();\n\n nextKeyframe.boneRotations.forEach((nextRot, boneName) => {\n const prevRot = prevKeyframe.boneRotations.get(boneName);\n if (!prevRot) {\n dirtyBones.add(boneName);\n return;\n }\n\n // Check if rotation changed significantly\n const dx = Math.abs(nextRot.x - prevRot.x);\n const dy = Math.abs(nextRot.y - prevRot.y);\n const dz = Math.abs(nextRot.z - prevRot.z);\n\n if (dx > threshold || dy > threshold || dz > threshold) {\n dirtyBones.add(boneName);\n }\n });\n\n // Check positions if animated\n if (nextKeyframe.bonePositions && nextKeyframe.bonePositions.size > 0) {\n nextKeyframe.bonePositions.forEach((nextPos, boneName) => {\n const prevPos = prevKeyframe.bonePositions?.get(boneName);\n if (!prevPos) {\n dirtyBones.add(boneName);\n return;\n }\n\n // Check if position changed\n const distance = nextPos.distanceTo(prevPos);\n if (distance > threshold) {\n dirtyBones.add(boneName);\n }\n });\n }\n\n return dirtyBones;\n}\n\n/**\n * Animation performance metrics\n *\n * @korean 애니메이션성능지표\n */\nexport interface AnimationPerformanceMetrics {\n /** Average frame time (ms) */\n avgFrameTime: number;\n /** Maximum frame time (ms) */\n maxFrameTime: number;\n /** Minimum frame time (ms) */\n minFrameTime: number;\n /** Number of frames measured */\n frameCount: number;\n /** Cache hit rate (0-1) */\n cacheHitRate: number;\n /** Total cache entries */\n cacheEntries: number;\n}\n\n/**\n * Performance monitor for animation system\n *\n * @korean 성능모니터\n */\nclass AnimationPerformanceMonitor {\n private frameTimes: number[] = [];\n private cacheHits = 0;\n private cacheMisses = 0;\n private readonly maxSamples = 120; // 2 seconds at 60fps\n\n /**\n * Record frame time\n * @param time - Frame time in milliseconds\n */\n recordFrame(time: number): void {\n this.frameTimes.push(time);\n if (this.frameTimes.length > this.maxSamples) {\n this.frameTimes.shift();\n }\n }\n\n /**\n * Record cache hit\n */\n recordCacheHit(): void {\n this.cacheHits++;\n }\n\n /**\n * Record cache miss\n */\n recordCacheMiss(): void {\n this.cacheMisses++;\n }\n\n /**\n * Get current metrics\n */\n getMetrics(): AnimationPerformanceMetrics {\n const totalHits = this.cacheHits + this.cacheMisses;\n const cacheHitRate = totalHits > 0 ? this.cacheHits / totalHits : 0;\n\n const stats = animationCache.getStats();\n\n if (this.frameTimes.length === 0) {\n return {\n avgFrameTime: 0,\n maxFrameTime: 0,\n minFrameTime: 0,\n frameCount: 0,\n cacheHitRate,\n cacheEntries: stats.totalKeyframes,\n };\n }\n\n const avgFrameTime =\n this.frameTimes.reduce((a, b) => a + b, 0) / this.frameTimes.length;\n const maxFrameTime = Math.max(...this.frameTimes);\n const minFrameTime = Math.min(...this.frameTimes);\n\n return {\n avgFrameTime,\n maxFrameTime,\n minFrameTime,\n frameCount: this.frameTimes.length,\n cacheHitRate,\n cacheEntries: stats.totalKeyframes,\n };\n }\n\n /**\n * Reset metrics\n */\n reset(): void {\n this.frameTimes = [];\n this.cacheHits = 0;\n this.cacheMisses = 0;\n }\n}\n\n/**\n * Global performance monitor instance\n *\n * @korean 전역성능모니터\n */\nexport const performanceMonitor = new AnimationPerformanceMonitor();\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"AnimationOptimizations.js","names":[],"sources":["../../../../src/systems/animation/core/AnimationOptimizations.ts"],"sourcesContent":["/**\n * Optimized Animation System with Caching and Batch Processing\n *\n * High-performance animation pipeline optimizations:\n * - Keyframe caching to avoid redundant interpolations\n * - Batch bone transformation updates\n * - Dirty flag optimization\n * - Precomputed interpolation curves\n *\n * Target: Reduce animation overhead from ~8ms to <5ms per frame\n *\n * @module systems/animation/AnimationOptimizations\n * @category Animation System\n * @korean 애니메이션최적화\n */\n\nimport type {\n AnimationKeyframe,\n SkeletalAnimation,\n SkeletalRig,\n} from \"@/types/skeletal\";\nimport { ThreeObjectPools } from \"@/utils/threeObjectPool\";\nimport * as THREE from \"three\";\n\n/**\n * Cached keyframe with interpolated values\n * Reduces redundant interpolation calculations\n *\n * @korean 캐시된키프레임\n */\ninterface CachedKeyframe {\n /** Original keyframe */\n readonly keyframe: AnimationKeyframe;\n /** Cache timestamp (for invalidation) */\n readonly timestamp: number;\n /** Interpolated bone rotations (Map to avoid allocations) */\n readonly rotations: Map<string, THREE.Euler>;\n /** Interpolated bone positions (if animated) */\n readonly positions: Map<string, THREE.Vector3>;\n}\n\n/**\n * Animation cache entry\n *\n * @korean 애니메이션캐시항목\n */\ninterface AnimationCache {\n /** Animation being cached */\n readonly animation: SkeletalAnimation;\n /** Cached keyframes by time */\n readonly keyframes: Map<number, CachedKeyframe>;\n /** Last access time (for LRU eviction) */\n lastAccessTime: number;\n}\n\n/**\n * Animation Cache Manager\n *\n * Caches interpolated keyframes to avoid redundant calculations.\n * Uses LRU eviction when cache is full.\n *\n * @korean 애니메이션캐시관리자\n */\nclass AnimationCacheManager {\n private cache = new Map<string, AnimationCache>();\n private readonly maxCacheSize: number;\n private readonly maxKeyframesPerAnimation: number;\n\n constructor(maxCacheSize = 50, maxKeyframesPerAnimation = 200) {\n this.maxCacheSize = maxCacheSize;\n this.maxKeyframesPerAnimation = maxKeyframesPerAnimation;\n }\n\n /**\n * Get cached keyframe or create new entry\n *\n * @param animationId - Animation identifier\n * @param animation - Animation data (reserved for future cache invalidation logic)\n * @param time - Current time\n * @returns Cached keyframe or null if not cached\n */\n get(\n animationId: string,\n animation: SkeletalAnimation,\n time: number,\n ): CachedKeyframe | null {\n // Mark parameter as used for future extensibility (e.g., cache invalidation)\n void animation;\n const entry = this.cache.get(animationId);\n if (!entry) {\n return null;\n }\n\n // Update access time for LRU\n entry.lastAccessTime = performance.now();\n\n // Round time to nearest 0.01s for cache hits (100 possible values per second)\n const roundedTime = Math.round(time * 100) / 100;\n return entry.keyframes.get(roundedTime) ?? null;\n }\n\n /**\n * Cache an interpolated keyframe\n *\n * @param animationId - Animation identifier\n * @param animation - Animation data\n * @param time - Current time\n * @param keyframe - Interpolated keyframe to cache\n */\n set(\n animationId: string,\n animation: SkeletalAnimation,\n time: number,\n keyframe: AnimationKeyframe,\n ): void {\n let entry = this.cache.get(animationId);\n\n if (!entry) {\n // Evict oldest entry if cache is full\n if (this.cache.size >= this.maxCacheSize) {\n this.evictLRU();\n }\n\n entry = {\n animation,\n keyframes: new Map(),\n lastAccessTime: performance.now(),\n };\n this.cache.set(animationId, entry);\n }\n\n // Round time for consistent cache keys\n const roundedTime = Math.round(time * 100) / 100;\n\n // Clone keyframe data for caching (avoid reference issues)\n const cachedRotations = new Map<string, THREE.Euler>();\n keyframe.boneRotations.forEach((rotation, boneName) => {\n cachedRotations.set(boneName, rotation.clone());\n });\n\n const cachedPositions = new Map<string, THREE.Vector3>();\n if (keyframe.bonePositions && keyframe.bonePositions.size > 0) {\n keyframe.bonePositions.forEach((position, boneName) => {\n cachedPositions.set(boneName, position.clone());\n });\n }\n\n entry.keyframes.set(roundedTime, {\n keyframe,\n timestamp: performance.now(),\n rotations: cachedRotations,\n positions: cachedPositions,\n });\n\n // Evict oldest keyframes if this animation has too many cached\n if (entry.keyframes.size > this.maxKeyframesPerAnimation) {\n this.evictOldestKeyframes(\n entry,\n entry.keyframes.size - this.maxKeyframesPerAnimation,\n );\n }\n }\n\n /**\n * Evict oldest keyframes from an animation cache entry\n * @param entry - Animation cache entry\n * @param count - Number of keyframes to evict\n */\n private evictOldestKeyframes(entry: AnimationCache, count: number): void {\n const keyframeTimes = Array.from(entry.keyframes.entries())\n .sort((a, b) => a[1].timestamp - b[1].timestamp)\n .slice(0, count)\n .map(([time]) => time);\n\n keyframeTimes.forEach((time) => entry.keyframes.delete(time));\n }\n\n /**\n * Evict least recently used cache entry\n */\n private evictLRU(): void {\n let oldestKey: string | null = null;\n let oldestTime = Infinity;\n\n this.cache.forEach((entry, key) => {\n if (entry.lastAccessTime < oldestTime) {\n oldestTime = entry.lastAccessTime;\n oldestKey = key;\n }\n });\n\n if (oldestKey) {\n this.cache.delete(oldestKey);\n }\n }\n\n /**\n * Clear all cached keyframes\n */\n clear(): void {\n this.cache.clear();\n }\n\n /**\n * Get cache statistics\n */\n getStats(): {\n totalAnimations: number;\n totalKeyframes: number;\n cacheSize: number;\n } {\n let totalKeyframes = 0;\n this.cache.forEach((entry) => {\n totalKeyframes += entry.keyframes.size;\n });\n\n return {\n totalAnimations: this.cache.size,\n totalKeyframes,\n cacheSize: this.maxCacheSize,\n };\n }\n}\n\n/**\n * Global animation cache instance\n *\n * @korean 전역애니메이션캐시\n */\nexport const animationCache = new AnimationCacheManager(50);\n\n/**\n * Batch update multiple bones with dirty flag optimization\n *\n * Only updates bones that have changed rotations/positions.\n * Uses object pooling to avoid allocations.\n *\n * @param rig - Skeletal rig to update\n * @param keyframe - Keyframe with bone transforms\n * @param dirtyBones - Set of bone names that changed (optional, updates all if not provided)\n *\n * @korean 배치뼈업데이트\n */\nexport function batchUpdateBones(\n rig: SkeletalRig,\n keyframe: AnimationKeyframe,\n dirtyBones?: Set<string>,\n): void {\n const bonesToUpdate = dirtyBones ?? new Set(keyframe.boneRotations.keys());\n\n bonesToUpdate.forEach((boneName) => {\n const bone = rig.bones.get(boneName);\n if (!bone) {\n return;\n }\n\n // Update rotation\n const rotation = keyframe.boneRotations.get(boneName);\n if (rotation) {\n bone.rotation.copy(rotation);\n }\n\n // Update position (if animated) - apply as offset from rest position\n const position = keyframe.bonePositions?.get(boneName);\n if (position) {\n // Position values in animations are offsets from rest position, not absolute values\n bone.position.copy(bone.restPosition).add(position);\n }\n });\n}\n\n/**\n * Precompute and cache animation interpolation\n *\n * Generates cached keyframes at regular intervals for smooth playback.\n * Call this during asset loading or idle time.\n *\n * @param animationId - Animation identifier\n * @param animation - Animation to precompute\n * @param sampleRate - Samples per second (default: 60fps = 60 samples/s)\n *\n * @korean 애니메이션사전계산\n */\nexport function precomputeAnimation(\n animationId: string,\n animation: SkeletalAnimation,\n sampleRate = 60,\n): void {\n const duration = animation.duration;\n const step = 1 / sampleRate;\n\n for (let t = 0; t <= duration; t += step) {\n // This will populate the cache\n const keyframe = interpolateKeyframeCached(animationId, animation, t);\n if (keyframe) {\n animationCache.set(animationId, animation, t, keyframe);\n }\n }\n}\n\n/**\n * Interpolate keyframe with caching\n *\n * Checks cache before performing interpolation.\n * Automatically caches result for future use.\n *\n * @param animationId - Animation identifier\n * @param animation - Animation data\n * @param time - Current time\n * @returns Interpolated keyframe\n *\n * @korean 캐시된키프레임보간\n */\nexport function interpolateKeyframeCached(\n animationId: string,\n animation: SkeletalAnimation,\n time: number,\n): AnimationKeyframe | null {\n // Check cache first\n const cached = animationCache.get(animationId, animation, time);\n if (cached) {\n return cached.keyframe;\n }\n\n // Find surrounding keyframes\n const keyframes = animation.keyframes;\n if (keyframes.length === 0) {\n return null;\n }\n\n // Find previous and next keyframes\n let prevKeyframe = keyframes[0];\n let nextKeyframe = keyframes[keyframes.length - 1];\n\n for (let i = 0; i < keyframes.length - 1; i++) {\n if (time >= keyframes[i].time && time <= keyframes[i + 1].time) {\n prevKeyframe = keyframes[i];\n nextKeyframe = keyframes[i + 1];\n break;\n }\n }\n\n // If at exact keyframe, return it directly\n if (Math.abs(time - prevKeyframe.time) < 0.001) {\n animationCache.set(animationId, animation, time, prevKeyframe);\n return prevKeyframe;\n }\n\n // Calculate interpolation factor\n const timeDiff = nextKeyframe.time - prevKeyframe.time;\n const t = timeDiff > 0 ? (time - prevKeyframe.time) / timeDiff : 0;\n\n // Interpolate rotations using object pool\n const interpolatedRotations = new Map<string, THREE.Euler>();\n\n prevKeyframe.boneRotations.forEach((prevRot, boneName) => {\n const nextRot = nextKeyframe.boneRotations.get(boneName);\n if (nextRot) {\n // Use pooled quaternions for slerp interpolation\n const prevQuat = ThreeObjectPools.quaternion.acquire();\n const nextQuat = ThreeObjectPools.quaternion.acquire();\n const resultQuat = ThreeObjectPools.quaternion.acquire();\n\n prevQuat.setFromEuler(prevRot);\n nextQuat.setFromEuler(nextRot);\n resultQuat.slerpQuaternions(prevQuat, nextQuat, t);\n\n // Use pooled Euler, then clone for storage to avoid pooled object reuse issues\n const tempEuler = ThreeObjectPools.euler.acquire();\n tempEuler.setFromQuaternion(resultQuat);\n interpolatedRotations.set(boneName, tempEuler.clone());\n\n // Release pooled objects\n ThreeObjectPools.quaternion.release(prevQuat);\n ThreeObjectPools.quaternion.release(nextQuat);\n ThreeObjectPools.quaternion.release(resultQuat);\n ThreeObjectPools.euler.release(tempEuler);\n } else {\n interpolatedRotations.set(boneName, prevRot.clone());\n }\n });\n\n // Interpolate positions if present\n const interpolatedPositions = new Map<string, THREE.Vector3>();\n if (\n prevKeyframe.bonePositions &&\n prevKeyframe.bonePositions.size > 0 &&\n nextKeyframe.bonePositions &&\n nextKeyframe.bonePositions.size > 0\n ) {\n prevKeyframe.bonePositions.forEach((prevPos, boneName) => {\n const nextPos = nextKeyframe.bonePositions?.get(boneName);\n if (nextPos) {\n // Use pooled Vector3, then clone for storage to avoid pooled object reuse issues\n const tempVec = ThreeObjectPools.vector3.acquire();\n tempVec.lerpVectors(prevPos, nextPos, t);\n interpolatedPositions.set(boneName, tempVec.clone());\n ThreeObjectPools.vector3.release(tempVec);\n }\n });\n }\n\n // ═══════════════════════════════════════════════════════════════════════════\n // ANATOMY STATE INTERPOLATION (해부학 상태 보간)\n // ═══════════════════════════════════════════════════════════════════════════\n\n // For anatomy state, we use discrete values (no interpolation - nearest keyframe wins)\n // At t < 0.5 use prevKeyframe, at t >= 0.5 use nextKeyframe\n const useNextAnatomy = t >= 0.5;\n const anatomySource = useNextAnatomy ? nextKeyframe : prevKeyframe;\n\n // Interpolate muscle activations if present\n let interpolatedMuscles: Map<string, number> | undefined;\n if (prevKeyframe.muscleActivations || nextKeyframe.muscleActivations) {\n interpolatedMuscles = new Map();\n const prevMuscles = prevKeyframe.muscleActivations ?? new Map();\n const nextMuscles = nextKeyframe.muscleActivations ?? new Map();\n\n // Collect all muscle groups from both keyframes\n const allMuscles = new Set([...prevMuscles.keys(), ...nextMuscles.keys()]);\n allMuscles.forEach((muscle) => {\n const prevVal = prevMuscles.get(muscle) ?? 0;\n const nextVal = nextMuscles.get(muscle) ?? 0;\n // Linear interpolation for muscle tension\n interpolatedMuscles?.set(muscle, prevVal + (nextVal - prevVal) * t);\n });\n }\n\n const interpolatedKeyframe: AnimationKeyframe = {\n time,\n boneRotations: interpolatedRotations,\n bonePositions: interpolatedPositions,\n easing: prevKeyframe.easing,\n // Anatomy state (discrete, from nearest keyframe)\n leftHandPose: anatomySource.leftHandPose,\n rightHandPose: anatomySource.rightHandPose,\n leftHandHighlightMode: anatomySource.leftHandHighlightMode,\n rightHandHighlightMode: anatomySource.rightHandHighlightMode,\n leftFootHighlight: anatomySource.leftFootHighlight,\n rightFootHighlight: anatomySource.rightFootHighlight,\n facialExpression: anatomySource.facialExpression,\n // Muscle activations (interpolated)\n muscleActivations: interpolatedMuscles,\n };\n\n // Cache for future use\n animationCache.set(animationId, animation, time, interpolatedKeyframe);\n\n return interpolatedKeyframe;\n}\n\n/**\n * Batch transform multiple bones in a single operation\n *\n * Applies transformations to all bones efficiently using temporary objects.\n * Reduces per-bone overhead by batching operations.\n *\n * @param rig - Skeletal rig\n * @param transforms - Map of bone names to transforms\n *\n * @korean 배치변환\n */\nexport function batchTransformBones(\n rig: SkeletalRig,\n transforms: Map<string, { rotation?: THREE.Euler; position?: THREE.Vector3 }>,\n): void {\n transforms.forEach((transform, boneName) => {\n const bone = rig.bones.get(boneName);\n if (!bone) {\n return;\n }\n\n if (transform.rotation) {\n bone.rotation.copy(transform.rotation);\n }\n\n if (transform.position) {\n bone.position.copy(transform.position);\n }\n });\n}\n\n/**\n * Calculate dirty bones between two keyframes\n *\n * Identifies which bones have changed between keyframes for dirty flag optimization.\n * Only changed bones need to be updated.\n *\n * @param prevKeyframe - Previous keyframe\n * @param nextKeyframe - Next keyframe\n * @param threshold - Minimum rotation difference in radians (default: 0.01)\n * @returns Set of bone names that changed\n *\n * @korean 변경된뼈계산\n */\nexport function calculateDirtyBones(\n prevKeyframe: AnimationKeyframe,\n nextKeyframe: AnimationKeyframe,\n threshold = 0.01,\n): Set<string> {\n const dirtyBones = new Set<string>();\n\n nextKeyframe.boneRotations.forEach((nextRot, boneName) => {\n const prevRot = prevKeyframe.boneRotations.get(boneName);\n if (!prevRot) {\n dirtyBones.add(boneName);\n return;\n }\n\n // Check if rotation changed significantly\n const dx = Math.abs(nextRot.x - prevRot.x);\n const dy = Math.abs(nextRot.y - prevRot.y);\n const dz = Math.abs(nextRot.z - prevRot.z);\n\n if (dx > threshold || dy > threshold || dz > threshold) {\n dirtyBones.add(boneName);\n }\n });\n\n // Check positions if animated\n if (nextKeyframe.bonePositions && nextKeyframe.bonePositions.size > 0) {\n nextKeyframe.bonePositions.forEach((nextPos, boneName) => {\n const prevPos = prevKeyframe.bonePositions?.get(boneName);\n if (!prevPos) {\n dirtyBones.add(boneName);\n return;\n }\n\n // Check if position changed\n const distance = nextPos.distanceTo(prevPos);\n if (distance > threshold) {\n dirtyBones.add(boneName);\n }\n });\n }\n\n return dirtyBones;\n}\n\n/**\n * Animation performance metrics\n *\n * @korean 애니메이션성능지표\n */\nexport interface AnimationPerformanceMetrics {\n /** Average frame time (ms) */\n avgFrameTime: number;\n /** Maximum frame time (ms) */\n maxFrameTime: number;\n /** Minimum frame time (ms) */\n minFrameTime: number;\n /** Number of frames measured */\n frameCount: number;\n /** Cache hit rate (0-1) */\n cacheHitRate: number;\n /** Total cache entries */\n cacheEntries: number;\n}\n\n/**\n * Performance monitor for animation system\n *\n * @korean 성능모니터\n */\nclass AnimationPerformanceMonitor {\n private frameTimes: number[] = [];\n private cacheHits = 0;\n private cacheMisses = 0;\n private readonly maxSamples = 120; // 2 seconds at 60fps\n\n /**\n * Record frame time\n * @param time - Frame time in milliseconds\n */\n recordFrame(time: number): void {\n this.frameTimes.push(time);\n if (this.frameTimes.length > this.maxSamples) {\n this.frameTimes.shift();\n }\n }\n\n /**\n * Record cache hit\n */\n recordCacheHit(): void {\n this.cacheHits++;\n }\n\n /**\n * Record cache miss\n */\n recordCacheMiss(): void {\n this.cacheMisses++;\n }\n\n /**\n * Get current metrics\n */\n getMetrics(): AnimationPerformanceMetrics {\n const totalHits = this.cacheHits + this.cacheMisses;\n const cacheHitRate = totalHits > 0 ? this.cacheHits / totalHits : 0;\n\n const stats = animationCache.getStats();\n\n if (this.frameTimes.length === 0) {\n return {\n avgFrameTime: 0,\n maxFrameTime: 0,\n minFrameTime: 0,\n frameCount: 0,\n cacheHitRate,\n cacheEntries: stats.totalKeyframes,\n };\n }\n\n const avgFrameTime =\n this.frameTimes.reduce((a, b) => a + b, 0) / this.frameTimes.length;\n const maxFrameTime = Math.max(...this.frameTimes);\n const minFrameTime = Math.min(...this.frameTimes);\n\n return {\n avgFrameTime,\n maxFrameTime,\n minFrameTime,\n frameCount: this.frameTimes.length,\n cacheHitRate,\n cacheEntries: stats.totalKeyframes,\n };\n }\n\n /**\n * Reset metrics\n */\n reset(): void {\n this.frameTimes = [];\n this.cacheHits = 0;\n this.cacheMisses = 0;\n }\n}\n\n/**\n * Global performance monitor instance\n *\n * @korean 전역성능모니터\n */\nexport const performanceMonitor = new AnimationPerformanceMonitor();\n"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"AnimationPriority.js","names":[],"sources":["../../../../src/systems/animation/core/AnimationPriority.ts"],"sourcesContent":["/**\n * Animation priority system for Black Trigram\n * \n * Determines which animations can interrupt others based on priority levels.\n * Higher priority animations can interrupt lower priority ones.\n * \n * Priority order: recovery > fall > ko > hit > attack > defend > stance_change > movement > idle\n * \n * Enhanced with:\n * - Deterministic conflict resolution for simultaneous equal-priority animations\n * - Animation queue system for pending animations\n * - Interruptibility windows (frame-based interrupt control)\n * - Korean terminology for all priority levels and conflicts\n * \n * @module systems/animation/AnimationPriority\n * @category Animation\n * @korean 애니메이션우선순위\n */\n\nimport { AnimationPriority, AnimationState, STEP_PRIORITY } from \"./types\";\n\n/**\n * Map animation states to their priority levels\n * \n * Stance guard animations have same priority as idle (0) since they are\n * also idle states, just stance-specific.\n * Tactical steps have priority 5 (same as attacks) to ensure commitment.\n * \n * @korean 애니메이션우선순위맵\n */\nexport const ANIMATION_PRIORITY_MAP: Record<AnimationState, AnimationPriority> = {\n idle: AnimationPriority.IDLE,\n walk: AnimationPriority.WALK,\n run: AnimationPriority.RUN,\n stance_change: AnimationPriority.STANCE_CHANGE,\n stance_side_switch: AnimationPriority.STANCE_CHANGE, // Same priority as stance_change\n defend: AnimationPriority.DEFEND,\n // Defensive animations (방어 애니메이션)\n defend_block_success: AnimationPriority.HIT, // Priority 6 - same as hit\n defend_parry: AnimationPriority.KO, // Priority 7 - higher than block\n defend_guard_break: AnimationPriority.FALL, // Priority 8 - highest (same as fall)\n defend_recovery: AnimationPriority.RUN, // Priority 2 - interruptible recovery\n attack: AnimationPriority.ATTACK,\n hit: AnimationPriority.HIT,\n ko: AnimationPriority.KO,\n // Stance-specific guard animations (팔괘 방어 자세)\n stance_guard_geon: AnimationPriority.IDLE,\n stance_guard_tae: AnimationPriority.IDLE,\n stance_guard_li: AnimationPriority.IDLE,\n stance_guard_jin: AnimationPriority.IDLE,\n stance_guard_son: AnimationPriority.IDLE,\n stance_guard_gam: AnimationPriority.IDLE,\n stance_guard_gan: AnimationPriority.IDLE,\n stance_guard_gon: AnimationPriority.IDLE,\n // Tactical step animations (전술적 발걸음) - non-interruptible\n step_forward: STEP_PRIORITY,\n step_back: STEP_PRIORITY,\n step_left: STEP_PRIORITY,\n step_right: STEP_PRIORITY,\n step_forward_left: STEP_PRIORITY,\n step_forward_right: STEP_PRIORITY,\n step_back_left: STEP_PRIORITY,\n step_back_right: STEP_PRIORITY,\n // Fall animations (낙법) - highest priority\n fall_forward: AnimationPriority.FALL,\n fall_backward: AnimationPriority.FALL,\n fall_side_left: AnimationPriority.FALL,\n fall_side_right: AnimationPriority.FALL,\n // Ground states (지면 자세) - idle priority\n ground_prone: AnimationPriority.IDLE,\n ground_supine: AnimationPriority.IDLE,\n ground_side_left: AnimationPriority.IDLE,\n ground_side_right: AnimationPriority.IDLE,\n // 180-degree turn animations (180도 회전) - same as steps (committed action)\n turn_left: STEP_PRIORITY,\n turn_right: STEP_PRIORITY,\n // Footwork patterns (보법) - Korean martial arts specialized footwork\n footwork_circular_left: STEP_PRIORITY,\n footwork_circular_right: STEP_PRIORITY,\n footwork_pivot_left: STEP_PRIORITY,\n footwork_pivot_right: STEP_PRIORITY,\n footwork_slide_forward: AnimationPriority.DEFEND,\n footwork_slide_back: AnimationPriority.DEFEND,\n footwork_slide_left: AnimationPriority.DEFEND,\n footwork_slide_right: AnimationPriority.DEFEND,\n footwork_shuffle: AnimationPriority.STANCE_CHANGE,\n // Recovery animations (기상 애니메이션) - highest priority (can interrupt anything)\n recovery_prone_standup: AnimationPriority.RECOVERY,\n recovery_supine_standup: AnimationPriority.RECOVERY,\n recovery_roll: AnimationPriority.RECOVERY,\n recovery_defensive: AnimationPriority.RECOVERY,\n // Grappling animations (잡기 애니메이션) - high priority like attacks/hits\n grapple_entry: AnimationPriority.ATTACK, // Priority 5 - same as attack\n grapple_control: AnimationPriority.ATTACK, // Priority 5 - maintain control\n grapple_struggle: AnimationPriority.HIT, // Priority 6 - struggling escape\n grapple_escape: AnimationPriority.HIT, // Priority 6 - successful escape\n};\n\n/**\n * Check if an animation can interrupt another based on priority\n * \n * @param current - Current animation state\n * @param requested - Requested animation state\n * @param currentInterruptible - Whether current animation is interruptible\n * @returns Whether the requested animation can interrupt the current one\n * \n * @example\n * ```typescript\n * // Hit can interrupt attack\n * canInterrupt(\"attack\", \"hit\", true); // true\n * \n * // Attack cannot interrupt hit\n * canInterrupt(\"hit\", \"attack\", true); // false\n * \n * // Nothing can interrupt non-interruptible animations\n * canInterrupt(\"attack\", \"hit\", false); // false (unless same priority)\n * ```\n * \n * @korean 중단가능여부확인\n */\nexport function canInterrupt(\n current: AnimationState,\n requested: AnimationState,\n currentInterruptible: boolean\n): boolean {\n const currentPriority = ANIMATION_PRIORITY_MAP[current];\n const requestedPriority = ANIMATION_PRIORITY_MAP[requested];\n\n // Same priority animations can always transition\n if (currentPriority === requestedPriority) {\n return true;\n }\n\n // Non-interruptible animations can only be interrupted by higher priority\n if (!currentInterruptible) {\n return requestedPriority > currentPriority;\n }\n\n // Interruptible animations can be interrupted by same or higher priority\n return requestedPriority >= currentPriority;\n}\n\n/**\n * Get the priority level for an animation state\n * \n * @param state - Animation state\n * @returns Priority level\n * \n * @korean 우선순위가져오기\n */\nexport function getPriority(state: AnimationState): AnimationPriority {\n return ANIMATION_PRIORITY_MAP[state];\n}\n\n/**\n * Compare two animation priorities\n * \n * @param state1 - First animation state\n * @param state2 - Second animation state\n * @returns Positive if state1 has higher priority, negative if state2 has higher priority, 0 if equal\n * \n * @korean 우선순위비교\n */\nexport function comparePriority(\n state1: AnimationState,\n state2: AnimationState\n): number {\n return ANIMATION_PRIORITY_MAP[state1] - ANIMATION_PRIORITY_MAP[state2];\n}\n\n// ===== Korean Priority Level Terminology (우선순위 등급 용어) =====\n\n/**\n * Korean names for each priority level\n * \n * Maps priority levels to their Korean martial arts terminology:\n * - IDLE (0): 대기 (Daegi) - Waiting/Ready state\n * - WALK (1): 보행 (Bohaeng) - Walking movement\n * - RUN (2): 질주 (Jilju) - Running movement\n * - STANCE_CHANGE (3): 자세전환 (Jase Jeonhwan) - Stance transition\n * - DEFEND (4): 방어 (Bangeo) - Defense action\n * - ATTACK (5): 공격 (Gonggyeok) - Attack action\n * - HIT (6): 피격 (Pigyeok) - Being hit\n * - KO (7): 기절 (Gijeol) - Knockout\n * - FALL (8): 낙법 (Nakbeop) - Falling technique\n * - RECOVERY (9): 기상 (Gisang) - Recovery/Getting up\n * \n * @korean 우선순위한글용어\n */\nexport const PRIORITY_LEVEL_KOREAN_NAMES: Record<AnimationPriority, {\n korean: string;\n romanized: string;\n english: string;\n}> = {\n [AnimationPriority.IDLE]: {\n korean: \"대기\",\n romanized: \"Daegi\",\n english: \"Ready State\",\n },\n [AnimationPriority.WALK]: {\n korean: \"보행\",\n romanized: \"Bohaeng\",\n english: \"Walking\",\n },\n [AnimationPriority.RUN]: {\n korean: \"질주\",\n romanized: \"Jilju\",\n english: \"Running\",\n },\n [AnimationPriority.STANCE_CHANGE]: {\n korean: \"자세전환\",\n romanized: \"Jase Jeonhwan\",\n english: \"Stance Change\",\n },\n [AnimationPriority.DEFEND]: {\n korean: \"방어\",\n romanized: \"Bangeo\",\n english: \"Defense\",\n },\n [AnimationPriority.ATTACK]: {\n korean: \"공격\",\n romanized: \"Gonggyeok\",\n english: \"Attack\",\n },\n [AnimationPriority.HIT]: {\n korean: \"피격\",\n romanized: \"Pigyeok\",\n english: \"Hit\",\n },\n [AnimationPriority.KO]: {\n korean: \"기절\",\n romanized: \"Gijeol\",\n english: \"Knockout\",\n },\n [AnimationPriority.FALL]: {\n korean: \"낙법\",\n romanized: \"Nakbeop\",\n english: \"Falling\",\n },\n [AnimationPriority.RECOVERY]: {\n korean: \"기상\",\n romanized: \"Gisang\",\n english: \"Recovery\",\n },\n};\n\n/**\n * Get Korean name for a priority level\n * \n * @param priority - Animation priority level\n * @returns Korean terminology object\n * \n * @korean 우선순위한글이름가져오기\n */\nexport function getPriorityKoreanName(priority: AnimationPriority): {\n korean: string;\n romanized: string;\n english: string;\n} {\n return PRIORITY_LEVEL_KOREAN_NAMES[priority];\n}\n\n// ===== Conflict Resolution (충돌 해결) =====\n\n/**\n * Conflict resolution strategy for equal-priority animations\n * \n * **Korean**: 충돌 해결 전략\n * \n * Determines how to handle when two animations of equal priority\n * are requested simultaneously:\n * \n * - `timestamp`: First animation requested wins (FIFO - 선입선출)\n * - `state_order`: Use AnimationState enum order as tiebreaker\n * - `current`: Keep current animation running\n * - `requested`: Always switch to requested animation\n * \n * @korean 충돌해결전략\n */\nexport type ConflictResolutionStrategy =\n | \"timestamp\" // 시간순서 (First requested wins)\n | \"state_order\" // 상태순서 (Enum order tiebreaker)\n | \"current\" // 현재유지 (Keep current)\n | \"requested\"; // 요청수락 (Accept requested)\n\n/**\n * Animation request with timestamp for conflict resolution\n * \n * **Korean**: 애니메이션 요청\n * \n * Tracks animation requests with timestamps to enable deterministic\n * conflict resolution when multiple equal-priority animations compete.\n * \n * @korean 애니메이션요청\n */\nexport interface AnimationRequest {\n /** Requested animation state */\n readonly state: AnimationState;\n /** Request timestamp in milliseconds (from performance.now()) */\n readonly timestamp: number;\n /** Request priority */\n readonly priority: AnimationPriority;\n /** \n * Whether this is a forced request (wins conflict resolution against non-forced requests)\n * \n * Note: This only affects conflict resolution between equal-priority animations in the queue.\n * It does not bypass priority checks or interrupt rules for the initial transition.\n * \n * @korean 강제요청\n */\n readonly forced?: boolean;\n}\n\n/**\n * Resolve conflict between two equal-priority animations\n * \n * **Korean**: 충돌 해결\n * \n * Uses the specified strategy to deterministically decide which animation\n * should play when both have equal priority.\n * \n * @param current - Current animation request\n * @param requested - Requested animation request\n * @param strategy - Conflict resolution strategy\n * @returns Which animation should play: 'current' or 'requested'\n * \n * @example\n * ```typescript\n * const current = {\n * state: AnimationState.ATTACK,\n * timestamp: 1000,\n * priority: AnimationPriority.ATTACK,\n * };\n * const requested = {\n * state: AnimationState.STEP_FORWARD,\n * timestamp: 1001,\n * priority: AnimationPriority.ATTACK,\n * };\n * \n * // Timestamp strategy: current wins (1000 < 1001)\n * resolveConflict(current, requested, \"timestamp\"); // \"current\"\n * \n * // Requested strategy: always take new animation\n * resolveConflict(current, requested, \"requested\"); // \"requested\"\n * ```\n * \n * @korean 충돌해결\n */\nexport function resolveConflict(\n current: AnimationRequest,\n requested: AnimationRequest,\n strategy: ConflictResolutionStrategy = \"timestamp\"\n): \"current\" | \"requested\" {\n // If only one is forced, forced wins\n if (requested.forced && !current.forced) {\n return \"requested\";\n }\n if (current.forced && !requested.forced) {\n return \"current\";\n }\n \n // If both are forced OR both are normal, use conflict resolution strategy\n\n // Apply conflict resolution strategy\n switch (strategy) {\n case \"timestamp\":\n // Earlier timestamp wins (FIFO)\n return current.timestamp <= requested.timestamp ? \"current\" : \"requested\";\n \n case \"state_order\":\n // Use enum value comparison as tiebreaker\n return current.state <= requested.state ? \"current\" : \"requested\";\n \n case \"current\":\n // Always keep current animation\n return \"current\";\n \n case \"requested\":\n // Always accept requested animation\n return \"requested\";\n \n default:\n // Default to timestamp strategy\n return current.timestamp <= requested.timestamp ? \"current\" : \"requested\";\n }\n}\n\n// ===== Animation Queue System (애니메이션 대기열) =====\n\n/**\n * Animation queue for managing pending animations\n * \n * **Korean**: 애니메이션 대기열\n * \n * Stores pending animation requests that should play after the current\n * animation completes. Useful for:\n * - Buffering input during non-interruptible animations\n * - Creating animation chains/combos\n * - Handling rapid input sequences\n * \n * Queue is priority-ordered: highest priority animations are dequeued first.\n * \n * @korean 애니메이션대기열\n */\nexport class AnimationQueue {\n private queue: AnimationRequest[] = [];\n private maxSize: number;\n private conflictStrategy: ConflictResolutionStrategy;\n\n /**\n * Create a new animation queue\n * \n * @param maxSize - Maximum queue size (default: 3)\n * @param conflictStrategy - Strategy for resolving equal-priority conflicts\n * \n * @korean 생성자\n */\n constructor(\n maxSize: number = 3,\n conflictStrategy: ConflictResolutionStrategy = \"timestamp\"\n ) {\n this.maxSize = maxSize;\n this.conflictStrategy = conflictStrategy;\n }\n\n /**\n * Add animation request to queue\n * \n * **Korean**: 대기열에 추가\n * \n * Adds an animation request to the queue if space is available.\n * Queue is kept sorted by priority (highest first).\n * \n * @param request - Animation request to enqueue\n * @returns Whether request was successfully enqueued\n * \n * @korean 대기열추가\n */\n enqueue(request: AnimationRequest): boolean {\n // Check if queue is full\n if (this.queue.length >= this.maxSize) {\n // Queue is sorted by priority (highest first), so last element is lowest priority\n const lowestPriorityItem = this.queue[this.queue.length - 1];\n \n if (request.priority > lowestPriorityItem.priority) {\n // Remove lowest priority item to make space\n this.queue.pop();\n } else {\n return false; // Queue full, request discarded\n }\n }\n\n // Insert request into queue while maintaining sort by priority (highest first).\n // Use binary search (O(log n)) to find insertion position, then splice (O(n)) to insert.\n // Overall: O(n) per insertion, avoiding an O(n log n) full sort of the entire queue.\n let low = 0;\n let high = this.queue.length;\n\n // Binary search to find insertion index\n while (low < high) {\n const mid = (low + high) >> 1;\n const midPriority = this.queue[mid].priority;\n\n if (midPriority < request.priority) {\n // New request has higher priority; search left half\n high = mid;\n } else {\n // New request has equal or lower priority; search right half\n low = mid + 1;\n }\n }\n\n // Insert at computed index to keep queue sorted (highest priority first)\n this.queue.splice(low, 0, request);\n \n return true;\n }\n\n /**\n * Remove and return highest priority request from queue\n * \n * **Korean**: 대기열에서 제거\n * \n * Dequeues the highest priority animation request.\n * If multiple requests have equal priority, uses conflict resolution strategy.\n * \n * @returns Next animation request or null if queue is empty\n * \n * @korean 대기열제거\n */\n dequeue(): AnimationRequest | null {\n if (this.queue.length === 0) {\n return null;\n }\n\n // Get all requests with highest priority\n const highestPriority = this.queue[0].priority;\n const highestPriorityRequests = this.queue.filter(\n r => r.priority === highestPriority\n );\n\n let selectedRequest: AnimationRequest;\n\n if (highestPriorityRequests.length === 1) {\n selectedRequest = highestPriorityRequests[0];\n } else {\n // Multiple equal-priority requests - use conflict resolution\n selectedRequest = highestPriorityRequests.reduce((current, next) => {\n const winner = resolveConflict(current, next, this.conflictStrategy);\n return winner === \"current\" ? current : next;\n });\n }\n\n // Remove selected request from queue\n const index = this.queue.indexOf(selectedRequest);\n this.queue.splice(index, 1);\n\n return selectedRequest;\n }\n\n /**\n * Peek at next request without removing it\n * \n * **Korean**: 다음 요청 확인\n * \n * @returns Next animation request or null if queue is empty\n * \n * @korean 다음요청확인\n */\n peek(): AnimationRequest | null {\n return this.queue.length > 0 ? this.queue[0] : null;\n }\n\n /**\n * Clear all pending requests\n * \n * **Korean**: 대기열 초기화\n * \n * @korean 대기열초기화\n */\n clear(): void {\n this.queue = [];\n }\n\n /**\n * Get number of pending requests\n * \n * **Korean**: 대기열 크기\n * \n * @returns Number of pending requests\n * \n * @korean 대기열크기\n */\n size(): number {\n return this.queue.length;\n }\n\n /**\n * Check if queue is empty\n * \n * **Korean**: 대기열 비어있음\n * \n * @returns True if queue has no pending requests\n * \n * @korean 대기열비어있음\n */\n isEmpty(): boolean {\n return this.queue.length === 0;\n }\n\n /**\n * Check if queue is full\n * \n * **Korean**: 대기열 가득참\n * \n * @returns True if queue is at maximum capacity\n * \n * @korean 대기열가득찬\n */\n isFull(): boolean {\n return this.queue.length >= this.maxSize;\n }\n\n /**\n * Get all pending requests (read-only)\n * \n * **Korean**: 모든 대기 요청\n * \n * @returns Array of pending requests\n * \n * @korean 모든대기요청\n */\n getAll(): readonly AnimationRequest[] {\n return [...this.queue];\n }\n\n /**\n * Get maximum queue size\n * \n * **Korean**: 최대 대기열 크기\n * \n * @returns Maximum queue capacity\n * \n * @korean 최대대기열크기\n */\n getMaxSize(): number {\n return this.maxSize;\n }\n\n /**\n * Set conflict resolution strategy\n * \n * **Korean**: 충돌 해결 전략 설정\n * \n * Updates the strategy used to resolve equal-priority conflicts.\n * \n * @param strategy - New conflict resolution strategy\n * \n * @korean 충돌해결전략설정\n */\n setConflictStrategy(strategy: ConflictResolutionStrategy): void {\n this.conflictStrategy = strategy;\n }\n}\n\n// ===== Interruptibility Windows (중단 가능 구간) =====\n\n/**\n * Interruptibility window definition\n * \n * **Korean**: 중단 가능 구간\n * \n * Defines specific frame ranges where an animation can be interrupted,\n * allowing for more nuanced control than simple boolean interruptibility.\n * \n * Example: Attack animation might be interruptible only in startup frames (0-3)\n * and recovery frames (10-12), but not during active frames (4-9).\n * \n * @korean 중단가능구간\n */\nexport interface InterruptibilityWindow {\n /** Starting frame (inclusive) */\n readonly startFrame: number;\n /** Ending frame (inclusive) */\n readonly endFrame: number;\n /** Minimum priority required to interrupt during this window */\n readonly minPriorityToInterrupt: AnimationPriority;\n}\n\n/**\n * Check if an animation can be interrupted at a specific frame\n * \n * **Korean**: 프레임별 중단 가능 여부\n * \n * Determines if an animation can be interrupted based on:\n * - Current frame index\n * - Interruptibility windows\n * - Requested animation priority\n * \n * @param currentFrame - Current frame index in animation\n * @param requestedPriority - Priority of animation trying to interrupt\n * @param windows - Array of interruptibility windows\n * @returns Whether animation can be interrupted at this frame\n * \n * @example\n * ```typescript\n * const attackWindows: InterruptibilityWindow[] = [\n * { startFrame: 0, endFrame: 3, minPriorityToInterrupt: AnimationPriority.ATTACK },\n * { startFrame: 10, endFrame: 12, minPriorityToInterrupt: AnimationPriority.DEFEND },\n * ];\n * \n * // Frame 2: interruptible by attacks or higher\n * canInterruptAtFrame(2, AnimationPriority.HIT, attackWindows); // true\n * canInterruptAtFrame(2, AnimationPriority.DEFEND, attackWindows); // false\n * \n * // Frame 5: not interruptible (no window)\n * canInterruptAtFrame(5, AnimationPriority.KO, attackWindows); // false\n * ```\n * \n * @korean 프레임별중단가능여부\n */\nexport function canInterruptAtFrame(\n currentFrame: number,\n requestedPriority: AnimationPriority,\n windows: readonly InterruptibilityWindow[]\n): boolean {\n // Find window that contains current frame\n const activeWindow = windows.find(\n w => currentFrame >= w.startFrame && currentFrame <= w.endFrame\n );\n\n // If no window contains this frame, cannot interrupt\n if (!activeWindow) {\n return false;\n }\n\n // Check if requested priority meets window's minimum\n return requestedPriority >= activeWindow.minPriorityToInterrupt;\n}\n\n/**\n * Get interruptibility window for current frame\n * \n * **Korean**: 현재 프레임 중단 가능 구간\n * \n * Returns the active interruptibility window for the given frame,\n * or null if frame is not in any window.\n * \n * @param currentFrame - Current frame index\n * @param windows - Array of interruptibility windows\n * @returns Active window or null\n * \n * @korean 현재프레임중단가능구간\n */\nexport function getInterruptibilityWindow(\n currentFrame: number,\n windows: readonly InterruptibilityWindow[]\n): InterruptibilityWindow | null {\n return windows.find(\n w => currentFrame >= w.startFrame && currentFrame <= w.endFrame\n ) ?? null;\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"AnimationPriority.js","names":[],"sources":["../../../../src/systems/animation/core/AnimationPriority.ts"],"sourcesContent":["/**\n * Animation priority system for Black Trigram\n * \n * Determines which animations can interrupt others based on priority levels.\n * Higher priority animations can interrupt lower priority ones.\n * \n * Priority order: recovery > fall > ko > hit > attack > defend > stance_change > movement > idle\n * \n * Enhanced with:\n * - Deterministic conflict resolution for simultaneous equal-priority animations\n * - Animation queue system for pending animations\n * - Interruptibility windows (frame-based interrupt control)\n * - Korean terminology for all priority levels and conflicts\n * \n * @module systems/animation/AnimationPriority\n * @category Animation\n * @korean 애니메이션우선순위\n */\n\nimport { AnimationPriority, AnimationState, STEP_PRIORITY } from \"./types\";\n\n/**\n * Map animation states to their priority levels\n * \n * Stance guard animations have same priority as idle (0) since they are\n * also idle states, just stance-specific.\n * Tactical steps have priority 5 (same as attacks) to ensure commitment.\n * \n * @korean 애니메이션우선순위맵\n */\nexport const ANIMATION_PRIORITY_MAP: Record<AnimationState, AnimationPriority> = {\n idle: AnimationPriority.IDLE,\n walk: AnimationPriority.WALK,\n run: AnimationPriority.RUN,\n stance_change: AnimationPriority.STANCE_CHANGE,\n stance_side_switch: AnimationPriority.STANCE_CHANGE, // Same priority as stance_change\n defend: AnimationPriority.DEFEND,\n // Defensive animations (방어 애니메이션)\n defend_block_success: AnimationPriority.HIT, // Priority 6 - same as hit\n defend_parry: AnimationPriority.KO, // Priority 7 - higher than block\n defend_guard_break: AnimationPriority.FALL, // Priority 8 - highest (same as fall)\n defend_recovery: AnimationPriority.RUN, // Priority 2 - interruptible recovery\n attack: AnimationPriority.ATTACK,\n hit: AnimationPriority.HIT,\n ko: AnimationPriority.KO,\n // Stance-specific guard animations (팔괘 방어 자세)\n stance_guard_geon: AnimationPriority.IDLE,\n stance_guard_tae: AnimationPriority.IDLE,\n stance_guard_li: AnimationPriority.IDLE,\n stance_guard_jin: AnimationPriority.IDLE,\n stance_guard_son: AnimationPriority.IDLE,\n stance_guard_gam: AnimationPriority.IDLE,\n stance_guard_gan: AnimationPriority.IDLE,\n stance_guard_gon: AnimationPriority.IDLE,\n // Tactical step animations (전술적 발걸음) - non-interruptible\n step_forward: STEP_PRIORITY,\n step_back: STEP_PRIORITY,\n step_left: STEP_PRIORITY,\n step_right: STEP_PRIORITY,\n step_forward_left: STEP_PRIORITY,\n step_forward_right: STEP_PRIORITY,\n step_back_left: STEP_PRIORITY,\n step_back_right: STEP_PRIORITY,\n // Fall animations (낙법) - highest priority\n fall_forward: AnimationPriority.FALL,\n fall_backward: AnimationPriority.FALL,\n fall_side_left: AnimationPriority.FALL,\n fall_side_right: AnimationPriority.FALL,\n // Ground states (지면 자세) - idle priority\n ground_prone: AnimationPriority.IDLE,\n ground_supine: AnimationPriority.IDLE,\n ground_side_left: AnimationPriority.IDLE,\n ground_side_right: AnimationPriority.IDLE,\n // 180-degree turn animations (180도 회전) - same as steps (committed action)\n turn_left: STEP_PRIORITY,\n turn_right: STEP_PRIORITY,\n // Footwork patterns (보법) - Korean martial arts specialized footwork\n footwork_circular_left: STEP_PRIORITY,\n footwork_circular_right: STEP_PRIORITY,\n footwork_pivot_left: STEP_PRIORITY,\n footwork_pivot_right: STEP_PRIORITY,\n footwork_slide_forward: AnimationPriority.DEFEND,\n footwork_slide_back: AnimationPriority.DEFEND,\n footwork_slide_left: AnimationPriority.DEFEND,\n footwork_slide_right: AnimationPriority.DEFEND,\n footwork_shuffle: AnimationPriority.STANCE_CHANGE,\n // Recovery animations (기상 애니메이션) - highest priority (can interrupt anything)\n recovery_prone_standup: AnimationPriority.RECOVERY,\n recovery_supine_standup: AnimationPriority.RECOVERY,\n recovery_roll: AnimationPriority.RECOVERY,\n recovery_defensive: AnimationPriority.RECOVERY,\n // Grappling animations (잡기 애니메이션) - high priority like attacks/hits\n grapple_entry: AnimationPriority.ATTACK, // Priority 5 - same as attack\n grapple_control: AnimationPriority.ATTACK, // Priority 5 - maintain control\n grapple_struggle: AnimationPriority.HIT, // Priority 6 - struggling escape\n grapple_escape: AnimationPriority.HIT, // Priority 6 - successful escape\n};\n\n/**\n * Check if an animation can interrupt another based on priority\n * \n * @param current - Current animation state\n * @param requested - Requested animation state\n * @param currentInterruptible - Whether current animation is interruptible\n * @returns Whether the requested animation can interrupt the current one\n * \n * @example\n * ```typescript\n * // Hit can interrupt attack\n * canInterrupt(\"attack\", \"hit\", true); // true\n * \n * // Attack cannot interrupt hit\n * canInterrupt(\"hit\", \"attack\", true); // false\n * \n * // Nothing can interrupt non-interruptible animations\n * canInterrupt(\"attack\", \"hit\", false); // false (unless same priority)\n * ```\n * \n * @korean 중단가능여부확인\n */\nexport function canInterrupt(\n current: AnimationState,\n requested: AnimationState,\n currentInterruptible: boolean\n): boolean {\n const currentPriority = ANIMATION_PRIORITY_MAP[current];\n const requestedPriority = ANIMATION_PRIORITY_MAP[requested];\n\n // Same priority animations can always transition\n if (currentPriority === requestedPriority) {\n return true;\n }\n\n // Non-interruptible animations can only be interrupted by higher priority\n if (!currentInterruptible) {\n return requestedPriority > currentPriority;\n }\n\n // Interruptible animations can be interrupted by same or higher priority\n return requestedPriority >= currentPriority;\n}\n\n/**\n * Get the priority level for an animation state\n * \n * @param state - Animation state\n * @returns Priority level\n * \n * @korean 우선순위가져오기\n */\nexport function getPriority(state: AnimationState): AnimationPriority {\n return ANIMATION_PRIORITY_MAP[state];\n}\n\n/**\n * Compare two animation priorities\n * \n * @param state1 - First animation state\n * @param state2 - Second animation state\n * @returns Positive if state1 has higher priority, negative if state2 has higher priority, 0 if equal\n * \n * @korean 우선순위비교\n */\nexport function comparePriority(\n state1: AnimationState,\n state2: AnimationState\n): number {\n return ANIMATION_PRIORITY_MAP[state1] - ANIMATION_PRIORITY_MAP[state2];\n}\n\n// ===== Korean Priority Level Terminology (우선순위 등급 용어) =====\n\n/**\n * Korean names for each priority level\n * \n * Maps priority levels to their Korean martial arts terminology:\n * - IDLE (0): 대기 (Daegi) - Waiting/Ready state\n * - WALK (1): 보행 (Bohaeng) - Walking movement\n * - RUN (2): 질주 (Jilju) - Running movement\n * - STANCE_CHANGE (3): 자세전환 (Jase Jeonhwan) - Stance transition\n * - DEFEND (4): 방어 (Bangeo) - Defense action\n * - ATTACK (5): 공격 (Gonggyeok) - Attack action\n * - HIT (6): 피격 (Pigyeok) - Being hit\n * - KO (7): 기절 (Gijeol) - Knockout\n * - FALL (8): 낙법 (Nakbeop) - Falling technique\n * - RECOVERY (9): 기상 (Gisang) - Recovery/Getting up\n * \n * @korean 우선순위한글용어\n */\nexport const PRIORITY_LEVEL_KOREAN_NAMES: Record<AnimationPriority, {\n korean: string;\n romanized: string;\n english: string;\n}> = {\n [AnimationPriority.IDLE]: {\n korean: \"대기\",\n romanized: \"Daegi\",\n english: \"Ready State\",\n },\n [AnimationPriority.WALK]: {\n korean: \"보행\",\n romanized: \"Bohaeng\",\n english: \"Walking\",\n },\n [AnimationPriority.RUN]: {\n korean: \"질주\",\n romanized: \"Jilju\",\n english: \"Running\",\n },\n [AnimationPriority.STANCE_CHANGE]: {\n korean: \"자세전환\",\n romanized: \"Jase Jeonhwan\",\n english: \"Stance Change\",\n },\n [AnimationPriority.DEFEND]: {\n korean: \"방어\",\n romanized: \"Bangeo\",\n english: \"Defense\",\n },\n [AnimationPriority.ATTACK]: {\n korean: \"공격\",\n romanized: \"Gonggyeok\",\n english: \"Attack\",\n },\n [AnimationPriority.HIT]: {\n korean: \"피격\",\n romanized: \"Pigyeok\",\n english: \"Hit\",\n },\n [AnimationPriority.KO]: {\n korean: \"기절\",\n romanized: \"Gijeol\",\n english: \"Knockout\",\n },\n [AnimationPriority.FALL]: {\n korean: \"낙법\",\n romanized: \"Nakbeop\",\n english: \"Falling\",\n },\n [AnimationPriority.RECOVERY]: {\n korean: \"기상\",\n romanized: \"Gisang\",\n english: \"Recovery\",\n },\n};\n\n/**\n * Get Korean name for a priority level\n * \n * @param priority - Animation priority level\n * @returns Korean terminology object\n * \n * @korean 우선순위한글이름가져오기\n */\nexport function getPriorityKoreanName(priority: AnimationPriority): {\n korean: string;\n romanized: string;\n english: string;\n} {\n return PRIORITY_LEVEL_KOREAN_NAMES[priority];\n}\n\n// ===== Conflict Resolution (충돌 해결) =====\n\n/**\n * Conflict resolution strategy for equal-priority animations\n * \n * **Korean**: 충돌 해결 전략\n * \n * Determines how to handle when two animations of equal priority\n * are requested simultaneously:\n * \n * - `timestamp`: First animation requested wins (FIFO - 선입선출)\n * - `state_order`: Use AnimationState enum order as tiebreaker\n * - `current`: Keep current animation running\n * - `requested`: Always switch to requested animation\n * \n * @korean 충돌해결전략\n */\nexport type ConflictResolutionStrategy =\n | \"timestamp\" // 시간순서 (First requested wins)\n | \"state_order\" // 상태순서 (Enum order tiebreaker)\n | \"current\" // 현재유지 (Keep current)\n | \"requested\"; // 요청수락 (Accept requested)\n\n/**\n * Animation request with timestamp for conflict resolution\n * \n * **Korean**: 애니메이션 요청\n * \n * Tracks animation requests with timestamps to enable deterministic\n * conflict resolution when multiple equal-priority animations compete.\n * \n * @korean 애니메이션요청\n */\nexport interface AnimationRequest {\n /** Requested animation state */\n readonly state: AnimationState;\n /** Request timestamp in milliseconds (from performance.now()) */\n readonly timestamp: number;\n /** Request priority */\n readonly priority: AnimationPriority;\n /** \n * Whether this is a forced request (wins conflict resolution against non-forced requests)\n * \n * Note: This only affects conflict resolution between equal-priority animations in the queue.\n * It does not bypass priority checks or interrupt rules for the initial transition.\n * \n * @korean 강제요청\n */\n readonly forced?: boolean;\n}\n\n/**\n * Resolve conflict between two equal-priority animations\n * \n * **Korean**: 충돌 해결\n * \n * Uses the specified strategy to deterministically decide which animation\n * should play when both have equal priority.\n * \n * @param current - Current animation request\n * @param requested - Requested animation request\n * @param strategy - Conflict resolution strategy\n * @returns Which animation should play: 'current' or 'requested'\n * \n * @example\n * ```typescript\n * const current = {\n * state: AnimationState.ATTACK,\n * timestamp: 1000,\n * priority: AnimationPriority.ATTACK,\n * };\n * const requested = {\n * state: AnimationState.STEP_FORWARD,\n * timestamp: 1001,\n * priority: AnimationPriority.ATTACK,\n * };\n * \n * // Timestamp strategy: current wins (1000 < 1001)\n * resolveConflict(current, requested, \"timestamp\"); // \"current\"\n * \n * // Requested strategy: always take new animation\n * resolveConflict(current, requested, \"requested\"); // \"requested\"\n * ```\n * \n * @korean 충돌해결\n */\nexport function resolveConflict(\n current: AnimationRequest,\n requested: AnimationRequest,\n strategy: ConflictResolutionStrategy = \"timestamp\"\n): \"current\" | \"requested\" {\n // If only one is forced, forced wins\n if (requested.forced && !current.forced) {\n return \"requested\";\n }\n if (current.forced && !requested.forced) {\n return \"current\";\n }\n \n // If both are forced OR both are normal, use conflict resolution strategy\n\n // Apply conflict resolution strategy\n switch (strategy) {\n case \"timestamp\":\n // Earlier timestamp wins (FIFO)\n return current.timestamp <= requested.timestamp ? \"current\" : \"requested\";\n \n case \"state_order\":\n // Use enum value comparison as tiebreaker\n return current.state <= requested.state ? \"current\" : \"requested\";\n \n case \"current\":\n // Always keep current animation\n return \"current\";\n \n case \"requested\":\n // Always accept requested animation\n return \"requested\";\n \n default:\n // Default to timestamp strategy\n return current.timestamp <= requested.timestamp ? \"current\" : \"requested\";\n }\n}\n\n// ===== Animation Queue System (애니메이션 대기열) =====\n\n/**\n * Animation queue for managing pending animations\n * \n * **Korean**: 애니메이션 대기열\n * \n * Stores pending animation requests that should play after the current\n * animation completes. Useful for:\n * - Buffering input during non-interruptible animations\n * - Creating animation chains/combos\n * - Handling rapid input sequences\n * \n * Queue is priority-ordered: highest priority animations are dequeued first.\n * \n * @korean 애니메이션대기열\n */\nexport class AnimationQueue {\n private queue: AnimationRequest[] = [];\n private maxSize: number;\n private conflictStrategy: ConflictResolutionStrategy;\n\n /**\n * Create a new animation queue\n * \n * @param maxSize - Maximum queue size (default: 3)\n * @param conflictStrategy - Strategy for resolving equal-priority conflicts\n * \n * @korean 생성자\n */\n constructor(\n maxSize: number = 3,\n conflictStrategy: ConflictResolutionStrategy = \"timestamp\"\n ) {\n this.maxSize = maxSize;\n this.conflictStrategy = conflictStrategy;\n }\n\n /**\n * Add animation request to queue\n * \n * **Korean**: 대기열에 추가\n * \n * Adds an animation request to the queue if space is available.\n * Queue is kept sorted by priority (highest first).\n * \n * @param request - Animation request to enqueue\n * @returns Whether request was successfully enqueued\n * \n * @korean 대기열추가\n */\n enqueue(request: AnimationRequest): boolean {\n // Check if queue is full\n if (this.queue.length >= this.maxSize) {\n // Queue is sorted by priority (highest first), so last element is lowest priority\n const lowestPriorityItem = this.queue[this.queue.length - 1];\n \n if (request.priority > lowestPriorityItem.priority) {\n // Remove lowest priority item to make space\n this.queue.pop();\n } else {\n return false; // Queue full, request discarded\n }\n }\n\n // Insert request into queue while maintaining sort by priority (highest first).\n // Use binary search (O(log n)) to find insertion position, then splice (O(n)) to insert.\n // Overall: O(n) per insertion, avoiding an O(n log n) full sort of the entire queue.\n let low = 0;\n let high = this.queue.length;\n\n // Binary search to find insertion index\n while (low < high) {\n const mid = (low + high) >> 1;\n const midPriority = this.queue[mid].priority;\n\n if (midPriority < request.priority) {\n // New request has higher priority; search left half\n high = mid;\n } else {\n // New request has equal or lower priority; search right half\n low = mid + 1;\n }\n }\n\n // Insert at computed index to keep queue sorted (highest priority first)\n this.queue.splice(low, 0, request);\n \n return true;\n }\n\n /**\n * Remove and return highest priority request from queue\n * \n * **Korean**: 대기열에서 제거\n * \n * Dequeues the highest priority animation request.\n * If multiple requests have equal priority, uses conflict resolution strategy.\n * \n * @returns Next animation request or null if queue is empty\n * \n * @korean 대기열제거\n */\n dequeue(): AnimationRequest | null {\n if (this.queue.length === 0) {\n return null;\n }\n\n // Get all requests with highest priority\n const highestPriority = this.queue[0].priority;\n const highestPriorityRequests = this.queue.filter(\n r => r.priority === highestPriority\n );\n\n let selectedRequest: AnimationRequest;\n\n if (highestPriorityRequests.length === 1) {\n selectedRequest = highestPriorityRequests[0];\n } else {\n // Multiple equal-priority requests - use conflict resolution\n selectedRequest = highestPriorityRequests.reduce((current, next) => {\n const winner = resolveConflict(current, next, this.conflictStrategy);\n return winner === \"current\" ? current : next;\n });\n }\n\n // Remove selected request from queue\n const index = this.queue.indexOf(selectedRequest);\n this.queue.splice(index, 1);\n\n return selectedRequest;\n }\n\n /**\n * Peek at next request without removing it\n * \n * **Korean**: 다음 요청 확인\n * \n * @returns Next animation request or null if queue is empty\n * \n * @korean 다음요청확인\n */\n peek(): AnimationRequest | null {\n return this.queue.length > 0 ? this.queue[0] : null;\n }\n\n /**\n * Clear all pending requests\n * \n * **Korean**: 대기열 초기화\n * \n * @korean 대기열초기화\n */\n clear(): void {\n this.queue = [];\n }\n\n /**\n * Get number of pending requests\n * \n * **Korean**: 대기열 크기\n * \n * @returns Number of pending requests\n * \n * @korean 대기열크기\n */\n size(): number {\n return this.queue.length;\n }\n\n /**\n * Check if queue is empty\n * \n * **Korean**: 대기열 비어있음\n * \n * @returns True if queue has no pending requests\n * \n * @korean 대기열비어있음\n */\n isEmpty(): boolean {\n return this.queue.length === 0;\n }\n\n /**\n * Check if queue is full\n * \n * **Korean**: 대기열 가득참\n * \n * @returns True if queue is at maximum capacity\n * \n * @korean 대기열가득찬\n */\n isFull(): boolean {\n return this.queue.length >= this.maxSize;\n }\n\n /**\n * Get all pending requests (read-only)\n * \n * **Korean**: 모든 대기 요청\n * \n * @returns Array of pending requests\n * \n * @korean 모든대기요청\n */\n getAll(): readonly AnimationRequest[] {\n return [...this.queue];\n }\n\n /**\n * Get maximum queue size\n * \n * **Korean**: 최대 대기열 크기\n * \n * @returns Maximum queue capacity\n * \n * @korean 최대대기열크기\n */\n getMaxSize(): number {\n return this.maxSize;\n }\n\n /**\n * Set conflict resolution strategy\n * \n * **Korean**: 충돌 해결 전략 설정\n * \n * Updates the strategy used to resolve equal-priority conflicts.\n * \n * @param strategy - New conflict resolution strategy\n * \n * @korean 충돌해결전략설정\n */\n setConflictStrategy(strategy: ConflictResolutionStrategy): void {\n this.conflictStrategy = strategy;\n }\n}\n\n// ===== Interruptibility Windows (중단 가능 구간) =====\n\n/**\n * Interruptibility window definition\n * \n * **Korean**: 중단 가능 구간\n * \n * Defines specific frame ranges where an animation can be interrupted,\n * allowing for more nuanced control than simple boolean interruptibility.\n * \n * Example: Attack animation might be interruptible only in startup frames (0-3)\n * and recovery frames (10-12), but not during active frames (4-9).\n * \n * @korean 중단가능구간\n */\nexport interface InterruptibilityWindow {\n /** Starting frame (inclusive) */\n readonly startFrame: number;\n /** Ending frame (inclusive) */\n readonly endFrame: number;\n /** Minimum priority required to interrupt during this window */\n readonly minPriorityToInterrupt: AnimationPriority;\n}\n\n/**\n * Check if an animation can be interrupted at a specific frame\n * \n * **Korean**: 프레임별 중단 가능 여부\n * \n * Determines if an animation can be interrupted based on:\n * - Current frame index\n * - Interruptibility windows\n * - Requested animation priority\n * \n * @param currentFrame - Current frame index in animation\n * @param requestedPriority - Priority of animation trying to interrupt\n * @param windows - Array of interruptibility windows\n * @returns Whether animation can be interrupted at this frame\n * \n * @example\n * ```typescript\n * const attackWindows: InterruptibilityWindow[] = [\n * { startFrame: 0, endFrame: 3, minPriorityToInterrupt: AnimationPriority.ATTACK },\n * { startFrame: 10, endFrame: 12, minPriorityToInterrupt: AnimationPriority.DEFEND },\n * ];\n * \n * // Frame 2: interruptible by attacks or higher\n * canInterruptAtFrame(2, AnimationPriority.HIT, attackWindows); // true\n * canInterruptAtFrame(2, AnimationPriority.DEFEND, attackWindows); // false\n * \n * // Frame 5: not interruptible (no window)\n * canInterruptAtFrame(5, AnimationPriority.KO, attackWindows); // false\n * ```\n * \n * @korean 프레임별중단가능여부\n */\nexport function canInterruptAtFrame(\n currentFrame: number,\n requestedPriority: AnimationPriority,\n windows: readonly InterruptibilityWindow[]\n): boolean {\n // Find window that contains current frame\n const activeWindow = windows.find(\n w => currentFrame >= w.startFrame && currentFrame <= w.endFrame\n );\n\n // If no window contains this frame, cannot interrupt\n if (!activeWindow) {\n return false;\n }\n\n // Check if requested priority meets window's minimum\n return requestedPriority >= activeWindow.minPriorityToInterrupt;\n}\n\n/**\n * Get interruptibility window for current frame\n * \n * **Korean**: 현재 프레임 중단 가능 구간\n * \n * Returns the active interruptibility window for the given frame,\n * or null if frame is not in any window.\n * \n * @param currentFrame - Current frame index\n * @param windows - Array of interruptibility windows\n * @returns Active window or null\n * \n * @korean 현재프레임중단가능구간\n */\nexport function getInterruptibilityWindow(\n currentFrame: number,\n windows: readonly InterruptibilityWindow[]\n): InterruptibilityWindow | null {\n return windows.find(\n w => currentFrame >= w.startFrame && currentFrame <= w.endFrame\n ) ?? null;\n}\n"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"AnimationRegistry.js","names":[],"sources":["../../../../src/systems/animation/core/AnimationRegistry.ts"],"sourcesContent":["/**\n * Animation Registry\n *\n * Central registry for all martial arts animations.\n * Combines all animation modules and provides unified access.\n *\n * 무술 애니메이션 레지스트리 - 모든 애니메이션 통합 관리\n *\n * **RECOVERY PHASE INTEGRATION (복귀 단계 통합)**\n *\n * Enhanced animations with realistic recovery phases are now the default for:\n * - JAB_ANIMATION → JAB_ANIMATION_ENHANCED (200ms recovery)\n * - CROSS_ANIMATION → CROSS_ANIMATION_ENHANCED (220ms recovery)\n * - FRONT_KICK_ANIMATION → FRONT_KICK_ANIMATION_ENHANCED (170ms recovery)\n * - ROUNDHOUSE_KICK_ANIMATION → ROUNDHOUSE_KICK_ANIMATION_ENHANCED (180ms recovery)\n * - ELBOW_STRIKE_ANIMATION → ELBOW_STRIKE_ANIMATION_ENHANCED (160ms recovery)\n * - ELBOW_UPPERCUT_ANIMATION → ELBOW_UPPERCUT_ANIMATION_ENHANCED (170ms recovery)\n * - KNEE_STRIKE_ANIMATION → KNEE_STRIKE_ANIMATION_ENHANCED (190ms recovery)\n *\n * Enhanced animations follow Korean martial arts principles (복귀/Bokgwi):\n * - 균형회복 (Gyunhyeong Hoebog) - Balance restoration\n * - 자세복귀 (Jase Bokgwi) - Stance return\n * - 호흡조절 (Hoheup Jojoel) - Breath control during recovery\n * - 근육이완 (Geunryuk Ihwan) - Muscle relaxation after tension\n *\n * All code using ANIMATION_REGISTRY or getAnimationForTechnique() will\n * automatically use enhanced versions with no code changes required.\n *\n * @module systems/animation/AnimationRegistry\n * @korean 애니메이션레지스트리\n */\n\nimport type { SkeletalAnimation } from \"@/types/skeletal\";\nimport { AnimationType } from \"../builders/MartialArtsAnimationBuilder\";\nimport {\n BACKWARD_RETREAT_ANIMATION,\n FORWARD_DASH_ANIMATION,\n IDLE_STANCE_ANIMATION,\n SIDE_STEP_ANIMATION,\n} from \"../catalogs/AttackAnimations\";\nimport { BASIC_ANIMATIONS } from \"../catalogs/BasicAnimations\";\nimport { COMBO_ANIMATIONS } from \"../catalogs/ComboAnimations\";\nimport { DARKOPS_ANIMATIONS } from \"../catalogs/DarkOpsAnimations\";\nimport {\n GAM_FLOW_DEFENSE,\n GAN_COUNTER_FORTRESS,\n GAN_IMMOVABLE_BLOCK,\n GEON_COUNTER_STRIKE,\n GEON_HIGH_BLOCK,\n GON_GROUNDING_DEFENSE,\n GON_TAKEDOWN_COUNTER,\n JIN_EXPLOSIVE_BLOCK,\n JIN_SHOCKING_COUNTER,\n LI_NERVE_STRIKE_COUNTER,\n LI_PRECISION_PARRY,\n SON_CONTINUOUS_DEFLECTION,\n SON_PRESSURE_COUNTER,\n TAE_SWEEP_DEFENSE,\n} from \"../catalogs/DefensiveAnimations\";\nimport {\n BRACHIAL_ELBOW_ANIMATION,\n CLINCH_KNEE_ANIMATION,\n ELBOW_KNEE_ANIMATIONS,\n ELBOW_STRIKE_ANIMATION,\n ELBOW_UPPERCUT_ANIMATION,\n FEMORAL_KNEE_ANIMATION,\n FLYING_KNEE_ANIMATION,\n KIDNEY_KNEE_ANIMATION,\n KNEE_KICK_ANIMATION,\n KNEE_STRIKE_ANIMATION,\n SPINAL_ELBOW_ANIMATION,\n SPINNING_BACK_ELBOW_ANIMATION,\n SPINNING_ELBOW_ANIMATION,\n TEMPLE_ELBOW_ANIMATION,\n} from \"../catalogs/ElbowKneeAnimations\";\nimport {\n ARM_BAR_ANIMATION,\n BLOCK_ANIMATION,\n BODY_LOCK_THROW_ANIMATION,\n CAROTID_CHOKE_ANIMATION,\n COUNTER_ATTACK_ANIMATION,\n COUNTER_STRIKE_ANIMATION,\n EARTH_EMBRACE_ANIMATION,\n ELBOW_LOCK_ANIMATION,\n FINGER_LOCK_ANIMATION,\n FLOWING_ARM_BAR_ANIMATION,\n GRAPPLE_ANIMATION,\n GRAPPLING_ANIMATIONS,\n HIGH_BLOCK_ANIMATION,\n HIP_THROW_ANIMATION,\n JOINT_LOCK_DEFENSE_ANIMATION,\n LEG_REAP_ANIMATION,\n LOW_BLOCK_ANIMATION,\n MOUNTAIN_LOCK_ANIMATION,\n PARRY_COUNTER_ANIMATION,\n REAR_NAKED_CHOKE_ANIMATION,\n REDIRECT_THROW_ANIMATION,\n SHOULDER_LOCK_ANIMATION,\n SHOULDER_MANIPULATION_ANIMATION,\n SLAM_ANIMATION,\n SMALL_CIRCLE_LOCK_ANIMATION,\n SUPLEX_ANIMATION,\n SWEEP_DEFENSE_ANIMATION,\n TAKEDOWN_ANIMATION,\n THROW_ANIMATION,\n WRIST_LOCK_ANIMATION,\n} from \"../catalogs/GrapplingAnimations\";\nimport {\n AXE_KICK_ANIMATION,\n BACK_KICK_ANIMATION,\n CRESCENT_KICK_ANIMATION,\n FLYING_KICK_ANIMATION,\n FRONT_KICK_ANIMATION,\n JUMPING_KICK_ANIMATION,\n KICK_ANIMATIONS,\n LOW_KICK_ANIMATION,\n PUSH_KICK_ANIMATION,\n ROUNDHOUSE_KICK_ANIMATION,\n SIDE_KICK_ANIMATION,\n SPINNING_HEEL_KICK_ANIMATION,\n SPINNING_HOOK_KICK_ANIMATION,\n SWEEP_ANIMATION,\n TORNADO_KICK_ANIMATION,\n} from \"../catalogs/KickAnimations\";\nimport { MOVEMENT_ANIMATIONS } from \"../catalogs/MovementAnimations\";\nimport {\n BACKFIST_ANIMATION,\n CROSS_ANIMATION,\n HAMMER_FIST_ANIMATION,\n HOOK_ANIMATION,\n JAB_ANIMATION,\n OVERHAND_ANIMATION,\n PALM_STRIKE_ANIMATION,\n PUNCH_ANIMATIONS,\n UPPERCUT_ANIMATION,\n} from \"../catalogs/PunchAnimations\";\n\nimport { STANCE_ANIMATIONS } from \"../catalogs/StanceAnimations\";\nimport { ALL_ATTACK_ANIMATIONS } from \"../catalogs/StanceAttackAnimations\";\nimport { TRIGRAM_IDLE_ANIMATIONS_BY_NAME } from \"../catalogs/StanceIdleAnimations\";\nimport { STANCE_LOCOMOTION_ANIMATIONS } from \"../catalogs/StanceLocomotionAnimations\";\n\n// Trigram-specific stance and technique animation maps\nimport { GAM_WATER_FLOW_COUNTER_ANIMATION } from \"../catalogs/GamTechniqueAnimations\";\nimport { GAN_ROCK_DEFENSE_ANIMATION } from \"../catalogs/GanTechniqueAnimations\";\nimport { GAN_STANCE_ANIMATIONS } from \"../catalogs/GanStanceAnimations\";\nimport { GAN_TECHNIQUE_ANIMATIONS } from \"../catalogs/GanTechniqueAnimations\";\nimport { GEON_ANIMATIONS } from \"../catalogs/GeonStanceAnimations\";\nimport { JIN_ANIMATIONS } from \"../catalogs/JinStanceAnimations\";\nimport { JIN_TECHNIQUE_ANIMATIONS } from \"../catalogs/JinTechniqueAnimations\";\nimport { SON_STANCE_ANIMATIONS } from \"../catalogs/SonStanceAnimations\";\nimport { SON_TECHNIQUE_ANIMATIONS } from \"../catalogs/SonTechniqueAnimations\";\nimport { TAE_STANCE_ANIMATIONS } from \"../catalogs/TaeStanceAnimations\";\nimport {\n getAnimationForTechniqueOrDefault,\n getAnimationForTechnique as getTechniqueAnimationConfig,\n hasAnimationMapping,\n type AnimationConfig,\n} from \"./TechniqueAnimationMapping\";\n\n// Enhanced animations with recovery phases (복귀 애니메이션 강화)\nimport {\n CROSS_ANIMATION_ENHANCED,\n FRONT_KICK_ANIMATION_ENHANCED,\n JAB_ANIMATION_ENHANCED,\n ROUNDHOUSE_KICK_ANIMATION_ENHANCED,\n} from \"../catalogs/EnhancedAttackAnimations\";\nimport {\n ELBOW_STRIKE_ANIMATION_ENHANCED,\n ELBOW_UPPERCUT_ANIMATION_ENHANCED,\n KNEE_STRIKE_ANIMATION_ENHANCED,\n} from \"../catalogs/EnhancedElbowKneeAnimations\";\n\nimport {\n TAE_ARM_BAR,\n TAE_ELBOW_CONTROL,\n TAE_FINGER_LOCK,\n TAE_FLOWING_COUNTER,\n TAE_FLOWING_STRIKES,\n TAE_SHOULDER_LOCK,\n TAE_SMALL_CIRCLE,\n TAE_WRIST_LOCK_SEQUENCE,\n} from \"../catalogs/TaeJointLockAnimations\";\n\n// TODO: Register Tae stance animations when trigram-specific idle/movement system is implemented\n// import {\n// TAE_IDLE_FLOWING,\n// TAE_CIRCULAR_SIDESTEP,\n// TAE_DIAGONAL_CIRCULAR_APPROACH,\n// TAE_FLEXIBLE_GUARD_TRANSITION,\n// } from \"../catalogs/TaeStanceAnimations\";\n\nimport {\n GAM_FLOWING_BLOCK,\n GAM_FLOWING_RIVER_STRIKE,\n GAM_REDIRECTION_COUNTER,\n GAM_TIDAL_WAVE_PALM,\n GAM_WHIRLPOOL_COUNTER,\n} from \"../catalogs/GamRedirectionAnimations\";\n\n// Specialized punch variant animations (특수 주먹 변형 애니메이션)\nimport {\n EAR_STRIKE_ANIMATION,\n EYE_GOUGE_ANIMATION,\n FLOWING_CROSS_ANIMATION,\n FLOWING_PUSH_ANIMATION,\n HEAVEN_STRIKE_ANIMATION,\n LIGHTNING_STRIKE_ANIMATION,\n LIVER_DISRUPTION_ANIMATION,\n NERVE_PARALYSIS_ANIMATION,\n NERVE_STRIKE_ANIMATION,\n PRESSURE_POINT_STRIKE_ANIMATION,\n RAPID_BARRAGE_ANIMATION,\n RHYTHMIC_STRIKES_ANIMATION,\n SOLAR_PLEXUS_STRIKE_ANIMATION,\n SPEAR_HAND_STRIKE_ANIMATION as SPEAR_HAND_ANIMATION,\n SPECIALIZED_PUNCH_ANIMATIONS,\n THROAT_STRIKE_ANIMATION,\n} from \"../catalogs/SpecializedPunchAnimations\";\n\n// ═══════════════════════════════════════════════════════════════════════════\n// MASTER ANIMATION REGISTRY\n// ═══════════════════════════════════════════════════════════════════════════\n\n/**\n * Master registry of all animations by AnimationType\n * 애니메이션 타입별 마스터 레지스트리\n */\nexport const ANIMATION_REGISTRY: ReadonlyMap<AnimationType, SkeletalAnimation> =\n new Map([\n // Kicks (발차기) - Using enhanced versions with recovery phases\n [AnimationType.FRONT_KICK, FRONT_KICK_ANIMATION_ENHANCED],\n [AnimationType.ROUNDHOUSE_KICK, ROUNDHOUSE_KICK_ANIMATION_ENHANCED],\n [AnimationType.SIDE_KICK, SIDE_KICK_ANIMATION],\n [AnimationType.AXE_KICK, AXE_KICK_ANIMATION],\n [AnimationType.BACK_KICK, BACK_KICK_ANIMATION],\n [AnimationType.TORNADO_KICK, TORNADO_KICK_ANIMATION],\n [AnimationType.JUMPING_KICK, JUMPING_KICK_ANIMATION],\n [AnimationType.LOW_KICK, LOW_KICK_ANIMATION],\n [AnimationType.CRESCENT_KICK, CRESCENT_KICK_ANIMATION],\n [AnimationType.PUSH_KICK, PUSH_KICK_ANIMATION],\n [AnimationType.SPINNING_HEEL_KICK, SPINNING_HEEL_KICK_ANIMATION],\n [AnimationType.SPINNING_HOOK, SPINNING_HOOK_KICK_ANIMATION],\n [AnimationType.FLYING_KICK, FLYING_KICK_ANIMATION],\n\n // Punches (주먹) - Using enhanced versions with recovery phases\n [AnimationType.JAB, JAB_ANIMATION_ENHANCED],\n [AnimationType.CROSS, CROSS_ANIMATION_ENHANCED],\n [AnimationType.HOOK, HOOK_ANIMATION],\n [AnimationType.UPPERCUT, UPPERCUT_ANIMATION],\n [AnimationType.OVERHAND, OVERHAND_ANIMATION],\n [AnimationType.PALM_STRIKE, PALM_STRIKE_ANIMATION],\n [AnimationType.BACKFIST, BACKFIST_ANIMATION],\n [AnimationType.HAMMER_FIST, HAMMER_FIST_ANIMATION],\n\n // Specialized Punch Variants (특수 주먹 변형)\n [AnimationType.SPEAR_HAND_STRIKE, SPEAR_HAND_ANIMATION],\n [AnimationType.NERVE_STRIKE, NERVE_STRIKE_ANIMATION],\n [AnimationType.PRESSURE_POINT_STRIKE, PRESSURE_POINT_STRIKE_ANIMATION],\n [AnimationType.LIGHTNING_STRIKE, LIGHTNING_STRIKE_ANIMATION],\n [AnimationType.HEAVEN_STRIKE, HEAVEN_STRIKE_ANIMATION],\n [AnimationType.FLOWING_CROSS, FLOWING_CROSS_ANIMATION],\n [AnimationType.RAPID_BARRAGE, RAPID_BARRAGE_ANIMATION],\n [AnimationType.RHYTHMIC_STRIKES, RHYTHMIC_STRIKES_ANIMATION],\n [AnimationType.FLOWING_PUSH, FLOWING_PUSH_ANIMATION],\n [AnimationType.SOLAR_PLEXUS_STRIKE, SOLAR_PLEXUS_STRIKE_ANIMATION],\n [AnimationType.THROAT_STRIKE, THROAT_STRIKE_ANIMATION],\n [AnimationType.EYE_GOUGE, EYE_GOUGE_ANIMATION],\n [AnimationType.NERVE_PARALYSIS, NERVE_PARALYSIS_ANIMATION],\n [AnimationType.LIVER_DISRUPTION, LIVER_DISRUPTION_ANIMATION],\n [AnimationType.EAR_STRIKE, EAR_STRIKE_ANIMATION],\n\n // ═══ TRIGRAM SPECIFIC ALIASES (괘별 기술 매핑) ═══\n // Geon (건)\n [AnimationType.GEON_HEAVEN_STRIKE, HEAVEN_STRIKE_ANIMATION],\n [AnimationType.GEON_ROUNDHOUSE, ROUNDHOUSE_KICK_ANIMATION_ENHANCED],\n [AnimationType.CRUSHING_ELBOW, ELBOW_STRIKE_ANIMATION_ENHANCED],\n [AnimationType.GEON_COUNTER, GEON_COUNTER_STRIKE],\n [AnimationType.HIGH_BLOCK, GEON_HIGH_BLOCK],\n\n // Li (리)\n [AnimationType.PHOENIX_EYE_STRIKE, NERVE_STRIKE_ANIMATION],\n [AnimationType.NERVE_STRIKE_COUNTER, LI_NERVE_STRIKE_COUNTER],\n [AnimationType.PRECISION_PARRY, LI_PRECISION_PARRY],\n [AnimationType.LI_SOLAR_PLEXUS, SOLAR_PLEXUS_STRIKE_ANIMATION],\n [AnimationType.SOLAR_PLEXUS_SPEAR, SPEAR_HAND_ANIMATION],\n\n // Tae (태) - Lake: Joint Manipulation\n [AnimationType.WRIST_LOCK_STRIKE, WRIST_LOCK_ANIMATION],\n [AnimationType.WRIST_LOCK, TAE_WRIST_LOCK_SEQUENCE], // Enhanced Hapkido wrist lock\n [AnimationType.SPIRAL_SHOULDER_THROW, HIP_THROW_ANIMATION],\n [AnimationType.JOINT_LOCK_DEFENSE, TAE_FLOWING_COUNTER],\n [AnimationType.SWEEP_DEFENSE, TAE_SWEEP_DEFENSE],\n [AnimationType.WRIST_TWIST_COUNTER, TAE_FLOWING_COUNTER],\n [AnimationType.FLOWING_CROSS, TAE_FLOWING_STRIKES], // Continuous palm strikes\n [AnimationType.SMALL_CIRCLE_LOCK, TAE_SMALL_CIRCLE], // Advanced dual joint control\n [AnimationType.FINGER_LOCK, TAE_FINGER_LOCK],\n [AnimationType.ELBOW_LOCK, TAE_ELBOW_CONTROL], // Enhanced two-handed control\n [AnimationType.ELBOW_HYPEREXTEND, TAE_ELBOW_CONTROL],\n [AnimationType.SHOULDER_MANIPULATION, TAE_SHOULDER_LOCK], // Enhanced shoulder lock\n [AnimationType.ARM_BAR, TAE_ARM_BAR], // Most powerful submission\n [AnimationType.FLOWING_ARM_BAR, TAE_ARM_BAR], // Alias for flowing arm bar\n\n // Jin (진)\n [AnimationType.EXPLOSIVE_KNEE, KNEE_STRIKE_ANIMATION_ENHANCED],\n [AnimationType.LIGHTNING_STRAIGHT, JAB_ANIMATION_ENHANCED],\n [AnimationType.SHOCKING_COUNTER, JIN_SHOCKING_COUNTER],\n [AnimationType.SHOCKING_HAMMER_FIST, HAMMER_FIST_ANIMATION],\n [AnimationType.EXPLOSIVE_BLOCK, JIN_EXPLOSIVE_BLOCK],\n\n // Son (손)\n [AnimationType.CONTINUOUS_DEFLECTION, SON_CONTINUOUS_DEFLECTION],\n [AnimationType.PRESSURE_COUNTER, SON_PRESSURE_COUNTER],\n [AnimationType.RAPID_FOOTWORK, FLOWING_PUSH_ANIMATION], // Placeholder until dedicated footwork\n [AnimationType.PENETRATING_PALM_RUSH, FLOWING_PUSH_ANIMATION],\n [AnimationType.PRESSURE_POINT_CHAIN, RAPID_BARRAGE_ANIMATION],\n\n // Gam (감)\n [AnimationType.WATER_COUNTER, GAM_REDIRECTION_COUNTER],\n [AnimationType.CIRCULAR_PARRY, GAM_FLOWING_BLOCK], // Updated to Flowing Block\n [AnimationType.FLOW_DEFENSE, GAM_FLOWING_BLOCK], // Updated to Flowing Block\n [AnimationType.FLOWING_RIVER_STRIKE, GAM_FLOWING_RIVER_STRIKE],\n [AnimationType.REDIRECTION_COUNTER, GAM_REDIRECTION_COUNTER],\n [AnimationType.TIDAL_WAVE_PALM, GAM_TIDAL_WAVE_PALM],\n [AnimationType.WHIRLPOOL_COUNTER, GAM_WHIRLPOOL_COUNTER],\n\n // Gan (간)\n [AnimationType.ROCK_COUNTER, GAN_COUNTER_FORTRESS],\n [AnimationType.ROCK_DEFENSE, GAN_IMMOVABLE_BLOCK],\n [AnimationType.AVALANCHE_HAMMER, HAMMER_FIST_ANIMATION],\n [AnimationType.COUNTER_FORTRESS, GAN_COUNTER_FORTRESS],\n [AnimationType.FORTRESS_COUNTER_STRIKE, COUNTER_STRIKE_ANIMATION],\n [AnimationType.STONE_WALL_THRUST, PUSH_KICK_ANIMATION],\n\n // General Grappling\n [AnimationType.TAKEDOWN, TAKEDOWN_ANIMATION],\n [AnimationType.JOINT_LOCK, JOINT_LOCK_DEFENSE_ANIMATION],\n [AnimationType.HIP_WHEEL_THROW, HIP_THROW_ANIMATION],\n [AnimationType.SSIREUM_THROW, BODY_LOCK_THROW_ANIMATION], // Korean wrestling style\n [AnimationType.SACRIFICE_THROW, SUPLEX_ANIMATION], // Close match\n [AnimationType.CLINCH, CLINCH_KNEE_ANIMATION],\n [AnimationType.PARRY, PARRY_COUNTER_ANIMATION],\n\n // Movement/Basic\n [AnimationType.SIDESTEP, SIDE_STEP_ANIMATION],\n [AnimationType.PIVOT, SIDE_STEP_ANIMATION], // Reusing side step\n [AnimationType.STEP_FORWARD, FORWARD_DASH_ANIMATION],\n [AnimationType.STEP_BACK, BACKWARD_RETREAT_ANIMATION],\n [AnimationType.FORWARD_DASH, FORWARD_DASH_ANIMATION],\n [AnimationType.BACKWARD_RETREAT, BACKWARD_RETREAT_ANIMATION],\n [AnimationType.WALK, FORWARD_DASH_ANIMATION], // Placeholder\n [AnimationType.RECOVERY, IDLE_STANCE_ANIMATION],\n [AnimationType.STANCE, IDLE_STANCE_ANIMATION],\n [AnimationType.IDLE, IDLE_STANCE_ANIMATION],\n [AnimationType.DUCK, IDLE_STANCE_ANIMATION], // Placeholder\n [AnimationType.LEAN, IDLE_STANCE_ANIMATION], // Placeholder\n\n // Defense Aliases\n [AnimationType.BLOCK, BLOCK_ANIMATION],\n [AnimationType.BLOCK_HIGH, HIGH_BLOCK_ANIMATION],\n [AnimationType.BLOCK_LOW, LOW_BLOCK_ANIMATION],\n [AnimationType.FLOWING_BLOCK, GAM_FLOWING_BLOCK],\n [AnimationType.IRON_BLOCK, GAN_IMMOVABLE_BLOCK],\n [AnimationType.IMMOVABLE_BLOCK, GAN_IMMOVABLE_BLOCK],\n [AnimationType.THUNDEROUS_UPPERCUT, ELBOW_UPPERCUT_ANIMATION],\n\n // Gon (곤)\n [AnimationType.GROUNDING_DEFENSE, GON_GROUNDING_DEFENSE],\n [AnimationType.TAKEDOWN_COUNTER, GON_TAKEDOWN_COUNTER],\n [AnimationType.EARTHQUAKE_STOMP, AXE_KICK_ANIMATION],\n [AnimationType.GROUND_SWEEP_STRIKE, SWEEP_ANIMATION],\n [AnimationType.ROOTING_TAKEDOWN, TAKEDOWN_ANIMATION],\n [AnimationType.TAKEDOWN_COUNTER, GON_TAKEDOWN_COUNTER],\n [AnimationType.ANKLE_PICK, TAKEDOWN_ANIMATION],\n [AnimationType.BODY_LOCK_SLAM, BODY_LOCK_THROW_ANIMATION],\n\n // Dark Ops (암살자)\n [AnimationType.TEMPLE_STRIKE, TEMPLE_ELBOW_ANIMATION],\n [AnimationType.BRACHIAL_PLEXUS, BRACHIAL_ELBOW_ANIMATION],\n [AnimationType.ACHILLES_ATTACK, LEG_REAP_ANIMATION],\n [AnimationType.CERVICAL_TWIST, REAR_NAKED_CHOKE_ANIMATION],\n [AnimationType.ELBOW_HYPEREXTEND, ARM_BAR_ANIMATION],\n [AnimationType.FEMORAL_NERVE, FEMORAL_KNEE_ANIMATION],\n [AnimationType.LARYNX_CRUSH, THROAT_STRIKE_ANIMATION],\n [AnimationType.JAW_DISLOCATION, HOOK_ANIMATION],\n [AnimationType.FINGER_BREAK, FINGER_LOCK_ANIMATION],\n [AnimationType.GUILLOTINE_CHOKE, CAROTID_CHOKE_ANIMATION],\n [AnimationType.JUGULAR_STRIKE, THROAT_STRIKE_ANIMATION],\n [AnimationType.KNEECAP_STRIKE, LOW_KICK_ANIMATION],\n [AnimationType.OCCIPITAL_STRIKE, HAMMER_FIST_ANIMATION],\n [AnimationType.SCIATIC_NERVE_STRIKE, KNEE_STRIKE_ANIMATION_ENHANCED],\n [AnimationType.SILENT_TAKEDOWN, REAR_NAKED_CHOKE_ANIMATION],\n [AnimationType.SLEEPER_HOLD, REAR_NAKED_CHOKE_ANIMATION],\n [AnimationType.TRIANGLE_CHOKE, LEG_REAP_ANIMATION], // Uses legs\n [AnimationType.SPLEEN_RUPTURE, KNEE_STRIKE_ANIMATION_ENHANCED],\n\n // Musa (무사)\n [AnimationType.DRAGON_FIST, HEAVEN_STRIKE_ANIMATION],\n [AnimationType.THUNDER_STRIKE, HEAVEN_STRIKE_ANIMATION],\n [AnimationType.MOUNTAIN_BREAKER, HAMMER_FIST_ANIMATION],\n [AnimationType.IRON_DEFENSE, GEON_HIGH_BLOCK],\n\n // Amsalja (암살자)\n [AnimationType.DEADLY_PRECISION, NERVE_STRIKE_ANIMATION],\n [AnimationType.SHADOW_NERVE_STRIKE, NERVE_STRIKE_ANIMATION],\n [AnimationType.SHADOW_STRIKE, CROSS_ANIMATION_ENHANCED],\n [AnimationType.SILENT_DEATH, HEAVEN_STRIKE_ANIMATION], // Finisher\n\n // Hacker (해커)\n [AnimationType.CYBER_OVERDRIVE, RAPID_BARRAGE_ANIMATION],\n [AnimationType.DATA_STRIKE, NERVE_STRIKE_ANIMATION],\n [AnimationType.ELECTRIC_SHOCK, PALM_STRIKE_ANIMATION],\n [AnimationType.SYSTEM_CRASH, HEAVEN_STRIKE_ANIMATION],\n\n // Jeongbo (정보요원)\n [AnimationType.TACTICAL_STRIKE, JAB_ANIMATION_ENHANCED],\n [AnimationType.INTELLIGENCE_STRIKE, NERVE_STRIKE_ANIMATION],\n [AnimationType.COUNTER_INTELLIGENCE, PARRY_COUNTER_ANIMATION],\n [AnimationType.PSYCHOLOGICAL_WARFARE, IDLE_STANCE_ANIMATION],\n\n // Jojik (조직폭력배)\n [AnimationType.RUTHLESS_ASSAULT, RAPID_BARRAGE_ANIMATION],\n [AnimationType.STREET_BRAWL, OVERHAND_ANIMATION],\n [AnimationType.IMPROVISED_WEAPON, HAMMER_FIST_ANIMATION],\n [AnimationType.BRUTAL_TAKEDOWN, BODY_LOCK_THROW_ANIMATION],\n\n // Misc\n [AnimationType.IDLE_STANCE, IDLE_STANCE_ANIMATION],\n\n // Movement Aliases\n [AnimationType.SIDE_STEP, SIDE_STEP_ANIMATION],\n\n // [AnimationType.FLOWING_PUSH, FLOWING_PUSH_ANIMATION],\n // [AnimationType.THROAT_STRIKE, THROAT_STRIKE_ANIMATION],\n // [AnimationType.EYE_GOUGE, EYE_GOUGE_ANIMATION],\n // [AnimationType.NERVE_PARALYSIS, NERVE_PARALYSIS_ANIMATION],\n // [AnimationType.LIVER_DISRUPTION, LIVER_DISRUPTION_ANIMATION],\n // [AnimationType.EAR_STRIKE, EAR_STRIKE_ANIMATION],\n\n // Elbow/Knee (팔꿈치/무릎) - Using enhanced versions with recovery phases\n [AnimationType.ELBOW_STRIKE, ELBOW_STRIKE_ANIMATION_ENHANCED],\n [AnimationType.ELBOW_UPPERCUT, ELBOW_UPPERCUT_ANIMATION_ENHANCED],\n [AnimationType.SPINNING_ELBOW, SPINNING_ELBOW_ANIMATION],\n [AnimationType.KNEE_STRIKE, KNEE_STRIKE_ANIMATION_ENHANCED],\n [AnimationType.FLYING_KNEE, FLYING_KNEE_ANIMATION],\n [AnimationType.KNEE_KICK, KNEE_KICK_ANIMATION],\n [AnimationType.CLINCH_KNEE, CLINCH_KNEE_ANIMATION],\n [AnimationType.TEMPLE_ELBOW, TEMPLE_ELBOW_ANIMATION],\n [AnimationType.SPINNING_BACK_ELBOW, SPINNING_BACK_ELBOW_ANIMATION],\n [AnimationType.SPINAL_ELBOW, SPINAL_ELBOW_ANIMATION],\n [AnimationType.BRACHIAL_ELBOW, BRACHIAL_ELBOW_ANIMATION],\n [AnimationType.KIDNEY_KNEE, KIDNEY_KNEE_ANIMATION],\n [AnimationType.FEMORAL_KNEE, FEMORAL_KNEE_ANIMATION],\n\n // Grappling (잡기)\n [AnimationType.THROW, THROW_ANIMATION],\n [AnimationType.GRAPPLE, GRAPPLE_ANIMATION],\n [AnimationType.SWEEP, SWEEP_ANIMATION],\n [AnimationType.SLAM, SLAM_ANIMATION],\n [AnimationType.WRIST_LOCK, WRIST_LOCK_ANIMATION],\n [AnimationType.ARM_BAR, ARM_BAR_ANIMATION],\n [AnimationType.SHOULDER_LOCK, SHOULDER_LOCK_ANIMATION],\n [AnimationType.HIP_THROW, HIP_THROW_ANIMATION],\n [AnimationType.LEG_REAP, LEG_REAP_ANIMATION],\n [AnimationType.SMALL_CIRCLE_LOCK, SMALL_CIRCLE_LOCK_ANIMATION],\n [AnimationType.FINGER_LOCK, FINGER_LOCK_ANIMATION],\n [AnimationType.ELBOW_LOCK, ELBOW_LOCK_ANIMATION],\n [AnimationType.SHOULDER_MANIPULATION, SHOULDER_MANIPULATION_ANIMATION],\n [AnimationType.FLOWING_ARM_BAR, FLOWING_ARM_BAR_ANIMATION],\n [AnimationType.MOUNTAIN_LOCK, MOUNTAIN_LOCK_ANIMATION],\n [AnimationType.EARTH_EMBRACE, EARTH_EMBRACE_ANIMATION],\n [AnimationType.CAROTID_CHOKE, CAROTID_CHOKE_ANIMATION],\n [AnimationType.REAR_NAKED_CHOKE, REAR_NAKED_CHOKE_ANIMATION],\n [AnimationType.REDIRECT_THROW, REDIRECT_THROW_ANIMATION],\n\n // Counters (반격)\n [AnimationType.COUNTER_ATTACK, COUNTER_ATTACK_ANIMATION],\n [AnimationType.COUNTER_STRIKE, COUNTER_STRIKE_ANIMATION],\n [AnimationType.PARRY_COUNTER, PARRY_COUNTER_ANIMATION],\n\n // Defense (방어)\n [AnimationType.BLOCK, BLOCK_ANIMATION],\n [AnimationType.BLOCK_HIGH, HIGH_BLOCK_ANIMATION],\n [AnimationType.BLOCK_LOW, LOW_BLOCK_ANIMATION],\n [AnimationType.JOINT_LOCK_DEFENSE, JOINT_LOCK_DEFENSE_ANIMATION],\n [AnimationType.SWEEP_DEFENSE, SWEEP_DEFENSE_ANIMATION],\n ]);\n\n/**\n * All animations combined into a single map by name\n * 이름별 전체 애니메이션 맵\n */\nexport const ALL_ANIMATIONS: ReadonlyMap<string, SkeletalAnimation> = new Map([\n ...KICK_ANIMATIONS,\n ...PUNCH_ANIMATIONS,\n ...SPECIALIZED_PUNCH_ANIMATIONS, // Add specialized punch variants\n ...ELBOW_KNEE_ANIMATIONS,\n ...GRAPPLING_ANIMATIONS,\n ...STANCE_ANIMATIONS,\n ...DARKOPS_ANIMATIONS,\n ...COMBO_ANIMATIONS,\n ...MOVEMENT_ANIMATIONS,\n ...ALL_ATTACK_ANIMATIONS, // Stance-specific attack animations (24 unique)\n ...BASIC_ANIMATIONS, // Idle, Walk, Run, Fall animations\n ...STANCE_LOCOMOTION_ANIMATIONS, // Stance-specific walk/run animations (16 unique)\n // Trigram idle animations with breathing/weight shifts (overrides static stance poses)\n // Must come after STANCE_ANIMATIONS to properly override stance_geon, stance_tae, etc.\n ...TRIGRAM_IDLE_ANIMATIONS_BY_NAME,\n\n // ═══ Trigram-specific stance and technique animations (팔괘 자세/기술 애니메이션) ═══\n // Each trigram has unique movement patterns and combat techniques\n ...GEON_ANIMATIONS, // ☰ Heaven: Direct force, power strikes\n ...TAE_STANCE_ANIMATIONS, // ☱ Lake: Fluid joint manipulation\n ...JIN_ANIMATIONS, // ☳ Thunder: Explosive power\n ...JIN_TECHNIQUE_ANIMATIONS, // ☳ Thunder techniques\n ...SON_STANCE_ANIMATIONS, // ☴ Wind: Continuous pressure\n ...SON_TECHNIQUE_ANIMATIONS, // ☴ Wind techniques\n ...GAN_STANCE_ANIMATIONS, // ☶ Mountain: Defensive mastery\n ...GAN_TECHNIQUE_ANIMATIONS, // ☶ Mountain techniques\n // Additional animations from AttackAnimations not in other maps\n [\"idle_stance\", IDLE_STANCE_ANIMATION],\n [\"forward_dash\", FORWARD_DASH_ANIMATION],\n [\"backward_retreat\", BACKWARD_RETREAT_ANIMATION],\n [\"side_step\", SIDE_STEP_ANIMATION],\n]);\n\n// ═══════════════════════════════════════════════════════════════════════════\n// ANIMATION ID-BASED REGISTRY (animationId → SkeletalAnimation)\n// Direct 1-1 mapping for technique animations using new architecture\n// ═══════════════════════════════════════════════════════════════════════════\n\n/**\n * Animation ID Registry - Direct mapping from technique animationId to SkeletalAnimation\n *\n * This provides 1-1 mapping between technique IDs and animations, using the new\n * animationId field instead of the legacy AnimationType enum.\n *\n * 기술 ID에서 애니메이션으로의 직접 매핑 (1-1 관계)\n *\n * @see TechniqueAnimationMapping.ts for legacy technique → AnimationType mappings\n * @korean 애니메이션ID레지스트리\n */\nexport const ANIMATION_ID_REGISTRY: ReadonlyMap<string, SkeletalAnimation> =\n new Map([\n // Complete mapping for all 57 techniques using existing animations\n [\"amsalja_silent_death\", SPEAR_HAND_ANIMATION],\n [\"darkops_ear_strike\", EAR_STRIKE_ANIMATION],\n [\"darkops_eye_gouge\", EYE_GOUGE_ANIMATION],\n [\"darkops_finger_break\", FINGER_LOCK_ANIMATION],\n [\"darkops_kidney_strike\", KIDNEY_KNEE_ANIMATION],\n [\"darkops_larynx_crush\", THROAT_STRIKE_ANIMATION],\n [\"darkops_sleeper_hold\", REAR_NAKED_CHOKE_ANIMATION],\n [\"darkops_spinal_strike\", SPINAL_ELBOW_ANIMATION],\n // eslint-disable-next-line @typescript-eslint/no-non-null-assertion -- animation guaranteed to exist in registry\n [\"gam_circular_parry\", STANCE_ANIMATIONS.get(\"gam_circular_parry\")!], // Use dedicated circular parry animation\n [\"gam_flow_defense\", GAM_FLOW_DEFENSE],\n [\"gam_flowing_block\", GAM_FLOWING_BLOCK],\n [\"gam_hip_throw\", HIP_THROW_ANIMATION],\n [\"gam_redirect_throw\", REDIRECT_THROW_ANIMATION],\n [\"gam_water_counter\", GAM_WATER_FLOW_COUNTER_ANIMATION],\n [\"gan_counter_strike\", GAN_COUNTER_FORTRESS],\n // eslint-disable-next-line @typescript-eslint/no-non-null-assertion -- animation guaranteed to exist in registry\n [\"gan_immovable_stance\", STANCE_ANIMATIONS.get(\"gan_immovable_stance\")!], // Use dedicated immovable stance animation\n [\"gan_iron_block\", GAN_IMMOVABLE_BLOCK],\n [\"gan_rock_defense\", GAN_ROCK_DEFENSE_ANIMATION],\n [\"geon_axe_kick\", AXE_KICK_ANIMATION],\n [\"geon_counter_strike\", GEON_COUNTER_STRIKE],\n [\"geon_crushing_elbow\", ELBOW_STRIKE_ANIMATION_ENHANCED],\n [\"geon_elbow_smash\", ELBOW_STRIKE_ANIMATION_ENHANCED],\n [\"geon_frontal_kick\", FRONT_KICK_ANIMATION_ENHANCED],\n [\"geon_heaven_strike\", HEAVEN_STRIKE_ANIMATION],\n [\"geon_heavenly_fist\", JAB_ANIMATION_ENHANCED],\n [\"geon_high_block\", GEON_HIGH_BLOCK],\n [\"geon_palm_strike\", PALM_STRIKE_ANIMATION],\n [\"gon_ankle_pick\", SWEEP_ANIMATION],\n [\"gon_earth_embrace\", EARTH_EMBRACE_ANIMATION],\n [\"gon_ground_pound\", SLAM_ANIMATION],\n [\"gon_leg_sweep\", SWEEP_ANIMATION],\n [\"gon_ssireum_throw\", HIP_THROW_ANIMATION],\n [\"hacker_data_strike\", PALM_STRIKE_ANIMATION],\n [\"hacker_system_crash\", HAMMER_FIST_ANIMATION],\n [\"jin_back_kick\", BACK_KICK_ANIMATION],\n [\"jin_explosive_knee\", KNEE_STRIKE_ANIMATION_ENHANCED],\n [\"jin_flying_sidekick\", FLYING_KICK_ANIMATION],\n [\"jin_knee_strike\", KNEE_STRIKE_ANIMATION_ENHANCED],\n [\"jin_tornado_kick\", TORNADO_KICK_ANIMATION],\n [\"jojik_street_brawl\", HOOK_ANIMATION],\n [\"li_flame_spear\", SPEAR_HAND_ANIMATION],\n [\"li_nerve_strike\", NERVE_STRIKE_ANIMATION],\n [\"li_precision_parry\", LI_PRECISION_PARRY],\n [\"li_sidekick\", SIDE_KICK_ANIMATION],\n [\"li_temple_strike\", TEMPLE_ELBOW_ANIMATION],\n [\"musa_dragon_fist\", CROSS_ANIMATION_ENHANCED],\n [\"musa_iron_defense\", HIGH_BLOCK_ANIMATION],\n [\"musa_thunder_strike\", UPPERCUT_ANIMATION],\n [\"son_flowing_push\", FLOWING_PUSH_ANIMATION],\n [\"son_rapid_footwork\", SIDE_STEP_ANIMATION],\n [\"son_sweeping_low_kick\", LOW_KICK_ANIMATION],\n [\"tae_arm_bar\", ARM_BAR_ANIMATION],\n [\"tae_elbow_lock\", ELBOW_LOCK_ANIMATION],\n [\"tae_finger_lock\", FINGER_LOCK_ANIMATION],\n [\"tae_flowing_strikes\", FLOWING_CROSS_ANIMATION],\n [\"tae_sweep_defense\", SWEEP_DEFENSE_ANIMATION],\n [\"tae_wrist_lock\", WRIST_LOCK_ANIMATION],\n ]);\n\n/**\n * Category-based default animations for fallback\n *\n * When an animationId is not found, fallback to category default.\n *\n * 카테고리별 기본 애니메이션 (fallback용)\n */\nexport const CATEGORY_DEFAULT_ANIMATIONS: ReadonlyMap<string, SkeletalAnimation> =\n new Map([\n [\"punch\", JAB_ANIMATION_ENHANCED],\n [\"kick\", FRONT_KICK_ANIMATION_ENHANCED],\n [\"strike\", PALM_STRIKE_ANIMATION],\n [\"joint_lock\", WRIST_LOCK_ANIMATION],\n [\"throw\", HIP_THROW_ANIMATION],\n [\"defensive\", BLOCK_ANIMATION],\n [\"elbow_strike\", ELBOW_STRIKE_ANIMATION_ENHANCED],\n [\"counter\", COUNTER_STRIKE_ANIMATION],\n [\"jumping_kick\", JUMPING_KICK_ANIMATION],\n [\"grapple\", GRAPPLE_ANIMATION],\n [\"sweep\", SWEEP_ANIMATION],\n [\"knee_strike\", KNEE_STRIKE_ANIMATION_ENHANCED],\n [\"footwork\", SIDE_STEP_ANIMATION],\n [\"stance\", IDLE_STANCE_ANIMATION],\n [\"takedown\", SLAM_ANIMATION],\n ]);\n\n// ═══════════════════════════════════════════════════════════════════════════\n// ANIMATION LOOKUP FUNCTIONS\n// ═══════════════════════════════════════════════════════════════════════════\n\n/**\n * Get animation by AnimationType\n *\n * @param type - Animation type enum value\n * @returns Skeletal animation or undefined\n *\n * @korean 애니메이션타입으로조회\n */\nexport function getAnimationByType(\n type: AnimationType,\n): SkeletalAnimation | undefined {\n return ANIMATION_REGISTRY.get(type);\n}\n\n/**\n * Get animation by AnimationType with fallback\n *\n * @param type - Animation type enum value\n * @param fallback - Fallback animation type\n * @returns Skeletal animation (never undefined)\n *\n * @korean 애니메이션타입으로조회_기본값\n */\nexport function getAnimationByTypeOrDefault(\n type: AnimationType,\n fallback: AnimationType = AnimationType.JAB,\n): SkeletalAnimation {\n const animation =\n ANIMATION_REGISTRY.get(type) ?? ANIMATION_REGISTRY.get(fallback);\n\n if (!animation) {\n throw new Error(`Missing animation for ${type} with fallback ${fallback}`);\n }\n\n return animation;\n}\n\n/**\n * Get animation for a technique by technique ID (using AnimationType mapping)\n *\n * Uses the TechniqueAnimationMapping to find the correct animation\n * for any technique in the game.\n *\n * @param techniqueId - Technique identifier (e.g., \"geon_frontal_kick\")\n * @returns Skeletal animation or undefined\n *\n * @korean 기술ID로애니메이션조회\n */\nexport function getAnimationForTechniqueId(\n techniqueId: string,\n): SkeletalAnimation | undefined {\n const config = getTechniqueAnimationConfig(techniqueId);\n if (!config) return undefined;\n return ANIMATION_REGISTRY.get(config.type);\n}\n\n/**\n * Get animation for a technique with fallback\n *\n * @param techniqueId - Technique identifier\n * @param fallbackType - Fallback animation type\n * @returns Animation with speed modifier\n *\n * @korean 기술ID로애니메이션조회_기본값\n */\nexport function getAnimationForTechniqueIdWithConfig(\n techniqueId: string,\n fallbackType: AnimationType = AnimationType.JAB,\n): { animation: SkeletalAnimation; speed: number } {\n const config = getAnimationForTechniqueOrDefault(techniqueId, fallbackType);\n const animation =\n ANIMATION_REGISTRY.get(config.type) ?? ANIMATION_REGISTRY.get(fallbackType);\n\n if (!animation) {\n throw new Error(\n `Missing animation for ${config.type} with fallback ${fallbackType}`,\n );\n }\n return { animation, speed: config.speed };\n}\n\n// ═══════════════════════════════════════════════════════════════════════════\n// NEW ARCHITECTURE: ID-Based Animation Lookup Functions\n// ═══════════════════════════════════════════════════════════════════════════\n\n/**\n * Get animation by animationId (new architecture)\n *\n * Direct 1-1 lookup using the new animationId field from technique.\n * This is the preferred method for the new animation architecture.\n *\n * @param animationId - Technique's animationId (e.g., \"geon_heaven_strike\")\n * @returns SkeletalAnimation or undefined if not found\n *\n * @korean 애니메이션ID로조회\n */\nexport function getAnimationById(\n animationId: string,\n): SkeletalAnimation | undefined {\n return ANIMATION_ID_REGISTRY.get(animationId);\n}\n\n/**\n * Get animation by ID with category-based fallback\n *\n * Implements a 3-tier fallback system:\n * 1. Try direct ID lookup in ANIMATION_ID_REGISTRY\n * 2. If not found, use category default from CATEGORY_DEFAULT_ANIMATIONS\n * 3. If no category, use IDLE_STANCE as ultimate fallback\n *\n * This function never returns undefined, making it safe for use in game logic.\n *\n * @param animationId - Technique's animationId\n * @param animationCategory - Technique's animationCategory for fallback\n * @returns SkeletalAnimation (never undefined)\n *\n * @korean 애니메이션ID로조회_카테고리대체\n */\nexport function getAnimationByIdWithFallback(\n animationId: string | undefined,\n animationCategory?: string,\n): SkeletalAnimation {\n // Try direct ID lookup\n if (animationId) {\n const animation = ANIMATION_ID_REGISTRY.get(animationId);\n if (animation) return animation;\n }\n\n // Fallback to category default\n if (animationCategory) {\n const categoryDefault = CATEGORY_DEFAULT_ANIMATIONS.get(animationCategory);\n if (categoryDefault) return categoryDefault;\n }\n\n // Ultimate fallback\n return IDLE_STANCE_ANIMATION;\n}\n\n/**\n * Check if animation ID exists in registry\n *\n * @param animationId - Animation ID to check\n * @returns true if animation exists in ANIMATION_ID_REGISTRY\n *\n * @korean 애니메이션ID존재확인\n */\nexport function hasAnimationId(animationId: string): boolean {\n return ANIMATION_ID_REGISTRY.has(animationId);\n}\n\n/**\n * Get category default animation\n *\n * Returns the default animation for a given category, useful for fallback logic.\n *\n * @param category - Animation category (e.g., \"punch\", \"kick\")\n * @returns SkeletalAnimation or undefined if category not found\n *\n * @korean 카테고리기본애니메이션조회\n */\nexport function getCategoryDefaultAnimation(\n category: string,\n): SkeletalAnimation | undefined {\n return CATEGORY_DEFAULT_ANIMATIONS.get(category);\n}\n\n/**\n * Get animation by name (legacy support)\n *\n * @param name - Animation name (e.g., \"front_kick\")\n * @returns Skeletal animation or undefined\n *\n * @korean 이름으로애니메이션조회\n */\nexport function getAnimationByName(\n name: string,\n): SkeletalAnimation | undefined {\n return ALL_ANIMATIONS.get(name);\n}\n\n/**\n * Get animation by name - unified lookup across all animation registries\n *\n * Searches ALL_ANIMATIONS which includes:\n * - BASIC_ANIMATIONS (idle, walk, run, fall)\n * - KICK_ANIMATIONS, PUNCH_ANIMATIONS, etc.\n * - STANCE_ANIMATIONS, MOVEMENT_ANIMATIONS\n * - ALL_ATTACK_ANIMATIONS (stance-specific attacks)\n *\n * @param name - Animation name (e.g., \"idle\", \"front_kick\", \"walk\")\n * @returns Skeletal animation or undefined\n *\n * @korean 애니메이션가져오기\n */\nexport function getAnimation(name: string): SkeletalAnimation | undefined {\n return ALL_ANIMATIONS.get(name);\n}\n\n// ═══════════════════════════════════════════════════════════════════════════\n// TECHNIQUE TO ANIMATION LOOKUP\n// ═══════════════════════════════════════════════════════════════════════════\n\n/**\n * Regex fallback patterns for technique-to-animation mapping\n * Used when technique ID is not found in ALL_ANIMATIONS\n *\n * NOTE: Order matters - more specific patterns must come first\n *\n * @korean 기술애니메이션폴백매핑\n */\nconst TECHNIQUE_ANIMATION_FALLBACK: ReadonlyArray<readonly [RegExp, string]> = [\n // ───────────────────────────────────────────────────────────────────────\n // Stance-specific distinctive strikes - MUST be checked before the generic\n // /strike/ rule so that \"Thunder Strike\", \"Heaven Strike\", \"Nerve Strike\",\n // \"Precision Strike\", etc. don't collapse to the generic \"jab\" fallback.\n // ───────────────────────────────────────────────────────────────────────\n [/heaven.?strike|천둥벽력|하늘치기/i, \"heaven_strike\"],\n [/lightning.?(strike|flash)|번개|뇌격/i, \"lightning_strike\"],\n [/nerve.?strike|신경.?타격|신경치기/i, \"nerve_strike\"],\n [/pressure.?point|혈도|급소/i, \"pressure_point_strike\"],\n [/solar.?plexus|명치|태양신경총/i, \"solar_plexus_strike\"],\n [/throat.?strike|후두타격|목치기/i, \"throat_strike\"],\n [/temple.?(strike|elbow)|관자놀이/i, \"temple_strike\"],\n [/eye.?gouge|눈찌르기/i, \"eye_gouge\"],\n [/ear.?strike|귀치기/i, \"ear_strike\"],\n [/liver.?(disruption|strike)|간장|간치기/i, \"liver_disruption\"],\n [/kidney.?(strike|punch)|신장치기/i, \"kidney_strike\"],\n [/spear.?hand|관수|손끝찌르기/i, \"spear_hand_strike\"],\n [/flowing.?cross|flowing.?strike|유수타격/i, \"flowing_cross\"],\n [/flowing.?push|flowing.?palm/i, \"flowing_push\"],\n [/rapid.?barrage|연환타/i, \"rapid_barrage\"],\n [/rhythmic.?strike|리듬타격/i, \"rhythmic_strikes\"],\n // Heavenly/dragon fist names are stance-specific and should map to the\n // proper Geon/Jin animations, not the generic jab/cross fallbacks.\n // Note: intentionally exclude \"정권\" (generic straight punch) here — it\n // should continue to resolve to the generic `jab` via the kick/jab\n // section below, not to Geon's heaven_strike.\n [/heavenly.?fist|천권/i, \"heaven_strike\"],\n [/dragon.?fist|용권/i, \"jin_lightning_flash\"],\n\n // Kicks (차기) - more specific patterns first\n [/axe.?kick|내려차기|naeryeo/i, \"axe_kick\"],\n [/back.?kick|뒤차기|dwi.?chagi/i, \"back_kick\"],\n [/tornado|회오리|hoe.?ori/i, \"tornado_kick\"],\n [/jump|뛰어|ttwi|flying/i, \"jumping_kick\"],\n [/sweep|쓸기|걸기|품밟기|dari.?geolgi/i, \"sweep\"],\n [/side.?kick|옆차기|yeop.?chagi/i, \"side_kick\"],\n [/low.?kick|하단차기|낮은차기|thigh.?kick|leg.?kick/i, \"low_kick\"],\n [/front.?kick|앞차기|snap.?kick|ap.?chagi/i, \"front_kick\"],\n [/roundhouse|돌려차기|dolryeo/i, \"roundhouse_kick\"],\n // Knee strikes (무릎)\n [/knee|무릎|mureup/i, \"knee_strike\"],\n // Elbow strikes (팔꿈치)\n [/elbow|팔꿈치|팔굽|palkkumchi/i, \"elbow_strike\"],\n // Throws & Slams (던지기/내던지기)\n [/slam|내던지기|smash/i, \"slam\"],\n [/throw|던지기|deonjigi|ground.?pound/i, \"throw\"],\n // Grapple/Lock (꺾기/잡기) - specific locks first\n [/arm.?bar|팔꺾기|팔관절기|juji.?gatame/i, \"arm_bar\"],\n [/wrist.?lock|손목꺾기|손목관절기|kote.?gaeshi/i, \"wrist_lock\"],\n [/lock|grapple|꺾기|잡기|embrace|kkeokgi|japgi|submission/i, \"grapple\"],\n // Counter attacks (반격)\n [/counter.?strike|반격타격|카운터스트라이크/i, \"counter_strike\"],\n [/counter|반격|bangyeok|parry|redirect/i, \"counter_attack\"],\n // Blocks (막기)\n [/block|막기|makgi|defense|방어/i, \"block\"],\n // Punches (주먹) - check after specific patterns\n [/hook|후크|횡타|갈고리/i, \"hook\"],\n [/palm|장권|jang.?gwon/i, \"palm_strike\"],\n [/cross|십자|교차/i, \"cross\"],\n [/uppercut|upper|올려|치올/i, \"uppercut\"],\n [/jab|잽|직권|찌르기|punch|주먹|권/i, \"jab\"],\n // Generic strikes last - only used when no more specific pattern matched above\n [/strike|타격|격|chigi/i, \"jab\"],\n] as const;\n\n/**\n * Get animation name for a technique\n *\n * PRIORITY ORDER:\n * 1. Direct lookup in ALL_ANIMATIONS by technique ID (stance-specific)\n * 2. Regex pattern matching for generic techniques\n * 3. Fallback to \"jab\"\n *\n * This ensures stance-specific animations like \"geon_heaven_strike\"\n * are used when available, while still supporting generic technique names.\n *\n * @param techniqueNameOrId - Technique name, ID, or Korean name\n * @returns Animation name from ALL_ANIMATIONS\n *\n * @example\n * ```typescript\n * getAnimationForTechnique(\"geon_heaven_strike\") // \"geon_heaven_strike\" (exact match)\n * getAnimationForTechnique(\"tae_wrist_lock\") // \"tae_wrist_lock\" (exact match)\n * getAnimationForTechnique(\"roundhouse_kick\") // \"roundhouse_kick\" (regex match)\n * getAnimationForTechnique(\"앞차기\") // \"front_kick\" (regex match)\n * ```\n *\n * @korean 기술에맞는애니메이션가져오기\n */\nexport function getAnimationForTechnique(techniqueNameOrId: string): string {\n if (!techniqueNameOrId) return \"jab\";\n\n // 1. First check if technique ID exists directly in ALL_ANIMATIONS\n // This handles stance-specific animations like \"geon_heaven_strike\"\n if (ALL_ANIMATIONS.has(techniqueNameOrId)) {\n return techniqueNameOrId;\n }\n\n // 2. Try a normalized form: \"Thunder Strike\" → \"thunder_strike\".\n // This lets us catch techniques whose English name happens to match\n // an animation key exactly once spaces are collapsed.\n const normalized = techniqueNameOrId\n .trim()\n .toLowerCase()\n .replace(/[\\s-]+/g, \"_\")\n .replace(/[^a-z0-9_]/g, \"\");\n if (normalized && ALL_ANIMATIONS.has(normalized)) {\n return normalized;\n }\n\n // 3. Try regex pattern matching for generic technique names\n for (const [pattern, animationName] of TECHNIQUE_ANIMATION_FALLBACK) {\n if (pattern.test(techniqueNameOrId)) {\n return animationName;\n }\n }\n\n // 4. Ultimate fallback to jab\n return \"jab\";\n}\n\n/**\n * Resolve a technique object to its best-matching animation key.\n *\n * This is the single, authoritative entry point for screens and systems\n * that need to know which skeletal animation to play for a given technique.\n *\n * Resolution order (most specific → least):\n * 1. `technique.animationId` if present and registered (KoreanTechnique / TrigramStanceTechnique).\n * 2. `technique.id` if registered (Technique objects from {@link getTechniquesForStanceAndArchetype}\n * already carry their stance-specific id, e.g. `\"geon_heaven_strike\"`).\n * 3. Regex-based fallback on `technique.name.english`, then `technique.name.korean`.\n * 4. `\"jab\"` as ultimate fallback.\n *\n * Passing the technique object (rather than a loose string) avoids the\n * long-standing bug where `technique.name.english` — e.g. `\"Thunder Strike\"` —\n * would match only the generic `/strike/` rule and collapse every\n * stance-specific attack to the jab animation.\n *\n * @param technique - Technique or technique-like object.\n * @returns Animation key registered in {@link ALL_ANIMATIONS}.\n *\n * @korean 기술객체로애니메이션해결\n */\nexport function resolveTechniqueAnimation(\n technique:\n | {\n readonly id?: string;\n readonly animationId?: string;\n readonly name?: {\n readonly english?: string;\n readonly korean?: string;\n };\n }\n | null\n | undefined,\n): string {\n if (!technique) return \"jab\";\n\n // 1. Prefer animationId (authoritative 1-1 mapping on KoreanTechnique).\n if (technique.animationId && ALL_ANIMATIONS.has(technique.animationId)) {\n return technique.animationId;\n }\n\n // 2. technique.id (Technique objects preserve stance-prefixed ids here).\n if (technique.id && ALL_ANIMATIONS.has(technique.id)) {\n return technique.id;\n }\n\n // 3. Regex/normalized fallback on the most specific string we have.\n const english = technique.name?.english;\n if (english) {\n const byEnglish = getAnimationForTechnique(english);\n // Guard: only accept the result if it's not the dumb \"jab\" default when\n // we still have an id/animationId we can try as a last resort.\n if (byEnglish !== \"jab\" || !technique.id) {\n return byEnglish;\n }\n }\n\n // 4. Try id as a string (may go through regex fallback if not registered).\n if (technique.id) {\n return getAnimationForTechnique(technique.id);\n }\n\n // 5. Korean name as last resort.\n const korean = technique.name?.korean;\n if (korean) {\n return getAnimationForTechnique(korean);\n }\n\n return \"jab\";\n}\n\n// ═══════════════════════════════════════════════════════════════════════════\n// RE-EXPORTS\n// ═══════════════════════════════════════════════════════════════════════════\n\n// Re-export all individual animations for direct access\nexport {\n ARM_BAR_ANIMATION,\n AXE_KICK_ANIMATION,\n BACK_KICK_ANIMATION,\n BLOCK_ANIMATION,\n COUNTER_ATTACK_ANIMATION,\n COUNTER_STRIKE_ANIMATION,\n CROSS_ANIMATION,\n // Elbow/Knee\n ELBOW_STRIKE_ANIMATION,\n ELBOW_UPPERCUT_ANIMATION,\n // Kicks\n FRONT_KICK_ANIMATION,\n GRAPPLE_ANIMATION,\n HOOK_ANIMATION,\n // Punches\n JAB_ANIMATION,\n JUMPING_KICK_ANIMATION,\n KNEE_STRIKE_ANIMATION,\n LOW_KICK_ANIMATION,\n PALM_STRIKE_ANIMATION,\n ROUNDHOUSE_KICK_ANIMATION,\n SIDE_KICK_ANIMATION,\n SLAM_ANIMATION,\n SWEEP_ANIMATION,\n // Grappling\n THROW_ANIMATION,\n TORNADO_KICK_ANIMATION,\n WRIST_LOCK_ANIMATION,\n};\n\n// Re-export category maps\nexport {\n COMBO_ANIMATIONS,\n DARKOPS_ANIMATIONS,\n ELBOW_KNEE_ANIMATIONS,\n GRAPPLING_ANIMATIONS,\n KICK_ANIMATIONS,\n MOVEMENT_ANIMATIONS,\n PUNCH_ANIMATIONS,\n STANCE_ANIMATIONS,\n};\n\n// Re-export animation types and mapping\nexport { AnimationType } from \"../builders/MartialArtsAnimationBuilder\";\nexport { hasAnimationMapping };\nexport type { AnimationConfig };\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"AnimationRegistry.js","names":[],"sources":["../../../../src/systems/animation/core/AnimationRegistry.ts"],"sourcesContent":["/**\n * Animation Registry\n *\n * Central registry for all martial arts animations.\n * Combines all animation modules and provides unified access.\n *\n * 무술 애니메이션 레지스트리 - 모든 애니메이션 통합 관리\n *\n * **RECOVERY PHASE INTEGRATION (복귀 단계 통합)**\n *\n * Enhanced animations with realistic recovery phases are now the default for:\n * - JAB_ANIMATION → JAB_ANIMATION_ENHANCED (200ms recovery)\n * - CROSS_ANIMATION → CROSS_ANIMATION_ENHANCED (220ms recovery)\n * - FRONT_KICK_ANIMATION → FRONT_KICK_ANIMATION_ENHANCED (170ms recovery)\n * - ROUNDHOUSE_KICK_ANIMATION → ROUNDHOUSE_KICK_ANIMATION_ENHANCED (180ms recovery)\n * - ELBOW_STRIKE_ANIMATION → ELBOW_STRIKE_ANIMATION_ENHANCED (160ms recovery)\n * - ELBOW_UPPERCUT_ANIMATION → ELBOW_UPPERCUT_ANIMATION_ENHANCED (170ms recovery)\n * - KNEE_STRIKE_ANIMATION → KNEE_STRIKE_ANIMATION_ENHANCED (190ms recovery)\n *\n * Enhanced animations follow Korean martial arts principles (복귀/Bokgwi):\n * - 균형회복 (Gyunhyeong Hoebog) - Balance restoration\n * - 자세복귀 (Jase Bokgwi) - Stance return\n * - 호흡조절 (Hoheup Jojoel) - Breath control during recovery\n * - 근육이완 (Geunryuk Ihwan) - Muscle relaxation after tension\n *\n * All code using ANIMATION_REGISTRY or getAnimationForTechnique() will\n * automatically use enhanced versions with no code changes required.\n *\n * @module systems/animation/AnimationRegistry\n * @korean 애니메이션레지스트리\n */\n\nimport type { SkeletalAnimation } from \"@/types/skeletal\";\nimport { AnimationType } from \"../builders/MartialArtsAnimationBuilder\";\nimport {\n BACKWARD_RETREAT_ANIMATION,\n FORWARD_DASH_ANIMATION,\n IDLE_STANCE_ANIMATION,\n SIDE_STEP_ANIMATION,\n} from \"../catalogs/AttackAnimations\";\nimport { BASIC_ANIMATIONS } from \"../catalogs/BasicAnimations\";\nimport { COMBO_ANIMATIONS } from \"../catalogs/ComboAnimations\";\nimport { DARKOPS_ANIMATIONS } from \"../catalogs/DarkOpsAnimations\";\nimport {\n GAM_FLOW_DEFENSE,\n GAN_COUNTER_FORTRESS,\n GAN_IMMOVABLE_BLOCK,\n GEON_COUNTER_STRIKE,\n GEON_HIGH_BLOCK,\n GON_GROUNDING_DEFENSE,\n GON_TAKEDOWN_COUNTER,\n JIN_EXPLOSIVE_BLOCK,\n JIN_SHOCKING_COUNTER,\n LI_NERVE_STRIKE_COUNTER,\n LI_PRECISION_PARRY,\n SON_CONTINUOUS_DEFLECTION,\n SON_PRESSURE_COUNTER,\n TAE_SWEEP_DEFENSE,\n} from \"../catalogs/DefensiveAnimations\";\nimport {\n BRACHIAL_ELBOW_ANIMATION,\n CLINCH_KNEE_ANIMATION,\n ELBOW_KNEE_ANIMATIONS,\n ELBOW_STRIKE_ANIMATION,\n ELBOW_UPPERCUT_ANIMATION,\n FEMORAL_KNEE_ANIMATION,\n FLYING_KNEE_ANIMATION,\n KIDNEY_KNEE_ANIMATION,\n KNEE_KICK_ANIMATION,\n KNEE_STRIKE_ANIMATION,\n SPINAL_ELBOW_ANIMATION,\n SPINNING_BACK_ELBOW_ANIMATION,\n SPINNING_ELBOW_ANIMATION,\n TEMPLE_ELBOW_ANIMATION,\n} from \"../catalogs/ElbowKneeAnimations\";\nimport {\n ARM_BAR_ANIMATION,\n BLOCK_ANIMATION,\n BODY_LOCK_THROW_ANIMATION,\n CAROTID_CHOKE_ANIMATION,\n COUNTER_ATTACK_ANIMATION,\n COUNTER_STRIKE_ANIMATION,\n EARTH_EMBRACE_ANIMATION,\n ELBOW_LOCK_ANIMATION,\n FINGER_LOCK_ANIMATION,\n FLOWING_ARM_BAR_ANIMATION,\n GRAPPLE_ANIMATION,\n GRAPPLING_ANIMATIONS,\n HIGH_BLOCK_ANIMATION,\n HIP_THROW_ANIMATION,\n JOINT_LOCK_DEFENSE_ANIMATION,\n LEG_REAP_ANIMATION,\n LOW_BLOCK_ANIMATION,\n MOUNTAIN_LOCK_ANIMATION,\n PARRY_COUNTER_ANIMATION,\n REAR_NAKED_CHOKE_ANIMATION,\n REDIRECT_THROW_ANIMATION,\n SHOULDER_LOCK_ANIMATION,\n SHOULDER_MANIPULATION_ANIMATION,\n SLAM_ANIMATION,\n SMALL_CIRCLE_LOCK_ANIMATION,\n SUPLEX_ANIMATION,\n SWEEP_DEFENSE_ANIMATION,\n TAKEDOWN_ANIMATION,\n THROW_ANIMATION,\n WRIST_LOCK_ANIMATION,\n} from \"../catalogs/GrapplingAnimations\";\nimport {\n AXE_KICK_ANIMATION,\n BACK_KICK_ANIMATION,\n CRESCENT_KICK_ANIMATION,\n FLYING_KICK_ANIMATION,\n FRONT_KICK_ANIMATION,\n JUMPING_KICK_ANIMATION,\n KICK_ANIMATIONS,\n LOW_KICK_ANIMATION,\n PUSH_KICK_ANIMATION,\n ROUNDHOUSE_KICK_ANIMATION,\n SIDE_KICK_ANIMATION,\n SPINNING_HEEL_KICK_ANIMATION,\n SPINNING_HOOK_KICK_ANIMATION,\n SWEEP_ANIMATION,\n TORNADO_KICK_ANIMATION,\n} from \"../catalogs/KickAnimations\";\nimport { MOVEMENT_ANIMATIONS } from \"../catalogs/MovementAnimations\";\nimport {\n BACKFIST_ANIMATION,\n CROSS_ANIMATION,\n HAMMER_FIST_ANIMATION,\n HOOK_ANIMATION,\n JAB_ANIMATION,\n OVERHAND_ANIMATION,\n PALM_STRIKE_ANIMATION,\n PUNCH_ANIMATIONS,\n UPPERCUT_ANIMATION,\n} from \"../catalogs/PunchAnimations\";\n\nimport { STANCE_ANIMATIONS } from \"../catalogs/StanceAnimations\";\nimport { ALL_ATTACK_ANIMATIONS } from \"../catalogs/StanceAttackAnimations\";\nimport { TRIGRAM_IDLE_ANIMATIONS_BY_NAME } from \"../catalogs/StanceIdleAnimations\";\nimport { STANCE_LOCOMOTION_ANIMATIONS } from \"../catalogs/StanceLocomotionAnimations\";\n\n// Trigram-specific stance and technique animation maps\nimport { GAM_WATER_FLOW_COUNTER_ANIMATION } from \"../catalogs/GamTechniqueAnimations\";\nimport { GAN_ROCK_DEFENSE_ANIMATION } from \"../catalogs/GanTechniqueAnimations\";\nimport { GAN_STANCE_ANIMATIONS } from \"../catalogs/GanStanceAnimations\";\nimport { GAN_TECHNIQUE_ANIMATIONS } from \"../catalogs/GanTechniqueAnimations\";\nimport { GEON_ANIMATIONS } from \"../catalogs/GeonStanceAnimations\";\nimport { JIN_ANIMATIONS } from \"../catalogs/JinStanceAnimations\";\nimport { JIN_TECHNIQUE_ANIMATIONS } from \"../catalogs/JinTechniqueAnimations\";\nimport { SON_STANCE_ANIMATIONS } from \"../catalogs/SonStanceAnimations\";\nimport { SON_TECHNIQUE_ANIMATIONS } from \"../catalogs/SonTechniqueAnimations\";\nimport { TAE_STANCE_ANIMATIONS } from \"../catalogs/TaeStanceAnimations\";\nimport {\n getAnimationForTechniqueOrDefault,\n getAnimationForTechnique as getTechniqueAnimationConfig,\n hasAnimationMapping,\n type AnimationConfig,\n} from \"./TechniqueAnimationMapping\";\n\n// Enhanced animations with recovery phases (복귀 애니메이션 강화)\nimport {\n CROSS_ANIMATION_ENHANCED,\n FRONT_KICK_ANIMATION_ENHANCED,\n JAB_ANIMATION_ENHANCED,\n ROUNDHOUSE_KICK_ANIMATION_ENHANCED,\n} from \"../catalogs/EnhancedAttackAnimations\";\nimport {\n ELBOW_STRIKE_ANIMATION_ENHANCED,\n ELBOW_UPPERCUT_ANIMATION_ENHANCED,\n KNEE_STRIKE_ANIMATION_ENHANCED,\n} from \"../catalogs/EnhancedElbowKneeAnimations\";\n\nimport {\n TAE_ARM_BAR,\n TAE_ELBOW_CONTROL,\n TAE_FINGER_LOCK,\n TAE_FLOWING_COUNTER,\n TAE_FLOWING_STRIKES,\n TAE_SHOULDER_LOCK,\n TAE_SMALL_CIRCLE,\n TAE_WRIST_LOCK_SEQUENCE,\n} from \"../catalogs/TaeJointLockAnimations\";\n\n// TODO: Register Tae stance animations when trigram-specific idle/movement system is implemented\n// import {\n// TAE_IDLE_FLOWING,\n// TAE_CIRCULAR_SIDESTEP,\n// TAE_DIAGONAL_CIRCULAR_APPROACH,\n// TAE_FLEXIBLE_GUARD_TRANSITION,\n// } from \"../catalogs/TaeStanceAnimations\";\n\nimport {\n GAM_FLOWING_BLOCK,\n GAM_FLOWING_RIVER_STRIKE,\n GAM_REDIRECTION_COUNTER,\n GAM_TIDAL_WAVE_PALM,\n GAM_WHIRLPOOL_COUNTER,\n} from \"../catalogs/GamRedirectionAnimations\";\n\n// Specialized punch variant animations (특수 주먹 변형 애니메이션)\nimport {\n EAR_STRIKE_ANIMATION,\n EYE_GOUGE_ANIMATION,\n FLOWING_CROSS_ANIMATION,\n FLOWING_PUSH_ANIMATION,\n HEAVEN_STRIKE_ANIMATION,\n LIGHTNING_STRIKE_ANIMATION,\n LIVER_DISRUPTION_ANIMATION,\n NERVE_PARALYSIS_ANIMATION,\n NERVE_STRIKE_ANIMATION,\n PRESSURE_POINT_STRIKE_ANIMATION,\n RAPID_BARRAGE_ANIMATION,\n RHYTHMIC_STRIKES_ANIMATION,\n SOLAR_PLEXUS_STRIKE_ANIMATION,\n SPEAR_HAND_STRIKE_ANIMATION as SPEAR_HAND_ANIMATION,\n SPECIALIZED_PUNCH_ANIMATIONS,\n THROAT_STRIKE_ANIMATION,\n} from \"../catalogs/SpecializedPunchAnimations\";\n\n// ═══════════════════════════════════════════════════════════════════════════\n// MASTER ANIMATION REGISTRY\n// ═══════════════════════════════════════════════════════════════════════════\n\n/**\n * Master registry of all animations by AnimationType\n * 애니메이션 타입별 마스터 레지스트리\n */\nexport const ANIMATION_REGISTRY: ReadonlyMap<AnimationType, SkeletalAnimation> =\n new Map([\n // Kicks (발차기) - Using enhanced versions with recovery phases\n [AnimationType.FRONT_KICK, FRONT_KICK_ANIMATION_ENHANCED],\n [AnimationType.ROUNDHOUSE_KICK, ROUNDHOUSE_KICK_ANIMATION_ENHANCED],\n [AnimationType.SIDE_KICK, SIDE_KICK_ANIMATION],\n [AnimationType.AXE_KICK, AXE_KICK_ANIMATION],\n [AnimationType.BACK_KICK, BACK_KICK_ANIMATION],\n [AnimationType.TORNADO_KICK, TORNADO_KICK_ANIMATION],\n [AnimationType.JUMPING_KICK, JUMPING_KICK_ANIMATION],\n [AnimationType.LOW_KICK, LOW_KICK_ANIMATION],\n [AnimationType.CRESCENT_KICK, CRESCENT_KICK_ANIMATION],\n [AnimationType.PUSH_KICK, PUSH_KICK_ANIMATION],\n [AnimationType.SPINNING_HEEL_KICK, SPINNING_HEEL_KICK_ANIMATION],\n [AnimationType.SPINNING_HOOK, SPINNING_HOOK_KICK_ANIMATION],\n [AnimationType.FLYING_KICK, FLYING_KICK_ANIMATION],\n\n // Punches (주먹) - Using enhanced versions with recovery phases\n [AnimationType.JAB, JAB_ANIMATION_ENHANCED],\n [AnimationType.CROSS, CROSS_ANIMATION_ENHANCED],\n [AnimationType.HOOK, HOOK_ANIMATION],\n [AnimationType.UPPERCUT, UPPERCUT_ANIMATION],\n [AnimationType.OVERHAND, OVERHAND_ANIMATION],\n [AnimationType.PALM_STRIKE, PALM_STRIKE_ANIMATION],\n [AnimationType.BACKFIST, BACKFIST_ANIMATION],\n [AnimationType.HAMMER_FIST, HAMMER_FIST_ANIMATION],\n\n // Specialized Punch Variants (특수 주먹 변형)\n [AnimationType.SPEAR_HAND_STRIKE, SPEAR_HAND_ANIMATION],\n [AnimationType.NERVE_STRIKE, NERVE_STRIKE_ANIMATION],\n [AnimationType.PRESSURE_POINT_STRIKE, PRESSURE_POINT_STRIKE_ANIMATION],\n [AnimationType.LIGHTNING_STRIKE, LIGHTNING_STRIKE_ANIMATION],\n [AnimationType.HEAVEN_STRIKE, HEAVEN_STRIKE_ANIMATION],\n [AnimationType.FLOWING_CROSS, FLOWING_CROSS_ANIMATION],\n [AnimationType.RAPID_BARRAGE, RAPID_BARRAGE_ANIMATION],\n [AnimationType.RHYTHMIC_STRIKES, RHYTHMIC_STRIKES_ANIMATION],\n [AnimationType.FLOWING_PUSH, FLOWING_PUSH_ANIMATION],\n [AnimationType.SOLAR_PLEXUS_STRIKE, SOLAR_PLEXUS_STRIKE_ANIMATION],\n [AnimationType.THROAT_STRIKE, THROAT_STRIKE_ANIMATION],\n [AnimationType.EYE_GOUGE, EYE_GOUGE_ANIMATION],\n [AnimationType.NERVE_PARALYSIS, NERVE_PARALYSIS_ANIMATION],\n [AnimationType.LIVER_DISRUPTION, LIVER_DISRUPTION_ANIMATION],\n [AnimationType.EAR_STRIKE, EAR_STRIKE_ANIMATION],\n\n // ═══ TRIGRAM SPECIFIC ALIASES (괘별 기술 매핑) ═══\n // Geon (건)\n [AnimationType.GEON_HEAVEN_STRIKE, HEAVEN_STRIKE_ANIMATION],\n [AnimationType.GEON_ROUNDHOUSE, ROUNDHOUSE_KICK_ANIMATION_ENHANCED],\n [AnimationType.CRUSHING_ELBOW, ELBOW_STRIKE_ANIMATION_ENHANCED],\n [AnimationType.GEON_COUNTER, GEON_COUNTER_STRIKE],\n [AnimationType.HIGH_BLOCK, GEON_HIGH_BLOCK],\n\n // Li (리)\n [AnimationType.PHOENIX_EYE_STRIKE, NERVE_STRIKE_ANIMATION],\n [AnimationType.NERVE_STRIKE_COUNTER, LI_NERVE_STRIKE_COUNTER],\n [AnimationType.PRECISION_PARRY, LI_PRECISION_PARRY],\n [AnimationType.LI_SOLAR_PLEXUS, SOLAR_PLEXUS_STRIKE_ANIMATION],\n [AnimationType.SOLAR_PLEXUS_SPEAR, SPEAR_HAND_ANIMATION],\n\n // Tae (태) - Lake: Joint Manipulation\n [AnimationType.WRIST_LOCK_STRIKE, WRIST_LOCK_ANIMATION],\n [AnimationType.WRIST_LOCK, TAE_WRIST_LOCK_SEQUENCE], // Enhanced Hapkido wrist lock\n [AnimationType.SPIRAL_SHOULDER_THROW, HIP_THROW_ANIMATION],\n [AnimationType.JOINT_LOCK_DEFENSE, TAE_FLOWING_COUNTER],\n [AnimationType.SWEEP_DEFENSE, TAE_SWEEP_DEFENSE],\n [AnimationType.WRIST_TWIST_COUNTER, TAE_FLOWING_COUNTER],\n [AnimationType.FLOWING_CROSS, TAE_FLOWING_STRIKES], // Continuous palm strikes\n [AnimationType.SMALL_CIRCLE_LOCK, TAE_SMALL_CIRCLE], // Advanced dual joint control\n [AnimationType.FINGER_LOCK, TAE_FINGER_LOCK],\n [AnimationType.ELBOW_LOCK, TAE_ELBOW_CONTROL], // Enhanced two-handed control\n [AnimationType.ELBOW_HYPEREXTEND, TAE_ELBOW_CONTROL],\n [AnimationType.SHOULDER_MANIPULATION, TAE_SHOULDER_LOCK], // Enhanced shoulder lock\n [AnimationType.ARM_BAR, TAE_ARM_BAR], // Most powerful submission\n [AnimationType.FLOWING_ARM_BAR, TAE_ARM_BAR], // Alias for flowing arm bar\n\n // Jin (진)\n [AnimationType.EXPLOSIVE_KNEE, KNEE_STRIKE_ANIMATION_ENHANCED],\n [AnimationType.LIGHTNING_STRAIGHT, JAB_ANIMATION_ENHANCED],\n [AnimationType.SHOCKING_COUNTER, JIN_SHOCKING_COUNTER],\n [AnimationType.SHOCKING_HAMMER_FIST, HAMMER_FIST_ANIMATION],\n [AnimationType.EXPLOSIVE_BLOCK, JIN_EXPLOSIVE_BLOCK],\n\n // Son (손)\n [AnimationType.CONTINUOUS_DEFLECTION, SON_CONTINUOUS_DEFLECTION],\n [AnimationType.PRESSURE_COUNTER, SON_PRESSURE_COUNTER],\n [AnimationType.RAPID_FOOTWORK, FLOWING_PUSH_ANIMATION], // Placeholder until dedicated footwork\n [AnimationType.PENETRATING_PALM_RUSH, FLOWING_PUSH_ANIMATION],\n [AnimationType.PRESSURE_POINT_CHAIN, RAPID_BARRAGE_ANIMATION],\n\n // Gam (감)\n [AnimationType.WATER_COUNTER, GAM_REDIRECTION_COUNTER],\n [AnimationType.CIRCULAR_PARRY, GAM_FLOWING_BLOCK], // Updated to Flowing Block\n [AnimationType.FLOW_DEFENSE, GAM_FLOWING_BLOCK], // Updated to Flowing Block\n [AnimationType.FLOWING_RIVER_STRIKE, GAM_FLOWING_RIVER_STRIKE],\n [AnimationType.REDIRECTION_COUNTER, GAM_REDIRECTION_COUNTER],\n [AnimationType.TIDAL_WAVE_PALM, GAM_TIDAL_WAVE_PALM],\n [AnimationType.WHIRLPOOL_COUNTER, GAM_WHIRLPOOL_COUNTER],\n\n // Gan (간)\n [AnimationType.ROCK_COUNTER, GAN_COUNTER_FORTRESS],\n [AnimationType.ROCK_DEFENSE, GAN_IMMOVABLE_BLOCK],\n [AnimationType.AVALANCHE_HAMMER, HAMMER_FIST_ANIMATION],\n [AnimationType.COUNTER_FORTRESS, GAN_COUNTER_FORTRESS],\n [AnimationType.FORTRESS_COUNTER_STRIKE, COUNTER_STRIKE_ANIMATION],\n [AnimationType.STONE_WALL_THRUST, PUSH_KICK_ANIMATION],\n\n // General Grappling\n [AnimationType.TAKEDOWN, TAKEDOWN_ANIMATION],\n [AnimationType.JOINT_LOCK, JOINT_LOCK_DEFENSE_ANIMATION],\n [AnimationType.HIP_WHEEL_THROW, HIP_THROW_ANIMATION],\n [AnimationType.SSIREUM_THROW, BODY_LOCK_THROW_ANIMATION], // Korean wrestling style\n [AnimationType.SACRIFICE_THROW, SUPLEX_ANIMATION], // Close match\n [AnimationType.CLINCH, CLINCH_KNEE_ANIMATION],\n [AnimationType.PARRY, PARRY_COUNTER_ANIMATION],\n\n // Movement/Basic\n [AnimationType.SIDESTEP, SIDE_STEP_ANIMATION],\n [AnimationType.PIVOT, SIDE_STEP_ANIMATION], // Reusing side step\n [AnimationType.STEP_FORWARD, FORWARD_DASH_ANIMATION],\n [AnimationType.STEP_BACK, BACKWARD_RETREAT_ANIMATION],\n [AnimationType.FORWARD_DASH, FORWARD_DASH_ANIMATION],\n [AnimationType.BACKWARD_RETREAT, BACKWARD_RETREAT_ANIMATION],\n [AnimationType.WALK, FORWARD_DASH_ANIMATION], // Placeholder\n [AnimationType.RECOVERY, IDLE_STANCE_ANIMATION],\n [AnimationType.STANCE, IDLE_STANCE_ANIMATION],\n [AnimationType.IDLE, IDLE_STANCE_ANIMATION],\n [AnimationType.DUCK, IDLE_STANCE_ANIMATION], // Placeholder\n [AnimationType.LEAN, IDLE_STANCE_ANIMATION], // Placeholder\n\n // Defense Aliases\n [AnimationType.BLOCK, BLOCK_ANIMATION],\n [AnimationType.BLOCK_HIGH, HIGH_BLOCK_ANIMATION],\n [AnimationType.BLOCK_LOW, LOW_BLOCK_ANIMATION],\n [AnimationType.FLOWING_BLOCK, GAM_FLOWING_BLOCK],\n [AnimationType.IRON_BLOCK, GAN_IMMOVABLE_BLOCK],\n [AnimationType.IMMOVABLE_BLOCK, GAN_IMMOVABLE_BLOCK],\n [AnimationType.THUNDEROUS_UPPERCUT, ELBOW_UPPERCUT_ANIMATION],\n\n // Gon (곤)\n [AnimationType.GROUNDING_DEFENSE, GON_GROUNDING_DEFENSE],\n [AnimationType.TAKEDOWN_COUNTER, GON_TAKEDOWN_COUNTER],\n [AnimationType.EARTHQUAKE_STOMP, AXE_KICK_ANIMATION],\n [AnimationType.GROUND_SWEEP_STRIKE, SWEEP_ANIMATION],\n [AnimationType.ROOTING_TAKEDOWN, TAKEDOWN_ANIMATION],\n [AnimationType.TAKEDOWN_COUNTER, GON_TAKEDOWN_COUNTER],\n [AnimationType.ANKLE_PICK, TAKEDOWN_ANIMATION],\n [AnimationType.BODY_LOCK_SLAM, BODY_LOCK_THROW_ANIMATION],\n\n // Dark Ops (암살자)\n [AnimationType.TEMPLE_STRIKE, TEMPLE_ELBOW_ANIMATION],\n [AnimationType.BRACHIAL_PLEXUS, BRACHIAL_ELBOW_ANIMATION],\n [AnimationType.ACHILLES_ATTACK, LEG_REAP_ANIMATION],\n [AnimationType.CERVICAL_TWIST, REAR_NAKED_CHOKE_ANIMATION],\n [AnimationType.ELBOW_HYPEREXTEND, ARM_BAR_ANIMATION],\n [AnimationType.FEMORAL_NERVE, FEMORAL_KNEE_ANIMATION],\n [AnimationType.LARYNX_CRUSH, THROAT_STRIKE_ANIMATION],\n [AnimationType.JAW_DISLOCATION, HOOK_ANIMATION],\n [AnimationType.FINGER_BREAK, FINGER_LOCK_ANIMATION],\n [AnimationType.GUILLOTINE_CHOKE, CAROTID_CHOKE_ANIMATION],\n [AnimationType.JUGULAR_STRIKE, THROAT_STRIKE_ANIMATION],\n [AnimationType.KNEECAP_STRIKE, LOW_KICK_ANIMATION],\n [AnimationType.OCCIPITAL_STRIKE, HAMMER_FIST_ANIMATION],\n [AnimationType.SCIATIC_NERVE_STRIKE, KNEE_STRIKE_ANIMATION_ENHANCED],\n [AnimationType.SILENT_TAKEDOWN, REAR_NAKED_CHOKE_ANIMATION],\n [AnimationType.SLEEPER_HOLD, REAR_NAKED_CHOKE_ANIMATION],\n [AnimationType.TRIANGLE_CHOKE, LEG_REAP_ANIMATION], // Uses legs\n [AnimationType.SPLEEN_RUPTURE, KNEE_STRIKE_ANIMATION_ENHANCED],\n\n // Musa (무사)\n [AnimationType.DRAGON_FIST, HEAVEN_STRIKE_ANIMATION],\n [AnimationType.THUNDER_STRIKE, HEAVEN_STRIKE_ANIMATION],\n [AnimationType.MOUNTAIN_BREAKER, HAMMER_FIST_ANIMATION],\n [AnimationType.IRON_DEFENSE, GEON_HIGH_BLOCK],\n\n // Amsalja (암살자)\n [AnimationType.DEADLY_PRECISION, NERVE_STRIKE_ANIMATION],\n [AnimationType.SHADOW_NERVE_STRIKE, NERVE_STRIKE_ANIMATION],\n [AnimationType.SHADOW_STRIKE, CROSS_ANIMATION_ENHANCED],\n [AnimationType.SILENT_DEATH, HEAVEN_STRIKE_ANIMATION], // Finisher\n\n // Hacker (해커)\n [AnimationType.CYBER_OVERDRIVE, RAPID_BARRAGE_ANIMATION],\n [AnimationType.DATA_STRIKE, NERVE_STRIKE_ANIMATION],\n [AnimationType.ELECTRIC_SHOCK, PALM_STRIKE_ANIMATION],\n [AnimationType.SYSTEM_CRASH, HEAVEN_STRIKE_ANIMATION],\n\n // Jeongbo (정보요원)\n [AnimationType.TACTICAL_STRIKE, JAB_ANIMATION_ENHANCED],\n [AnimationType.INTELLIGENCE_STRIKE, NERVE_STRIKE_ANIMATION],\n [AnimationType.COUNTER_INTELLIGENCE, PARRY_COUNTER_ANIMATION],\n [AnimationType.PSYCHOLOGICAL_WARFARE, IDLE_STANCE_ANIMATION],\n\n // Jojik (조직폭력배)\n [AnimationType.RUTHLESS_ASSAULT, RAPID_BARRAGE_ANIMATION],\n [AnimationType.STREET_BRAWL, OVERHAND_ANIMATION],\n [AnimationType.IMPROVISED_WEAPON, HAMMER_FIST_ANIMATION],\n [AnimationType.BRUTAL_TAKEDOWN, BODY_LOCK_THROW_ANIMATION],\n\n // Misc\n [AnimationType.IDLE_STANCE, IDLE_STANCE_ANIMATION],\n\n // Movement Aliases\n [AnimationType.SIDE_STEP, SIDE_STEP_ANIMATION],\n\n // [AnimationType.FLOWING_PUSH, FLOWING_PUSH_ANIMATION],\n // [AnimationType.THROAT_STRIKE, THROAT_STRIKE_ANIMATION],\n // [AnimationType.EYE_GOUGE, EYE_GOUGE_ANIMATION],\n // [AnimationType.NERVE_PARALYSIS, NERVE_PARALYSIS_ANIMATION],\n // [AnimationType.LIVER_DISRUPTION, LIVER_DISRUPTION_ANIMATION],\n // [AnimationType.EAR_STRIKE, EAR_STRIKE_ANIMATION],\n\n // Elbow/Knee (팔꿈치/무릎) - Using enhanced versions with recovery phases\n [AnimationType.ELBOW_STRIKE, ELBOW_STRIKE_ANIMATION_ENHANCED],\n [AnimationType.ELBOW_UPPERCUT, ELBOW_UPPERCUT_ANIMATION_ENHANCED],\n [AnimationType.SPINNING_ELBOW, SPINNING_ELBOW_ANIMATION],\n [AnimationType.KNEE_STRIKE, KNEE_STRIKE_ANIMATION_ENHANCED],\n [AnimationType.FLYING_KNEE, FLYING_KNEE_ANIMATION],\n [AnimationType.KNEE_KICK, KNEE_KICK_ANIMATION],\n [AnimationType.CLINCH_KNEE, CLINCH_KNEE_ANIMATION],\n [AnimationType.TEMPLE_ELBOW, TEMPLE_ELBOW_ANIMATION],\n [AnimationType.SPINNING_BACK_ELBOW, SPINNING_BACK_ELBOW_ANIMATION],\n [AnimationType.SPINAL_ELBOW, SPINAL_ELBOW_ANIMATION],\n [AnimationType.BRACHIAL_ELBOW, BRACHIAL_ELBOW_ANIMATION],\n [AnimationType.KIDNEY_KNEE, KIDNEY_KNEE_ANIMATION],\n [AnimationType.FEMORAL_KNEE, FEMORAL_KNEE_ANIMATION],\n\n // Grappling (잡기)\n [AnimationType.THROW, THROW_ANIMATION],\n [AnimationType.GRAPPLE, GRAPPLE_ANIMATION],\n [AnimationType.SWEEP, SWEEP_ANIMATION],\n [AnimationType.SLAM, SLAM_ANIMATION],\n [AnimationType.WRIST_LOCK, WRIST_LOCK_ANIMATION],\n [AnimationType.ARM_BAR, ARM_BAR_ANIMATION],\n [AnimationType.SHOULDER_LOCK, SHOULDER_LOCK_ANIMATION],\n [AnimationType.HIP_THROW, HIP_THROW_ANIMATION],\n [AnimationType.LEG_REAP, LEG_REAP_ANIMATION],\n [AnimationType.SMALL_CIRCLE_LOCK, SMALL_CIRCLE_LOCK_ANIMATION],\n [AnimationType.FINGER_LOCK, FINGER_LOCK_ANIMATION],\n [AnimationType.ELBOW_LOCK, ELBOW_LOCK_ANIMATION],\n [AnimationType.SHOULDER_MANIPULATION, SHOULDER_MANIPULATION_ANIMATION],\n [AnimationType.FLOWING_ARM_BAR, FLOWING_ARM_BAR_ANIMATION],\n [AnimationType.MOUNTAIN_LOCK, MOUNTAIN_LOCK_ANIMATION],\n [AnimationType.EARTH_EMBRACE, EARTH_EMBRACE_ANIMATION],\n [AnimationType.CAROTID_CHOKE, CAROTID_CHOKE_ANIMATION],\n [AnimationType.REAR_NAKED_CHOKE, REAR_NAKED_CHOKE_ANIMATION],\n [AnimationType.REDIRECT_THROW, REDIRECT_THROW_ANIMATION],\n\n // Counters (반격)\n [AnimationType.COUNTER_ATTACK, COUNTER_ATTACK_ANIMATION],\n [AnimationType.COUNTER_STRIKE, COUNTER_STRIKE_ANIMATION],\n [AnimationType.PARRY_COUNTER, PARRY_COUNTER_ANIMATION],\n\n // Defense (방어)\n [AnimationType.BLOCK, BLOCK_ANIMATION],\n [AnimationType.BLOCK_HIGH, HIGH_BLOCK_ANIMATION],\n [AnimationType.BLOCK_LOW, LOW_BLOCK_ANIMATION],\n [AnimationType.JOINT_LOCK_DEFENSE, JOINT_LOCK_DEFENSE_ANIMATION],\n [AnimationType.SWEEP_DEFENSE, SWEEP_DEFENSE_ANIMATION],\n ]);\n\n/**\n * All animations combined into a single map by name\n * 이름별 전체 애니메이션 맵\n */\nexport const ALL_ANIMATIONS: ReadonlyMap<string, SkeletalAnimation> = new Map([\n ...KICK_ANIMATIONS,\n ...PUNCH_ANIMATIONS,\n ...SPECIALIZED_PUNCH_ANIMATIONS, // Add specialized punch variants\n ...ELBOW_KNEE_ANIMATIONS,\n ...GRAPPLING_ANIMATIONS,\n ...STANCE_ANIMATIONS,\n ...DARKOPS_ANIMATIONS,\n ...COMBO_ANIMATIONS,\n ...MOVEMENT_ANIMATIONS,\n ...ALL_ATTACK_ANIMATIONS, // Stance-specific attack animations (24 unique)\n ...BASIC_ANIMATIONS, // Idle, Walk, Run, Fall animations\n ...STANCE_LOCOMOTION_ANIMATIONS, // Stance-specific walk/run animations (16 unique)\n // Trigram idle animations with breathing/weight shifts (overrides static stance poses)\n // Must come after STANCE_ANIMATIONS to properly override stance_geon, stance_tae, etc.\n ...TRIGRAM_IDLE_ANIMATIONS_BY_NAME,\n\n // ═══ Trigram-specific stance and technique animations (팔괘 자세/기술 애니메이션) ═══\n // Each trigram has unique movement patterns and combat techniques\n ...GEON_ANIMATIONS, // ☰ Heaven: Direct force, power strikes\n ...TAE_STANCE_ANIMATIONS, // ☱ Lake: Fluid joint manipulation\n ...JIN_ANIMATIONS, // ☳ Thunder: Explosive power\n ...JIN_TECHNIQUE_ANIMATIONS, // ☳ Thunder techniques\n ...SON_STANCE_ANIMATIONS, // ☴ Wind: Continuous pressure\n ...SON_TECHNIQUE_ANIMATIONS, // ☴ Wind techniques\n ...GAN_STANCE_ANIMATIONS, // ☶ Mountain: Defensive mastery\n ...GAN_TECHNIQUE_ANIMATIONS, // ☶ Mountain techniques\n // Additional animations from AttackAnimations not in other maps\n [\"idle_stance\", IDLE_STANCE_ANIMATION],\n [\"forward_dash\", FORWARD_DASH_ANIMATION],\n [\"backward_retreat\", BACKWARD_RETREAT_ANIMATION],\n [\"side_step\", SIDE_STEP_ANIMATION],\n]);\n\n// ═══════════════════════════════════════════════════════════════════════════\n// ANIMATION ID-BASED REGISTRY (animationId → SkeletalAnimation)\n// Direct 1-1 mapping for technique animations using new architecture\n// ═══════════════════════════════════════════════════════════════════════════\n\n/**\n * Animation ID Registry - Direct mapping from technique animationId to SkeletalAnimation\n *\n * This provides 1-1 mapping between technique IDs and animations, using the new\n * animationId field instead of the legacy AnimationType enum.\n *\n * 기술 ID에서 애니메이션으로의 직접 매핑 (1-1 관계)\n *\n * @see TechniqueAnimationMapping.ts for legacy technique → AnimationType mappings\n * @korean 애니메이션ID레지스트리\n */\nexport const ANIMATION_ID_REGISTRY: ReadonlyMap<string, SkeletalAnimation> =\n new Map([\n // Complete mapping for all 57 techniques using existing animations\n [\"amsalja_silent_death\", SPEAR_HAND_ANIMATION],\n [\"darkops_ear_strike\", EAR_STRIKE_ANIMATION],\n [\"darkops_eye_gouge\", EYE_GOUGE_ANIMATION],\n [\"darkops_finger_break\", FINGER_LOCK_ANIMATION],\n [\"darkops_kidney_strike\", KIDNEY_KNEE_ANIMATION],\n [\"darkops_larynx_crush\", THROAT_STRIKE_ANIMATION],\n [\"darkops_sleeper_hold\", REAR_NAKED_CHOKE_ANIMATION],\n [\"darkops_spinal_strike\", SPINAL_ELBOW_ANIMATION],\n // eslint-disable-next-line @typescript-eslint/no-non-null-assertion -- animation guaranteed to exist in registry\n [\"gam_circular_parry\", STANCE_ANIMATIONS.get(\"gam_circular_parry\")!], // Use dedicated circular parry animation\n [\"gam_flow_defense\", GAM_FLOW_DEFENSE],\n [\"gam_flowing_block\", GAM_FLOWING_BLOCK],\n [\"gam_hip_throw\", HIP_THROW_ANIMATION],\n [\"gam_redirect_throw\", REDIRECT_THROW_ANIMATION],\n [\"gam_water_counter\", GAM_WATER_FLOW_COUNTER_ANIMATION],\n [\"gan_counter_strike\", GAN_COUNTER_FORTRESS],\n // eslint-disable-next-line @typescript-eslint/no-non-null-assertion -- animation guaranteed to exist in registry\n [\"gan_immovable_stance\", STANCE_ANIMATIONS.get(\"gan_immovable_stance\")!], // Use dedicated immovable stance animation\n [\"gan_iron_block\", GAN_IMMOVABLE_BLOCK],\n [\"gan_rock_defense\", GAN_ROCK_DEFENSE_ANIMATION],\n [\"geon_axe_kick\", AXE_KICK_ANIMATION],\n [\"geon_counter_strike\", GEON_COUNTER_STRIKE],\n [\"geon_crushing_elbow\", ELBOW_STRIKE_ANIMATION_ENHANCED],\n [\"geon_elbow_smash\", ELBOW_STRIKE_ANIMATION_ENHANCED],\n [\"geon_frontal_kick\", FRONT_KICK_ANIMATION_ENHANCED],\n [\"geon_heaven_strike\", HEAVEN_STRIKE_ANIMATION],\n [\"geon_heavenly_fist\", JAB_ANIMATION_ENHANCED],\n [\"geon_high_block\", GEON_HIGH_BLOCK],\n [\"geon_palm_strike\", PALM_STRIKE_ANIMATION],\n [\"gon_ankle_pick\", SWEEP_ANIMATION],\n [\"gon_earth_embrace\", EARTH_EMBRACE_ANIMATION],\n [\"gon_ground_pound\", SLAM_ANIMATION],\n [\"gon_leg_sweep\", SWEEP_ANIMATION],\n [\"gon_ssireum_throw\", HIP_THROW_ANIMATION],\n [\"hacker_data_strike\", PALM_STRIKE_ANIMATION],\n [\"hacker_system_crash\", HAMMER_FIST_ANIMATION],\n [\"jin_back_kick\", BACK_KICK_ANIMATION],\n [\"jin_explosive_knee\", KNEE_STRIKE_ANIMATION_ENHANCED],\n [\"jin_flying_sidekick\", FLYING_KICK_ANIMATION],\n [\"jin_knee_strike\", KNEE_STRIKE_ANIMATION_ENHANCED],\n [\"jin_tornado_kick\", TORNADO_KICK_ANIMATION],\n [\"jojik_street_brawl\", HOOK_ANIMATION],\n [\"li_flame_spear\", SPEAR_HAND_ANIMATION],\n [\"li_nerve_strike\", NERVE_STRIKE_ANIMATION],\n [\"li_precision_parry\", LI_PRECISION_PARRY],\n [\"li_sidekick\", SIDE_KICK_ANIMATION],\n [\"li_temple_strike\", TEMPLE_ELBOW_ANIMATION],\n [\"musa_dragon_fist\", CROSS_ANIMATION_ENHANCED],\n [\"musa_iron_defense\", HIGH_BLOCK_ANIMATION],\n [\"musa_thunder_strike\", UPPERCUT_ANIMATION],\n [\"son_flowing_push\", FLOWING_PUSH_ANIMATION],\n [\"son_rapid_footwork\", SIDE_STEP_ANIMATION],\n [\"son_sweeping_low_kick\", LOW_KICK_ANIMATION],\n [\"tae_arm_bar\", ARM_BAR_ANIMATION],\n [\"tae_elbow_lock\", ELBOW_LOCK_ANIMATION],\n [\"tae_finger_lock\", FINGER_LOCK_ANIMATION],\n [\"tae_flowing_strikes\", FLOWING_CROSS_ANIMATION],\n [\"tae_sweep_defense\", SWEEP_DEFENSE_ANIMATION],\n [\"tae_wrist_lock\", WRIST_LOCK_ANIMATION],\n ]);\n\n/**\n * Category-based default animations for fallback\n *\n * When an animationId is not found, fallback to category default.\n *\n * 카테고리별 기본 애니메이션 (fallback용)\n */\nexport const CATEGORY_DEFAULT_ANIMATIONS: ReadonlyMap<string, SkeletalAnimation> =\n new Map([\n [\"punch\", JAB_ANIMATION_ENHANCED],\n [\"kick\", FRONT_KICK_ANIMATION_ENHANCED],\n [\"strike\", PALM_STRIKE_ANIMATION],\n [\"joint_lock\", WRIST_LOCK_ANIMATION],\n [\"throw\", HIP_THROW_ANIMATION],\n [\"defensive\", BLOCK_ANIMATION],\n [\"elbow_strike\", ELBOW_STRIKE_ANIMATION_ENHANCED],\n [\"counter\", COUNTER_STRIKE_ANIMATION],\n [\"jumping_kick\", JUMPING_KICK_ANIMATION],\n [\"grapple\", GRAPPLE_ANIMATION],\n [\"sweep\", SWEEP_ANIMATION],\n [\"knee_strike\", KNEE_STRIKE_ANIMATION_ENHANCED],\n [\"footwork\", SIDE_STEP_ANIMATION],\n [\"stance\", IDLE_STANCE_ANIMATION],\n [\"takedown\", SLAM_ANIMATION],\n ]);\n\n// ═══════════════════════════════════════════════════════════════════════════\n// ANIMATION LOOKUP FUNCTIONS\n// ═══════════════════════════════════════════════════════════════════════════\n\n/**\n * Get animation by AnimationType\n *\n * @param type - Animation type enum value\n * @returns Skeletal animation or undefined\n *\n * @korean 애니메이션타입으로조회\n */\nexport function getAnimationByType(\n type: AnimationType,\n): SkeletalAnimation | undefined {\n return ANIMATION_REGISTRY.get(type);\n}\n\n/**\n * Get animation by AnimationType with fallback\n *\n * @param type - Animation type enum value\n * @param fallback - Fallback animation type\n * @returns Skeletal animation (never undefined)\n *\n * @korean 애니메이션타입으로조회_기본값\n */\nexport function getAnimationByTypeOrDefault(\n type: AnimationType,\n fallback: AnimationType = AnimationType.JAB,\n): SkeletalAnimation {\n const animation =\n ANIMATION_REGISTRY.get(type) ?? ANIMATION_REGISTRY.get(fallback);\n\n if (!animation) {\n throw new Error(`Missing animation for ${type} with fallback ${fallback}`);\n }\n\n return animation;\n}\n\n/**\n * Get animation for a technique by technique ID (using AnimationType mapping)\n *\n * Uses the TechniqueAnimationMapping to find the correct animation\n * for any technique in the game.\n *\n * @param techniqueId - Technique identifier (e.g., \"geon_frontal_kick\")\n * @returns Skeletal animation or undefined\n *\n * @korean 기술ID로애니메이션조회\n */\nexport function getAnimationForTechniqueId(\n techniqueId: string,\n): SkeletalAnimation | undefined {\n const config = getTechniqueAnimationConfig(techniqueId);\n if (!config) return undefined;\n return ANIMATION_REGISTRY.get(config.type);\n}\n\n/**\n * Get animation for a technique with fallback\n *\n * @param techniqueId - Technique identifier\n * @param fallbackType - Fallback animation type\n * @returns Animation with speed modifier\n *\n * @korean 기술ID로애니메이션조회_기본값\n */\nexport function getAnimationForTechniqueIdWithConfig(\n techniqueId: string,\n fallbackType: AnimationType = AnimationType.JAB,\n): { animation: SkeletalAnimation; speed: number } {\n const config = getAnimationForTechniqueOrDefault(techniqueId, fallbackType);\n const animation =\n ANIMATION_REGISTRY.get(config.type) ?? ANIMATION_REGISTRY.get(fallbackType);\n\n if (!animation) {\n throw new Error(\n `Missing animation for ${config.type} with fallback ${fallbackType}`,\n );\n }\n return { animation, speed: config.speed };\n}\n\n// ═══════════════════════════════════════════════════════════════════════════\n// NEW ARCHITECTURE: ID-Based Animation Lookup Functions\n// ═══════════════════════════════════════════════════════════════════════════\n\n/**\n * Get animation by animationId (new architecture)\n *\n * Direct 1-1 lookup using the new animationId field from technique.\n * This is the preferred method for the new animation architecture.\n *\n * @param animationId - Technique's animationId (e.g., \"geon_heaven_strike\")\n * @returns SkeletalAnimation or undefined if not found\n *\n * @korean 애니메이션ID로조회\n */\nexport function getAnimationById(\n animationId: string,\n): SkeletalAnimation | undefined {\n return ANIMATION_ID_REGISTRY.get(animationId);\n}\n\n/**\n * Get animation by ID with category-based fallback\n *\n * Implements a 3-tier fallback system:\n * 1. Try direct ID lookup in ANIMATION_ID_REGISTRY\n * 2. If not found, use category default from CATEGORY_DEFAULT_ANIMATIONS\n * 3. If no category, use IDLE_STANCE as ultimate fallback\n *\n * This function never returns undefined, making it safe for use in game logic.\n *\n * @param animationId - Technique's animationId\n * @param animationCategory - Technique's animationCategory for fallback\n * @returns SkeletalAnimation (never undefined)\n *\n * @korean 애니메이션ID로조회_카테고리대체\n */\nexport function getAnimationByIdWithFallback(\n animationId: string | undefined,\n animationCategory?: string,\n): SkeletalAnimation {\n // Try direct ID lookup\n if (animationId) {\n const animation = ANIMATION_ID_REGISTRY.get(animationId);\n if (animation) return animation;\n }\n\n // Fallback to category default\n if (animationCategory) {\n const categoryDefault = CATEGORY_DEFAULT_ANIMATIONS.get(animationCategory);\n if (categoryDefault) return categoryDefault;\n }\n\n // Ultimate fallback\n return IDLE_STANCE_ANIMATION;\n}\n\n/**\n * Check if animation ID exists in registry\n *\n * @param animationId - Animation ID to check\n * @returns true if animation exists in ANIMATION_ID_REGISTRY\n *\n * @korean 애니메이션ID존재확인\n */\nexport function hasAnimationId(animationId: string): boolean {\n return ANIMATION_ID_REGISTRY.has(animationId);\n}\n\n/**\n * Get category default animation\n *\n * Returns the default animation for a given category, useful for fallback logic.\n *\n * @param category - Animation category (e.g., \"punch\", \"kick\")\n * @returns SkeletalAnimation or undefined if category not found\n *\n * @korean 카테고리기본애니메이션조회\n */\nexport function getCategoryDefaultAnimation(\n category: string,\n): SkeletalAnimation | undefined {\n return CATEGORY_DEFAULT_ANIMATIONS.get(category);\n}\n\n/**\n * Get animation by name (legacy support)\n *\n * @param name - Animation name (e.g., \"front_kick\")\n * @returns Skeletal animation or undefined\n *\n * @korean 이름으로애니메이션조회\n */\nexport function getAnimationByName(\n name: string,\n): SkeletalAnimation | undefined {\n return ALL_ANIMATIONS.get(name);\n}\n\n/**\n * Get animation by name - unified lookup across all animation registries\n *\n * Searches ALL_ANIMATIONS which includes:\n * - BASIC_ANIMATIONS (idle, walk, run, fall)\n * - KICK_ANIMATIONS, PUNCH_ANIMATIONS, etc.\n * - STANCE_ANIMATIONS, MOVEMENT_ANIMATIONS\n * - ALL_ATTACK_ANIMATIONS (stance-specific attacks)\n *\n * @param name - Animation name (e.g., \"idle\", \"front_kick\", \"walk\")\n * @returns Skeletal animation or undefined\n *\n * @korean 애니메이션가져오기\n */\nexport function getAnimation(name: string): SkeletalAnimation | undefined {\n return ALL_ANIMATIONS.get(name);\n}\n\n// ═══════════════════════════════════════════════════════════════════════════\n// TECHNIQUE TO ANIMATION LOOKUP\n// ═══════════════════════════════════════════════════════════════════════════\n\n/**\n * Regex fallback patterns for technique-to-animation mapping\n * Used when technique ID is not found in ALL_ANIMATIONS\n *\n * NOTE: Order matters - more specific patterns must come first\n *\n * @korean 기술애니메이션폴백매핑\n */\nconst TECHNIQUE_ANIMATION_FALLBACK: ReadonlyArray<readonly [RegExp, string]> = [\n // ───────────────────────────────────────────────────────────────────────\n // Stance-specific distinctive strikes - MUST be checked before the generic\n // /strike/ rule so that \"Thunder Strike\", \"Heaven Strike\", \"Nerve Strike\",\n // \"Precision Strike\", etc. don't collapse to the generic \"jab\" fallback.\n // ───────────────────────────────────────────────────────────────────────\n [/heaven.?strike|천둥벽력|하늘치기/i, \"heaven_strike\"],\n [/lightning.?(strike|flash)|번개|뇌격/i, \"lightning_strike\"],\n [/nerve.?strike|신경.?타격|신경치기/i, \"nerve_strike\"],\n [/pressure.?point|혈도|급소/i, \"pressure_point_strike\"],\n [/solar.?plexus|명치|태양신경총/i, \"solar_plexus_strike\"],\n [/throat.?strike|후두타격|목치기/i, \"throat_strike\"],\n [/temple.?(strike|elbow)|관자놀이/i, \"temple_strike\"],\n [/eye.?gouge|눈찌르기/i, \"eye_gouge\"],\n [/ear.?strike|귀치기/i, \"ear_strike\"],\n [/liver.?(disruption|strike)|간장|간치기/i, \"liver_disruption\"],\n [/kidney.?(strike|punch)|신장치기/i, \"kidney_strike\"],\n [/spear.?hand|관수|손끝찌르기/i, \"spear_hand_strike\"],\n [/flowing.?cross|flowing.?strike|유수타격/i, \"flowing_cross\"],\n [/flowing.?push|flowing.?palm/i, \"flowing_push\"],\n [/rapid.?barrage|연환타/i, \"rapid_barrage\"],\n [/rhythmic.?strike|리듬타격/i, \"rhythmic_strikes\"],\n // Heavenly/dragon fist names are stance-specific and should map to the\n // proper Geon/Jin animations, not the generic jab/cross fallbacks.\n // Note: intentionally exclude \"정권\" (generic straight punch) here — it\n // should continue to resolve to the generic `jab` via the kick/jab\n // section below, not to Geon's heaven_strike.\n [/heavenly.?fist|천권/i, \"heaven_strike\"],\n [/dragon.?fist|용권/i, \"jin_lightning_flash\"],\n\n // Kicks (차기) - more specific patterns first\n [/axe.?kick|내려차기|naeryeo/i, \"axe_kick\"],\n [/back.?kick|뒤차기|dwi.?chagi/i, \"back_kick\"],\n [/tornado|회오리|hoe.?ori/i, \"tornado_kick\"],\n [/jump|뛰어|ttwi|flying/i, \"jumping_kick\"],\n [/sweep|쓸기|걸기|품밟기|dari.?geolgi/i, \"sweep\"],\n [/side.?kick|옆차기|yeop.?chagi/i, \"side_kick\"],\n [/low.?kick|하단차기|낮은차기|thigh.?kick|leg.?kick/i, \"low_kick\"],\n [/front.?kick|앞차기|snap.?kick|ap.?chagi/i, \"front_kick\"],\n [/roundhouse|돌려차기|dolryeo/i, \"roundhouse_kick\"],\n // Knee strikes (무릎)\n [/knee|무릎|mureup/i, \"knee_strike\"],\n // Elbow strikes (팔꿈치)\n [/elbow|팔꿈치|팔굽|palkkumchi/i, \"elbow_strike\"],\n // Throws & Slams (던지기/내던지기)\n [/slam|내던지기|smash/i, \"slam\"],\n [/throw|던지기|deonjigi|ground.?pound/i, \"throw\"],\n // Grapple/Lock (꺾기/잡기) - specific locks first\n [/arm.?bar|팔꺾기|팔관절기|juji.?gatame/i, \"arm_bar\"],\n [/wrist.?lock|손목꺾기|손목관절기|kote.?gaeshi/i, \"wrist_lock\"],\n [/lock|grapple|꺾기|잡기|embrace|kkeokgi|japgi|submission/i, \"grapple\"],\n // Counter attacks (반격)\n [/counter.?strike|반격타격|카운터스트라이크/i, \"counter_strike\"],\n [/counter|반격|bangyeok|parry|redirect/i, \"counter_attack\"],\n // Blocks (막기)\n [/block|막기|makgi|defense|방어/i, \"block\"],\n // Punches (주먹) - check after specific patterns\n [/hook|후크|횡타|갈고리/i, \"hook\"],\n [/palm|장권|jang.?gwon/i, \"palm_strike\"],\n [/cross|십자|교차/i, \"cross\"],\n [/uppercut|upper|올려|치올/i, \"uppercut\"],\n [/jab|잽|직권|찌르기|punch|주먹|권/i, \"jab\"],\n // Generic strikes last - only used when no more specific pattern matched above\n [/strike|타격|격|chigi/i, \"jab\"],\n] as const;\n\n/**\n * Get animation name for a technique\n *\n * PRIORITY ORDER:\n * 1. Direct lookup in ALL_ANIMATIONS by technique ID (stance-specific)\n * 2. Regex pattern matching for generic techniques\n * 3. Fallback to \"jab\"\n *\n * This ensures stance-specific animations like \"geon_heaven_strike\"\n * are used when available, while still supporting generic technique names.\n *\n * @param techniqueNameOrId - Technique name, ID, or Korean name\n * @returns Animation name from ALL_ANIMATIONS\n *\n * @example\n * ```typescript\n * getAnimationForTechnique(\"geon_heaven_strike\") // \"geon_heaven_strike\" (exact match)\n * getAnimationForTechnique(\"tae_wrist_lock\") // \"tae_wrist_lock\" (exact match)\n * getAnimationForTechnique(\"roundhouse_kick\") // \"roundhouse_kick\" (regex match)\n * getAnimationForTechnique(\"앞차기\") // \"front_kick\" (regex match)\n * ```\n *\n * @korean 기술에맞는애니메이션가져오기\n */\nexport function getAnimationForTechnique(techniqueNameOrId: string): string {\n if (!techniqueNameOrId) return \"jab\";\n\n // 1. First check if technique ID exists directly in ALL_ANIMATIONS\n // This handles stance-specific animations like \"geon_heaven_strike\"\n if (ALL_ANIMATIONS.has(techniqueNameOrId)) {\n return techniqueNameOrId;\n }\n\n // 2. Try a normalized form: \"Thunder Strike\" → \"thunder_strike\".\n // This lets us catch techniques whose English name happens to match\n // an animation key exactly once spaces are collapsed.\n const normalized = techniqueNameOrId\n .trim()\n .toLowerCase()\n .replace(/[\\s-]+/g, \"_\")\n .replace(/[^a-z0-9_]/g, \"\");\n if (normalized && ALL_ANIMATIONS.has(normalized)) {\n return normalized;\n }\n\n // 3. Try regex pattern matching for generic technique names\n for (const [pattern, animationName] of TECHNIQUE_ANIMATION_FALLBACK) {\n if (pattern.test(techniqueNameOrId)) {\n return animationName;\n }\n }\n\n // 4. Ultimate fallback to jab\n return \"jab\";\n}\n\n/**\n * Resolve a technique object to its best-matching animation key.\n *\n * This is the single, authoritative entry point for screens and systems\n * that need to know which skeletal animation to play for a given technique.\n *\n * Resolution order (most specific → least):\n * 1. `technique.animationId` if present and registered (KoreanTechnique / TrigramStanceTechnique).\n * 2. `technique.id` if registered (Technique objects from {@link getTechniquesForStanceAndArchetype}\n * already carry their stance-specific id, e.g. `\"geon_heaven_strike\"`).\n * 3. Regex-based fallback on `technique.name.english`, then `technique.name.korean`.\n * 4. `\"jab\"` as ultimate fallback.\n *\n * Passing the technique object (rather than a loose string) avoids the\n * long-standing bug where `technique.name.english` — e.g. `\"Thunder Strike\"` —\n * would match only the generic `/strike/` rule and collapse every\n * stance-specific attack to the jab animation.\n *\n * @param technique - Technique or technique-like object.\n * @returns Animation key registered in {@link ALL_ANIMATIONS}.\n *\n * @korean 기술객체로애니메이션해결\n */\nexport function resolveTechniqueAnimation(\n technique:\n | {\n readonly id?: string;\n readonly animationId?: string;\n readonly name?: {\n readonly english?: string;\n readonly korean?: string;\n };\n }\n | null\n | undefined,\n): string {\n if (!technique) return \"jab\";\n\n // 1. Prefer animationId (authoritative 1-1 mapping on KoreanTechnique).\n if (technique.animationId && ALL_ANIMATIONS.has(technique.animationId)) {\n return technique.animationId;\n }\n\n // 2. technique.id (Technique objects preserve stance-prefixed ids here).\n if (technique.id && ALL_ANIMATIONS.has(technique.id)) {\n return technique.id;\n }\n\n // 3. Regex/normalized fallback on the most specific string we have.\n const english = technique.name?.english;\n if (english) {\n const byEnglish = getAnimationForTechnique(english);\n // Guard: only accept the result if it's not the dumb \"jab\" default when\n // we still have an id/animationId we can try as a last resort.\n if (byEnglish !== \"jab\" || !technique.id) {\n return byEnglish;\n }\n }\n\n // 4. Try id as a string (may go through regex fallback if not registered).\n if (technique.id) {\n return getAnimationForTechnique(technique.id);\n }\n\n // 5. Korean name as last resort.\n const korean = technique.name?.korean;\n if (korean) {\n return getAnimationForTechnique(korean);\n }\n\n return \"jab\";\n}\n\n// ═══════════════════════════════════════════════════════════════════════════\n// RE-EXPORTS\n// ═══════════════════════════════════════════════════════════════════════════\n\n// Re-export all individual animations for direct access\nexport {\n ARM_BAR_ANIMATION,\n AXE_KICK_ANIMATION,\n BACK_KICK_ANIMATION,\n BLOCK_ANIMATION,\n COUNTER_ATTACK_ANIMATION,\n COUNTER_STRIKE_ANIMATION,\n CROSS_ANIMATION,\n // Elbow/Knee\n ELBOW_STRIKE_ANIMATION,\n ELBOW_UPPERCUT_ANIMATION,\n // Kicks\n FRONT_KICK_ANIMATION,\n GRAPPLE_ANIMATION,\n HOOK_ANIMATION,\n // Punches\n JAB_ANIMATION,\n JUMPING_KICK_ANIMATION,\n KNEE_STRIKE_ANIMATION,\n LOW_KICK_ANIMATION,\n PALM_STRIKE_ANIMATION,\n ROUNDHOUSE_KICK_ANIMATION,\n SIDE_KICK_ANIMATION,\n SLAM_ANIMATION,\n SWEEP_ANIMATION,\n // Grappling\n THROW_ANIMATION,\n TORNADO_KICK_ANIMATION,\n WRIST_LOCK_ANIMATION,\n};\n\n// Re-export category maps\nexport {\n COMBO_ANIMATIONS,\n DARKOPS_ANIMATIONS,\n ELBOW_KNEE_ANIMATIONS,\n GRAPPLING_ANIMATIONS,\n KICK_ANIMATIONS,\n MOVEMENT_ANIMATIONS,\n PUNCH_ANIMATIONS,\n STANCE_ANIMATIONS,\n};\n\n// Re-export animation types and mapping\nexport { AnimationType } from \"../builders/MartialArtsAnimationBuilder\";\nexport { hasAnimationMapping };\nexport type { AnimationConfig };\n"],"mappings":"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"}
|