blacktrigram 0.7.47 → 0.7.49
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/App2.js.map +1 -1
- package/lib/audio/AudioAssetLoader.js.map +1 -1
- package/lib/audio/AudioAssetRegistry.js.map +1 -1
- package/lib/audio/AudioCache.js.map +1 -1
- package/lib/audio/AudioManager.js.map +1 -1
- package/lib/audio/AudioMonitor.js.map +1 -1
- package/lib/audio/AudioPool.js.map +1 -1
- package/lib/audio/AudioProvider.js.map +1 -1
- package/lib/audio/AudioUtils.js.map +1 -1
- package/lib/audio/BoneImpactAudioMap.js.map +1 -1
- package/lib/audio/VariantSelector.js.map +1 -1
- package/lib/audio/types.js.map +1 -1
- package/lib/components/screens/combat/CombatScreen3D.d.ts.map +1 -1
- package/lib/components/screens/combat/CombatScreen3D.js +29 -25
- package/lib/components/screens/combat/CombatScreen3D.js.map +1 -1
- package/lib/components/screens/combat/components/controls/CombatButtons.js.map +1 -1
- package/lib/components/screens/combat/components/controls/CombatControlsPanel.js.map +1 -1
- package/lib/components/screens/combat/components/controls/ControlsGuide.js.map +1 -1
- package/lib/components/screens/combat/components/controls/KeyboardHints.js.map +1 -1
- package/lib/components/screens/combat/components/controls/PauseMenu.js.map +1 -1
- package/lib/components/screens/combat/components/controls/PauseMenuButton.js.map +1 -1
- package/lib/components/screens/combat/components/controls/QuickSettings.js.map +1 -1
- package/lib/components/screens/combat/components/effects/BloodDecals3D.js.map +1 -1
- package/lib/components/screens/combat/components/effects/BloodLossOverlayHtml.js.map +1 -1
- package/lib/components/screens/combat/components/effects/BloodParticles3D.js.map +1 -1
- package/lib/components/screens/combat/components/effects/BloodViscosity3D.js.map +1 -1
- package/lib/components/screens/combat/components/effects/CombatParticleEffects3D.js.map +1 -1
- package/lib/components/screens/combat/components/effects/ConsciousnessBlur.js.map +1 -1
- package/lib/components/screens/combat/components/effects/InternalDamage3D.js.map +1 -1
- package/lib/components/screens/combat/components/effects/PainVignette.js.map +1 -1
- package/lib/components/screens/combat/components/effects/ParticleAudio3D.js.map +1 -1
- package/lib/components/screens/combat/components/effects/TraumaOverlay3D.js.map +1 -1
- package/lib/components/screens/combat/components/feedback/MatchCountdown.js.map +1 -1
- package/lib/components/screens/combat/components/feedback/RoundAnnouncementOverlayHtml.js.map +1 -1
- package/lib/components/screens/combat/components/feedback/RoundDisplayStatus.js.map +1 -1
- package/lib/components/screens/combat/components/feedback/RoundStartAnnouncementOverlayHtml.js.map +1 -1
- package/lib/components/screens/combat/components/hud/CombatBottomHUD.d.ts.map +1 -1
- package/lib/components/screens/combat/components/hud/CombatBottomHUD.js +2 -2
- package/lib/components/screens/combat/components/hud/CombatBottomHUD.js.map +1 -1
- package/lib/components/screens/combat/components/hud/CombatLeftHUD.js.map +1 -1
- package/lib/components/screens/combat/components/hud/CombatPortraitStatusStrip.js +1 -1
- package/lib/components/screens/combat/components/hud/CombatPortraitStatusStrip.js.map +1 -1
- package/lib/components/screens/combat/components/hud/CombatRightHUD.js.map +1 -1
- package/lib/components/screens/combat/components/hud/CombatTopHUD.d.ts.map +1 -1
- package/lib/components/screens/combat/components/hud/CombatTopHUD.js +2 -1
- package/lib/components/screens/combat/components/hud/CombatTopHUD.js.map +1 -1
- package/lib/components/screens/combat/components/hud/DifficultyIndicator.js.map +1 -1
- package/lib/components/screens/combat/components/hud/FPSMonitor.js.map +1 -1
- package/lib/components/screens/combat/components/hud/MobileControlsWrapper.js.map +1 -1
- package/lib/components/screens/combat/components/hud/PlayerStateOverlayHtml.js.map +1 -1
- package/lib/components/screens/combat/components/indicators/BalanceIndicator.js.map +1 -1
- package/lib/components/screens/combat/components/indicators/InputBufferDisplay.js.map +1 -1
- package/lib/components/screens/combat/components/indicators/StaminaWarning.js.map +1 -1
- package/lib/components/screens/combat/components/indicators/TechniqueNameDisplay.js.map +1 -1
- package/lib/components/screens/combat/helpers/AnimationUpdater.d.ts.map +1 -1
- package/lib/components/screens/combat/helpers/AnimationUpdater.js +4 -2
- package/lib/components/screens/combat/helpers/AnimationUpdater.js.map +1 -1
- package/lib/components/screens/combat/helpers/combatHelpers.js.map +1 -1
- package/lib/components/screens/combat/hooks/useAICombat.js.map +1 -1
- package/lib/components/screens/combat/hooks/useCombatActions.js.map +1 -1
- package/lib/components/screens/combat/hooks/useCombatAttackMovement.js.map +1 -1
- package/lib/components/screens/combat/hooks/useCombatAudio.js.map +1 -1
- package/lib/components/screens/combat/hooks/useCombatLayout.d.ts.map +1 -1
- package/lib/components/screens/combat/hooks/useCombatLayout.js +11 -5
- package/lib/components/screens/combat/hooks/useCombatLayout.js.map +1 -1
- package/lib/components/screens/combat/hooks/useCombatState.js.map +1 -1
- package/lib/components/screens/controls/ControlsScreen3D.js +1 -1
- package/lib/components/screens/controls/ControlsScreen3D.js.map +1 -1
- package/lib/components/screens/controls/components/ControlBindingsOverlayHtml.js.map +1 -1
- package/lib/components/screens/controls/components/ControlCategoryTabsOverlayHtml.js.map +1 -1
- package/lib/components/screens/controls/components/GamepadVisualization3D.js.map +1 -1
- package/lib/components/screens/controls/components/InteractiveControlDemoOverlayHtml.js.map +1 -1
- package/lib/components/screens/controls/components/Key3D.js.map +1 -1
- package/lib/components/screens/controls/components/VisualKeyboard3D.js.map +1 -1
- package/lib/components/screens/controls/constants/ControlsConstants.js.map +1 -1
- package/lib/components/screens/controls/hooks/useControlsState.js.map +1 -1
- package/lib/components/screens/endscreen/EndScreen3D.js.map +1 -1
- package/lib/components/screens/endscreen/components/DefeatAnimation3D.js.map +1 -1
- package/lib/components/screens/endscreen/components/MatchStatisticsDisplayOverlayHtml.js.map +1 -1
- package/lib/components/screens/endscreen/components/NavigationButtonsOverlayHtml.js.map +1 -1
- package/lib/components/screens/endscreen/components/PerformanceBreakdownOverlayHtml.js.map +1 -1
- package/lib/components/screens/endscreen/components/PerformanceRatingOverlayHtml.js.map +1 -1
- package/lib/components/screens/endscreen/components/VictoryAnimation3D.js.map +1 -1
- package/lib/components/screens/endscreen/components/WinnerDisplayOverlayHtml.js.map +1 -1
- package/lib/components/screens/intro/IntroScreen3D.js +1 -1
- package/lib/components/screens/intro/IntroScreen3D.js.map +1 -1
- package/lib/components/screens/intro/components/AbilityListOverlayHtml.js.map +1 -1
- package/lib/components/screens/intro/components/ArchetypeCardGridOverlayHtml.js.map +1 -1
- package/lib/components/screens/intro/components/ArchetypeCardOverlayHtml.js.map +1 -1
- package/lib/components/screens/intro/components/ArchetypeDisplayOverlayHtml.js.map +1 -1
- package/lib/components/screens/intro/components/EnhancedArchetypeDisplayOverlayHtml.js.map +1 -1
- package/lib/components/screens/intro/components/MenuButtonsOverlayHtml.js.map +1 -1
- package/lib/components/screens/intro/components/MenuSectionOverlayHtml.js.map +1 -1
- package/lib/components/screens/intro/components/StatBarOverlayHtml.js.map +1 -1
- package/lib/components/screens/philosophy/PhilosophyScreen3D.js +1 -1
- package/lib/components/screens/philosophy/PhilosophyScreen3D.js.map +1 -1
- package/lib/components/screens/training/TrainingScreen3D.d.ts.map +1 -1
- package/lib/components/screens/training/TrainingScreen3D.js +3 -11
- package/lib/components/screens/training/TrainingScreen3D.js.map +1 -1
- package/lib/components/screens/training/components/AnatomyControlsOverlayHtml.js.map +1 -1
- package/lib/components/screens/training/components/AnatomyOverlay3D.js.map +1 -1
- package/lib/components/screens/training/components/FootPlacementMarkers3D.js.map +1 -1
- package/lib/components/screens/training/components/FootworkDrillsOverlayHtml.js.map +1 -1
- package/lib/components/screens/training/components/HitFeedbackEffect3D.js.map +1 -1
- package/lib/components/screens/training/components/TrainingButtonsOverlayHtml.js.map +1 -1
- package/lib/components/screens/training/components/TrainingControlsOverlayHtml.js.map +1 -1
- package/lib/components/screens/training/components/TrainingDummy3D.js.map +1 -1
- package/lib/components/screens/training/components/TrainingFeedbackOverlayHtml.js.map +1 -1
- package/lib/components/screens/training/components/TrainingModeSelectorOverlayHtml.js.map +1 -1
- package/lib/components/screens/training/components/TrainingStatsOverlayHtml.js.map +1 -1
- package/lib/components/screens/training/components/VitalPointMarker3D.js.map +1 -1
- package/lib/components/screens/training/components/VitalPointTrainingOverlayHtml.js.map +1 -1
- package/lib/components/screens/training/components/hud/TrainingBottomHUD.d.ts.map +1 -1
- package/lib/components/screens/training/components/hud/TrainingBottomHUD.js +2 -2
- package/lib/components/screens/training/components/hud/TrainingBottomHUD.js.map +1 -1
- package/lib/components/screens/training/components/hud/TrainingLeftHUD.js.map +1 -1
- package/lib/components/screens/training/components/hud/TrainingRightHUD.js.map +1 -1
- package/lib/components/screens/training/components/hud/TrainingTopHUD.js.map +1 -1
- package/lib/components/screens/training/hooks/useAttackMovement.js.map +1 -1
- package/lib/components/screens/training/hooks/useTrainingActions.d.ts +1 -0
- package/lib/components/screens/training/hooks/useTrainingActions.d.ts.map +1 -1
- package/lib/components/screens/training/hooks/useTrainingActions.js +6 -4
- package/lib/components/screens/training/hooks/useTrainingActions.js.map +1 -1
- package/lib/components/screens/training/hooks/useTrainingLayout.d.ts.map +1 -1
- package/lib/components/screens/training/hooks/useTrainingLayout.js +11 -5
- package/lib/components/screens/training/hooks/useTrainingLayout.js.map +1 -1
- package/lib/components/screens/training/hooks/useTrainingState.js.map +1 -1
- package/lib/components/shared/base/BaseButton.js.map +1 -1
- package/lib/components/shared/base/BaseButtonOverlayHtml.js.map +1 -1
- package/lib/components/shared/base/BasePanel.js.map +1 -1
- package/lib/components/shared/base/BaseText.js.map +1 -1
- package/lib/components/shared/base/useKoreanTheme.js.map +1 -1
- package/lib/components/shared/debug/PerformanceDebugOverlayHtml.js.map +1 -1
- package/lib/components/shared/mobile/ActionButtons.js.map +1 -1
- package/lib/components/shared/mobile/GestureRecognizerPure.js.map +1 -1
- package/lib/components/shared/mobile/HapticController.js.map +1 -1
- package/lib/components/shared/mobile/MobileControlsPure.js.map +1 -1
- package/lib/components/shared/mobile/StanceWheelPure.js.map +1 -1
- package/lib/components/shared/mobile/TouchOptimizer.js.map +1 -1
- package/lib/components/shared/mobile/VirtualDPad.js.map +1 -1
- package/lib/components/shared/three/anatomy/BodySurface.js.map +1 -1
- package/lib/components/shared/three/anatomy/BoneAttachedMuscles.js.map +1 -1
- package/lib/components/shared/three/anatomy/BoneClothing.js.map +1 -1
- package/lib/components/shared/three/anatomy/BoneRenderer.js.map +1 -1
- package/lib/components/shared/three/anatomy/Face3D.js.map +1 -1
- package/lib/components/shared/three/anatomy/Foot3D.js.map +1 -1
- package/lib/components/shared/three/anatomy/Hand3D.js.map +1 -1
- package/lib/components/shared/three/effects/ActionFeedback.js.map +1 -1
- package/lib/components/shared/three/effects/DamageNumbers.js.map +1 -1
- package/lib/components/shared/three/effects/HitEffects3D.js.map +1 -1
- package/lib/components/shared/three/effects/PlayerStateIndicators.js.map +1 -1
- package/lib/components/shared/three/effects/StanceSymbol3D.js.map +1 -1
- package/lib/components/shared/three/effects/StanceTransitionEffect.js.map +1 -1
- package/lib/components/shared/three/effects/VitalPointMarkers3D.js.map +1 -1
- package/lib/components/shared/three/indicators/ElementalColorSystem.js.map +1 -1
- package/lib/components/shared/three/indicators/GuardIndicator.js.map +1 -1
- package/lib/components/shared/three/indicators/HapticFeedback.js.map +1 -1
- package/lib/components/shared/three/indicators/StanceChangeIndicator.js.map +1 -1
- package/lib/components/shared/three/models/Player3DWithTransitions.js.map +1 -1
- package/lib/components/shared/three/models/SkeletalPlayer3D.d.ts.map +1 -1
- package/lib/components/shared/three/models/SkeletalPlayer3D.js +7 -5
- package/lib/components/shared/three/models/SkeletalPlayer3D.js.map +1 -1
- package/lib/components/shared/three/optimization/AdaptiveQuality.js.map +1 -1
- package/lib/components/shared/three/scene/AtmosphericParticles3D.js.map +1 -1
- package/lib/components/shared/three/scene/BackgroundScene3D.js.map +1 -1
- package/lib/components/shared/three/scene/CombatArena3D.js.map +1 -1
- package/lib/components/shared/three/scene/KoreanSignage3D.js.map +1 -1
- package/lib/components/shared/three/ui/ArchetypeCard.js.map +1 -1
- package/lib/components/shared/three/ui/BodyPartHealthDisplay.js.map +1 -1
- package/lib/components/shared/three/ui/BreathingIndicator2.js.map +1 -1
- package/lib/components/shared/three/ui/CombatReadinessBar.js.map +1 -1
- package/lib/components/shared/three/ui/ComboCounter.js.map +1 -1
- package/lib/components/shared/three/ui/HealthBar.js.map +1 -1
- package/lib/components/shared/three/ui/KoreanButton.js.map +1 -1
- package/lib/components/shared/three/ui/KoreanPanel.js.map +1 -1
- package/lib/components/shared/three/ui/KoreanText.js.map +1 -1
- package/lib/components/shared/three/ui/MenuList.js.map +1 -1
- package/lib/components/shared/three/ui/PlayerHUD.js.map +1 -1
- package/lib/components/shared/three/ui/ProgressBar.js.map +1 -1
- package/lib/components/shared/three/ui/SpeedIndicatorHUD.js.map +1 -1
- package/lib/components/shared/three/ui/StaminaBar.js.map +1 -1
- package/lib/components/shared/three/ui/TechniqueBar.js.map +1 -1
- package/lib/components/shared/three/ui/TechniqueCard.js.map +1 -1
- package/lib/components/shared/three/ui/VitalPointOverlayControlsHtml.js.map +1 -1
- package/lib/components/shared/ui/BackButton.js.map +1 -1
- package/lib/components/shared/ui/BaseHUDContainer.js.map +1 -1
- package/lib/components/shared/ui/CombatTimer.js.map +1 -1
- package/lib/components/shared/ui/ErrorModal.js.map +1 -1
- package/lib/components/shared/ui/LoadingState.js.map +1 -1
- package/lib/components/shared/ui/SplashScreen.js +2 -2
- package/lib/components/shared/ui/SplashScreen.js.map +1 -1
- package/lib/components/shared/ui/VitalPointOverlayControlsPure.js.map +1 -1
- package/lib/components/shared/ui/VolumeControl.js.map +1 -1
- package/lib/components/shared/ui/shared/ConfirmDialog.js.map +1 -1
- package/lib/components/ui/combat/BalanceIndicatorOverlayHtml.js.map +1 -1
- package/lib/constants/bodyDimensions.js.map +1 -1
- package/lib/constants/bodyRenderingConstants.js.map +1 -1
- package/lib/data/archetypeClothing.js.map +1 -1
- package/lib/data/archetypePhysicalAttributes.js.map +1 -1
- package/lib/data/techniqueMappings.js.map +1 -1
- package/lib/data/techniques.js.map +1 -1
- package/lib/hooks/useActionFeedback.js.map +1 -1
- package/lib/hooks/useBalanceAnimations.js.map +1 -1
- package/lib/hooks/useCombatTimer.js.map +1 -1
- package/lib/hooks/useDebounce.js.map +1 -1
- package/lib/hooks/useHUDLayout.d.ts.map +1 -1
- package/lib/hooks/useHUDLayout.js +3 -2
- package/lib/hooks/useHUDLayout.js.map +1 -1
- package/lib/hooks/useHandPoseTransitions.js.map +1 -1
- package/lib/hooks/useKeyboardControls.js.map +1 -1
- package/lib/hooks/useMatchCountdown.js.map +1 -1
- package/lib/hooks/useMuscleActivation.js.map +1 -1
- package/lib/hooks/usePauseMenu.js.map +1 -1
- package/lib/hooks/usePlayerAnimation.js.map +1 -1
- package/lib/hooks/useResponsiveLayout.js.map +1 -1
- package/lib/hooks/useRoundTransition.js.map +1 -1
- package/lib/hooks/useSkeletalAnimation.d.ts.map +1 -1
- package/lib/hooks/useSkeletalAnimation.js +1 -1
- package/lib/hooks/useSkeletalAnimation.js.map +1 -1
- package/lib/hooks/useTechniqueSelection.js.map +1 -1
- package/lib/hooks/useThrottle.js.map +1 -1
- package/lib/hooks/useTouchControls.js.map +1 -1
- package/lib/hooks/useWebGLContextLossHandler.js.map +1 -1
- package/lib/hooks/useWindowSize.js.map +1 -1
- package/lib/systems/CombatSystem.js.map +1 -1
- package/lib/systems/EffectCalculator.js.map +1 -1
- package/lib/systems/LayoutSystem.js.map +1 -1
- package/lib/systems/PlayerEffectManager.js.map +1 -1
- package/lib/systems/ResponsiveScaling.js.map +1 -1
- package/lib/systems/TrigramSystem.js.map +1 -1
- package/lib/systems/VitalPointSystem.js.map +1 -1
- package/lib/systems/ai/AIPersonality.js.map +1 -1
- package/lib/systems/ai/AdaptiveDifficulty.js +16 -16
- package/lib/systems/ai/AdaptiveDifficulty.js.map +1 -1
- package/lib/systems/ai/ArchetypeEnforcer.js.map +1 -1
- package/lib/systems/ai/ComboSystem.js.map +1 -1
- package/lib/systems/ai/DecisionTree.js.map +1 -1
- package/lib/systems/ai/TrainingAI.js.map +1 -1
- package/lib/systems/ai/types.js.map +1 -1
- package/lib/systems/animation/builders/AnimationBuilder.js.map +1 -1
- package/lib/systems/animation/builders/HandPoseApplicator.js.map +1 -1
- package/lib/systems/animation/builders/HandPoses.js.map +1 -1
- package/lib/systems/animation/builders/KeyframeConfig.js.map +1 -1
- package/lib/systems/animation/builders/KeyframeInterpolation.js.map +1 -1
- package/lib/systems/animation/builders/KickPhaseApplicator.d.ts +6 -0
- package/lib/systems/animation/builders/KickPhaseApplicator.d.ts.map +1 -1
- package/lib/systems/animation/builders/KickPhaseApplicator.js +16 -9
- package/lib/systems/animation/builders/KickPhaseApplicator.js.map +1 -1
- package/lib/systems/animation/builders/KoreanGuardPositions.d.ts +4 -4
- package/lib/systems/animation/builders/KoreanGuardPositions.js.map +1 -1
- package/lib/systems/animation/builders/MartialArtsAnimationBuilder.d.ts +1 -1
- package/lib/systems/animation/builders/MartialArtsAnimationBuilder.d.ts.map +1 -1
- package/lib/systems/animation/builders/MartialArtsAnimationBuilder.js +5 -5
- package/lib/systems/animation/builders/MartialArtsAnimationBuilder.js.map +1 -1
- package/lib/systems/animation/builders/MartialArtsConstants.d.ts +112 -71
- package/lib/systems/animation/builders/MartialArtsConstants.d.ts.map +1 -1
- package/lib/systems/animation/builders/MartialArtsConstants.js +113 -72
- package/lib/systems/animation/builders/MartialArtsConstants.js.map +1 -1
- package/lib/systems/animation/builders/MartialPoseApplicator.js.map +1 -1
- package/lib/systems/animation/builders/PunchPhaseApplicator.js.map +1 -1
- package/lib/systems/animation/builders/SkeletonRig.js.map +1 -1
- package/lib/systems/animation/builders/TrigramGuardApplicator.js.map +1 -1
- package/lib/systems/animation/catalogs/AttackAnimations.js.map +1 -1
- package/lib/systems/animation/catalogs/BasicAnimations.js.map +1 -1
- package/lib/systems/animation/catalogs/ComboAnimations.js.map +1 -1
- package/lib/systems/animation/catalogs/DarkOpsAnimations.js.map +1 -1
- package/lib/systems/animation/catalogs/DefensiveAnimations.js.map +1 -1
- package/lib/systems/animation/catalogs/ElbowKneeAnimations.js.map +1 -1
- package/lib/systems/animation/catalogs/EnhancedAttackAnimations.js.map +1 -1
- package/lib/systems/animation/catalogs/EnhancedElbowKneeAnimations.js.map +1 -1
- package/lib/systems/animation/catalogs/FootworkSkeletalAnimations.js.map +1 -1
- package/lib/systems/animation/catalogs/GamRedirectionAnimations.js.map +1 -1
- package/lib/systems/animation/catalogs/GamStanceAnimations.js +21 -0
- package/lib/systems/animation/catalogs/GamStanceAnimations.js.map +1 -0
- package/lib/systems/animation/catalogs/GamTechniqueAnimations.js +34 -2
- package/lib/systems/animation/catalogs/GamTechniqueAnimations.js.map +1 -1
- package/lib/systems/animation/catalogs/GanStanceAnimations.js.map +1 -1
- package/lib/systems/animation/catalogs/GanTechniqueAnimations.js.map +1 -1
- package/lib/systems/animation/catalogs/GeonStanceAnimations.js.map +1 -1
- package/lib/systems/animation/catalogs/GonTechniqueAnimations.d.ts +9 -0
- package/lib/systems/animation/catalogs/GonTechniqueAnimations.d.ts.map +1 -1
- package/lib/systems/animation/catalogs/GonTechniqueAnimations.js +288 -0
- package/lib/systems/animation/catalogs/GonTechniqueAnimations.js.map +1 -0
- package/lib/systems/animation/catalogs/GrapplingAnimations.js.map +1 -1
- package/lib/systems/animation/catalogs/JinStanceAnimations.js.map +1 -1
- package/lib/systems/animation/catalogs/JinTechniqueAnimations.js.map +1 -1
- package/lib/systems/animation/catalogs/KickAnimations.d.ts +2 -2
- package/lib/systems/animation/catalogs/KickAnimations.js +2 -2
- package/lib/systems/animation/catalogs/KickAnimations.js.map +1 -1
- package/lib/systems/animation/catalogs/LiStanceAnimations.js +14 -1
- package/lib/systems/animation/catalogs/LiStanceAnimations.js.map +1 -1
- package/lib/systems/animation/catalogs/LiTechniqueAnimations.js.map +1 -1
- package/lib/systems/animation/catalogs/MovementAnimations.js.map +1 -1
- package/lib/systems/animation/catalogs/PunchAnimations.d.ts +1 -1
- package/lib/systems/animation/catalogs/PunchAnimations.js +1 -1
- package/lib/systems/animation/catalogs/PunchAnimations.js.map +1 -1
- package/lib/systems/animation/catalogs/RecoveryAnimations.js.map +1 -1
- package/lib/systems/animation/catalogs/SonStanceAnimations.js.map +1 -1
- package/lib/systems/animation/catalogs/SonTechniqueAnimations.js.map +1 -1
- package/lib/systems/animation/catalogs/SpecializedPunchAnimations.js.map +1 -1
- package/lib/systems/animation/catalogs/StanceAnimations.js.map +1 -1
- package/lib/systems/animation/catalogs/StanceAttackAnimations.js.map +1 -1
- package/lib/systems/animation/catalogs/StanceGuardPoses.d.ts +6 -6
- package/lib/systems/animation/catalogs/StanceGuardPoses.js +36 -36
- package/lib/systems/animation/catalogs/StanceGuardPoses.js.map +1 -1
- package/lib/systems/animation/catalogs/StanceIdleAnimations.js.map +1 -1
- package/lib/systems/animation/catalogs/StanceLocomotionAnimations.js.map +1 -1
- package/lib/systems/animation/catalogs/StepSkeletalAnimations.js.map +1 -1
- package/lib/systems/animation/catalogs/TaeJointLockAnimations.js.map +1 -1
- package/lib/systems/animation/catalogs/TaeStanceAnimations.js.map +1 -1
- package/lib/systems/animation/constants/AnatomicalLimits.js.map +1 -1
- package/lib/systems/animation/core/AnimationHitTiming.js.map +1 -1
- package/lib/systems/animation/core/AnimationOptimizations.js.map +1 -1
- package/lib/systems/animation/core/AnimationPriority.js +15 -15
- package/lib/systems/animation/core/AnimationPriority.js.map +1 -1
- package/lib/systems/animation/core/AnimationRegistry.d.ts +30 -0
- package/lib/systems/animation/core/AnimationRegistry.d.ts.map +1 -1
- package/lib/systems/animation/core/AnimationRegistry.js +74 -12
- package/lib/systems/animation/core/AnimationRegistry.js.map +1 -1
- package/lib/systems/animation/core/AnimationStateMachine.js +16 -16
- package/lib/systems/animation/core/AnimationStateMachine.js.map +1 -1
- package/lib/systems/animation/core/AnimationTransitions.d.ts.map +1 -1
- package/lib/systems/animation/core/AnimationTransitions.js +34 -0
- package/lib/systems/animation/core/AnimationTransitions.js.map +1 -1
- package/lib/systems/animation/core/LateralityTransform.js.map +1 -1
- package/lib/systems/animation/core/RecoveryPhaseEnhancer.js.map +1 -1
- package/lib/systems/animation/core/TechniqueAnimationMapper.js.map +1 -1
- package/lib/systems/animation/core/TechniqueAnimationMapping.js.map +1 -1
- package/lib/systems/animation/core/index.d.ts +1 -1
- package/lib/systems/animation/core/index.d.ts.map +1 -1
- package/lib/systems/animation/core/types.d.ts +24 -0
- package/lib/systems/animation/core/types.d.ts.map +1 -1
- package/lib/systems/animation/core/types.js +27 -11
- package/lib/systems/animation/core/types.js.map +1 -1
- package/lib/systems/animation/systems/AdvancedJointMovements.js.map +1 -1
- package/lib/systems/animation/systems/BodyFacingSystem.js.map +1 -1
- package/lib/systems/animation/systems/FacialExpressions.js.map +1 -1
- package/lib/systems/animation/systems/FallAnimations.js.map +1 -1
- package/lib/systems/animation/systems/MuscleActivation.js.map +1 -1
- package/lib/systems/bodypart/BodyPartDamageIntegration.js.map +1 -1
- package/lib/systems/bodypart/BodyPartHealthSystem.js.map +1 -1
- package/lib/systems/bodypart/BodyPartPositionMapping.js.map +1 -1
- package/lib/systems/bodypart/CombatInjuryIntegration.js.map +1 -1
- package/lib/systems/bodypart/InjuryIntegration.js.map +1 -1
- package/lib/systems/bodypart/InjuryTracker.js.map +1 -1
- package/lib/systems/bodypart/MovementPenaltySystem.js.map +1 -1
- package/lib/systems/bodypart/PlayerInjuryTrackingManager.js.map +1 -1
- package/lib/systems/bodypart/types.js.map +1 -1
- package/lib/systems/breathing/BreathingDisruptionSystem.js +19 -19
- package/lib/systems/breathing/BreathingDisruptionSystem.js.map +1 -1
- package/lib/systems/breathing/feedback.js.map +1 -1
- package/lib/systems/breathing/integration.js.map +1 -1
- package/lib/systems/combat/BalanceSystem.js +19 -19
- package/lib/systems/combat/BalanceSystem.js.map +1 -1
- package/lib/systems/combat/BreakingStatusEffects.js.map +1 -1
- package/lib/systems/combat/CombatStateSystem.js +17 -17
- package/lib/systems/combat/CombatStateSystem.js.map +1 -1
- package/lib/systems/combat/ConsciousnessSystem.js +24 -24
- package/lib/systems/combat/ConsciousnessSystem.js.map +1 -1
- package/lib/systems/combat/FallIntegration.js.map +1 -1
- package/lib/systems/combat/GrappleSystem.js.map +1 -1
- package/lib/systems/combat/LimbExposureSystem.js.map +1 -1
- package/lib/systems/combat/PainResponseSystem.js +21 -21
- package/lib/systems/combat/PainResponseSystem.js.map +1 -1
- package/lib/systems/combat/TrainingCombatSystem.js.map +1 -1
- package/lib/systems/combat/painConsciousnessUtils.js.map +1 -1
- package/lib/systems/combat/typeGuards.js.map +1 -1
- package/lib/systems/effects.js.map +1 -1
- package/lib/systems/game.js.map +1 -1
- package/lib/systems/movement/InjuryMovementModifier.js.map +1 -1
- package/lib/systems/movement/helpers/AccelerationUpdater.js.map +1 -1
- package/lib/systems/movement/helpers/accelerationUtils.js.map +1 -1
- package/lib/systems/movement/integration.js.map +1 -1
- package/lib/systems/physics/AttackMovementPhysics.js.map +1 -1
- package/lib/systems/physics/CollisionDetection.js.map +1 -1
- package/lib/systems/physics/CoordinateMapper.js.map +1 -1
- package/lib/systems/physics/KnockbackPhysics.js.map +1 -1
- package/lib/systems/physics/MovementPhysics.js.map +1 -1
- package/lib/systems/physics/PhysicalReachCalculator.js.map +1 -1
- package/lib/systems/physics/SpeedModifierSystem.js +6 -6
- package/lib/systems/physics/SpeedModifierSystem.js.map +1 -1
- package/lib/systems/trigram/KoreanCulture.js.map +1 -1
- package/lib/systems/trigram/KoreanTechniques.js.map +1 -1
- package/lib/systems/trigram/StanceManager.js.map +1 -1
- package/lib/systems/trigram/TransitionCalculator.js.map +1 -1
- package/lib/systems/trigram/TrigramCalculator.js.map +1 -1
- package/lib/systems/trigram/techniques/DarkOpsTechniques.js.map +1 -1
- package/lib/systems/trigram/techniques/GamTechniques.js.map +1 -1
- package/lib/systems/trigram/techniques/GanTechniques.js.map +1 -1
- package/lib/systems/trigram/techniques/GeonTechniques.js.map +1 -1
- package/lib/systems/trigram/techniques/GonTechniques.js.map +1 -1
- package/lib/systems/trigram/techniques/JinTechniques.js.map +1 -1
- package/lib/systems/trigram/techniques/LiTechniques.js.map +1 -1
- package/lib/systems/trigram/techniques/SonTechniques.js.map +1 -1
- package/lib/systems/trigram/techniques/TaeTechniques.js.map +1 -1
- package/lib/systems/trigram/techniques/TechniqueConfig.js.map +1 -1
- package/lib/systems/trigram/techniques/index.js.map +1 -1
- package/lib/systems/trigram/types/GonTechniqueExtensions.js.map +1 -1
- package/lib/systems/trigram/types.js.map +1 -1
- package/lib/systems/types.js.map +1 -1
- package/lib/systems/vitalpoint/DamageCalculator.js.map +1 -1
- package/lib/systems/vitalpoint/HitDetection.js.map +1 -1
- package/lib/systems/vitalpoint/KoreanAnatomy.js.map +1 -1
- package/lib/systems/vitalpoint/KoreanVitalPoints.js.map +1 -1
- package/lib/systems/vitalpoint/MeridianVitalPointMapping.js.map +1 -1
- package/lib/systems/vitalpoint/VitalPointsData.js.map +1 -1
- package/lib/types/AccessibilityTypes.js.map +1 -1
- package/lib/types/LayoutTypes.js.map +1 -1
- package/lib/types/PhysicsTypes.js.map +1 -1
- package/lib/types/common.js.map +1 -1
- package/lib/types/constants/animations.js.map +1 -1
- package/lib/types/constants/colors.js.map +1 -1
- package/lib/types/constants/designSystem.js.map +1 -1
- package/lib/types/constants/index.js.map +1 -1
- package/lib/types/constants/layout.d.ts +21 -0
- package/lib/types/constants/layout.d.ts.map +1 -1
- package/lib/types/constants/layout.js +22 -1
- package/lib/types/constants/layout.js.map +1 -1
- package/lib/types/constants/performance.js.map +1 -1
- package/lib/types/constants/typography.js.map +1 -1
- package/lib/types/constants/ui.js.map +1 -1
- package/lib/types/facial.js +19 -19
- package/lib/types/facial.js.map +1 -1
- package/lib/types/hand-animation.js.map +1 -1
- package/lib/types/injury.js.map +1 -1
- package/lib/types/muscle.js.map +1 -1
- package/lib/types/physics.js.map +1 -1
- package/lib/types/physicsConstants.js.map +1 -1
- package/lib/types/player-visual.d.ts +1 -1
- package/lib/types/player-visual.d.ts.map +1 -1
- package/lib/types/skeletal.js.map +1 -1
- package/lib/types/techniqueId.js.map +1 -1
- package/lib/utils/accessibility.js.map +1 -1
- package/lib/utils/arenaWorldDimensions.js.map +1 -1
- package/lib/utils/assetConfig.js.map +1 -1
- package/lib/utils/characterScaling.js.map +1 -1
- package/lib/utils/colorHelpers.js.map +1 -1
- package/lib/utils/colorUtils.js.map +1 -1
- package/lib/utils/combatReadiness.js.map +1 -1
- package/lib/utils/controlMapping.js.map +1 -1
- package/lib/utils/deviceDetection.js +6 -7
- package/lib/utils/deviceDetection.js.map +1 -1
- package/lib/utils/effectUtils.js.map +1 -1
- package/lib/utils/fabricTextures.js.map +1 -1
- package/lib/utils/hapticFeedback.js.map +1 -1
- package/lib/utils/haptics.js.map +1 -1
- package/lib/utils/htmlOverlayHelpers.js.map +1 -1
- package/lib/utils/inputSystem.js.map +1 -1
- package/lib/utils/koreanThemeHelpers.js.map +1 -1
- package/lib/utils/math.js.map +1 -1
- package/lib/utils/mobileLayoutHelpers.js.map +1 -1
- package/lib/utils/mobileUIUtils.js.map +1 -1
- package/lib/utils/performance/PerformanceMonitor.js.map +1 -1
- package/lib/utils/performance/PerformanceOverlay3D.js.map +1 -1
- package/lib/utils/performance/usePerformanceMonitor.js.map +1 -1
- package/lib/utils/performanceOptimization.js.map +1 -1
- package/lib/utils/player3DHelpers.js.map +1 -1
- package/lib/utils/playerUtils.js.map +1 -1
- package/lib/utils/responsiveLayout.js.map +1 -1
- package/lib/utils/responsiveLayoutHelpers.d.ts +7 -0
- package/lib/utils/responsiveLayoutHelpers.d.ts.map +1 -1
- package/lib/utils/responsiveLayoutHelpers.js +16 -2
- package/lib/utils/responsiveLayoutHelpers.js.map +1 -1
- package/lib/utils/responsiveOrientationConstants.js.map +1 -1
- package/lib/utils/safeAreaUtils.js.map +1 -1
- package/lib/utils/sharedPhysicsConfig.js.map +1 -1
- package/lib/utils/skeletonScaling.js.map +1 -1
- package/lib/utils/stanceHelpers.js.map +1 -1
- package/lib/utils/threeObjectPool.js.map +1 -1
- package/lib/utils/visualEffects.js.map +1 -1
- package/package.json +7 -7
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"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,4 +1,4 @@
|
|
|
1
|
-
import { AnimationPriority
|
|
1
|
+
import { AnimationPriority } from "./types.js";
|
|
2
2
|
//#region src/systems/animation/core/AnimationPriority.ts
|
|
3
3
|
/**
|
|
4
4
|
* Animation priority system for Black Trigram
|
|
@@ -49,14 +49,14 @@ var ANIMATION_PRIORITY_MAP = {
|
|
|
49
49
|
stance_guard_gam: AnimationPriority.IDLE,
|
|
50
50
|
stance_guard_gan: AnimationPriority.IDLE,
|
|
51
51
|
stance_guard_gon: AnimationPriority.IDLE,
|
|
52
|
-
step_forward:
|
|
53
|
-
step_back:
|
|
54
|
-
step_left:
|
|
55
|
-
step_right:
|
|
56
|
-
step_forward_left:
|
|
57
|
-
step_forward_right:
|
|
58
|
-
step_back_left:
|
|
59
|
-
step_back_right:
|
|
52
|
+
step_forward: 5,
|
|
53
|
+
step_back: 5,
|
|
54
|
+
step_left: 5,
|
|
55
|
+
step_right: 5,
|
|
56
|
+
step_forward_left: 5,
|
|
57
|
+
step_forward_right: 5,
|
|
58
|
+
step_back_left: 5,
|
|
59
|
+
step_back_right: 5,
|
|
60
60
|
fall_forward: AnimationPriority.FALL,
|
|
61
61
|
fall_backward: AnimationPriority.FALL,
|
|
62
62
|
fall_side_left: AnimationPriority.FALL,
|
|
@@ -65,12 +65,12 @@ var ANIMATION_PRIORITY_MAP = {
|
|
|
65
65
|
ground_supine: AnimationPriority.IDLE,
|
|
66
66
|
ground_side_left: AnimationPriority.IDLE,
|
|
67
67
|
ground_side_right: AnimationPriority.IDLE,
|
|
68
|
-
turn_left:
|
|
69
|
-
turn_right:
|
|
70
|
-
footwork_circular_left:
|
|
71
|
-
footwork_circular_right:
|
|
72
|
-
footwork_pivot_left:
|
|
73
|
-
footwork_pivot_right:
|
|
68
|
+
turn_left: 5,
|
|
69
|
+
turn_right: 5,
|
|
70
|
+
footwork_circular_left: 5,
|
|
71
|
+
footwork_circular_right: 5,
|
|
72
|
+
footwork_pivot_left: 5,
|
|
73
|
+
footwork_pivot_right: 5,
|
|
74
74
|
footwork_slide_forward: AnimationPriority.DEFEND,
|
|
75
75
|
footwork_slide_back: AnimationPriority.DEFEND,
|
|
76
76
|
footwork_slide_left: AnimationPriority.DEFEND,
|
|
@@ -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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"}
|
|
@@ -166,27 +166,57 @@ export declare function getCategoryDefaultAnimation(category: string): SkeletalA
|
|
|
166
166
|
/**
|
|
167
167
|
* Get animation by name (legacy support)
|
|
168
168
|
*
|
|
169
|
+
* Legacy semantic alias for callers that specifically receive a skeletal
|
|
170
|
+
* animation name from UI/rendering code.
|
|
171
|
+
*
|
|
169
172
|
* @param name - Animation name (e.g., "front_kick")
|
|
170
173
|
* @returns Skeletal animation or undefined
|
|
171
174
|
*
|
|
175
|
+
* Checks {@link ALL_ANIMATIONS} first because it owns renderable skeletal
|
|
176
|
+
* animation names, then {@link ANIMATION_ID_REGISTRY} for technique animation
|
|
177
|
+
* IDs that intentionally alias existing skeletal animation objects.
|
|
178
|
+
*
|
|
172
179
|
* @korean 이름으로애니메이션조회
|
|
173
180
|
*/
|
|
174
181
|
export declare function getAnimationByName(name: string): SkeletalAnimation | undefined;
|
|
175
182
|
/**
|
|
176
183
|
* Get animation by name - unified lookup across all animation registries
|
|
177
184
|
*
|
|
185
|
+
* Preferred general-purpose lookup for gameplay systems that may pass either a
|
|
186
|
+
* concrete skeletal animation name or a technique animation ID.
|
|
187
|
+
*
|
|
178
188
|
* Searches ALL_ANIMATIONS which includes:
|
|
179
189
|
* - BASIC_ANIMATIONS (idle, walk, run, fall)
|
|
180
190
|
* - KICK_ANIMATIONS, PUNCH_ANIMATIONS, etc.
|
|
181
191
|
* - STANCE_ANIMATIONS, MOVEMENT_ANIMATIONS
|
|
182
192
|
* - ALL_ATTACK_ANIMATIONS (stance-specific attacks)
|
|
183
193
|
*
|
|
194
|
+
* Falls back to {@link ANIMATION_ID_REGISTRY} so technique IDs resolved from
|
|
195
|
+
* combat data can still retrieve aliased animations without dropping to generic
|
|
196
|
+
* jab/strike fallbacks.
|
|
197
|
+
*
|
|
184
198
|
* @param name - Animation name (e.g., "idle", "front_kick", "walk")
|
|
185
199
|
* @returns Skeletal animation or undefined
|
|
186
200
|
*
|
|
187
201
|
* @korean 애니메이션가져오기
|
|
188
202
|
*/
|
|
189
203
|
export declare function getAnimation(name: string): SkeletalAnimation | undefined;
|
|
204
|
+
/**
|
|
205
|
+
* Get a skeletal animation duration or the shared short-technique fallback.
|
|
206
|
+
*
|
|
207
|
+
* @param name - Animation name or technique animation ID. Empty or omitted
|
|
208
|
+
* returns {@link DEFAULT_TECHNIQUE_DURATION_SECONDS}.
|
|
209
|
+
* @returns Duration in seconds.
|
|
210
|
+
*
|
|
211
|
+
* @example
|
|
212
|
+
* ```typescript
|
|
213
|
+
* getAnimationDurationOrFallback("gon_earth_embrace"); // registered duration
|
|
214
|
+
* getAnimationDurationOrFallback(); // DEFAULT_TECHNIQUE_DURATION_SECONDS
|
|
215
|
+
* ```
|
|
216
|
+
*
|
|
217
|
+
* @korean 애니메이션지속시간조회
|
|
218
|
+
*/
|
|
219
|
+
export declare function getAnimationDurationOrFallback(name?: string): number;
|
|
190
220
|
/**
|
|
191
221
|
* Get animation name for a technique
|
|
192
222
|
*
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"AnimationRegistry.d.ts","sourceRoot":"","sources":["../../../../src/systems/animation/core/AnimationRegistry.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8BG;AAEH,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,kBAAkB,CAAC;AAC1D,OAAO,EAAE,aAAa,EAAE,MAAM,yCAAyC,CAAC;
|
|
1
|
+
{"version":3,"file":"AnimationRegistry.d.ts","sourceRoot":"","sources":["../../../../src/systems/animation/core/AnimationRegistry.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import "./types.js";
|
|
1
2
|
import { AnimationType } from "../builders/MartialArtsConstants.js";
|
|
2
3
|
import { BACKWARD_RETREAT_ANIMATION, FORWARD_DASH_ANIMATION, IDLE_STANCE_ANIMATION, SIDE_STEP_ANIMATION } from "../catalogs/AttackAnimations.js";
|
|
3
4
|
import { BASIC_ANIMATIONS } from "../catalogs/BasicAnimations.js";
|
|
@@ -9,6 +10,7 @@ import { ARM_BAR_ANIMATION, BLOCK_ANIMATION, BODY_LOCK_THROW_ANIMATION, CAROTID_
|
|
|
9
10
|
import { AXE_KICK_ANIMATION, BACK_KICK_ANIMATION, CRESCENT_KICK_ANIMATION, FLYING_KICK_ANIMATION, JUMPING_KICK_ANIMATION, KICK_ANIMATIONS, LOW_KICK_ANIMATION, PUSH_KICK_ANIMATION, SIDE_KICK_ANIMATION, SPINNING_HEEL_KICK_ANIMATION, SPINNING_HOOK_KICK_ANIMATION, SWEEP_ANIMATION, TORNADO_KICK_ANIMATION } from "../catalogs/KickAnimations.js";
|
|
10
11
|
import { MOVEMENT_ANIMATIONS } from "../catalogs/MovementAnimations.js";
|
|
11
12
|
import { BACKFIST_ANIMATION, HAMMER_FIST_ANIMATION, HOOK_ANIMATION, OVERHAND_ANIMATION, PALM_STRIKE_ANIMATION, PUNCH_ANIMATIONS, UPPERCUT_ANIMATION } from "../catalogs/PunchAnimations.js";
|
|
13
|
+
import { LI_STANCE_ANIMATIONS } from "../catalogs/LiStanceAnimations.js";
|
|
12
14
|
import { JIN_TECHNIQUE_ANIMATIONS } from "../catalogs/JinTechniqueAnimations.js";
|
|
13
15
|
import { JIN_ANIMATIONS } from "../catalogs/JinStanceAnimations.js";
|
|
14
16
|
import { GAN_ROCK_DEFENSE_ANIMATION, GAN_TECHNIQUE_ANIMATIONS } from "../catalogs/GanTechniqueAnimations.js";
|
|
@@ -16,9 +18,11 @@ import { STANCE_ANIMATIONS } from "../catalogs/StanceAnimations.js";
|
|
|
16
18
|
import { ALL_ATTACK_ANIMATIONS } from "../catalogs/StanceAttackAnimations.js";
|
|
17
19
|
import { TRIGRAM_IDLE_ANIMATIONS_BY_NAME } from "../catalogs/StanceIdleAnimations.js";
|
|
18
20
|
import { STANCE_LOCOMOTION_ANIMATIONS } from "../catalogs/StanceLocomotionAnimations.js";
|
|
19
|
-
import { GAM_WATER_FLOW_COUNTER_ANIMATION } from "../catalogs/GamTechniqueAnimations.js";
|
|
21
|
+
import { GAM_TECHNIQUE_ANIMATIONS, GAM_WATER_FLOW_COUNTER_ANIMATION } from "../catalogs/GamTechniqueAnimations.js";
|
|
22
|
+
import { GAM_STANCE_ANIMATIONS } from "../catalogs/GamStanceAnimations.js";
|
|
20
23
|
import { GAN_STANCE_ANIMATIONS } from "../catalogs/GanStanceAnimations.js";
|
|
21
24
|
import { GEON_ANIMATIONS } from "../catalogs/GeonStanceAnimations.js";
|
|
25
|
+
import { GON_TECHNIQUE_ANIMATIONS } from "../catalogs/GonTechniqueAnimations.js";
|
|
22
26
|
import { SON_TECHNIQUE_ANIMATIONS } from "../catalogs/SonTechniqueAnimations.js";
|
|
23
27
|
import { SON_STANCE_ANIMATIONS } from "../catalogs/SonStanceAnimations.js";
|
|
24
28
|
import { TAE_ARM_BAR, TAE_ELBOW_CONTROL, TAE_FINGER_LOCK, TAE_FLOWING_COUNTER, TAE_FLOWING_STRIKES, TAE_SHOULDER_LOCK, TAE_SMALL_CIRCLE, TAE_WRIST_LOCK_SEQUENCE } from "../catalogs/TaeJointLockAnimations.js";
|
|
@@ -27,6 +31,13 @@ import { CROSS_ANIMATION_ENHANCED, FRONT_KICK_ANIMATION_ENHANCED, JAB_ANIMATION_
|
|
|
27
31
|
import { ELBOW_STRIKE_ANIMATION_ENHANCED, ELBOW_UPPERCUT_ANIMATION_ENHANCED, KNEE_STRIKE_ANIMATION_ENHANCED } from "../catalogs/EnhancedElbowKneeAnimations.js";
|
|
28
32
|
import { GAM_FLOWING_BLOCK, GAM_FLOWING_RIVER_STRIKE, GAM_REDIRECTION_COUNTER, GAM_TIDAL_WAVE_PALM, GAM_WHIRLPOOL_COUNTER } from "../catalogs/GamRedirectionAnimations.js";
|
|
29
33
|
import { EAR_STRIKE_ANIMATION, EYE_GOUGE_ANIMATION, FLOWING_CROSS_ANIMATION, FLOWING_PUSH_ANIMATION, HEAVEN_STRIKE_ANIMATION, LIGHTNING_STRIKE_ANIMATION, LIVER_DISRUPTION_ANIMATION, NERVE_PARALYSIS_ANIMATION, NERVE_STRIKE_ANIMATION, PRESSURE_POINT_STRIKE_ANIMATION, RAPID_BARRAGE_ANIMATION, RHYTHMIC_STRIKES_ANIMATION, SOLAR_PLEXUS_STRIKE_ANIMATION, SPEAR_HAND_STRIKE_ANIMATION, SPECIALIZED_PUNCH_ANIMATIONS, THROAT_STRIKE_ANIMATION } from "../catalogs/SpecializedPunchAnimations.js";
|
|
34
|
+
//#region src/systems/animation/core/AnimationRegistry.ts
|
|
35
|
+
var STANCE_TRANSITION_ANIMATION = {
|
|
36
|
+
...IDLE_STANCE_ANIMATION,
|
|
37
|
+
name: "stance_change",
|
|
38
|
+
koreanName: "자세전환",
|
|
39
|
+
loop: false
|
|
40
|
+
};
|
|
30
41
|
new Map([
|
|
31
42
|
[AnimationType.FRONT_KICK, FRONT_KICK_ANIMATION_ENHANCED],
|
|
32
43
|
[AnimationType.ROUNDHOUSE_KICK, ROUNDHOUSE_KICK_ANIMATION_ENHANCED],
|
|
@@ -246,18 +257,39 @@ var ALL_ANIMATIONS = new Map([
|
|
|
246
257
|
...TRIGRAM_IDLE_ANIMATIONS_BY_NAME,
|
|
247
258
|
...GEON_ANIMATIONS,
|
|
248
259
|
...TAE_STANCE_ANIMATIONS,
|
|
260
|
+
...LI_STANCE_ANIMATIONS,
|
|
249
261
|
...JIN_ANIMATIONS,
|
|
250
262
|
...JIN_TECHNIQUE_ANIMATIONS,
|
|
251
263
|
...SON_STANCE_ANIMATIONS,
|
|
252
264
|
...SON_TECHNIQUE_ANIMATIONS,
|
|
265
|
+
["gam_idle_flowing", GAM_STANCE_ANIMATIONS.idle],
|
|
266
|
+
["gam_yielding_sidestep", GAM_STANCE_ANIMATIONS.movement.yieldingSidestep],
|
|
267
|
+
["gam_flowing_retreat_step", GAM_STANCE_ANIMATIONS.movement.flowingRetreat],
|
|
268
|
+
["gam_water_flow_counter", GAM_STANCE_ANIMATIONS.techniques.waterFlowCounter],
|
|
269
|
+
["gam_flowing_takedown", GAM_STANCE_ANIMATIONS.techniques.flowingTakedown],
|
|
270
|
+
["gam_counter", GAM_TECHNIQUE_ANIMATIONS.counter],
|
|
271
|
+
["gam_takedown", GAM_TECHNIQUE_ANIMATIONS.takedown],
|
|
253
272
|
...GAN_STANCE_ANIMATIONS,
|
|
254
273
|
...GAN_TECHNIQUE_ANIMATIONS,
|
|
274
|
+
...GON_TECHNIQUE_ANIMATIONS,
|
|
255
275
|
["idle_stance", IDLE_STANCE_ANIMATION],
|
|
276
|
+
["stance_change", STANCE_TRANSITION_ANIMATION],
|
|
256
277
|
["forward_dash", FORWARD_DASH_ANIMATION],
|
|
257
278
|
["backward_retreat", BACKWARD_RETREAT_ANIMATION],
|
|
258
279
|
["side_step", SIDE_STEP_ANIMATION]
|
|
259
280
|
]);
|
|
260
|
-
|
|
281
|
+
/**
|
|
282
|
+
* Animation ID Registry - Direct mapping from technique animationId to SkeletalAnimation
|
|
283
|
+
*
|
|
284
|
+
* This provides 1-1 mapping between technique IDs and animations, using the new
|
|
285
|
+
* animationId field instead of the legacy AnimationType enum.
|
|
286
|
+
*
|
|
287
|
+
* 기술 ID에서 애니메이션으로의 직접 매핑 (1-1 관계)
|
|
288
|
+
*
|
|
289
|
+
* @see TechniqueAnimationMapping.ts for legacy technique → AnimationType mappings
|
|
290
|
+
* @korean 애니메이션ID레지스트리
|
|
291
|
+
*/
|
|
292
|
+
var ANIMATION_ID_REGISTRY = new Map([
|
|
261
293
|
["amsalja_silent_death", SPEAR_HAND_STRIKE_ANIMATION],
|
|
262
294
|
["darkops_ear_strike", EAR_STRIKE_ANIMATION],
|
|
263
295
|
["darkops_eye_gouge", EYE_GOUGE_ANIMATION],
|
|
@@ -285,11 +317,7 @@ new Map([
|
|
|
285
317
|
["geon_heavenly_fist", JAB_ANIMATION_ENHANCED],
|
|
286
318
|
["geon_high_block", GEON_HIGH_BLOCK],
|
|
287
319
|
["geon_palm_strike", PALM_STRIKE_ANIMATION],
|
|
288
|
-
|
|
289
|
-
["gon_earth_embrace", EARTH_EMBRACE_ANIMATION],
|
|
290
|
-
["gon_ground_pound", SLAM_ANIMATION],
|
|
291
|
-
["gon_leg_sweep", SWEEP_ANIMATION],
|
|
292
|
-
["gon_ssireum_throw", HIP_THROW_ANIMATION],
|
|
320
|
+
...GON_TECHNIQUE_ANIMATIONS,
|
|
293
321
|
["hacker_data_strike", PALM_STRIKE_ANIMATION],
|
|
294
322
|
["hacker_system_crash", HAMMER_FIST_ANIMATION],
|
|
295
323
|
["jin_back_kick", BACK_KICK_ANIMATION],
|
|
@@ -319,30 +347,62 @@ new Map([
|
|
|
319
347
|
/**
|
|
320
348
|
* Get animation by name (legacy support)
|
|
321
349
|
*
|
|
350
|
+
* Legacy semantic alias for callers that specifically receive a skeletal
|
|
351
|
+
* animation name from UI/rendering code.
|
|
352
|
+
*
|
|
322
353
|
* @param name - Animation name (e.g., "front_kick")
|
|
323
354
|
* @returns Skeletal animation or undefined
|
|
324
355
|
*
|
|
356
|
+
* Checks {@link ALL_ANIMATIONS} first because it owns renderable skeletal
|
|
357
|
+
* animation names, then {@link ANIMATION_ID_REGISTRY} for technique animation
|
|
358
|
+
* IDs that intentionally alias existing skeletal animation objects.
|
|
359
|
+
*
|
|
325
360
|
* @korean 이름으로애니메이션조회
|
|
326
361
|
*/
|
|
327
362
|
function getAnimationByName(name) {
|
|
328
|
-
return ALL_ANIMATIONS.get(name);
|
|
363
|
+
return ALL_ANIMATIONS.get(name) ?? ANIMATION_ID_REGISTRY.get(name);
|
|
329
364
|
}
|
|
330
365
|
/**
|
|
331
366
|
* Get animation by name - unified lookup across all animation registries
|
|
332
367
|
*
|
|
368
|
+
* Preferred general-purpose lookup for gameplay systems that may pass either a
|
|
369
|
+
* concrete skeletal animation name or a technique animation ID.
|
|
370
|
+
*
|
|
333
371
|
* Searches ALL_ANIMATIONS which includes:
|
|
334
372
|
* - BASIC_ANIMATIONS (idle, walk, run, fall)
|
|
335
373
|
* - KICK_ANIMATIONS, PUNCH_ANIMATIONS, etc.
|
|
336
374
|
* - STANCE_ANIMATIONS, MOVEMENT_ANIMATIONS
|
|
337
375
|
* - ALL_ATTACK_ANIMATIONS (stance-specific attacks)
|
|
338
376
|
*
|
|
377
|
+
* Falls back to {@link ANIMATION_ID_REGISTRY} so technique IDs resolved from
|
|
378
|
+
* combat data can still retrieve aliased animations without dropping to generic
|
|
379
|
+
* jab/strike fallbacks.
|
|
380
|
+
*
|
|
339
381
|
* @param name - Animation name (e.g., "idle", "front_kick", "walk")
|
|
340
382
|
* @returns Skeletal animation or undefined
|
|
341
383
|
*
|
|
342
384
|
* @korean 애니메이션가져오기
|
|
343
385
|
*/
|
|
344
386
|
function getAnimation(name) {
|
|
345
|
-
return ALL_ANIMATIONS.get(name);
|
|
387
|
+
return ALL_ANIMATIONS.get(name) ?? ANIMATION_ID_REGISTRY.get(name);
|
|
388
|
+
}
|
|
389
|
+
/**
|
|
390
|
+
* Get a skeletal animation duration or the shared short-technique fallback.
|
|
391
|
+
*
|
|
392
|
+
* @param name - Animation name or technique animation ID. Empty or omitted
|
|
393
|
+
* returns {@link DEFAULT_TECHNIQUE_DURATION_SECONDS}.
|
|
394
|
+
* @returns Duration in seconds.
|
|
395
|
+
*
|
|
396
|
+
* @example
|
|
397
|
+
* ```typescript
|
|
398
|
+
* getAnimationDurationOrFallback("gon_earth_embrace"); // registered duration
|
|
399
|
+
* getAnimationDurationOrFallback(); // DEFAULT_TECHNIQUE_DURATION_SECONDS
|
|
400
|
+
* ```
|
|
401
|
+
*
|
|
402
|
+
* @korean 애니메이션지속시간조회
|
|
403
|
+
*/
|
|
404
|
+
function getAnimationDurationOrFallback(name = "") {
|
|
405
|
+
return getAnimation(name)?.duration ?? .55;
|
|
346
406
|
}
|
|
347
407
|
/**
|
|
348
408
|
* Regex fallback patterns for technique-to-animation mapping
|
|
@@ -424,8 +484,10 @@ var TECHNIQUE_ANIMATION_FALLBACK = [
|
|
|
424
484
|
function getAnimationForTechnique(techniqueNameOrId) {
|
|
425
485
|
if (!techniqueNameOrId) return "jab";
|
|
426
486
|
if (ALL_ANIMATIONS.has(techniqueNameOrId)) return techniqueNameOrId;
|
|
487
|
+
if (ANIMATION_ID_REGISTRY.has(techniqueNameOrId)) return techniqueNameOrId;
|
|
427
488
|
const normalized = techniqueNameOrId.trim().toLowerCase().replace(/[\s-]+/g, "_").replace(/[^a-z0-9_]/g, "");
|
|
428
489
|
if (normalized && ALL_ANIMATIONS.has(normalized)) return normalized;
|
|
490
|
+
if (normalized && ANIMATION_ID_REGISTRY.has(normalized)) return normalized;
|
|
429
491
|
for (const [pattern, animationName] of TECHNIQUE_ANIMATION_FALLBACK) if (pattern.test(techniqueNameOrId)) return animationName;
|
|
430
492
|
return "jab";
|
|
431
493
|
}
|
|
@@ -454,8 +516,8 @@ function getAnimationForTechnique(techniqueNameOrId) {
|
|
|
454
516
|
*/
|
|
455
517
|
function resolveTechniqueAnimation(technique) {
|
|
456
518
|
if (!technique) return "jab";
|
|
457
|
-
if (technique.animationId && ALL_ANIMATIONS.has(technique.animationId)) return technique.animationId;
|
|
458
|
-
if (technique.id && ALL_ANIMATIONS.has(technique.id)) return technique.id;
|
|
519
|
+
if (technique.animationId && (ALL_ANIMATIONS.has(technique.animationId) || ANIMATION_ID_REGISTRY.has(technique.animationId))) return technique.animationId;
|
|
520
|
+
if (technique.id && (ALL_ANIMATIONS.has(technique.id) || ANIMATION_ID_REGISTRY.has(technique.id))) return technique.id;
|
|
459
521
|
const english = technique.name?.english;
|
|
460
522
|
if (english) {
|
|
461
523
|
const byEnglish = getAnimationForTechnique(english);
|
|
@@ -467,6 +529,6 @@ function resolveTechniqueAnimation(technique) {
|
|
|
467
529
|
return "jab";
|
|
468
530
|
}
|
|
469
531
|
//#endregion
|
|
470
|
-
export { ALL_ANIMATIONS, getAnimation, getAnimationByName, getAnimationForTechnique, resolveTechniqueAnimation };
|
|
532
|
+
export { ALL_ANIMATIONS, getAnimation, getAnimationByName, getAnimationDurationOrFallback, getAnimationForTechnique, resolveTechniqueAnimation };
|
|
471
533
|
|
|
472
534
|
//# sourceMappingURL=AnimationRegistry.js.map
|