blacktrigram 0.7.98 → 0.7.99
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/AudioManager.js +2 -4
- package/lib/audio/AudioManager.js.map +1 -1
- package/lib/components/screens/combat/CombatScreen3D.js +8 -9
- 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 +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 +2 -1
- package/lib/components/screens/combat/components/effects/BloodViscosity3D.js.map +1 -1
- package/lib/components/screens/combat/components/effects/InternalDamage3D.js +3 -1
- package/lib/components/screens/combat/components/effects/InternalDamage3D.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 +16 -15
- package/lib/components/screens/combat/components/feedback/RoundAnnouncementOverlayHtml.js.map +1 -1
- package/lib/components/screens/combat/components/feedback/RoundDisplayStatus.js.map +1 -1
- package/lib/components/screens/combat/components/feedback/RoundStartAnnouncementOverlayHtml.js.map +1 -1
- package/lib/components/screens/combat/components/hud/CombatBottomHUD.js.map +1 -1
- package/lib/components/screens/combat/components/hud/CombatLeftHUD.js.map +1 -1
- package/lib/components/screens/combat/components/hud/CombatPortraitStatusStrip.js.map +1 -1
- package/lib/components/screens/combat/components/hud/CombatRightHUD.js.map +1 -1
- package/lib/components/screens/combat/components/hud/CombatTopHUD.js.map +1 -1
- package/lib/components/screens/combat/components/hud/DifficultyIndicator.js.map +1 -1
- package/lib/components/screens/combat/components/hud/FPSMonitor.js.map +1 -1
- package/lib/components/screens/combat/components/hud/PlayerStateOverlayHtml.js.map +1 -1
- package/lib/components/screens/combat/components/indicators/InputBufferDisplay.js.map +1 -1
- package/lib/components/screens/combat/components/indicators/TechniqueNameDisplay.js.map +1 -1
- package/lib/components/screens/combat/hooks/useAICombat.js +0 -1
- package/lib/components/screens/combat/hooks/useAICombat.js.map +1 -1
- package/lib/components/screens/combat/hooks/useCombatActions.js +7 -6
- package/lib/components/screens/combat/hooks/useCombatActions.js.map +1 -1
- package/lib/components/screens/combat/hooks/useCombatLayout.js +3 -1
- package/lib/components/screens/combat/hooks/useCombatLayout.js.map +1 -1
- package/lib/components/screens/controls/ControlsScreen3D.js.map +1 -1
- package/lib/components/screens/controls/components/ControlBindingsOverlayHtml.js.map +1 -1
- package/lib/components/screens/controls/components/ControlCategoryTabsOverlayHtml.js +7 -6
- 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 +34 -30
- 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/endscreen/EndScreen3D.js +2 -2
- package/lib/components/screens/endscreen/EndScreen3D.js.map +1 -1
- package/lib/components/screens/endscreen/components/DefeatAnimation3D.js +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 +2 -2
- package/lib/components/screens/endscreen/components/VictoryAnimation3D.js.map +1 -1
- package/lib/components/screens/endscreen/components/WinnerDisplayOverlayHtml.js +2 -1
- package/lib/components/screens/endscreen/components/WinnerDisplayOverlayHtml.js.map +1 -1
- package/lib/components/screens/intro/IntroScreen3D.js +2 -4
- package/lib/components/screens/intro/IntroScreen3D.js.map +1 -1
- package/lib/components/screens/intro/components/AbilityListOverlayHtml.js.map +1 -1
- package/lib/components/screens/intro/components/ArchetypeCardGridOverlayHtml.js.map +1 -1
- package/lib/components/screens/intro/components/ArchetypeCardOverlayHtml.js.map +1 -1
- package/lib/components/screens/intro/components/ArchetypeDisplayOverlayHtml.js.map +1 -1
- package/lib/components/screens/intro/components/EnhancedArchetypeDisplayOverlayHtml.js.map +1 -1
- package/lib/components/screens/intro/components/MenuButtonsOverlayHtml.js.map +1 -1
- package/lib/components/screens/intro/components/MenuSectionOverlayHtml.js.map +1 -1
- package/lib/components/screens/intro/components/StatBarOverlayHtml.js.map +1 -1
- package/lib/components/screens/philosophy/PhilosophyScreen3D.js.map +1 -1
- package/lib/components/screens/training/TrainingScreen3D.js +10 -9
- package/lib/components/screens/training/TrainingScreen3D.js.map +1 -1
- package/lib/components/screens/training/components/AnatomyControlsOverlayHtml.js +14 -13
- 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 +8 -7
- package/lib/components/screens/training/components/VitalPointMarker3D.js.map +1 -1
- package/lib/components/screens/training/components/VitalPointTrainingOverlayHtml.js +15 -13
- package/lib/components/screens/training/components/VitalPointTrainingOverlayHtml.js.map +1 -1
- package/lib/components/screens/training/components/hud/TrainingBottomHUD.js.map +1 -1
- package/lib/components/screens/training/components/hud/TrainingLeftHUD.js.map +1 -1
- package/lib/components/screens/training/components/hud/TrainingRightHUD.js +3 -2
- 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/useTrainingActions.js +1 -2
- package/lib/components/screens/training/hooks/useTrainingActions.js.map +1 -1
- package/lib/components/screens/training/hooks/useTrainingLayout.js +3 -1
- package/lib/components/screens/training/hooks/useTrainingLayout.js.map +1 -1
- package/lib/components/shared/base/BaseButton.js.map +1 -1
- package/lib/components/shared/base/BaseButtonOverlayHtml.js +59 -53
- package/lib/components/shared/base/BaseButtonOverlayHtml.js.map +1 -1
- package/lib/components/shared/base/BasePanel.js +19 -18
- package/lib/components/shared/base/BasePanel.js.map +1 -1
- package/lib/components/shared/base/BaseText.js.map +1 -1
- package/lib/components/shared/mobile/ActionButtons.js.map +1 -1
- package/lib/components/shared/mobile/GestureRecognizerPure.js +22 -18
- package/lib/components/shared/mobile/GestureRecognizerPure.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/VirtualDPad.js.map +1 -1
- package/lib/components/shared/three/anatomy/BoneAttachedMuscles.js +4 -5
- package/lib/components/shared/three/anatomy/BoneAttachedMuscles.js.map +1 -1
- package/lib/components/shared/three/anatomy/BoneClothing.js +29 -24
- 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 +3 -2
- package/lib/components/shared/three/effects/ActionFeedback.js.map +1 -1
- package/lib/components/shared/three/effects/DamageNumbers.js +2 -1
- package/lib/components/shared/three/effects/DamageNumbers.js.map +1 -1
- package/lib/components/shared/three/effects/HitEffects3D.js +3 -2
- 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 +6 -5
- package/lib/components/shared/three/effects/VitalPointMarkers3D.js.map +1 -1
- package/lib/components/shared/three/indicators/GuardIndicator.js.map +1 -1
- package/lib/components/shared/three/indicators/StanceChangeIndicator.js.map +1 -1
- package/lib/components/shared/three/models/Player3DWithTransitions.js.map +1 -1
- package/lib/components/shared/three/models/SkeletalPlayer3D.js.map +1 -1
- package/lib/components/shared/three/optimization/AdaptiveQuality.js.map +1 -1
- package/lib/components/shared/three/scene/AtmosphericParticles3D.js.map +1 -1
- package/lib/components/shared/three/scene/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 +3 -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/MenuList.js.map +1 -1
- package/lib/components/shared/three/ui/PlayerHUD.js +2 -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 +25 -24
- package/lib/components/shared/three/ui/TechniqueBar.js.map +1 -1
- package/lib/components/shared/three/ui/TechniqueCard.js +1 -3
- 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 +2 -1
- package/lib/components/shared/ui/BackButton.js.map +1 -1
- package/lib/components/shared/ui/BaseHUDContainer.js +69 -68
- 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/hooks/useKeyboardControls.js +0 -1
- package/lib/hooks/useKeyboardControls.js.map +1 -1
- package/lib/hooks/useSkeletalAnimation.js +12 -3
- package/lib/hooks/useSkeletalAnimation.js.map +1 -1
- package/lib/systems/CombatSystem.js +6 -2
- package/lib/systems/CombatSystem.js.map +1 -1
- package/lib/systems/PlayerEffectManager.js +0 -1
- package/lib/systems/PlayerEffectManager.js.map +1 -1
- package/lib/systems/ai/AdaptiveDifficulty.js.map +1 -1
- package/lib/systems/ai/DecisionTree.js +10 -21
- package/lib/systems/ai/DecisionTree.js.map +1 -1
- package/lib/systems/animation/core/AnimationStateMachine.js.map +1 -1
- package/lib/systems/animation/systems/AdvancedJointMovements.js +1 -3
- package/lib/systems/animation/systems/AdvancedJointMovements.js.map +1 -1
- package/lib/systems/animation/systems/MuscleActivation.js +3 -4
- package/lib/systems/animation/systems/MuscleActivation.js.map +1 -1
- package/lib/systems/bodypart/MovementPenaltySystem.js.map +1 -1
- package/lib/systems/bodypart/PlayerInjuryTrackingManager.js +2 -1
- package/lib/systems/bodypart/PlayerInjuryTrackingManager.js.map +1 -1
- package/lib/systems/breathing/BreathingDisruptionSystem.js.map +1 -1
- package/lib/systems/combat/BalanceSystem.js.map +1 -1
- package/lib/systems/combat/CombatStateSystem.js.map +1 -1
- package/lib/systems/combat/ConsciousnessSystem.js.map +1 -1
- package/lib/systems/combat/GrappleSystem.js +3 -9
- package/lib/systems/combat/GrappleSystem.js.map +1 -1
- package/lib/systems/combat/LimbExposureSystem.js.map +1 -1
- package/lib/systems/combat/PainResponseSystem.js.map +1 -1
- package/lib/systems/movement/InjuryMovementModifier.js +1 -1
- package/lib/systems/movement/InjuryMovementModifier.js.map +1 -1
- package/lib/systems/physics/CoordinateMapper.js.map +1 -1
- package/lib/systems/vitalpoint/DamageCalculator.js +8 -5
- package/lib/systems/vitalpoint/DamageCalculator.js.map +1 -1
- package/lib/types/facial.js.map +1 -1
- package/lib/utils/accessibility.js +0 -1
- package/lib/utils/accessibility.js.map +1 -1
- package/lib/utils/effectUtils.js +1 -3
- package/lib/utils/effectUtils.js.map +1 -1
- package/lib/utils/inputSystem.js +0 -2
- package/lib/utils/inputSystem.js.map +1 -1
- package/lib/utils/koreanThemeHelpers.js +6 -3
- package/lib/utils/koreanThemeHelpers.js.map +1 -1
- package/lib/utils/performance/PerformanceOverlay3D.js.map +1 -1
- package/lib/utils/playerUtils.js +4 -2
- package/lib/utils/playerUtils.js.map +1 -1
- package/package.json +5 -5
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"AnimationStateMachine.js","names":[],"sources":["../../../../src/systems/animation/core/AnimationStateMachine.ts"],"sourcesContent":["/**\n * Player Animation State Machine for Black Trigram\n *\n * Manages player character animations with frame-accurate timing at 60fps.\n * Supports animation priorities, transitions, and event callbacks.\n *\n * Based on game-design.md specifications:\n * - Attack: 12 frames (200ms at 60fps)\n * - Block: 4 frames (67ms at 60fps)\n * - Walk: 6 frames\n * - Stance change: 600ms\n *\n * @module systems/animation/AnimationStateMachine\n * @category Animation\n * @korean 애니메이션상태머신\n */\n\nimport { TrigramStance } from \"@/types/common\";\nimport type { AnimationKeyframe } from \"@/types/skeletal\";\nimport {\n createMotionPredictionState,\n predictFutureKeyframe,\n updateMotionPrediction,\n type EasingName,\n type MotionPredictionState,\n} from \"../builders/KeyframeInterpolation\";\nimport {\n AnimationQueue,\n canInterrupt,\n type AnimationRequest,\n type ConflictResolutionStrategy,\n} from \"./AnimationPriority\";\nimport {\n getStanceTransition,\n isTransitionAllowed,\n type StanceTransition,\n} from \"./AnimationTransitions\";\nimport type {\n AnimationConfig,\n AnimationEvents,\n AnimationMachineState,\n AnimationPriority,\n AnimationUpdateResult,\n FallType,\n MutableAnimationConfig,\n} from \"./types\";\nimport { AnimationState, FALL_TO_GROUND_MAP, STEP_PRIORITY } from \"./types\";\n\n/**\n * Default animation configurations based on game-design.md\n *\n * Frame timings:\n * - Attack: 12 frames = 200ms at 60fps\n * - Block: 4 frames = 67ms at 60fps\n * - Walk: 6 frames = 100ms at 60fps\n * - Hit: 4 frames = 67ms at 60fps\n * - Stance change: 36 frames = 600ms at 60fps\n * - Stance guards: 4-6 frames = breathing animation at 60fps\n * - Tactical steps: 18 frames = 300ms at 60fps, 30cm distance\n *\n * Defensive animations (방어 애니메이션):\n * - Block Success (막기): 8 frames = 133ms - absorb impact, maintain guard\n * - Parry Deflect (받아넘기기): 10 frames = 167ms - redirect attack, counter window\n * - Guard Break (방어붕괴): 15 frames = 250ms - arms forced wide, vulnerable\n * - Guard Recovery (방어복구): 12 frames = 200ms - restore guard position\n *\n * @korean 기본애니메이션설정\n */\nexport const DEFAULT_ANIMATION_CONFIGS: Map<AnimationState, AnimationConfig> =\n new Map<AnimationState, AnimationConfig>([\n [\n AnimationState.IDLE,\n {\n state: AnimationState.IDLE,\n frames: 4,\n fps: 60,\n loop: true,\n interruptible: true,\n priority: 0 as AnimationPriority,\n duration: 4 / 60,\n },\n ],\n [\n AnimationState.WALK,\n {\n state: AnimationState.WALK,\n frames: 6,\n fps: 60,\n loop: true,\n interruptible: true,\n priority: 1 as AnimationPriority,\n duration: 6 / 60,\n },\n ],\n [\n AnimationState.RUN,\n {\n state: AnimationState.RUN,\n frames: 8,\n fps: 60,\n loop: true,\n interruptible: true,\n priority: 2 as AnimationPriority,\n duration: 8 / 60,\n },\n ],\n [\n AnimationState.STANCE_CHANGE,\n {\n state: AnimationState.STANCE_CHANGE,\n frames: 36, // 600ms at 60fps\n fps: 60,\n loop: false,\n interruptible: false,\n priority: 3 as AnimationPriority,\n duration: 0.6,\n easing: \"smooth-transition\", // Smooth S-curve for stance changes\n },\n ],\n [\n AnimationState.STANCE_SIDE_SWITCH,\n {\n state: AnimationState.STANCE_SIDE_SWITCH,\n frames: 24, // 400ms at 60fps for left↔right switch\n fps: 60,\n loop: false,\n interruptible: false,\n priority: 3 as AnimationPriority,\n duration: 0.4,\n },\n ],\n [\n AnimationState.DEFEND,\n {\n state: AnimationState.DEFEND,\n frames: 4, // 67ms at 60fps\n fps: 60,\n loop: false,\n interruptible: true,\n priority: 4 as AnimationPriority,\n duration: 4 / 60,\n },\n ],\n // Defensive animations (방어 애니메이션) - Enhanced guard break system\n [\n AnimationState.DEFEND_BLOCK_SUCCESS,\n {\n state: AnimationState.DEFEND_BLOCK_SUCCESS,\n frames: 8, // 133ms at 60fps - absorb impact, maintain guard\n fps: 60,\n loop: false,\n interruptible: false, // Must complete block animation\n priority: 6 as AnimationPriority, // Higher than defend, same as hit\n duration: 0.133,\n easing: \"controlled-slow\", // Controlled deceleration for impact absorption\n },\n ],\n [\n AnimationState.DEFEND_PARRY,\n {\n state: AnimationState.DEFEND_PARRY,\n frames: 10, // 167ms at 60fps - redirect attack, open counter opportunity\n fps: 60,\n loop: false,\n interruptible: false, // Must complete parry animation\n priority: 7 as AnimationPriority, // Higher than block, creates counter window\n duration: 0.167,\n counterWindow: 0.2, // 200ms counter-attack opportunity after parry\n },\n ],\n [\n AnimationState.DEFEND_GUARD_BREAK,\n {\n state: AnimationState.DEFEND_GUARD_BREAK,\n frames: 15, // 250ms at 60fps - arms forced wide, vulnerable state\n fps: 60,\n loop: false,\n interruptible: false, // Cannot interrupt guard break\n priority: 8 as AnimationPriority, // Highest priority (same as fall)\n duration: 0.25,\n vulnerabilityDuration: 0.5, // 500ms vulnerable state after guard break\n },\n ],\n [\n AnimationState.DEFEND_RECOVERY,\n {\n state: AnimationState.DEFEND_RECOVERY,\n frames: 12, // 200ms at 60fps - restore guard position\n fps: 60,\n loop: false,\n interruptible: true, // Can be interrupted by attacks\n priority: 2 as AnimationPriority, // Same as run, lower than defend\n duration: 0.2,\n easing: \"natural-motion\", // Physics-based recovery\n },\n ],\n [\n AnimationState.ATTACK,\n {\n state: AnimationState.ATTACK,\n frames: 12, // 200ms at 60fps\n fps: 60,\n loop: false,\n interruptible: true,\n priority: STEP_PRIORITY,\n duration: 12 / 60,\n easing: \"explosive-power\", // Explosive acceleration for attacks\n },\n ],\n [\n AnimationState.HIT,\n {\n state: AnimationState.HIT,\n frames: 4, // 67ms at 60fps\n fps: 60,\n loop: false,\n interruptible: false,\n priority: 6 as AnimationPriority,\n duration: 4 / 60,\n },\n ],\n [\n AnimationState.KO,\n {\n state: AnimationState.KO,\n frames: 30, // 500ms at 60fps\n fps: 60,\n loop: false,\n interruptible: false,\n priority: 7 as AnimationPriority,\n duration: 0.5,\n },\n ],\n // Fall animations (낙법 애니메이션) - Priority 8 (highest)\n [\n AnimationState.FALL_FORWARD,\n {\n state: AnimationState.FALL_FORWARD,\n frames: 24, // 400ms at 60fps - stumble forward, knee collapse, hands brace, face-down\n fps: 60,\n loop: false,\n interruptible: false,\n priority: 8 as AnimationPriority,\n duration: 0.4,\n },\n ],\n [\n AnimationState.FALL_BACKWARD,\n {\n state: AnimationState.FALL_BACKWARD,\n frames: 30, // 500ms at 60fps - backward stumble, sit, back impact, supine\n fps: 60,\n loop: false,\n interruptible: false,\n priority: 8 as AnimationPriority,\n duration: 0.5,\n },\n ],\n [\n AnimationState.FALL_SIDE_LEFT,\n {\n state: AnimationState.FALL_SIDE_LEFT,\n frames: 27, // 450ms at 60fps - rotation, shoulder roll, side sprawl\n fps: 60,\n loop: false,\n interruptible: false,\n priority: 8 as AnimationPriority,\n duration: 0.45,\n },\n ],\n [\n AnimationState.FALL_SIDE_RIGHT,\n {\n state: AnimationState.FALL_SIDE_RIGHT,\n frames: 27, // 450ms at 60fps - rotation, shoulder roll, side sprawl\n fps: 60,\n loop: false,\n interruptible: false,\n priority: 8 as AnimationPriority,\n duration: 0.45,\n },\n ],\n // Ground state animations (지면 자세) - Breathing loops\n [\n AnimationState.GROUND_PRONE,\n {\n state: AnimationState.GROUND_PRONE,\n frames: 4, // Breathing loop on ground (face down)\n fps: 60,\n loop: true,\n interruptible: true,\n priority: 0 as AnimationPriority,\n duration: 4 / 60,\n },\n ],\n [\n AnimationState.GROUND_SUPINE,\n {\n state: AnimationState.GROUND_SUPINE,\n frames: 4, // Breathing loop on ground (face up)\n fps: 60,\n loop: true,\n interruptible: true,\n priority: 0 as AnimationPriority,\n duration: 4 / 60,\n },\n ],\n [\n AnimationState.GROUND_SIDE_LEFT,\n {\n state: AnimationState.GROUND_SIDE_LEFT,\n frames: 4, // Breathing loop on ground (left side)\n fps: 60,\n loop: true,\n interruptible: true,\n priority: 0 as AnimationPriority,\n duration: 4 / 60,\n },\n ],\n [\n AnimationState.GROUND_SIDE_RIGHT,\n {\n state: AnimationState.GROUND_SIDE_RIGHT,\n frames: 4, // Breathing loop on ground (right side)\n fps: 60,\n loop: true,\n interruptible: true,\n priority: 0 as AnimationPriority,\n duration: 4 / 60,\n },\n ],\n // Recovery animations (기상 애니메이션) - Priority 9 (higher than falls)\n [\n AnimationState.RECOVERY_PRONE_STANDUP,\n {\n state: AnimationState.RECOVERY_PRONE_STANDUP,\n frames: 30, // 500ms at 60fps - push up from prone, rise to standing\n fps: 60,\n loop: false,\n interruptible: false, // Last 6 frames (100ms) are interruptible\n priority: 9 as AnimationPriority,\n duration: 0.5,\n },\n ],\n [\n AnimationState.RECOVERY_SUPINE_STANDUP,\n {\n state: AnimationState.RECOVERY_SUPINE_STANDUP,\n frames: 36, // 600ms at 60fps - sit up, roll forward, stand\n fps: 60,\n loop: false,\n interruptible: false, // Last 6 frames (100ms) are interruptible\n priority: 9 as AnimationPriority,\n duration: 0.6,\n },\n ],\n [\n AnimationState.RECOVERY_ROLL,\n {\n state: AnimationState.RECOVERY_ROLL,\n frames: 24, // 400ms at 60fps - roll to side, spring to feet (quick recovery)\n fps: 60,\n loop: false,\n interruptible: false, // Last 6 frames (100ms) are interruptible\n priority: 9 as AnimationPriority,\n duration: 0.4,\n },\n ],\n [\n AnimationState.RECOVERY_DEFENSIVE,\n {\n state: AnimationState.RECOVERY_DEFENSIVE,\n frames: 42, // 700ms at 60fps - slow rise with guard up (vulnerable but defended)\n fps: 60,\n loop: false,\n interruptible: false, // Last 6 frames (100ms) are interruptible\n priority: 9 as AnimationPriority,\n duration: 0.7,\n },\n ],\n // 180-degree turn animations (180도 회전 애니메이션)\n [\n AnimationState.TURN_LEFT,\n {\n state: AnimationState.TURN_LEFT,\n frames: 12, // 200ms at 60fps for 180° turn\n fps: 60,\n loop: false,\n interruptible: false,\n priority: STEP_PRIORITY, // Same priority as attacks/steps - committed action\n duration: 12 / 60,\n },\n ],\n [\n AnimationState.TURN_RIGHT,\n {\n state: AnimationState.TURN_RIGHT,\n frames: 12, // 200ms at 60fps for 180° turn\n fps: 60,\n loop: false,\n interruptible: false,\n priority: STEP_PRIORITY, // Same priority as attacks/steps - committed action\n duration: 12 / 60,\n },\n ],\n // Stance-specific guard animations (팔괘 방어 자세)\n [\n AnimationState.STANCE_GUARD_GEON,\n {\n state: AnimationState.STANCE_GUARD_GEON,\n frames: 6, // Breathing animation\n fps: 60,\n loop: true,\n interruptible: true,\n priority: 0 as AnimationPriority,\n duration: 6 / 60,\n },\n ],\n [\n AnimationState.STANCE_GUARD_TAE,\n {\n state: AnimationState.STANCE_GUARD_TAE,\n frames: 6, // Breathing animation\n fps: 60,\n loop: true,\n interruptible: true,\n priority: 0 as AnimationPriority,\n duration: 6 / 60,\n },\n ],\n [\n AnimationState.STANCE_GUARD_LI,\n {\n state: AnimationState.STANCE_GUARD_LI,\n frames: 4, // Controlled breathing\n fps: 60,\n loop: true,\n interruptible: true,\n priority: 0 as AnimationPriority,\n duration: 4 / 60,\n },\n ],\n [\n AnimationState.STANCE_GUARD_JIN,\n {\n state: AnimationState.STANCE_GUARD_JIN,\n frames: 5, // Deep breathing\n fps: 60,\n loop: true,\n interruptible: true,\n priority: 0 as AnimationPriority,\n duration: 5 / 60,\n },\n ],\n [\n AnimationState.STANCE_GUARD_SON,\n {\n state: AnimationState.STANCE_GUARD_SON,\n frames: 6, // Rhythmic breathing\n fps: 60,\n loop: true,\n interruptible: true,\n priority: 0 as AnimationPriority,\n duration: 6 / 60,\n },\n ],\n [\n AnimationState.STANCE_GUARD_GAM,\n {\n state: AnimationState.STANCE_GUARD_GAM,\n frames: 6, // Flowing breathing\n fps: 60,\n loop: true,\n interruptible: true,\n priority: 0 as AnimationPriority,\n duration: 6 / 60,\n },\n ],\n [\n AnimationState.STANCE_GUARD_GAN,\n {\n state: AnimationState.STANCE_GUARD_GAN,\n frames: 4, // Steady breathing\n fps: 60,\n loop: true,\n interruptible: true,\n priority: 0 as AnimationPriority,\n duration: 4 / 60,\n },\n ],\n [\n AnimationState.STANCE_GUARD_GON,\n {\n state: AnimationState.STANCE_GUARD_GON,\n frames: 5, // Deep diaphragm breathing\n fps: 60,\n loop: true,\n interruptible: true,\n priority: 0 as AnimationPriority,\n duration: 5 / 60,\n },\n ],\n // Tactical step animations (전술적 발걸음)\n // 18 frames = 300ms at 60fps, 30cm distance per step\n [\n AnimationState.STEP_FORWARD,\n {\n state: AnimationState.STEP_FORWARD,\n frames: 18,\n fps: 60,\n loop: false,\n interruptible: false, // Non-interruptible for commitment\n priority: STEP_PRIORITY,\n duration: 0.3,\n },\n ],\n [\n AnimationState.STEP_BACK,\n {\n state: AnimationState.STEP_BACK,\n frames: 18,\n fps: 60,\n loop: false,\n interruptible: false,\n priority: STEP_PRIORITY,\n duration: 0.3,\n },\n ],\n [\n AnimationState.STEP_LEFT,\n {\n state: AnimationState.STEP_LEFT,\n frames: 18,\n fps: 60,\n loop: false,\n interruptible: false,\n priority: STEP_PRIORITY,\n duration: 0.3,\n },\n ],\n [\n AnimationState.STEP_RIGHT,\n {\n state: AnimationState.STEP_RIGHT,\n frames: 18,\n fps: 60,\n loop: false,\n interruptible: false,\n priority: STEP_PRIORITY,\n duration: 0.3,\n },\n ],\n [\n AnimationState.STEP_FORWARD_LEFT,\n {\n state: AnimationState.STEP_FORWARD_LEFT,\n frames: 18,\n fps: 60,\n loop: false,\n interruptible: false,\n priority: STEP_PRIORITY,\n duration: 0.3,\n },\n ],\n [\n AnimationState.STEP_FORWARD_RIGHT,\n {\n state: AnimationState.STEP_FORWARD_RIGHT,\n frames: 18,\n fps: 60,\n loop: false,\n interruptible: false,\n priority: STEP_PRIORITY,\n duration: 0.3,\n },\n ],\n [\n AnimationState.STEP_BACK_LEFT,\n {\n state: AnimationState.STEP_BACK_LEFT,\n frames: 18,\n fps: 60,\n loop: false,\n interruptible: false,\n priority: STEP_PRIORITY,\n duration: 0.3,\n },\n ],\n [\n AnimationState.STEP_BACK_RIGHT,\n {\n state: AnimationState.STEP_BACK_RIGHT,\n frames: 18,\n fps: 60,\n loop: false,\n interruptible: false,\n priority: STEP_PRIORITY,\n duration: 0.3,\n },\n ],\n // Footwork patterns (보법) - Korean martial arts specialized footwork\n // Circular step (원형보) - Lateral movement maintaining guard facing\n [\n AnimationState.FOOTWORK_CIRCULAR_LEFT,\n {\n state: AnimationState.FOOTWORK_CIRCULAR_LEFT,\n frames: 18, // 300ms at 60fps\n fps: 60,\n loop: false,\n interruptible: false, // Committed footwork\n priority: STEP_PRIORITY, // Same as tactical steps\n duration: 0.3,\n },\n ],\n [\n AnimationState.FOOTWORK_CIRCULAR_RIGHT,\n {\n state: AnimationState.FOOTWORK_CIRCULAR_RIGHT,\n frames: 18, // 300ms at 60fps\n fps: 60,\n loop: false,\n interruptible: false,\n priority: STEP_PRIORITY,\n duration: 0.3,\n },\n ],\n // Pivot step (축족회전) - Rotation on planted foot\n [\n AnimationState.FOOTWORK_PIVOT_LEFT,\n {\n state: AnimationState.FOOTWORK_PIVOT_LEFT,\n frames: 15, // 250ms at 60fps\n fps: 60,\n loop: false,\n interruptible: false,\n priority: STEP_PRIORITY,\n duration: 0.25,\n },\n ],\n [\n AnimationState.FOOTWORK_PIVOT_RIGHT,\n {\n state: AnimationState.FOOTWORK_PIVOT_RIGHT,\n frames: 15, // 250ms at 60fps\n fps: 60,\n loop: false,\n interruptible: false,\n priority: STEP_PRIORITY,\n duration: 0.25,\n },\n ],\n // Slide step (미끄럼보) - Both feet move together\n [\n AnimationState.FOOTWORK_SLIDE_FORWARD,\n {\n state: AnimationState.FOOTWORK_SLIDE_FORWARD,\n frames: 12, // 200ms at 60fps\n fps: 60,\n loop: false,\n interruptible: true, // Can be interrupted\n priority: 4 as AnimationPriority, // Same as defend\n duration: 0.2,\n },\n ],\n [\n AnimationState.FOOTWORK_SLIDE_BACK,\n {\n state: AnimationState.FOOTWORK_SLIDE_BACK,\n frames: 12, // 200ms at 60fps\n fps: 60,\n loop: false,\n interruptible: true,\n priority: 4 as AnimationPriority,\n duration: 0.2,\n },\n ],\n [\n AnimationState.FOOTWORK_SLIDE_LEFT,\n {\n state: AnimationState.FOOTWORK_SLIDE_LEFT,\n frames: 12, // 200ms at 60fps\n fps: 60,\n loop: false,\n interruptible: true,\n priority: 4 as AnimationPriority,\n duration: 0.2,\n },\n ],\n [\n AnimationState.FOOTWORK_SLIDE_RIGHT,\n {\n state: AnimationState.FOOTWORK_SLIDE_RIGHT,\n frames: 12, // 200ms at 60fps\n fps: 60,\n loop: false,\n interruptible: true,\n priority: 4 as AnimationPriority,\n duration: 0.2,\n },\n ],\n // Shuffle step (섞음보) - Quick micro-adjustment\n [\n AnimationState.FOOTWORK_SHUFFLE,\n {\n state: AnimationState.FOOTWORK_SHUFFLE,\n frames: 6, // 100ms at 60fps\n fps: 60,\n loop: false,\n interruptible: true,\n priority: 3 as AnimationPriority, // Same as stance_change\n duration: 0.1,\n },\n ],\n ]);\n\n/**\n * Player Animation State Machine\n *\n * Manages animation state, transitions, and timing with frame-accurate updates.\n * Integrates priority system, automatic animation queueing, and event callbacks.\n *\n * **Animation Queue**: Enabled by default with max size 3 and timestamp-based\n * conflict resolution. Automatically queues animations that cannot execute\n * immediately and processes them when the current animation completes.\n *\n * @example\n * ```typescript\n * const machine = new PlayerAnimationStateMachine(DEFAULT_ANIMATION_CONFIGS, {\n * onAnimationStart: (state) => console.log(`Started ${state}`),\n * onAnimationComplete: (state) => console.log(`Completed ${state}`),\n * onFrame: (frame, state) => {\n * if (state === \"attack\" && frame === 6) {\n * // Execute attack at midpoint (frame 6 of 12)\n * executeAttackLogic();\n * }\n * }\n * });\n *\n * // Animation queue is enabled by default\n * // Use transitionToQueued() for automatic queueing\n * machine.transitionToQueued(AnimationState.ATTACK); // Queues if can't execute\n *\n * // Or use regular transitionTo() (no queueing)\n * machine.transitionTo(AnimationState.ATTACK); // Fails if can't execute\n *\n * // In game loop (useFrame)\n * useFrame((state, delta) => {\n * const result = machine.update(delta);\n * updatePlayerVisuals(result.state, result.frame);\n * });\n * ```\n *\n * @korean 플레이어애니메이션상태머신\n */\nexport class PlayerAnimationStateMachine {\n /**\n * Static mapping from TrigramStance to guard AnimationState\n * Prevents repeated object allocation in transitionToStanceGuard()\n * @korean 자세방어상태맵\n */\n private static readonly GUARD_STATE_MAP: Record<\n TrigramStance,\n AnimationState\n > = {\n [TrigramStance.GEON]: AnimationState.STANCE_GUARD_GEON,\n [TrigramStance.TAE]: AnimationState.STANCE_GUARD_TAE,\n [TrigramStance.LI]: AnimationState.STANCE_GUARD_LI,\n [TrigramStance.JIN]: AnimationState.STANCE_GUARD_JIN,\n [TrigramStance.SON]: AnimationState.STANCE_GUARD_SON,\n [TrigramStance.GAM]: AnimationState.STANCE_GUARD_GAM,\n [TrigramStance.GAN]: AnimationState.STANCE_GUARD_GAN,\n [TrigramStance.GON]: AnimationState.STANCE_GUARD_GON,\n };\n\n /**\n * Static reverse mapping from guard AnimationState to TrigramStance\n * Prevents repeated object allocation in getCurrentGuardStance()\n * @korean 방어상태자세맵\n */\n private static readonly STANCE_FROM_GUARD_MAP: Record<string, TrigramStance> =\n {\n [AnimationState.STANCE_GUARD_GEON]: TrigramStance.GEON,\n [AnimationState.STANCE_GUARD_TAE]: TrigramStance.TAE,\n [AnimationState.STANCE_GUARD_LI]: TrigramStance.LI,\n [AnimationState.STANCE_GUARD_JIN]: TrigramStance.JIN,\n [AnimationState.STANCE_GUARD_SON]: TrigramStance.SON,\n [AnimationState.STANCE_GUARD_GAM]: TrigramStance.GAM,\n [AnimationState.STANCE_GUARD_GAN]: TrigramStance.GAN,\n [AnimationState.STANCE_GUARD_GON]: TrigramStance.GON,\n };\n\n private currentState: AnimationState = AnimationState.IDLE;\n private frameIndex = 0;\n private timeAccumulator = 0;\n private previousState: AnimationState | null = null;\n private justStarted = false;\n private justCompleted = false;\n\n /**\n * Current stance transition data (null when not in stance_change animation)\n *\n * **Korean**: 현재 자세 전환 데이터\n *\n * Tracks the active stance transition for use during stance_change animation.\n * Provides access to keyframes and blend weights for smooth interpolation.\n *\n * @korean 현재자세전환데이터\n */\n private currentStanceTransition: StanceTransition | null = null;\n\n /**\n * Motion prediction state for latency reduction\n *\n * **Korean**: 동작 예측 상태\n *\n * Tracks animation velocities for motion prediction to reduce perceived latency.\n * Updated each frame with velocity calculations for smooth anticipation.\n *\n * @korean 동작예측상태\n */\n private motionPrediction: MotionPredictionState =\n createMotionPredictionState();\n\n /**\n * Enable motion prediction for latency reduction\n *\n * **Korean**: 동작 예측 활성화\n *\n * When enabled, predicts future animation frames based on current velocity\n * to reduce perceived input latency by 16-33ms (1-2 frames at 60fps).\n *\n * @korean 동작예측활성화\n */\n private enableMotionPrediction: boolean = false;\n\n /**\n * Motion prediction time ahead (seconds)\n *\n * **Korean**: 예측 시간\n *\n * How far ahead to predict motion (default: 1 frame = 16.67ms at 60fps).\n * Typical range: 0.016-0.033 seconds for <50ms total latency.\n *\n * @korean 예측시간\n */\n private predictionTimeAhead: number = 0.01667; // 1 frame at 60fps\n\n /**\n * Previous keyframe for motion prediction velocity calculation\n *\n * **Korean**: 이전 키프레임\n *\n * @korean 이전키프레임\n */\n private previousKeyframe: AnimationKeyframe | null = null;\n\n /**\n * Preferred easing function for smooth transitions\n *\n * **Korean**: 선호 이징 함수\n *\n * Default easing curve for animation blending and transitions.\n * Can be overridden per animation or transition.\n *\n * @korean 선호이징함수\n */\n private preferredEasing: EasingName = \"natural-motion\";\n\n /**\n * Animation queue for pending animations\n *\n * **Korean**: 애니메이션 대기열\n *\n * Stores animation requests that couldn't be executed immediately\n * due to non-interruptible animations or priority conflicts.\n * Processed automatically when current animation completes.\n *\n * Enabled by default with max size 3 and timestamp-based conflict resolution.\n * Can be disabled with disableQueue() or reconfigured with enableQueue().\n *\n * @korean 애니메이션대기열\n */\n private animationQueue: AnimationQueue | null = new AnimationQueue(\n 3,\n \"timestamp\",\n );\n\n /**\n * Conflict resolution strategy for equal-priority animations\n *\n * **Korean**: 충돌 해결 전략\n *\n * Determines how to resolve conflicts when multiple animations\n * have equal priority. Default: timestamp (FIFO).\n *\n * @korean 충돌해결전략\n */\n private conflictStrategy: ConflictResolutionStrategy = \"timestamp\";\n\n /**\n * Create a new animation state machine\n *\n * Clones the provided config map so per-instance mutations\n * (e.g. dynamic attack duration) don't affect shared defaults.\n *\n * @param animations - Map of animation configurations (cloned internally)\n * @param events - Optional event callbacks\n *\n * @korean 생성자\n */\n private readonly animations: Map<AnimationState, MutableAnimationConfig>;\n\n constructor(\n animations: Map<AnimationState, AnimationConfig>,\n private readonly events?: AnimationEvents,\n ) {\n // Clone so we can safely mutate per-instance (e.g. attack duration)\n this.animations = new Map(\n Array.from(animations.entries()).map(([k, v]) => [k, { ...v }]),\n );\n }\n\n /**\n * Update animation state with delta time\n *\n * Call this in useFrame for 60fps updates.\n * Handles frame progression, looping, and completion.\n *\n * @param deltaTime - Time elapsed since last update (in seconds)\n * @returns Animation update result with current state and frame\n *\n * @korean 업데이트\n */\n update(deltaTime: number): AnimationUpdateResult {\n const currentAnim = this.animations.get(this.currentState);\n if (!currentAnim) {\n return {\n state: this.currentState,\n frame: 0,\n progress: 0,\n justCompleted: false,\n justStarted: false,\n };\n }\n\n // Reset just started/completed flags\n const wasJustStarted = this.justStarted;\n this.justStarted = false;\n const previousJustCompleted = this.justCompleted;\n this.justCompleted = false;\n\n // Accumulate time\n this.timeAccumulator += deltaTime;\n const frameDuration = 1 / currentAnim.fps;\n\n // Check if we should advance to next frame\n if (this.timeAccumulator >= frameDuration) {\n const previousFrame = this.frameIndex;\n this.frameIndex++;\n this.timeAccumulator -= frameDuration;\n\n // Emit frame event\n if (this.events?.onFrame && previousFrame !== this.frameIndex) {\n this.events.onFrame(this.frameIndex, this.currentState);\n }\n\n // Handle animation completion\n if (this.frameIndex >= currentAnim.frames) {\n if (currentAnim.loop) {\n // Loop back to start\n this.frameIndex = 0;\n } else {\n // Animation completed\n this.justCompleted = true;\n if (this.events?.onAnimationComplete) {\n this.events.onAnimationComplete(this.currentState);\n }\n\n // Auto-transition logic\n // Fall animations transition to ground states using the mapping\n if (this.currentState.startsWith(\"fall_\")) {\n const fallType = this.currentState.replace(\"fall_\", \"\");\n\n // Validate that fallType is a valid FallType before using in map\n if (\n fallType === \"forward\" ||\n fallType === \"backward\" ||\n fallType === \"side_left\" ||\n fallType === \"side_right\"\n ) {\n const groundState = FALL_TO_GROUND_MAP[fallType as FallType];\n const groundAnimKey = `ground_${groundState}`;\n\n // Validate that the constructed ground animation state actually exists\n if (\n DEFAULT_ANIMATION_CONFIGS.has(groundAnimKey as AnimationState)\n ) {\n const groundAnimState = groundAnimKey as AnimationState;\n\n this.previousState = this.currentState;\n this.currentState = groundAnimState;\n this.frameIndex = 0;\n this.timeAccumulator = 0;\n this.justStarted = true;\n\n if (this.events?.onAnimationStart) {\n this.events.onAnimationStart(groundAnimState);\n }\n } else {\n // Fallback: if mapping is invalid, safely transition to idle\n // instead of entering an undefined animation state.\n console.warn(\n \"[AnimationStateMachine] Invalid ground animation mapping for fall type:\",\n fallType,\n \"->\",\n groundAnimKey,\n );\n this.previousState = this.currentState;\n this.currentState = AnimationState.IDLE;\n this.frameIndex = 0;\n this.timeAccumulator = 0;\n this.justStarted = true;\n\n if (this.events?.onAnimationStart) {\n this.events.onAnimationStart(AnimationState.IDLE);\n }\n }\n } else {\n // Invalid fall type - fallback to idle\n console.warn(\n \"[AnimationStateMachine] Invalid fall animation state:\",\n this.currentState,\n );\n this.previousState = this.currentState;\n this.currentState = AnimationState.IDLE;\n this.frameIndex = 0;\n this.timeAccumulator = 0;\n this.justStarted = true;\n\n if (this.events?.onAnimationStart) {\n this.events.onAnimationStart(AnimationState.IDLE);\n }\n }\n }\n // Recovery animations transition to idle when complete\n else if (this.currentState.startsWith(\"recovery_\")) {\n this.previousState = this.currentState;\n this.currentState = AnimationState.IDLE;\n this.frameIndex = 0;\n this.timeAccumulator = 0;\n this.justStarted = true;\n\n if (this.events?.onAnimationStart) {\n this.events.onAnimationStart(AnimationState.IDLE);\n }\n }\n // Non-fall, non-recovery, non-looping animations transition to idle\n else if (\n this.currentState !== AnimationState.IDLE &&\n this.currentState !== AnimationState.KO &&\n !this.currentState.startsWith(\"ground_\")\n ) {\n // Clear stance transition data if completing stance_change\n if (this.currentState === AnimationState.STANCE_CHANGE) {\n this.clearStanceTransition();\n }\n\n // Transition to idle first\n this.previousState = this.currentState;\n this.currentState = AnimationState.IDLE;\n this.frameIndex = 0;\n this.timeAccumulator = 0;\n this.justStarted = true;\n\n if (this.events?.onAnimationStart) {\n this.events.onAnimationStart(AnimationState.IDLE);\n }\n\n // Then try to process next queued animation (from idle state)\n this.processNextQueuedAnimation();\n } else {\n // Stay on last frame (for ko and ground states)\n this.frameIndex = currentAnim.frames - 1;\n }\n }\n }\n }\n\n const progress =\n currentAnim.frames > 0 ? this.frameIndex / currentAnim.frames : 0;\n\n return {\n state: this.currentState,\n frame: this.frameIndex,\n progress,\n justCompleted: previousJustCompleted,\n justStarted: wasJustStarted,\n };\n }\n\n /**\n * Attempt to transition to a new animation state\n *\n * Checks transition rules and priority system before transitioning.\n *\n * @param newState - Target animation state\n * @returns Whether transition was successful\n *\n * @example\n * ```typescript\n * // Successful transitions\n * machine.transitionTo(\"walk\"); // idle -> walk\n * machine.transitionTo(\"attack\"); // walk -> attack\n *\n * // Failed transition (invalid or lower priority)\n * machine.transitionTo(\"walk\"); // attack -> walk (blocked, must complete first)\n * ```\n *\n * @korean 상태전환\n */\n transitionTo(newState: AnimationState): boolean {\n // Don't transition to same state\n if (this.currentState === newState) {\n return false;\n }\n\n // Check if transition is allowed by rules\n if (!isTransitionAllowed(this.currentState, newState)) {\n return false;\n }\n\n const currentAnim = this.animations.get(this.currentState);\n const newAnim = this.animations.get(newState);\n\n if (!newAnim) {\n return false;\n }\n\n // Check priority system\n if (\n currentAnim &&\n !canInterrupt(this.currentState, newState, currentAnim.interruptible)\n ) {\n return false;\n }\n\n // Emit interrupt event if current animation wasn't completed\n if (this.frameIndex < (currentAnim?.frames ?? 0) - 1) {\n if (this.events?.onAnimationInterrupted) {\n this.events.onAnimationInterrupted(this.currentState, newState);\n }\n }\n\n // Clear stance transition data if interrupting stance_change\n if (this.currentState === AnimationState.STANCE_CHANGE) {\n this.clearStanceTransition();\n }\n\n // Execute transition\n this.previousState = this.currentState;\n this.currentState = newState;\n this.frameIndex = 0;\n this.timeAccumulator = 0;\n this.justStarted = true;\n this.justCompleted = false;\n\n // Emit start event\n if (this.events?.onAnimationStart) {\n this.events.onAnimationStart(newState);\n }\n\n return true;\n }\n\n /**\n * Transition to ATTACK state with a technique-specific duration.\n *\n * The default ATTACK config is 200ms (12 frames), but real techniques\n * range from 350ms to 1200ms. This method overrides the ATTACK frame\n * count to match the actual skeletal animation duration so the state\n * machine stays in ATTACK for the full technique.\n *\n * @param durationSeconds - The skeletal animation duration in seconds\n * @returns Whether transition was successful\n *\n * @example\n * ```typescript\n * // Jab animation is 0.55s (TECHNIQUE_TIMING.FAST)\n * machine.transitionToAttack(0.55);\n * ```\n *\n * @korean 공격전환 (기술별 지속시간)\n */\n transitionToAttack(durationSeconds: number): boolean {\n const attackConfig = this.animations.get(AnimationState.ATTACK);\n if (attackConfig) {\n const fps = attackConfig.fps || 60;\n attackConfig.frames = Math.max(1, Math.round(durationSeconds * fps));\n attackConfig.duration = durationSeconds;\n }\n return this.transitionTo(AnimationState.ATTACK);\n }\n\n /**\n * Get current animation state\n *\n * @returns Current animation state\n * @korean 현재상태가져오기\n */\n getCurrentState(): AnimationState {\n return this.currentState;\n }\n\n /**\n * Get current frame index\n *\n * @returns Current frame index (0 to frames-1)\n * @korean 현재프레임가져오기\n */\n getCurrentFrame(): number {\n return this.frameIndex;\n }\n\n /**\n * Get previous animation state\n *\n * @returns Previous animation state or null\n * @korean 이전상태가져오기\n */\n getPreviousState(): AnimationState | null {\n return this.previousState;\n }\n\n /**\n * Get current animation configuration\n *\n * @returns Current animation config or undefined\n * @korean 현재애니메이션설정가져오기\n */\n getCurrentAnimation(): AnimationConfig | undefined {\n return this.animations.get(this.currentState);\n }\n\n /**\n * Reset animation state machine to idle\n *\n * @korean 초기화\n */\n reset(): void {\n this.currentState = AnimationState.IDLE;\n this.frameIndex = 0;\n this.timeAccumulator = 0;\n this.previousState = null;\n this.justStarted = false;\n this.justCompleted = false;\n }\n\n /**\n * Get full internal state (for debugging/testing)\n *\n * @returns Current state machine state\n * @korean 상태가져오기\n */\n getState(): AnimationMachineState {\n return {\n currentState: this.currentState,\n frameIndex: this.frameIndex,\n timeAccumulator: this.timeAccumulator,\n isPlaying: true,\n previousState: this.previousState,\n };\n }\n\n /**\n * Transition to stance-specific guard animation\n *\n * Convenience method to transition to a stance guard based on trigram stance.\n * Automatically maps trigram stance to corresponding guard animation state.\n *\n * @param stance - Trigram stance identifier\n * @returns Whether transition was successful\n *\n * @example\n * ```typescript\n * // When player changes to Fire stance\n * machine.transitionToStanceGuard(TrigramStance.LI);\n * // Internally transitions to \"stance_guard_li\" animation state\n * ```\n *\n * @korean 자세방어전환\n */\n transitionToStanceGuard(stance: TrigramStance): boolean {\n const guardAnimationState =\n PlayerAnimationStateMachine.GUARD_STATE_MAP[stance];\n\n // Verify the guard animation exists in our configs\n if (!guardAnimationState || !this.animations.has(guardAnimationState)) {\n console.warn(`No guard animation configured for stance: ${stance}`);\n return false;\n }\n\n return this.transitionTo(guardAnimationState);\n }\n\n /**\n * Check if current animation is a stance guard\n *\n * @returns True if currently in a stance guard animation\n * @korean 자세방어상태확인\n */\n isInStanceGuard(): boolean {\n return this.currentState.startsWith(\"stance_guard_\");\n }\n\n /**\n * Get current guard stance if in a guard animation\n *\n * @returns Trigram stance or null if not in guard\n * @korean 현재방어자세가져오기\n */\n getCurrentGuardStance(): TrigramStance | null {\n if (!this.isInStanceGuard()) {\n return null;\n }\n\n const stance =\n PlayerAnimationStateMachine.STANCE_FROM_GUARD_MAP[this.currentState];\n\n // Validate that we got a valid stance\n if (!stance) {\n console.warn(`Invalid guard state detected: ${this.currentState}`);\n return null;\n }\n\n return stance;\n }\n\n /**\n * Transition to stance_change animation with specific stance transition data\n *\n * **Korean**: 자세 전환 애니메이션 시작\n *\n * Initiates a stance change animation with the specific transition data\n * from the 64-transition matrix. This provides stance-specific keyframes\n * and blend weights for smooth interpolation.\n *\n * @param fromStance - Source trigram stance\n * @param toStance - Target trigram stance\n * @returns Whether transition was successful\n *\n * @example\n * ```typescript\n * // Start transition from Heaven to Lake stance\n * const success = machine.transitionToStanceChange(\n * TrigramStance.GEON,\n * TrigramStance.TAE\n * );\n *\n * if (success) {\n * // During update loop, use getStanceTransitionBlend() to interpolate\n * const blend = machine.getStanceTransitionBlend();\n * if (blend) {\n * // Apply blend weights to stance poses\n * applyStanceBlend(blend);\n * }\n * }\n * ```\n *\n * @korean 자세전환애니메이션시작\n */\n transitionToStanceChange(\n fromStance: TrigramStance,\n toStance: TrigramStance,\n ): boolean {\n // Get the specific transition data from the 64-transition matrix\n const transitionData = getStanceTransition(fromStance, toStance);\n\n if (!transitionData) {\n console.warn(\n `[AnimationStateMachine] No transition data found for ${fromStance} -> ${toStance}`,\n );\n return false;\n }\n\n // Store current transition data in case we need to restore it\n const previousTransitionData = this.currentStanceTransition;\n\n // Temporarily set the new transition data\n this.currentStanceTransition = transitionData;\n\n // Initiate the stance_change animation\n const success = this.transitionTo(AnimationState.STANCE_CHANGE);\n\n // If transition failed, restore previous transition data\n if (!success) {\n this.currentStanceTransition = previousTransitionData;\n }\n\n return success;\n }\n\n /**\n * Get current stance transition data\n *\n * **Korean**: 현재 자세 전환 데이터 가져오기\n *\n * Returns the active stance transition data during stance_change animation.\n * Null if not currently in a stance transition.\n *\n * @returns Current stance transition or null\n *\n * @korean 현재자세전환데이터가져오기\n */\n getCurrentStanceTransition(): StanceTransition | null {\n return this.currentStanceTransition;\n }\n\n /**\n * Get interpolated blend weights for current stance transition frame\n *\n * **Korean**: 현재 프레임 블렌드 가중치\n *\n * Returns the interpolated blend data for the current frame during\n * stance_change animation. Uses the keyframe data from the transition\n * matrix to provide smooth stance interpolation.\n *\n * @returns Blend data with stance and weight, or null if not in transition\n *\n * @example\n * ```typescript\n * // In rendering loop during stance transition\n * const blend = machine.getStanceTransitionBlend();\n * if (blend) {\n * console.log(`Frame ${blend.frame}: ${blend.stance} at ${blend.blend}x weight`);\n * // Apply blended pose: blend.blend * targetPose + (1 - blend.blend) * sourcePose\n * }\n * ```\n *\n * @korean 현재프레임블렌드가중치\n */\n getStanceTransitionBlend(): {\n frame: number;\n stance: TrigramStance | \"neutral\";\n blend: number;\n } | null {\n // Only valid during stance_change animation\n if (\n this.currentState !== AnimationState.STANCE_CHANGE ||\n !this.currentStanceTransition\n ) {\n return null;\n }\n\n const keyframes = this.currentStanceTransition.keyframes;\n const currentFrame = this.frameIndex;\n\n // Find the two keyframes to interpolate between\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 (\n keyframes[i].frame <= currentFrame &&\n keyframes[i + 1].frame > currentFrame\n ) {\n prevKeyframe = keyframes[i];\n nextKeyframe = keyframes[i + 1];\n break;\n }\n }\n\n // If we're exactly on a keyframe, return it directly\n const exactKeyframe = keyframes.find((kf) => kf.frame === currentFrame);\n if (exactKeyframe) {\n return {\n frame: currentFrame,\n stance: exactKeyframe.stance,\n blend: exactKeyframe.blend,\n };\n }\n\n // Linear interpolation between keyframes\n const frameRange = nextKeyframe.frame - prevKeyframe.frame;\n const frameProgress =\n frameRange > 0 ? (currentFrame - prevKeyframe.frame) / frameRange : 0;\n\n const interpolatedBlend =\n prevKeyframe.blend +\n (nextKeyframe.blend - prevKeyframe.blend) * frameProgress;\n\n // Use the next keyframe's stance as we're transitioning towards it\n return {\n frame: currentFrame,\n stance: nextKeyframe.stance,\n blend: interpolatedBlend,\n };\n }\n\n /**\n * Check if currently in a stance transition animation\n *\n * **Korean**: 자세 전환 중 확인\n *\n * @returns True if currently executing a stance_change animation\n * @korean 자세전환중확인\n */\n isInStanceTransition(): boolean {\n return (\n this.currentState === AnimationState.STANCE_CHANGE &&\n this.currentStanceTransition !== null\n );\n }\n\n /**\n * Clear stance transition data (called automatically when transition completes)\n *\n * **Korean**: 자세 전환 데이터 초기화\n *\n * @internal\n * @korean 자세전환데이터초기화\n */\n private clearStanceTransition(): void {\n this.currentStanceTransition = null;\n }\n\n /**\n * Enable or disable motion prediction\n *\n * **Korean**: 동작 예측 설정\n *\n * Enables motion prediction to reduce perceived input latency by predicting\n * future animation frames based on current velocity (1-2 frames ahead).\n *\n * @param enabled - Whether to enable motion prediction\n * @param predictionTime - Optional: time ahead to predict (default: 16.67ms)\n *\n * @example\n * ```typescript\n * // Enable motion prediction for 1 frame (16.67ms at 60fps)\n * machine.setMotionPrediction(true);\n *\n * // Enable with 2 frames prediction (33.33ms)\n * machine.setMotionPrediction(true, 0.03333);\n * ```\n *\n * @korean 동작예측설정\n */\n setMotionPrediction(enabled: boolean, predictionTime?: number): void {\n this.enableMotionPrediction = enabled;\n if (predictionTime !== undefined) {\n // Clamp to 50ms maximum for <50ms total latency\n this.predictionTimeAhead = Math.min(predictionTime, 0.05);\n }\n }\n\n /**\n * Get motion prediction state\n *\n * **Korean**: 동작 예측 상태 가져오기\n *\n * @returns Current motion prediction state\n * @korean 동작예측상태가져오기\n */\n getMotionPredictionState(): MotionPredictionState {\n return this.motionPrediction;\n }\n\n /**\n * Check if motion prediction is enabled\n *\n * **Korean**: 동작 예측 활성화 확인\n *\n * @returns True if motion prediction is enabled\n * @korean 동작예측활성화확인\n */\n isMotionPredictionEnabled(): boolean {\n return this.enableMotionPrediction;\n }\n\n /**\n * Set preferred easing function for transitions\n *\n * **Korean**: 선호 이징 함수 설정\n *\n * Sets the default easing curve for animation transitions.\n * Can use presets like \"natural-motion\", \"smooth-transition\", etc.\n *\n * @param easingName - Easing function name\n *\n * @example\n * ```typescript\n * // Use natural motion for Korean martial arts\n * machine.setPreferredEasing(\"natural-motion\");\n *\n * // Use explosive power for strike animations\n * machine.setPreferredEasing(\"explosive-power\");\n * ```\n *\n * @korean 선호이징함수설정\n */\n setPreferredEasing(easingName: EasingName): void {\n this.preferredEasing = easingName;\n }\n\n /**\n * Get preferred easing function\n *\n * **Korean**: 선호 이징 함수 가져오기\n *\n * @returns Current preferred easing name\n * @korean 선호이징함수가져오기\n */\n getPreferredEasing(): EasingName {\n return this.preferredEasing;\n }\n\n /**\n * Update motion prediction with skeletal keyframe data\n *\n * **Korean**: 동작 예측 업데이트\n *\n * This should be called from the skeletal animation layer when applying\n * interpolated keyframes to the rig. It updates velocity tracking for\n * motion prediction to reduce perceived latency.\n *\n * Integration point: Call this from your skeletal animation system after\n * computing the current interpolated keyframe (e.g., from getInterpolatedKeyframe).\n *\n * @param currentKeyframe - Current skeletal animation keyframe with bone positions/rotations\n * @param deltaTime - Time elapsed since last update\n *\n * @example\n * ```typescript\n * // In your skeletal animation update loop:\n * const currentKeyframe = getInterpolatedKeyframe(animation, time);\n *\n * // Update motion prediction (for next frame)\n * if (machine.isMotionPredictionEnabled()) {\n * machine.updateMotionPredictionState(currentKeyframe, deltaTime);\n * }\n *\n * // Apply keyframe to rig\n * applyKeyframeToRig(rig, currentKeyframe);\n * ```\n *\n * @korean 동작예측업데이트\n */\n updateMotionPredictionState(\n currentKeyframe: AnimationKeyframe,\n deltaTime: number,\n ): void {\n if (!this.enableMotionPrediction) {\n return;\n }\n\n // Update velocity tracking if we have a previous keyframe\n if (this.previousKeyframe) {\n this.motionPrediction = updateMotionPrediction(\n this.motionPrediction,\n this.previousKeyframe,\n currentKeyframe,\n deltaTime,\n );\n }\n\n // Store current keyframe for next update\n this.previousKeyframe = currentKeyframe;\n }\n\n /**\n * Get predicted future keyframe for latency reduction\n *\n * **Korean**: 예측된 미래 키프레임 가져오기\n *\n * Returns a keyframe predicted ahead by predictionTimeAhead (default: 1 frame).\n * This reduces perceived input latency by showing where the animation will be\n * in the near future rather than where it currently is.\n *\n * Integration point: Use this instead of the current keyframe when applying\n * to the rig if motion prediction is enabled.\n *\n * @param currentKeyframe - Current skeletal animation keyframe\n * @returns Predicted future keyframe, or current if prediction disabled\n *\n * @example\n * ```typescript\n * // In your skeletal animation update loop:\n * let keyframeToApply = currentKeyframe;\n *\n * if (machine.isMotionPredictionEnabled()) {\n * keyframeToApply = machine.getPredictedKeyframe(currentKeyframe);\n * }\n *\n * applyKeyframeToRig(rig, keyframeToApply);\n * ```\n *\n * @korean 예측키프레임가져오기\n */\n getPredictedKeyframe(currentKeyframe: AnimationKeyframe): AnimationKeyframe {\n if (!this.enableMotionPrediction || !this.previousKeyframe) {\n return currentKeyframe;\n }\n\n return predictFutureKeyframe(\n currentKeyframe,\n this.motionPrediction,\n this.predictionTimeAhead,\n );\n }\n\n // ===== Animation Queue Methods (애니메이션 대기열) =====\n\n /**\n * Enable or reconfigure animation queue system\n *\n * **Korean**: 애니메이션 대기열 활성화/재설정\n *\n * The queue is enabled by default. Use this method to reconfigure the\n * queue size or conflict resolution strategy.\n *\n * @param maxSize - Maximum queue size (default: 3)\n * @param conflictStrategy - Conflict resolution strategy (default: \"timestamp\")\n *\n * @example\n * ```typescript\n * // Queue is enabled by default, but you can reconfigure it\n * machine.enableQueue(5, \"requested\");\n * ```\n *\n * @korean 대기열활성화\n */\n enableQueue(\n maxSize: number = 3,\n conflictStrategy: ConflictResolutionStrategy = \"timestamp\",\n ): void {\n this.animationQueue = new AnimationQueue(maxSize, conflictStrategy);\n this.conflictStrategy = conflictStrategy;\n }\n\n /**\n * Disable animation queue system\n *\n * **Korean**: 애니메이션 대기열 비활성화\n *\n * Disables the queue and clears any pending animations.\n *\n * @korean 대기열비활성화\n */\n disableQueue(): void {\n this.animationQueue = null;\n }\n\n /**\n * Check if queue is enabled\n *\n * **Korean**: 대기열 활성화 여부\n *\n * @returns True if queue is enabled\n * @korean 대기열활성화여부\n */\n isQueueEnabled(): boolean {\n return this.animationQueue !== null;\n }\n\n /**\n * Attempt to transition to a new animation state with queue support\n *\n * **Korean**: 대기열 지원 상태 전환\n *\n * Enhanced version of transitionTo() that automatically queues animations\n * when they cannot be executed immediately. Since the queue is enabled by\n * default, this is the recommended method for animation transitions.\n *\n * The queued animation will be automatically processed when the current\n * animation completes, following priority and conflict resolution rules.\n *\n * @param newState - Target animation state\n * @returns Whether transition was successful, queued, or failed\n *\n * @example\n * ```typescript\n * // Queue is enabled by default\n * const result = machine.transitionToQueued(AnimationState.ATTACK);\n * // Returns \"success\" if transitioned immediately\n * // Returns \"queued\" if couldn't interrupt but was queued\n * // Returns \"failed\" if queue is full or disabled\n * ```\n *\n * @korean 대기열상태전환\n */\n transitionToQueued(\n newState: AnimationState,\n ): \"success\" | \"queued\" | \"failed\" {\n // Try normal transition first\n const timestamp = performance.now();\n const priority = this.animations.get(newState)?.priority ?? 0;\n\n const success = this.transitionTo(newState);\n if (success) {\n return \"success\";\n }\n\n // If transition failed and queue is enabled, try to enqueue\n if (this.animationQueue) {\n const request: AnimationRequest = {\n state: newState,\n timestamp,\n priority,\n };\n\n const enqueued = this.animationQueue.enqueue(request);\n return enqueued ? \"queued\" : \"failed\";\n }\n\n return \"failed\";\n }\n\n /**\n * Process next queued animation if available\n *\n * **Korean**: 다음 대기열 애니메이션 처리\n *\n * Should be called automatically when an animation completes.\n * Dequeues and executes the highest priority pending animation.\n *\n * @returns Whether a queued animation was executed\n *\n * @internal\n * @korean 다음대기열처리\n */\n private processNextQueuedAnimation(): boolean {\n if (!this.animationQueue || this.animationQueue.isEmpty()) {\n return false;\n }\n\n const nextRequest = this.animationQueue.dequeue();\n if (!nextRequest) {\n return false;\n }\n\n // Try to execute the queued animation\n const success = this.transitionTo(nextRequest.state);\n\n if (!success) {\n // Log failure so queued animations do not disappear silently\n // Korean: 대기열 애니메이션 전이가 실패했음을 로그로 남깁니다.\n // This helps diagnose cases where transition rules, missing states,\n // or priority conflicts prevent a queued animation from playing.\n console.warn(\n \"[AnimationStateMachine] Failed to transition to queued animation state\",\n {\n requestedState: nextRequest.state,\n currentState: this.currentState,\n },\n );\n }\n\n return success;\n }\n\n /**\n * Get current animation queue state\n *\n * **Korean**: 현재 대기열 상태\n *\n * Returns information about the current queue state for debugging\n * or UI display.\n *\n * @returns Queue state information\n *\n * @korean 현재대기열상태\n */\n getQueueState(): {\n enabled: boolean;\n size: number;\n maxSize: number;\n pending: readonly AnimationRequest[];\n } {\n if (!this.animationQueue) {\n return {\n enabled: false,\n size: 0,\n maxSize: 0,\n pending: [],\n };\n }\n\n return {\n enabled: true,\n size: this.animationQueue.size(),\n maxSize: this.animationQueue.getMaxSize(),\n pending: this.animationQueue.getAll(),\n };\n }\n\n /**\n * Clear all pending queued animations\n *\n * **Korean**: 모든 대기열 초기화\n *\n * Removes all pending animations from the queue.\n *\n * @korean 모든대기열초기화\n */\n clearQueue(): void {\n this.animationQueue?.clear();\n }\n\n /**\n * Set conflict resolution strategy\n *\n * **Korean**: 충돌 해결 전략 설정\n *\n * Changes the strategy used to resolve equal-priority conflicts.\n *\n * @param strategy - Conflict resolution strategy\n *\n * @korean 충돌해결전략설정\n */\n setConflictStrategy(strategy: ConflictResolutionStrategy): void {\n this.conflictStrategy = strategy;\n\n // Keep the animation queue's strategy in sync with the state machine\n if (this.animationQueue) {\n this.animationQueue.setConflictStrategy(strategy);\n }\n }\n\n /**\n * Get current conflict resolution strategy\n *\n * **Korean**: 충돌 해결 전략 가져오기\n *\n * @returns Current conflict resolution strategy\n * @korean 충돌해결전략가져오기\n */\n getConflictStrategy(): ConflictResolutionStrategy {\n return this.conflictStrategy;\n }\n\n /**\n * Dispose of the animation state machine\n *\n * **Korean**: 애니메이션 상태 머신 해제\n *\n * Clears all internal state, queues, and references to prevent memory leaks.\n * Should be called when the state machine is no longer needed (e.g., component unmount).\n *\n * @korean 애니메이션상태머신해제\n */\n dispose(): void {\n // Clear animation queue\n this.animationQueue?.clear();\n this.animationQueue = null;\n\n // Clear stance transition data\n this.currentStanceTransition = null;\n\n // Clear motion prediction state\n this.motionPrediction = createMotionPredictionState();\n this.previousKeyframe = null;\n\n // Reset state to initial values\n this.currentState = AnimationState.IDLE;\n this.previousState = AnimationState.IDLE;\n this.frameIndex = 0;\n this.timeAccumulator = 0;\n this.justStarted = false;\n this.justCompleted = false;\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAoEA,IAAa,4CACX,IAAI,IAAqC;CACvC,CACE,eAAe,MACf;EACE,OAAO,eAAe;EACtB,QAAQ;EACR,KAAK;EACL,MAAM;EACN,eAAe;EACf,UAAU;EACV,UAAU,IAAI;CAChB,CACF;CACA,CACE,eAAe,MACf;EACE,OAAO,eAAe;EACtB,QAAQ;EACR,KAAK;EACL,MAAM;EACN,eAAe;EACf,UAAU;EACV,UAAU,IAAI;CAChB,CACF;CACA,CACE,eAAe,KACf;EACE,OAAO,eAAe;EACtB,QAAQ;EACR,KAAK;EACL,MAAM;EACN,eAAe;EACf,UAAU;EACV,UAAU,IAAI;CAChB,CACF;CACA,CACE,eAAe,eACf;EACE,OAAO,eAAe;EACtB,QAAQ;EACR,KAAK;EACL,MAAM;EACN,eAAe;EACf,UAAU;EACV,UAAU;EACV,QAAQ;CACV,CACF;CACA,CACE,eAAe,oBACf;EACE,OAAO,eAAe;EACtB,QAAQ;EACR,KAAK;EACL,MAAM;EACN,eAAe;EACf,UAAU;EACV,UAAU;CACZ,CACF;CACA,CACE,eAAe,QACf;EACE,OAAO,eAAe;EACtB,QAAQ;EACR,KAAK;EACL,MAAM;EACN,eAAe;EACf,UAAU;EACV,UAAU,IAAI;CAChB,CACF;CAEA,CACE,eAAe,sBACf;EACE,OAAO,eAAe;EACtB,QAAQ;EACR,KAAK;EACL,MAAM;EACN,eAAe;EACf,UAAU;EACV,UAAU;EACV,QAAQ;CACV,CACF;CACA,CACE,eAAe,cACf;EACE,OAAO,eAAe;EACtB,QAAQ;EACR,KAAK;EACL,MAAM;EACN,eAAe;EACf,UAAU;EACV,UAAU;EACV,eAAe;CACjB,CACF;CACA,CACE,eAAe,oBACf;EACE,OAAO,eAAe;EACtB,QAAQ;EACR,KAAK;EACL,MAAM;EACN,eAAe;EACf,UAAU;EACV,UAAU;EACV,uBAAuB;CACzB,CACF;CACA,CACE,eAAe,iBACf;EACE,OAAO,eAAe;EACtB,QAAQ;EACR,KAAK;EACL,MAAM;EACN,eAAe;EACf,UAAU;EACV,UAAU;EACV,QAAQ;CACV,CACF;CACA,CACE,eAAe,QACf;EACE,OAAO,eAAe;EACtB,QAAQ;EACR,KAAK;EACL,MAAM;EACN,eAAe;EACf,UAAA;EACA,UAAU,KAAK;EACf,QAAQ;CACV,CACF;CACA,CACE,eAAe,KACf;EACE,OAAO,eAAe;EACtB,QAAQ;EACR,KAAK;EACL,MAAM;EACN,eAAe;EACf,UAAU;EACV,UAAU,IAAI;CAChB,CACF;CACA,CACE,eAAe,IACf;EACE,OAAO,eAAe;EACtB,QAAQ;EACR,KAAK;EACL,MAAM;EACN,eAAe;EACf,UAAU;EACV,UAAU;CACZ,CACF;CAEA,CACE,eAAe,cACf;EACE,OAAO,eAAe;EACtB,QAAQ;EACR,KAAK;EACL,MAAM;EACN,eAAe;EACf,UAAU;EACV,UAAU;CACZ,CACF;CACA,CACE,eAAe,eACf;EACE,OAAO,eAAe;EACtB,QAAQ;EACR,KAAK;EACL,MAAM;EACN,eAAe;EACf,UAAU;EACV,UAAU;CACZ,CACF;CACA,CACE,eAAe,gBACf;EACE,OAAO,eAAe;EACtB,QAAQ;EACR,KAAK;EACL,MAAM;EACN,eAAe;EACf,UAAU;EACV,UAAU;CACZ,CACF;CACA,CACE,eAAe,iBACf;EACE,OAAO,eAAe;EACtB,QAAQ;EACR,KAAK;EACL,MAAM;EACN,eAAe;EACf,UAAU;EACV,UAAU;CACZ,CACF;CAEA,CACE,eAAe,cACf;EACE,OAAO,eAAe;EACtB,QAAQ;EACR,KAAK;EACL,MAAM;EACN,eAAe;EACf,UAAU;EACV,UAAU,IAAI;CAChB,CACF;CACA,CACE,eAAe,eACf;EACE,OAAO,eAAe;EACtB,QAAQ;EACR,KAAK;EACL,MAAM;EACN,eAAe;EACf,UAAU;EACV,UAAU,IAAI;CAChB,CACF;CACA,CACE,eAAe,kBACf;EACE,OAAO,eAAe;EACtB,QAAQ;EACR,KAAK;EACL,MAAM;EACN,eAAe;EACf,UAAU;EACV,UAAU,IAAI;CAChB,CACF;CACA,CACE,eAAe,mBACf;EACE,OAAO,eAAe;EACtB,QAAQ;EACR,KAAK;EACL,MAAM;EACN,eAAe;EACf,UAAU;EACV,UAAU,IAAI;CAChB,CACF;CAEA,CACE,eAAe,wBACf;EACE,OAAO,eAAe;EACtB,QAAQ;EACR,KAAK;EACL,MAAM;EACN,eAAe;EACf,UAAU;EACV,UAAU;CACZ,CACF;CACA,CACE,eAAe,yBACf;EACE,OAAO,eAAe;EACtB,QAAQ;EACR,KAAK;EACL,MAAM;EACN,eAAe;EACf,UAAU;EACV,UAAU;CACZ,CACF;CACA,CACE,eAAe,eACf;EACE,OAAO,eAAe;EACtB,QAAQ;EACR,KAAK;EACL,MAAM;EACN,eAAe;EACf,UAAU;EACV,UAAU;CACZ,CACF;CACA,CACE,eAAe,oBACf;EACE,OAAO,eAAe;EACtB,QAAQ;EACR,KAAK;EACL,MAAM;EACN,eAAe;EACf,UAAU;EACV,UAAU;CACZ,CACF;CAEA,CACE,eAAe,WACf;EACE,OAAO,eAAe;EACtB,QAAQ;EACR,KAAK;EACL,MAAM;EACN,eAAe;EACf,UAAA;EACA,UAAU,KAAK;CACjB,CACF;CACA,CACE,eAAe,YACf;EACE,OAAO,eAAe;EACtB,QAAQ;EACR,KAAK;EACL,MAAM;EACN,eAAe;EACf,UAAA;EACA,UAAU,KAAK;CACjB,CACF;CAEA,CACE,eAAe,mBACf;EACE,OAAO,eAAe;EACtB,QAAQ;EACR,KAAK;EACL,MAAM;EACN,eAAe;EACf,UAAU;EACV,UAAU,IAAI;CAChB,CACF;CACA,CACE,eAAe,kBACf;EACE,OAAO,eAAe;EACtB,QAAQ;EACR,KAAK;EACL,MAAM;EACN,eAAe;EACf,UAAU;EACV,UAAU,IAAI;CAChB,CACF;CACA,CACE,eAAe,iBACf;EACE,OAAO,eAAe;EACtB,QAAQ;EACR,KAAK;EACL,MAAM;EACN,eAAe;EACf,UAAU;EACV,UAAU,IAAI;CAChB,CACF;CACA,CACE,eAAe,kBACf;EACE,OAAO,eAAe;EACtB,QAAQ;EACR,KAAK;EACL,MAAM;EACN,eAAe;EACf,UAAU;EACV,UAAU,IAAI;CAChB,CACF;CACA,CACE,eAAe,kBACf;EACE,OAAO,eAAe;EACtB,QAAQ;EACR,KAAK;EACL,MAAM;EACN,eAAe;EACf,UAAU;EACV,UAAU,IAAI;CAChB,CACF;CACA,CACE,eAAe,kBACf;EACE,OAAO,eAAe;EACtB,QAAQ;EACR,KAAK;EACL,MAAM;EACN,eAAe;EACf,UAAU;EACV,UAAU,IAAI;CAChB,CACF;CACA,CACE,eAAe,kBACf;EACE,OAAO,eAAe;EACtB,QAAQ;EACR,KAAK;EACL,MAAM;EACN,eAAe;EACf,UAAU;EACV,UAAU,IAAI;CAChB,CACF;CACA,CACE,eAAe,kBACf;EACE,OAAO,eAAe;EACtB,QAAQ;EACR,KAAK;EACL,MAAM;EACN,eAAe;EACf,UAAU;EACV,UAAU,IAAI;CAChB,CACF;CAGA,CACE,eAAe,cACf;EACE,OAAO,eAAe;EACtB,QAAQ;EACR,KAAK;EACL,MAAM;EACN,eAAe;EACf,UAAA;EACA,UAAU;CACZ,CACF;CACA,CACE,eAAe,WACf;EACE,OAAO,eAAe;EACtB,QAAQ;EACR,KAAK;EACL,MAAM;EACN,eAAe;EACf,UAAA;EACA,UAAU;CACZ,CACF;CACA,CACE,eAAe,WACf;EACE,OAAO,eAAe;EACtB,QAAQ;EACR,KAAK;EACL,MAAM;EACN,eAAe;EACf,UAAA;EACA,UAAU;CACZ,CACF;CACA,CACE,eAAe,YACf;EACE,OAAO,eAAe;EACtB,QAAQ;EACR,KAAK;EACL,MAAM;EACN,eAAe;EACf,UAAA;EACA,UAAU;CACZ,CACF;CACA,CACE,eAAe,mBACf;EACE,OAAO,eAAe;EACtB,QAAQ;EACR,KAAK;EACL,MAAM;EACN,eAAe;EACf,UAAA;EACA,UAAU;CACZ,CACF;CACA,CACE,eAAe,oBACf;EACE,OAAO,eAAe;EACtB,QAAQ;EACR,KAAK;EACL,MAAM;EACN,eAAe;EACf,UAAA;EACA,UAAU;CACZ,CACF;CACA,CACE,eAAe,gBACf;EACE,OAAO,eAAe;EACtB,QAAQ;EACR,KAAK;EACL,MAAM;EACN,eAAe;EACf,UAAA;EACA,UAAU;CACZ,CACF;CACA,CACE,eAAe,iBACf;EACE,OAAO,eAAe;EACtB,QAAQ;EACR,KAAK;EACL,MAAM;EACN,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"}
|
|
1
|
+
{"version":3,"file":"AnimationStateMachine.js","names":[],"sources":["../../../../src/systems/animation/core/AnimationStateMachine.ts"],"sourcesContent":["/**\n * Player Animation State Machine for Black Trigram\n *\n * Manages player character animations with frame-accurate timing at 60fps.\n * Supports animation priorities, transitions, and event callbacks.\n *\n * Based on game-design.md specifications:\n * - Attack: 12 frames (200ms at 60fps)\n * - Block: 4 frames (67ms at 60fps)\n * - Walk: 6 frames\n * - Stance change: 600ms\n *\n * @module systems/animation/AnimationStateMachine\n * @category Animation\n * @korean 애니메이션상태머신\n */\n\nimport { TrigramStance } from \"@/types/common\";\nimport type { AnimationKeyframe } from \"@/types/skeletal\";\nimport {\n createMotionPredictionState,\n predictFutureKeyframe,\n updateMotionPrediction,\n type EasingName,\n type MotionPredictionState,\n} from \"../builders/KeyframeInterpolation\";\nimport {\n AnimationQueue,\n canInterrupt,\n type AnimationRequest,\n type ConflictResolutionStrategy,\n} from \"./AnimationPriority\";\nimport {\n getStanceTransition,\n isTransitionAllowed,\n type StanceTransition,\n} from \"./AnimationTransitions\";\nimport type {\n AnimationConfig,\n AnimationEvents,\n AnimationMachineState,\n AnimationPriority,\n AnimationUpdateResult,\n FallType,\n MutableAnimationConfig,\n} from \"./types\";\nimport { AnimationState, FALL_TO_GROUND_MAP, STEP_PRIORITY } from \"./types\";\n\n/**\n * Default animation configurations based on game-design.md\n *\n * Frame timings:\n * - Attack: 12 frames = 200ms at 60fps\n * - Block: 4 frames = 67ms at 60fps\n * - Walk: 6 frames = 100ms at 60fps\n * - Hit: 4 frames = 67ms at 60fps\n * - Stance change: 36 frames = 600ms at 60fps\n * - Stance guards: 4-6 frames = breathing animation at 60fps\n * - Tactical steps: 18 frames = 300ms at 60fps, 30cm distance\n *\n * Defensive animations (방어 애니메이션):\n * - Block Success (막기): 8 frames = 133ms - absorb impact, maintain guard\n * - Parry Deflect (받아넘기기): 10 frames = 167ms - redirect attack, counter window\n * - Guard Break (방어붕괴): 15 frames = 250ms - arms forced wide, vulnerable\n * - Guard Recovery (방어복구): 12 frames = 200ms - restore guard position\n *\n * @korean 기본애니메이션설정\n */\nexport const DEFAULT_ANIMATION_CONFIGS: Map<AnimationState, AnimationConfig> =\n new Map<AnimationState, AnimationConfig>([\n [\n AnimationState.IDLE,\n {\n state: AnimationState.IDLE,\n frames: 4,\n fps: 60,\n loop: true,\n interruptible: true,\n priority: 0 as AnimationPriority,\n duration: 4 / 60,\n },\n ],\n [\n AnimationState.WALK,\n {\n state: AnimationState.WALK,\n frames: 6,\n fps: 60,\n loop: true,\n interruptible: true,\n priority: 1 as AnimationPriority,\n duration: 6 / 60,\n },\n ],\n [\n AnimationState.RUN,\n {\n state: AnimationState.RUN,\n frames: 8,\n fps: 60,\n loop: true,\n interruptible: true,\n priority: 2 as AnimationPriority,\n duration: 8 / 60,\n },\n ],\n [\n AnimationState.STANCE_CHANGE,\n {\n state: AnimationState.STANCE_CHANGE,\n frames: 36, // 600ms at 60fps\n fps: 60,\n loop: false,\n interruptible: false,\n priority: 3 as AnimationPriority,\n duration: 0.6,\n easing: \"smooth-transition\", // Smooth S-curve for stance changes\n },\n ],\n [\n AnimationState.STANCE_SIDE_SWITCH,\n {\n state: AnimationState.STANCE_SIDE_SWITCH,\n frames: 24, // 400ms at 60fps for left↔right switch\n fps: 60,\n loop: false,\n interruptible: false,\n priority: 3 as AnimationPriority,\n duration: 0.4,\n },\n ],\n [\n AnimationState.DEFEND,\n {\n state: AnimationState.DEFEND,\n frames: 4, // 67ms at 60fps\n fps: 60,\n loop: false,\n interruptible: true,\n priority: 4 as AnimationPriority,\n duration: 4 / 60,\n },\n ],\n // Defensive animations (방어 애니메이션) - Enhanced guard break system\n [\n AnimationState.DEFEND_BLOCK_SUCCESS,\n {\n state: AnimationState.DEFEND_BLOCK_SUCCESS,\n frames: 8, // 133ms at 60fps - absorb impact, maintain guard\n fps: 60,\n loop: false,\n interruptible: false, // Must complete block animation\n priority: 6 as AnimationPriority, // Higher than defend, same as hit\n duration: 0.133,\n easing: \"controlled-slow\", // Controlled deceleration for impact absorption\n },\n ],\n [\n AnimationState.DEFEND_PARRY,\n {\n state: AnimationState.DEFEND_PARRY,\n frames: 10, // 167ms at 60fps - redirect attack, open counter opportunity\n fps: 60,\n loop: false,\n interruptible: false, // Must complete parry animation\n priority: 7 as AnimationPriority, // Higher than block, creates counter window\n duration: 0.167,\n counterWindow: 0.2, // 200ms counter-attack opportunity after parry\n },\n ],\n [\n AnimationState.DEFEND_GUARD_BREAK,\n {\n state: AnimationState.DEFEND_GUARD_BREAK,\n frames: 15, // 250ms at 60fps - arms forced wide, vulnerable state\n fps: 60,\n loop: false,\n interruptible: false, // Cannot interrupt guard break\n priority: 8 as AnimationPriority, // Highest priority (same as fall)\n duration: 0.25,\n vulnerabilityDuration: 0.5, // 500ms vulnerable state after guard break\n },\n ],\n [\n AnimationState.DEFEND_RECOVERY,\n {\n state: AnimationState.DEFEND_RECOVERY,\n frames: 12, // 200ms at 60fps - restore guard position\n fps: 60,\n loop: false,\n interruptible: true, // Can be interrupted by attacks\n priority: 2 as AnimationPriority, // Same as run, lower than defend\n duration: 0.2,\n easing: \"natural-motion\", // Physics-based recovery\n },\n ],\n [\n AnimationState.ATTACK,\n {\n state: AnimationState.ATTACK,\n frames: 12, // 200ms at 60fps\n fps: 60,\n loop: false,\n interruptible: true,\n priority: STEP_PRIORITY,\n duration: 12 / 60,\n easing: \"explosive-power\", // Explosive acceleration for attacks\n },\n ],\n [\n AnimationState.HIT,\n {\n state: AnimationState.HIT,\n frames: 4, // 67ms at 60fps\n fps: 60,\n loop: false,\n interruptible: false,\n priority: 6 as AnimationPriority,\n duration: 4 / 60,\n },\n ],\n [\n AnimationState.KO,\n {\n state: AnimationState.KO,\n frames: 30, // 500ms at 60fps\n fps: 60,\n loop: false,\n interruptible: false,\n priority: 7 as AnimationPriority,\n duration: 0.5,\n },\n ],\n // Fall animations (낙법 애니메이션) - Priority 8 (highest)\n [\n AnimationState.FALL_FORWARD,\n {\n state: AnimationState.FALL_FORWARD,\n frames: 24, // 400ms at 60fps - stumble forward, knee collapse, hands brace, face-down\n fps: 60,\n loop: false,\n interruptible: false,\n priority: 8 as AnimationPriority,\n duration: 0.4,\n },\n ],\n [\n AnimationState.FALL_BACKWARD,\n {\n state: AnimationState.FALL_BACKWARD,\n frames: 30, // 500ms at 60fps - backward stumble, sit, back impact, supine\n fps: 60,\n loop: false,\n interruptible: false,\n priority: 8 as AnimationPriority,\n duration: 0.5,\n },\n ],\n [\n AnimationState.FALL_SIDE_LEFT,\n {\n state: AnimationState.FALL_SIDE_LEFT,\n frames: 27, // 450ms at 60fps - rotation, shoulder roll, side sprawl\n fps: 60,\n loop: false,\n interruptible: false,\n priority: 8 as AnimationPriority,\n duration: 0.45,\n },\n ],\n [\n AnimationState.FALL_SIDE_RIGHT,\n {\n state: AnimationState.FALL_SIDE_RIGHT,\n frames: 27, // 450ms at 60fps - rotation, shoulder roll, side sprawl\n fps: 60,\n loop: false,\n interruptible: false,\n priority: 8 as AnimationPriority,\n duration: 0.45,\n },\n ],\n // Ground state animations (지면 자세) - Breathing loops\n [\n AnimationState.GROUND_PRONE,\n {\n state: AnimationState.GROUND_PRONE,\n frames: 4, // Breathing loop on ground (face down)\n fps: 60,\n loop: true,\n interruptible: true,\n priority: 0 as AnimationPriority,\n duration: 4 / 60,\n },\n ],\n [\n AnimationState.GROUND_SUPINE,\n {\n state: AnimationState.GROUND_SUPINE,\n frames: 4, // Breathing loop on ground (face up)\n fps: 60,\n loop: true,\n interruptible: true,\n priority: 0 as AnimationPriority,\n duration: 4 / 60,\n },\n ],\n [\n AnimationState.GROUND_SIDE_LEFT,\n {\n state: AnimationState.GROUND_SIDE_LEFT,\n frames: 4, // Breathing loop on ground (left side)\n fps: 60,\n loop: true,\n interruptible: true,\n priority: 0 as AnimationPriority,\n duration: 4 / 60,\n },\n ],\n [\n AnimationState.GROUND_SIDE_RIGHT,\n {\n state: AnimationState.GROUND_SIDE_RIGHT,\n frames: 4, // Breathing loop on ground (right side)\n fps: 60,\n loop: true,\n interruptible: true,\n priority: 0 as AnimationPriority,\n duration: 4 / 60,\n },\n ],\n // Recovery animations (기상 애니메이션) - Priority 9 (higher than falls)\n [\n AnimationState.RECOVERY_PRONE_STANDUP,\n {\n state: AnimationState.RECOVERY_PRONE_STANDUP,\n frames: 30, // 500ms at 60fps - push up from prone, rise to standing\n fps: 60,\n loop: false,\n interruptible: false, // Last 6 frames (100ms) are interruptible\n priority: 9 as AnimationPriority,\n duration: 0.5,\n },\n ],\n [\n AnimationState.RECOVERY_SUPINE_STANDUP,\n {\n state: AnimationState.RECOVERY_SUPINE_STANDUP,\n frames: 36, // 600ms at 60fps - sit up, roll forward, stand\n fps: 60,\n loop: false,\n interruptible: false, // Last 6 frames (100ms) are interruptible\n priority: 9 as AnimationPriority,\n duration: 0.6,\n },\n ],\n [\n AnimationState.RECOVERY_ROLL,\n {\n state: AnimationState.RECOVERY_ROLL,\n frames: 24, // 400ms at 60fps - roll to side, spring to feet (quick recovery)\n fps: 60,\n loop: false,\n interruptible: false, // Last 6 frames (100ms) are interruptible\n priority: 9 as AnimationPriority,\n duration: 0.4,\n },\n ],\n [\n AnimationState.RECOVERY_DEFENSIVE,\n {\n state: AnimationState.RECOVERY_DEFENSIVE,\n frames: 42, // 700ms at 60fps - slow rise with guard up (vulnerable but defended)\n fps: 60,\n loop: false,\n interruptible: false, // Last 6 frames (100ms) are interruptible\n priority: 9 as AnimationPriority,\n duration: 0.7,\n },\n ],\n // 180-degree turn animations (180도 회전 애니메이션)\n [\n AnimationState.TURN_LEFT,\n {\n state: AnimationState.TURN_LEFT,\n frames: 12, // 200ms at 60fps for 180° turn\n fps: 60,\n loop: false,\n interruptible: false,\n priority: STEP_PRIORITY, // Same priority as attacks/steps - committed action\n duration: 12 / 60,\n },\n ],\n [\n AnimationState.TURN_RIGHT,\n {\n state: AnimationState.TURN_RIGHT,\n frames: 12, // 200ms at 60fps for 180° turn\n fps: 60,\n loop: false,\n interruptible: false,\n priority: STEP_PRIORITY, // Same priority as attacks/steps - committed action\n duration: 12 / 60,\n },\n ],\n // Stance-specific guard animations (팔괘 방어 자세)\n [\n AnimationState.STANCE_GUARD_GEON,\n {\n state: AnimationState.STANCE_GUARD_GEON,\n frames: 6, // Breathing animation\n fps: 60,\n loop: true,\n interruptible: true,\n priority: 0 as AnimationPriority,\n duration: 6 / 60,\n },\n ],\n [\n AnimationState.STANCE_GUARD_TAE,\n {\n state: AnimationState.STANCE_GUARD_TAE,\n frames: 6, // Breathing animation\n fps: 60,\n loop: true,\n interruptible: true,\n priority: 0 as AnimationPriority,\n duration: 6 / 60,\n },\n ],\n [\n AnimationState.STANCE_GUARD_LI,\n {\n state: AnimationState.STANCE_GUARD_LI,\n frames: 4, // Controlled breathing\n fps: 60,\n loop: true,\n interruptible: true,\n priority: 0 as AnimationPriority,\n duration: 4 / 60,\n },\n ],\n [\n AnimationState.STANCE_GUARD_JIN,\n {\n state: AnimationState.STANCE_GUARD_JIN,\n frames: 5, // Deep breathing\n fps: 60,\n loop: true,\n interruptible: true,\n priority: 0 as AnimationPriority,\n duration: 5 / 60,\n },\n ],\n [\n AnimationState.STANCE_GUARD_SON,\n {\n state: AnimationState.STANCE_GUARD_SON,\n frames: 6, // Rhythmic breathing\n fps: 60,\n loop: true,\n interruptible: true,\n priority: 0 as AnimationPriority,\n duration: 6 / 60,\n },\n ],\n [\n AnimationState.STANCE_GUARD_GAM,\n {\n state: AnimationState.STANCE_GUARD_GAM,\n frames: 6, // Flowing breathing\n fps: 60,\n loop: true,\n interruptible: true,\n priority: 0 as AnimationPriority,\n duration: 6 / 60,\n },\n ],\n [\n AnimationState.STANCE_GUARD_GAN,\n {\n state: AnimationState.STANCE_GUARD_GAN,\n frames: 4, // Steady breathing\n fps: 60,\n loop: true,\n interruptible: true,\n priority: 0 as AnimationPriority,\n duration: 4 / 60,\n },\n ],\n [\n AnimationState.STANCE_GUARD_GON,\n {\n state: AnimationState.STANCE_GUARD_GON,\n frames: 5, // Deep diaphragm breathing\n fps: 60,\n loop: true,\n interruptible: true,\n priority: 0 as AnimationPriority,\n duration: 5 / 60,\n },\n ],\n // Tactical step animations (전술적 발걸음)\n // 18 frames = 300ms at 60fps, 30cm distance per step\n [\n AnimationState.STEP_FORWARD,\n {\n state: AnimationState.STEP_FORWARD,\n frames: 18,\n fps: 60,\n loop: false,\n interruptible: false, // Non-interruptible for commitment\n priority: STEP_PRIORITY,\n duration: 0.3,\n },\n ],\n [\n AnimationState.STEP_BACK,\n {\n state: AnimationState.STEP_BACK,\n frames: 18,\n fps: 60,\n loop: false,\n interruptible: false,\n priority: STEP_PRIORITY,\n duration: 0.3,\n },\n ],\n [\n AnimationState.STEP_LEFT,\n {\n state: AnimationState.STEP_LEFT,\n frames: 18,\n fps: 60,\n loop: false,\n interruptible: false,\n priority: STEP_PRIORITY,\n duration: 0.3,\n },\n ],\n [\n AnimationState.STEP_RIGHT,\n {\n state: AnimationState.STEP_RIGHT,\n frames: 18,\n fps: 60,\n loop: false,\n interruptible: false,\n priority: STEP_PRIORITY,\n duration: 0.3,\n },\n ],\n [\n AnimationState.STEP_FORWARD_LEFT,\n {\n state: AnimationState.STEP_FORWARD_LEFT,\n frames: 18,\n fps: 60,\n loop: false,\n interruptible: false,\n priority: STEP_PRIORITY,\n duration: 0.3,\n },\n ],\n [\n AnimationState.STEP_FORWARD_RIGHT,\n {\n state: AnimationState.STEP_FORWARD_RIGHT,\n frames: 18,\n fps: 60,\n loop: false,\n interruptible: false,\n priority: STEP_PRIORITY,\n duration: 0.3,\n },\n ],\n [\n AnimationState.STEP_BACK_LEFT,\n {\n state: AnimationState.STEP_BACK_LEFT,\n frames: 18,\n fps: 60,\n loop: false,\n interruptible: false,\n priority: STEP_PRIORITY,\n duration: 0.3,\n },\n ],\n [\n AnimationState.STEP_BACK_RIGHT,\n {\n state: AnimationState.STEP_BACK_RIGHT,\n frames: 18,\n fps: 60,\n loop: false,\n interruptible: false,\n priority: STEP_PRIORITY,\n duration: 0.3,\n },\n ],\n // Footwork patterns (보법) - Korean martial arts specialized footwork\n // Circular step (원형보) - Lateral movement maintaining guard facing\n [\n AnimationState.FOOTWORK_CIRCULAR_LEFT,\n {\n state: AnimationState.FOOTWORK_CIRCULAR_LEFT,\n frames: 18, // 300ms at 60fps\n fps: 60,\n loop: false,\n interruptible: false, // Committed footwork\n priority: STEP_PRIORITY, // Same as tactical steps\n duration: 0.3,\n },\n ],\n [\n AnimationState.FOOTWORK_CIRCULAR_RIGHT,\n {\n state: AnimationState.FOOTWORK_CIRCULAR_RIGHT,\n frames: 18, // 300ms at 60fps\n fps: 60,\n loop: false,\n interruptible: false,\n priority: STEP_PRIORITY,\n duration: 0.3,\n },\n ],\n // Pivot step (축족회전) - Rotation on planted foot\n [\n AnimationState.FOOTWORK_PIVOT_LEFT,\n {\n state: AnimationState.FOOTWORK_PIVOT_LEFT,\n frames: 15, // 250ms at 60fps\n fps: 60,\n loop: false,\n interruptible: false,\n priority: STEP_PRIORITY,\n duration: 0.25,\n },\n ],\n [\n AnimationState.FOOTWORK_PIVOT_RIGHT,\n {\n state: AnimationState.FOOTWORK_PIVOT_RIGHT,\n frames: 15, // 250ms at 60fps\n fps: 60,\n loop: false,\n interruptible: false,\n priority: STEP_PRIORITY,\n duration: 0.25,\n },\n ],\n // Slide step (미끄럼보) - Both feet move together\n [\n AnimationState.FOOTWORK_SLIDE_FORWARD,\n {\n state: AnimationState.FOOTWORK_SLIDE_FORWARD,\n frames: 12, // 200ms at 60fps\n fps: 60,\n loop: false,\n interruptible: true, // Can be interrupted\n priority: 4 as AnimationPriority, // Same as defend\n duration: 0.2,\n },\n ],\n [\n AnimationState.FOOTWORK_SLIDE_BACK,\n {\n state: AnimationState.FOOTWORK_SLIDE_BACK,\n frames: 12, // 200ms at 60fps\n fps: 60,\n loop: false,\n interruptible: true,\n priority: 4 as AnimationPriority,\n duration: 0.2,\n },\n ],\n [\n AnimationState.FOOTWORK_SLIDE_LEFT,\n {\n state: AnimationState.FOOTWORK_SLIDE_LEFT,\n frames: 12, // 200ms at 60fps\n fps: 60,\n loop: false,\n interruptible: true,\n priority: 4 as AnimationPriority,\n duration: 0.2,\n },\n ],\n [\n AnimationState.FOOTWORK_SLIDE_RIGHT,\n {\n state: AnimationState.FOOTWORK_SLIDE_RIGHT,\n frames: 12, // 200ms at 60fps\n fps: 60,\n loop: false,\n interruptible: true,\n priority: 4 as AnimationPriority,\n duration: 0.2,\n },\n ],\n // Shuffle step (섞음보) - Quick micro-adjustment\n [\n AnimationState.FOOTWORK_SHUFFLE,\n {\n state: AnimationState.FOOTWORK_SHUFFLE,\n frames: 6, // 100ms at 60fps\n fps: 60,\n loop: false,\n interruptible: true,\n priority: 3 as AnimationPriority, // Same as stance_change\n duration: 0.1,\n },\n ],\n ]);\n\n/**\n * Player Animation State Machine\n *\n * Manages animation state, transitions, and timing with frame-accurate updates.\n * Integrates priority system, automatic animation queueing, and event callbacks.\n *\n * **Animation Queue**: Enabled by default with max size 3 and timestamp-based\n * conflict resolution. Automatically queues animations that cannot execute\n * immediately and processes them when the current animation completes.\n *\n * @example\n * ```typescript\n * const machine = new PlayerAnimationStateMachine(DEFAULT_ANIMATION_CONFIGS, {\n * onAnimationStart: (state) => console.log(`Started ${state}`),\n * onAnimationComplete: (state) => console.log(`Completed ${state}`),\n * onFrame: (frame, state) => {\n * if (state === \"attack\" && frame === 6) {\n * // Execute attack at midpoint (frame 6 of 12)\n * executeAttackLogic();\n * }\n * }\n * });\n *\n * // Animation queue is enabled by default\n * // Use transitionToQueued() for automatic queueing\n * machine.transitionToQueued(AnimationState.ATTACK); // Queues if can't execute\n *\n * // Or use regular transitionTo() (no queueing)\n * machine.transitionTo(AnimationState.ATTACK); // Fails if can't execute\n *\n * // In game loop (useFrame)\n * useFrame((state, delta) => {\n * const result = machine.update(delta);\n * updatePlayerVisuals(result.state, result.frame);\n * });\n * ```\n *\n * @korean 플레이어애니메이션상태머신\n */\nexport class PlayerAnimationStateMachine {\n /**\n * Static mapping from TrigramStance to guard AnimationState\n * Prevents repeated object allocation in transitionToStanceGuard()\n * @korean 자세방어상태맵\n */\n private static readonly GUARD_STATE_MAP: Record<\n TrigramStance,\n AnimationState\n > = {\n [TrigramStance.GEON]: AnimationState.STANCE_GUARD_GEON,\n [TrigramStance.TAE]: AnimationState.STANCE_GUARD_TAE,\n [TrigramStance.LI]: AnimationState.STANCE_GUARD_LI,\n [TrigramStance.JIN]: AnimationState.STANCE_GUARD_JIN,\n [TrigramStance.SON]: AnimationState.STANCE_GUARD_SON,\n [TrigramStance.GAM]: AnimationState.STANCE_GUARD_GAM,\n [TrigramStance.GAN]: AnimationState.STANCE_GUARD_GAN,\n [TrigramStance.GON]: AnimationState.STANCE_GUARD_GON,\n };\n\n /**\n * Static reverse mapping from guard AnimationState to TrigramStance\n * Prevents repeated object allocation in getCurrentGuardStance()\n * @korean 방어상태자세맵\n */\n private static readonly STANCE_FROM_GUARD_MAP: Record<string, TrigramStance> =\n {\n [AnimationState.STANCE_GUARD_GEON]: TrigramStance.GEON,\n [AnimationState.STANCE_GUARD_TAE]: TrigramStance.TAE,\n [AnimationState.STANCE_GUARD_LI]: TrigramStance.LI,\n [AnimationState.STANCE_GUARD_JIN]: TrigramStance.JIN,\n [AnimationState.STANCE_GUARD_SON]: TrigramStance.SON,\n [AnimationState.STANCE_GUARD_GAM]: TrigramStance.GAM,\n [AnimationState.STANCE_GUARD_GAN]: TrigramStance.GAN,\n [AnimationState.STANCE_GUARD_GON]: TrigramStance.GON,\n };\n\n private currentState: AnimationState = AnimationState.IDLE;\n private frameIndex = 0;\n private timeAccumulator = 0;\n private previousState: AnimationState | null = null;\n private justStarted = false;\n private justCompleted = false;\n\n /**\n * Current stance transition data (null when not in stance_change animation)\n *\n * **Korean**: 현재 자세 전환 데이터\n *\n * Tracks the active stance transition for use during stance_change animation.\n * Provides access to keyframes and blend weights for smooth interpolation.\n *\n * @korean 현재자세전환데이터\n */\n private currentStanceTransition: StanceTransition | null = null;\n\n /**\n * Motion prediction state for latency reduction\n *\n * **Korean**: 동작 예측 상태\n *\n * Tracks animation velocities for motion prediction to reduce perceived latency.\n * Updated each frame with velocity calculations for smooth anticipation.\n *\n * @korean 동작예측상태\n */\n private motionPrediction: MotionPredictionState =\n createMotionPredictionState();\n\n /**\n * Enable motion prediction for latency reduction\n *\n * **Korean**: 동작 예측 활성화\n *\n * When enabled, predicts future animation frames based on current velocity\n * to reduce perceived input latency by 16-33ms (1-2 frames at 60fps).\n *\n * @korean 동작예측활성화\n */\n private enableMotionPrediction: boolean = false;\n\n /**\n * Motion prediction time ahead (seconds)\n *\n * **Korean**: 예측 시간\n *\n * How far ahead to predict motion (default: 1 frame = 16.67ms at 60fps).\n * Typical range: 0.016-0.033 seconds for <50ms total latency.\n *\n * @korean 예측시간\n */\n private predictionTimeAhead: number = 0.01667; // 1 frame at 60fps\n\n /**\n * Previous keyframe for motion prediction velocity calculation\n *\n * **Korean**: 이전 키프레임\n *\n * @korean 이전키프레임\n */\n private previousKeyframe: AnimationKeyframe | null = null;\n\n /**\n * Preferred easing function for smooth transitions\n *\n * **Korean**: 선호 이징 함수\n *\n * Default easing curve for animation blending and transitions.\n * Can be overridden per animation or transition.\n *\n * @korean 선호이징함수\n */\n private preferredEasing: EasingName = \"natural-motion\";\n\n /**\n * Animation queue for pending animations\n *\n * **Korean**: 애니메이션 대기열\n *\n * Stores animation requests that couldn't be executed immediately\n * due to non-interruptible animations or priority conflicts.\n * Processed automatically when current animation completes.\n *\n * Enabled by default with max size 3 and timestamp-based conflict resolution.\n * Can be disabled with disableQueue() or reconfigured with enableQueue().\n *\n * @korean 애니메이션대기열\n */\n private animationQueue: AnimationQueue | null = new AnimationQueue(\n 3,\n \"timestamp\",\n );\n\n /**\n * Conflict resolution strategy for equal-priority animations\n *\n * **Korean**: 충돌 해결 전략\n *\n * Determines how to resolve conflicts when multiple animations\n * have equal priority. Default: timestamp (FIFO).\n *\n * @korean 충돌해결전략\n */\n private conflictStrategy: ConflictResolutionStrategy = \"timestamp\";\n\n /**\n * Create a new animation state machine\n *\n * Clones the provided config map so per-instance mutations\n * (e.g. dynamic attack duration) don't affect shared defaults.\n *\n * @param animations - Map of animation configurations (cloned internally)\n * @param events - Optional event callbacks\n *\n * @korean 생성자\n */\n private readonly animations: Map<AnimationState, MutableAnimationConfig>;\n\n constructor(\n animations: Map<AnimationState, AnimationConfig>,\n private readonly events?: AnimationEvents,\n ) {\n // Clone so we can safely mutate per-instance (e.g. attack duration)\n this.animations = new Map(\n Array.from(animations.entries()).map(([k, v]) => [k, { ...v }]),\n );\n }\n\n /**\n * Update animation state with delta time\n *\n * Call this in useFrame for 60fps updates.\n * Handles frame progression, looping, and completion.\n *\n * @param deltaTime - Time elapsed since last update (in seconds)\n * @returns Animation update result with current state and frame\n *\n * @korean 업데이트\n */\n update(deltaTime: number): AnimationUpdateResult {\n const currentAnim = this.animations.get(this.currentState);\n if (!currentAnim) {\n return {\n state: this.currentState,\n frame: 0,\n progress: 0,\n justCompleted: false,\n justStarted: false,\n };\n }\n\n // Reset just started/completed flags\n const wasJustStarted = this.justStarted;\n this.justStarted = false;\n const previousJustCompleted = this.justCompleted;\n this.justCompleted = false;\n\n // Accumulate time\n this.timeAccumulator += deltaTime;\n const frameDuration = 1 / currentAnim.fps;\n\n // Check if we should advance to next frame\n if (this.timeAccumulator >= frameDuration) {\n const previousFrame = this.frameIndex;\n this.frameIndex++;\n this.timeAccumulator -= frameDuration;\n\n // Emit frame event\n if (this.events?.onFrame && previousFrame !== this.frameIndex) {\n this.events.onFrame(this.frameIndex, this.currentState);\n }\n\n // Handle animation completion\n if (this.frameIndex >= currentAnim.frames) {\n if (currentAnim.loop) {\n // Loop back to start\n this.frameIndex = 0;\n } else {\n // Animation completed\n this.justCompleted = true;\n if (this.events?.onAnimationComplete) {\n this.events.onAnimationComplete(this.currentState);\n }\n\n // Auto-transition logic\n // Fall animations transition to ground states using the mapping\n if (this.currentState.startsWith(\"fall_\")) {\n const fallType = this.currentState.replace(\"fall_\", \"\");\n\n // Validate that fallType is a valid FallType before using in map\n if (\n fallType === \"forward\" ||\n fallType === \"backward\" ||\n fallType === \"side_left\" ||\n fallType === \"side_right\"\n ) {\n const groundState = FALL_TO_GROUND_MAP[fallType as FallType];\n const groundAnimKey = `ground_${groundState}`;\n\n // Validate that the constructed ground animation state actually exists\n if (\n DEFAULT_ANIMATION_CONFIGS.has(groundAnimKey as AnimationState)\n ) {\n const groundAnimState = groundAnimKey as AnimationState;\n\n this.previousState = this.currentState;\n this.currentState = groundAnimState;\n this.frameIndex = 0;\n this.timeAccumulator = 0;\n this.justStarted = true;\n\n if (this.events?.onAnimationStart) {\n this.events.onAnimationStart(groundAnimState);\n }\n } else {\n // Fallback: if mapping is invalid, safely transition to idle\n // instead of entering an undefined animation state.\n console.warn(\n \"[AnimationStateMachine] Invalid ground animation mapping for fall type:\",\n fallType,\n \"->\",\n groundAnimKey,\n );\n this.previousState = this.currentState;\n this.currentState = AnimationState.IDLE;\n this.frameIndex = 0;\n this.timeAccumulator = 0;\n this.justStarted = true;\n\n if (this.events?.onAnimationStart) {\n this.events.onAnimationStart(AnimationState.IDLE);\n }\n }\n } else {\n // Invalid fall type - fallback to idle\n console.warn(\n \"[AnimationStateMachine] Invalid fall animation state:\",\n this.currentState,\n );\n this.previousState = this.currentState;\n this.currentState = AnimationState.IDLE;\n this.frameIndex = 0;\n this.timeAccumulator = 0;\n this.justStarted = true;\n\n if (this.events?.onAnimationStart) {\n this.events.onAnimationStart(AnimationState.IDLE);\n }\n }\n }\n // Recovery animations transition to idle when complete\n else if (this.currentState.startsWith(\"recovery_\")) {\n this.previousState = this.currentState;\n this.currentState = AnimationState.IDLE;\n this.frameIndex = 0;\n this.timeAccumulator = 0;\n this.justStarted = true;\n\n if (this.events?.onAnimationStart) {\n this.events.onAnimationStart(AnimationState.IDLE);\n }\n }\n // Non-fall, non-recovery, non-looping animations transition to idle\n else if (\n this.currentState !== AnimationState.IDLE &&\n this.currentState !== AnimationState.KO &&\n !this.currentState.startsWith(\"ground_\")\n ) {\n // Clear stance transition data if completing stance_change\n if (this.currentState === AnimationState.STANCE_CHANGE) {\n this.clearStanceTransition();\n }\n\n // Transition to idle first\n this.previousState = this.currentState;\n this.currentState = AnimationState.IDLE;\n this.frameIndex = 0;\n this.timeAccumulator = 0;\n this.justStarted = true;\n\n if (this.events?.onAnimationStart) {\n this.events.onAnimationStart(AnimationState.IDLE);\n }\n\n // Then try to process next queued animation (from idle state)\n this.processNextQueuedAnimation();\n } else {\n // Stay on last frame (for ko and ground states)\n this.frameIndex = currentAnim.frames - 1;\n }\n }\n }\n }\n\n const progress =\n currentAnim.frames > 0 ? this.frameIndex / currentAnim.frames : 0;\n\n return {\n state: this.currentState,\n frame: this.frameIndex,\n progress,\n justCompleted: previousJustCompleted,\n justStarted: wasJustStarted,\n };\n }\n\n /**\n * Attempt to transition to a new animation state\n *\n * Checks transition rules and priority system before transitioning.\n *\n * @param newState - Target animation state\n * @returns Whether transition was successful\n *\n * @example\n * ```typescript\n * // Successful transitions\n * machine.transitionTo(\"walk\"); // idle -> walk\n * machine.transitionTo(\"attack\"); // walk -> attack\n *\n * // Failed transition (invalid or lower priority)\n * machine.transitionTo(\"walk\"); // attack -> walk (blocked, must complete first)\n * ```\n *\n * @korean 상태전환\n */\n transitionTo(newState: AnimationState): boolean {\n // Don't transition to same state\n if (this.currentState === newState) {\n return false;\n }\n\n // Check if transition is allowed by rules\n if (!isTransitionAllowed(this.currentState, newState)) {\n return false;\n }\n\n const currentAnim = this.animations.get(this.currentState);\n const newAnim = this.animations.get(newState);\n\n if (!newAnim) {\n return false;\n }\n\n // Check priority system\n if (\n currentAnim &&\n !canInterrupt(this.currentState, newState, currentAnim.interruptible)\n ) {\n return false;\n }\n\n // Emit interrupt event if current animation wasn't completed\n if (this.frameIndex < (currentAnim?.frames ?? 0) - 1) {\n if (this.events?.onAnimationInterrupted) {\n this.events.onAnimationInterrupted(this.currentState, newState);\n }\n }\n\n // Clear stance transition data if interrupting stance_change\n if (this.currentState === AnimationState.STANCE_CHANGE) {\n this.clearStanceTransition();\n }\n\n // Execute transition\n this.previousState = this.currentState;\n this.currentState = newState;\n this.frameIndex = 0;\n this.timeAccumulator = 0;\n this.justStarted = true;\n this.justCompleted = false;\n\n // Emit start event\n if (this.events?.onAnimationStart) {\n this.events.onAnimationStart(newState);\n }\n\n return true;\n }\n\n /**\n * Transition to ATTACK state with a technique-specific duration.\n *\n * The default ATTACK config is 200ms (12 frames), but real techniques\n * range from 350ms to 1200ms. This method overrides the ATTACK frame\n * count to match the actual skeletal animation duration so the state\n * machine stays in ATTACK for the full technique.\n *\n * @param durationSeconds - The skeletal animation duration in seconds\n * @returns Whether transition was successful\n *\n * @example\n * ```typescript\n * // Jab animation is 0.55s (TECHNIQUE_TIMING.FAST)\n * machine.transitionToAttack(0.55);\n * ```\n *\n * @korean 공격전환 (기술별 지속시간)\n */\n transitionToAttack(durationSeconds: number): boolean {\n const attackConfig = this.animations.get(AnimationState.ATTACK);\n if (attackConfig) {\n const fps = attackConfig.fps || 60;\n attackConfig.frames = Math.max(1, Math.round(durationSeconds * fps));\n attackConfig.duration = durationSeconds;\n }\n return this.transitionTo(AnimationState.ATTACK);\n }\n\n /**\n * Get current animation state\n *\n * @returns Current animation state\n * @korean 현재상태가져오기\n */\n getCurrentState(): AnimationState {\n return this.currentState;\n }\n\n /**\n * Get current frame index\n *\n * @returns Current frame index (0 to frames-1)\n * @korean 현재프레임가져오기\n */\n getCurrentFrame(): number {\n return this.frameIndex;\n }\n\n /**\n * Get previous animation state\n *\n * @returns Previous animation state or null\n * @korean 이전상태가져오기\n */\n getPreviousState(): AnimationState | null {\n return this.previousState;\n }\n\n /**\n * Get current animation configuration\n *\n * @returns Current animation config or undefined\n * @korean 현재애니메이션설정가져오기\n */\n getCurrentAnimation(): AnimationConfig | undefined {\n return this.animations.get(this.currentState);\n }\n\n /**\n * Reset animation state machine to idle\n *\n * @korean 초기화\n */\n reset(): void {\n this.currentState = AnimationState.IDLE;\n this.frameIndex = 0;\n this.timeAccumulator = 0;\n this.previousState = null;\n this.justStarted = false;\n this.justCompleted = false;\n }\n\n /**\n * Get full internal state (for debugging/testing)\n *\n * @returns Current state machine state\n * @korean 상태가져오기\n */\n getState(): AnimationMachineState {\n return {\n currentState: this.currentState,\n frameIndex: this.frameIndex,\n timeAccumulator: this.timeAccumulator,\n isPlaying: true,\n previousState: this.previousState,\n };\n }\n\n /**\n * Transition to stance-specific guard animation\n *\n * Convenience method to transition to a stance guard based on trigram stance.\n * Automatically maps trigram stance to corresponding guard animation state.\n *\n * @param stance - Trigram stance identifier\n * @returns Whether transition was successful\n *\n * @example\n * ```typescript\n * // When player changes to Fire stance\n * machine.transitionToStanceGuard(TrigramStance.LI);\n * // Internally transitions to \"stance_guard_li\" animation state\n * ```\n *\n * @korean 자세방어전환\n */\n transitionToStanceGuard(stance: TrigramStance): boolean {\n const guardAnimationState =\n PlayerAnimationStateMachine.GUARD_STATE_MAP[stance];\n\n // Verify the guard animation exists in our configs\n if (!guardAnimationState || !this.animations.has(guardAnimationState)) {\n console.warn(`No guard animation configured for stance: ${stance}`);\n return false;\n }\n\n return this.transitionTo(guardAnimationState);\n }\n\n /**\n * Check if current animation is a stance guard\n *\n * @returns True if currently in a stance guard animation\n * @korean 자세방어상태확인\n */\n isInStanceGuard(): boolean {\n return this.currentState.startsWith(\"stance_guard_\");\n }\n\n /**\n * Get current guard stance if in a guard animation\n *\n * @returns Trigram stance or null if not in guard\n * @korean 현재방어자세가져오기\n */\n getCurrentGuardStance(): TrigramStance | null {\n if (!this.isInStanceGuard()) {\n return null;\n }\n\n const stance =\n PlayerAnimationStateMachine.STANCE_FROM_GUARD_MAP[this.currentState];\n\n // Validate that we got a valid stance\n if (!stance) {\n console.warn(`Invalid guard state detected: ${this.currentState}`);\n return null;\n }\n\n return stance;\n }\n\n /**\n * Transition to stance_change animation with specific stance transition data\n *\n * **Korean**: 자세 전환 애니메이션 시작\n *\n * Initiates a stance change animation with the specific transition data\n * from the 64-transition matrix. This provides stance-specific keyframes\n * and blend weights for smooth interpolation.\n *\n * @param fromStance - Source trigram stance\n * @param toStance - Target trigram stance\n * @returns Whether transition was successful\n *\n * @example\n * ```typescript\n * // Start transition from Heaven to Lake stance\n * const success = machine.transitionToStanceChange(\n * TrigramStance.GEON,\n * TrigramStance.TAE\n * );\n *\n * if (success) {\n * // During update loop, use getStanceTransitionBlend() to interpolate\n * const blend = machine.getStanceTransitionBlend();\n * if (blend) {\n * // Apply blend weights to stance poses\n * applyStanceBlend(blend);\n * }\n * }\n * ```\n *\n * @korean 자세전환애니메이션시작\n */\n transitionToStanceChange(\n fromStance: TrigramStance,\n toStance: TrigramStance,\n ): boolean {\n // Get the specific transition data from the 64-transition matrix\n const transitionData = getStanceTransition(fromStance, toStance);\n\n if (!transitionData) {\n console.warn(\n `[AnimationStateMachine] No transition data found for ${fromStance} -> ${toStance}`,\n );\n return false;\n }\n\n // Store current transition data in case we need to restore it\n const previousTransitionData = this.currentStanceTransition;\n\n // Temporarily set the new transition data\n this.currentStanceTransition = transitionData;\n\n // Initiate the stance_change animation\n const success = this.transitionTo(AnimationState.STANCE_CHANGE);\n\n // If transition failed, restore previous transition data\n if (!success) {\n this.currentStanceTransition = previousTransitionData;\n }\n\n return success;\n }\n\n /**\n * Get current stance transition data\n *\n * **Korean**: 현재 자세 전환 데이터 가져오기\n *\n * Returns the active stance transition data during stance_change animation.\n * Null if not currently in a stance transition.\n *\n * @returns Current stance transition or null\n *\n * @korean 현재자세전환데이터가져오기\n */\n getCurrentStanceTransition(): StanceTransition | null {\n return this.currentStanceTransition;\n }\n\n /**\n * Get interpolated blend weights for current stance transition frame\n *\n * **Korean**: 현재 프레임 블렌드 가중치\n *\n * Returns the interpolated blend data for the current frame during\n * stance_change animation. Uses the keyframe data from the transition\n * matrix to provide smooth stance interpolation.\n *\n * @returns Blend data with stance and weight, or null if not in transition\n *\n * @example\n * ```typescript\n * // In rendering loop during stance transition\n * const blend = machine.getStanceTransitionBlend();\n * if (blend) {\n * console.log(`Frame ${blend.frame}: ${blend.stance} at ${blend.blend}x weight`);\n * // Apply blended pose: blend.blend * targetPose + (1 - blend.blend) * sourcePose\n * }\n * ```\n *\n * @korean 현재프레임블렌드가중치\n */\n getStanceTransitionBlend(): {\n frame: number;\n stance: TrigramStance | \"neutral\";\n blend: number;\n } | null {\n // Only valid during stance_change animation\n if (\n this.currentState !== AnimationState.STANCE_CHANGE ||\n !this.currentStanceTransition\n ) {\n return null;\n }\n\n const keyframes = this.currentStanceTransition.keyframes;\n const currentFrame = this.frameIndex;\n\n // Find the two keyframes to interpolate between\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 (\n keyframes[i].frame <= currentFrame &&\n keyframes[i + 1].frame > currentFrame\n ) {\n prevKeyframe = keyframes[i];\n nextKeyframe = keyframes[i + 1];\n break;\n }\n }\n\n // If we're exactly on a keyframe, return it directly\n const exactKeyframe = keyframes.find((kf) => kf.frame === currentFrame);\n if (exactKeyframe) {\n return {\n frame: currentFrame,\n stance: exactKeyframe.stance,\n blend: exactKeyframe.blend,\n };\n }\n\n // Linear interpolation between keyframes\n const frameRange = nextKeyframe.frame - prevKeyframe.frame;\n const frameProgress =\n frameRange > 0 ? (currentFrame - prevKeyframe.frame) / frameRange : 0;\n\n const interpolatedBlend =\n prevKeyframe.blend +\n (nextKeyframe.blend - prevKeyframe.blend) * frameProgress;\n\n // Use the next keyframe's stance as we're transitioning towards it\n return {\n frame: currentFrame,\n stance: nextKeyframe.stance,\n blend: interpolatedBlend,\n };\n }\n\n /**\n * Check if currently in a stance transition animation\n *\n * **Korean**: 자세 전환 중 확인\n *\n * @returns True if currently executing a stance_change animation\n * @korean 자세전환중확인\n */\n isInStanceTransition(): boolean {\n return (\n this.currentState === AnimationState.STANCE_CHANGE &&\n this.currentStanceTransition !== null\n );\n }\n\n /**\n * Clear stance transition data (called automatically when transition completes)\n *\n * **Korean**: 자세 전환 데이터 초기화\n *\n * @internal\n * @korean 자세전환데이터초기화\n */\n private clearStanceTransition(): void {\n this.currentStanceTransition = null;\n }\n\n /**\n * Enable or disable motion prediction\n *\n * **Korean**: 동작 예측 설정\n *\n * Enables motion prediction to reduce perceived input latency by predicting\n * future animation frames based on current velocity (1-2 frames ahead).\n *\n * @param enabled - Whether to enable motion prediction\n * @param predictionTime - Optional: time ahead to predict (default: 16.67ms)\n *\n * @example\n * ```typescript\n * // Enable motion prediction for 1 frame (16.67ms at 60fps)\n * machine.setMotionPrediction(true);\n *\n * // Enable with 2 frames prediction (33.33ms)\n * machine.setMotionPrediction(true, 0.03333);\n * ```\n *\n * @korean 동작예측설정\n */\n setMotionPrediction(enabled: boolean, predictionTime?: number): void {\n this.enableMotionPrediction = enabled;\n if (predictionTime !== undefined) {\n // Clamp to 50ms maximum for <50ms total latency\n this.predictionTimeAhead = Math.min(predictionTime, 0.05);\n }\n }\n\n /**\n * Get motion prediction state\n *\n * **Korean**: 동작 예측 상태 가져오기\n *\n * @returns Current motion prediction state\n * @korean 동작예측상태가져오기\n */\n getMotionPredictionState(): MotionPredictionState {\n return this.motionPrediction;\n }\n\n /**\n * Check if motion prediction is enabled\n *\n * **Korean**: 동작 예측 활성화 확인\n *\n * @returns True if motion prediction is enabled\n * @korean 동작예측활성화확인\n */\n isMotionPredictionEnabled(): boolean {\n return this.enableMotionPrediction;\n }\n\n /**\n * Set preferred easing function for transitions\n *\n * **Korean**: 선호 이징 함수 설정\n *\n * Sets the default easing curve for animation transitions.\n * Can use presets like \"natural-motion\", \"smooth-transition\", etc.\n *\n * @param easingName - Easing function name\n *\n * @example\n * ```typescript\n * // Use natural motion for Korean martial arts\n * machine.setPreferredEasing(\"natural-motion\");\n *\n * // Use explosive power for strike animations\n * machine.setPreferredEasing(\"explosive-power\");\n * ```\n *\n * @korean 선호이징함수설정\n */\n setPreferredEasing(easingName: EasingName): void {\n this.preferredEasing = easingName;\n }\n\n /**\n * Get preferred easing function\n *\n * **Korean**: 선호 이징 함수 가져오기\n *\n * @returns Current preferred easing name\n * @korean 선호이징함수가져오기\n */\n getPreferredEasing(): EasingName {\n return this.preferredEasing;\n }\n\n /**\n * Update motion prediction with skeletal keyframe data\n *\n * **Korean**: 동작 예측 업데이트\n *\n * This should be called from the skeletal animation layer when applying\n * interpolated keyframes to the rig. It updates velocity tracking for\n * motion prediction to reduce perceived latency.\n *\n * Integration point: Call this from your skeletal animation system after\n * computing the current interpolated keyframe (e.g., from getInterpolatedKeyframe).\n *\n * @param currentKeyframe - Current skeletal animation keyframe with bone positions/rotations\n * @param deltaTime - Time elapsed since last update\n *\n * @example\n * ```typescript\n * // In your skeletal animation update loop:\n * const currentKeyframe = getInterpolatedKeyframe(animation, time);\n *\n * // Update motion prediction (for next frame)\n * if (machine.isMotionPredictionEnabled()) {\n * machine.updateMotionPredictionState(currentKeyframe, deltaTime);\n * }\n *\n * // Apply keyframe to rig\n * applyKeyframeToRig(rig, currentKeyframe);\n * ```\n *\n * @korean 동작예측업데이트\n */\n updateMotionPredictionState(\n currentKeyframe: AnimationKeyframe,\n deltaTime: number,\n ): void {\n if (!this.enableMotionPrediction) {\n return;\n }\n\n // Update velocity tracking if we have a previous keyframe\n if (this.previousKeyframe) {\n this.motionPrediction = updateMotionPrediction(\n this.motionPrediction,\n this.previousKeyframe,\n currentKeyframe,\n deltaTime,\n );\n }\n\n // Store current keyframe for next update\n this.previousKeyframe = currentKeyframe;\n }\n\n /**\n * Get predicted future keyframe for latency reduction\n *\n * **Korean**: 예측된 미래 키프레임 가져오기\n *\n * Returns a keyframe predicted ahead by predictionTimeAhead (default: 1 frame).\n * This reduces perceived input latency by showing where the animation will be\n * in the near future rather than where it currently is.\n *\n * Integration point: Use this instead of the current keyframe when applying\n * to the rig if motion prediction is enabled.\n *\n * @param currentKeyframe - Current skeletal animation keyframe\n * @returns Predicted future keyframe, or current if prediction disabled\n *\n * @example\n * ```typescript\n * // In your skeletal animation update loop:\n * let keyframeToApply = currentKeyframe;\n *\n * if (machine.isMotionPredictionEnabled()) {\n * keyframeToApply = machine.getPredictedKeyframe(currentKeyframe);\n * }\n *\n * applyKeyframeToRig(rig, keyframeToApply);\n * ```\n *\n * @korean 예측키프레임가져오기\n */\n getPredictedKeyframe(currentKeyframe: AnimationKeyframe): AnimationKeyframe {\n if (!this.enableMotionPrediction || !this.previousKeyframe) {\n return currentKeyframe;\n }\n\n return predictFutureKeyframe(\n currentKeyframe,\n this.motionPrediction,\n this.predictionTimeAhead,\n );\n }\n\n // ===== Animation Queue Methods (애니메이션 대기열) =====\n\n /**\n * Enable or reconfigure animation queue system\n *\n * **Korean**: 애니메이션 대기열 활성화/재설정\n *\n * The queue is enabled by default. Use this method to reconfigure the\n * queue size or conflict resolution strategy.\n *\n * @param maxSize - Maximum queue size (default: 3)\n * @param conflictStrategy - Conflict resolution strategy (default: \"timestamp\")\n *\n * @example\n * ```typescript\n * // Queue is enabled by default, but you can reconfigure it\n * machine.enableQueue(5, \"requested\");\n * ```\n *\n * @korean 대기열활성화\n */\n enableQueue(\n maxSize: number = 3,\n conflictStrategy: ConflictResolutionStrategy = \"timestamp\",\n ): void {\n this.animationQueue = new AnimationQueue(maxSize, conflictStrategy);\n this.conflictStrategy = conflictStrategy;\n }\n\n /**\n * Disable animation queue system\n *\n * **Korean**: 애니메이션 대기열 비활성화\n *\n * Disables the queue and clears any pending animations.\n *\n * @korean 대기열비활성화\n */\n disableQueue(): void {\n this.animationQueue = null;\n }\n\n /**\n * Check if queue is enabled\n *\n * **Korean**: 대기열 활성화 여부\n *\n * @returns True if queue is enabled\n * @korean 대기열활성화여부\n */\n isQueueEnabled(): boolean {\n return this.animationQueue !== null;\n }\n\n /**\n * Attempt to transition to a new animation state with queue support\n *\n * **Korean**: 대기열 지원 상태 전환\n *\n * Enhanced version of transitionTo() that automatically queues animations\n * when they cannot be executed immediately. Since the queue is enabled by\n * default, this is the recommended method for animation transitions.\n *\n * The queued animation will be automatically processed when the current\n * animation completes, following priority and conflict resolution rules.\n *\n * @param newState - Target animation state\n * @returns Whether transition was successful, queued, or failed\n *\n * @example\n * ```typescript\n * // Queue is enabled by default\n * const result = machine.transitionToQueued(AnimationState.ATTACK);\n * // Returns \"success\" if transitioned immediately\n * // Returns \"queued\" if couldn't interrupt but was queued\n * // Returns \"failed\" if queue is full or disabled\n * ```\n *\n * @korean 대기열상태전환\n */\n transitionToQueued(\n newState: AnimationState,\n ): \"success\" | \"queued\" | \"failed\" {\n // Try normal transition first\n const timestamp = performance.now();\n const priority = this.animations.get(newState)?.priority ?? 0;\n\n const success = this.transitionTo(newState);\n if (success) {\n return \"success\";\n }\n\n // If transition failed and queue is enabled, try to enqueue\n if (this.animationQueue) {\n const request: AnimationRequest = {\n state: newState,\n timestamp,\n priority,\n };\n\n const enqueued = this.animationQueue.enqueue(request);\n return enqueued ? \"queued\" : \"failed\";\n }\n\n return \"failed\";\n }\n\n /**\n * Process next queued animation if available\n *\n * **Korean**: 다음 대기열 애니메이션 처리\n *\n * Should be called automatically when an animation completes.\n * Dequeues and executes the highest priority pending animation.\n *\n * @returns Whether a queued animation was executed\n *\n * @internal\n * @korean 다음대기열처리\n */\n private processNextQueuedAnimation(): boolean {\n if (!this.animationQueue || this.animationQueue.isEmpty()) {\n return false;\n }\n\n const nextRequest = this.animationQueue.dequeue();\n if (!nextRequest) {\n return false;\n }\n\n // Try to execute the queued animation\n const success = this.transitionTo(nextRequest.state);\n\n if (!success) {\n // Log failure so queued animations do not disappear silently\n // Korean: 대기열 애니메이션 전이가 실패했음을 로그로 남깁니다.\n // This helps diagnose cases where transition rules, missing states,\n // or priority conflicts prevent a queued animation from playing.\n console.warn(\n \"[AnimationStateMachine] Failed to transition to queued animation state\",\n {\n requestedState: nextRequest.state,\n currentState: this.currentState,\n },\n );\n }\n\n return success;\n }\n\n /**\n * Get current animation queue state\n *\n * **Korean**: 현재 대기열 상태\n *\n * Returns information about the current queue state for debugging\n * or UI display.\n *\n * @returns Queue state information\n *\n * @korean 현재대기열상태\n */\n getQueueState(): {\n enabled: boolean;\n size: number;\n maxSize: number;\n pending: readonly AnimationRequest[];\n } {\n if (!this.animationQueue) {\n return {\n enabled: false,\n size: 0,\n maxSize: 0,\n pending: [],\n };\n }\n\n return {\n enabled: true,\n size: this.animationQueue.size(),\n maxSize: this.animationQueue.getMaxSize(),\n pending: this.animationQueue.getAll(),\n };\n }\n\n /**\n * Clear all pending queued animations\n *\n * **Korean**: 모든 대기열 초기화\n *\n * Removes all pending animations from the queue.\n *\n * @korean 모든대기열초기화\n */\n clearQueue(): void {\n this.animationQueue?.clear();\n }\n\n /**\n * Set conflict resolution strategy\n *\n * **Korean**: 충돌 해결 전략 설정\n *\n * Changes the strategy used to resolve equal-priority conflicts.\n *\n * @param strategy - Conflict resolution strategy\n *\n * @korean 충돌해결전략설정\n */\n setConflictStrategy(strategy: ConflictResolutionStrategy): void {\n this.conflictStrategy = strategy;\n\n // Keep the animation queue's strategy in sync with the state machine\n if (this.animationQueue) {\n this.animationQueue.setConflictStrategy(strategy);\n }\n }\n\n /**\n * Get current conflict resolution strategy\n *\n * **Korean**: 충돌 해결 전략 가져오기\n *\n * @returns Current conflict resolution strategy\n * @korean 충돌해결전략가져오기\n */\n getConflictStrategy(): ConflictResolutionStrategy {\n return this.conflictStrategy;\n }\n\n /**\n * Dispose of the animation state machine\n *\n * **Korean**: 애니메이션 상태 머신 해제\n *\n * Clears all internal state, queues, and references to prevent memory leaks.\n * Should be called when the state machine is no longer needed (e.g., component unmount).\n *\n * @korean 애니메이션상태머신해제\n */\n dispose(): void {\n // Clear animation queue\n this.animationQueue?.clear();\n this.animationQueue = null;\n\n // Clear stance transition data\n this.currentStanceTransition = null;\n\n // Clear motion prediction state\n this.motionPrediction = createMotionPredictionState();\n this.previousKeyframe = null;\n\n // Reset state to initial values\n this.currentState = AnimationState.IDLE;\n this.previousState = AnimationState.IDLE;\n this.frameIndex = 0;\n this.timeAccumulator = 0;\n this.justStarted = false;\n this.justCompleted = false;\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAoEA,IAAa,4CACX,IAAI,IAAqC;CACvC,CACE,eAAe,MACf;EACE,OAAO,eAAe;EACtB,QAAQ;EACR,KAAK;EACL,MAAM;EACN,eAAe;EACf,UAAU;EACV,UAAU,IAAI;CAChB,CACF;CACA,CACE,eAAe,MACf;EACE,OAAO,eAAe;EACtB,QAAQ;EACR,KAAK;EACL,MAAM;EACN,eAAe;EACf,UAAU;EACV,UAAU,IAAI;CAChB,CACF;CACA,CACE,eAAe,KACf;EACE,OAAO,eAAe;EACtB,QAAQ;EACR,KAAK;EACL,MAAM;EACN,eAAe;EACf,UAAU;EACV,UAAU,IAAI;CAChB,CACF;CACA,CACE,eAAe,eACf;EACE,OAAO,eAAe;EACtB,QAAQ;EACR,KAAK;EACL,MAAM;EACN,eAAe;EACf,UAAU;EACV,UAAU;EACV,QAAQ;CACV,CACF;CACA,CACE,eAAe,oBACf;EACE,OAAO,eAAe;EACtB,QAAQ;EACR,KAAK;EACL,MAAM;EACN,eAAe;EACf,UAAU;EACV,UAAU;CACZ,CACF;CACA,CACE,eAAe,QACf;EACE,OAAO,eAAe;EACtB,QAAQ;EACR,KAAK;EACL,MAAM;EACN,eAAe;EACf,UAAU;EACV,UAAU,IAAI;CAChB,CACF;CAEA,CACE,eAAe,sBACf;EACE,OAAO,eAAe;EACtB,QAAQ;EACR,KAAK;EACL,MAAM;EACN,eAAe;EACf,UAAU;EACV,UAAU;EACV,QAAQ;CACV,CACF;CACA,CACE,eAAe,cACf;EACE,OAAO,eAAe;EACtB,QAAQ;EACR,KAAK;EACL,MAAM;EACN,eAAe;EACf,UAAU;EACV,UAAU;EACV,eAAe;CACjB,CACF;CACA,CACE,eAAe,oBACf;EACE,OAAO,eAAe;EACtB,QAAQ;EACR,KAAK;EACL,MAAM;EACN,eAAe;EACf,UAAU;EACV,UAAU;EACV,uBAAuB;CACzB,CACF;CACA,CACE,eAAe,iBACf;EACE,OAAO,eAAe;EACtB,QAAQ;EACR,KAAK;EACL,MAAM;EACN,eAAe;EACf,UAAU;EACV,UAAU;EACV,QAAQ;CACV,CACF;CACA,CACE,eAAe,QACf;EACE,OAAO,eAAe;EACtB,QAAQ;EACR,KAAK;EACL,MAAM;EACN,eAAe;EACf,UAAA;EACA,UAAU,KAAK;EACf,QAAQ;CACV,CACF;CACA,CACE,eAAe,KACf;EACE,OAAO,eAAe;EACtB,QAAQ;EACR,KAAK;EACL,MAAM;EACN,eAAe;EACf,UAAU;EACV,UAAU,IAAI;CAChB,CACF;CACA,CACE,eAAe,IACf;EACE,OAAO,eAAe;EACtB,QAAQ;EACR,KAAK;EACL,MAAM;EACN,eAAe;EACf,UAAU;EACV,UAAU;CACZ,CACF;CAEA,CACE,eAAe,cACf;EACE,OAAO,eAAe;EACtB,QAAQ;EACR,KAAK;EACL,MAAM;EACN,eAAe;EACf,UAAU;EACV,UAAU;CACZ,CACF;CACA,CACE,eAAe,eACf;EACE,OAAO,eAAe;EACtB,QAAQ;EACR,KAAK;EACL,MAAM;EACN,eAAe;EACf,UAAU;EACV,UAAU;CACZ,CACF;CACA,CACE,eAAe,gBACf;EACE,OAAO,eAAe;EACtB,QAAQ;EACR,KAAK;EACL,MAAM;EACN,eAAe;EACf,UAAU;EACV,UAAU;CACZ,CACF;CACA,CACE,eAAe,iBACf;EACE,OAAO,eAAe;EACtB,QAAQ;EACR,KAAK;EACL,MAAM;EACN,eAAe;EACf,UAAU;EACV,UAAU;CACZ,CACF;CAEA,CACE,eAAe,cACf;EACE,OAAO,eAAe;EACtB,QAAQ;EACR,KAAK;EACL,MAAM;EACN,eAAe;EACf,UAAU;EACV,UAAU,IAAI;CAChB,CACF;CACA,CACE,eAAe,eACf;EACE,OAAO,eAAe;EACtB,QAAQ;EACR,KAAK;EACL,MAAM;EACN,eAAe;EACf,UAAU;EACV,UAAU,IAAI;CAChB,CACF;CACA,CACE,eAAe,kBACf;EACE,OAAO,eAAe;EACtB,QAAQ;EACR,KAAK;EACL,MAAM;EACN,eAAe;EACf,UAAU;EACV,UAAU,IAAI;CAChB,CACF;CACA,CACE,eAAe,mBACf;EACE,OAAO,eAAe;EACtB,QAAQ;EACR,KAAK;EACL,MAAM;EACN,eAAe;EACf,UAAU;EACV,UAAU,IAAI;CAChB,CACF;CAEA,CACE,eAAe,wBACf;EACE,OAAO,eAAe;EACtB,QAAQ;EACR,KAAK;EACL,MAAM;EACN,eAAe;EACf,UAAU;EACV,UAAU;CACZ,CACF;CACA,CACE,eAAe,yBACf;EACE,OAAO,eAAe;EACtB,QAAQ;EACR,KAAK;EACL,MAAM;EACN,eAAe;EACf,UAAU;EACV,UAAU;CACZ,CACF;CACA,CACE,eAAe,eACf;EACE,OAAO,eAAe;EACtB,QAAQ;EACR,KAAK;EACL,MAAM;EACN,eAAe;EACf,UAAU;EACV,UAAU;CACZ,CACF;CACA,CACE,eAAe,oBACf;EACE,OAAO,eAAe;EACtB,QAAQ;EACR,KAAK;EACL,MAAM;EACN,eAAe;EACf,UAAU;EACV,UAAU;CACZ,CACF;CAEA,CACE,eAAe,WACf;EACE,OAAO,eAAe;EACtB,QAAQ;EACR,KAAK;EACL,MAAM;EACN,eAAe;EACf,UAAA;EACA,UAAU,KAAK;CACjB,CACF;CACA,CACE,eAAe,YACf;EACE,OAAO,eAAe;EACtB,QAAQ;EACR,KAAK;EACL,MAAM;EACN,eAAe;EACf,UAAA;EACA,UAAU,KAAK;CACjB,CACF;CAEA,CACE,eAAe,mBACf;EACE,OAAO,eAAe;EACtB,QAAQ;EACR,KAAK;EACL,MAAM;EACN,eAAe;EACf,UAAU;EACV,UAAU,IAAI;CAChB,CACF;CACA,CACE,eAAe,kBACf;EACE,OAAO,eAAe;EACtB,QAAQ;EACR,KAAK;EACL,MAAM;EACN,eAAe;EACf,UAAU;EACV,UAAU,IAAI;CAChB,CACF;CACA,CACE,eAAe,iBACf;EACE,OAAO,eAAe;EACtB,QAAQ;EACR,KAAK;EACL,MAAM;EACN,eAAe;EACf,UAAU;EACV,UAAU,IAAI;CAChB,CACF;CACA,CACE,eAAe,kBACf;EACE,OAAO,eAAe;EACtB,QAAQ;EACR,KAAK;EACL,MAAM;EACN,eAAe;EACf,UAAU;EACV,UAAU,IAAI;CAChB,CACF;CACA,CACE,eAAe,kBACf;EACE,OAAO,eAAe;EACtB,QAAQ;EACR,KAAK;EACL,MAAM;EACN,eAAe;EACf,UAAU;EACV,UAAU,IAAI;CAChB,CACF;CACA,CACE,eAAe,kBACf;EACE,OAAO,eAAe;EACtB,QAAQ;EACR,KAAK;EACL,MAAM;EACN,eAAe;EACf,UAAU;EACV,UAAU,IAAI;CAChB,CACF;CACA,CACE,eAAe,kBACf;EACE,OAAO,eAAe;EACtB,QAAQ;EACR,KAAK;EACL,MAAM;EACN,eAAe;EACf,UAAU;EACV,UAAU,IAAI;CAChB,CACF;CACA,CACE,eAAe,kBACf;EACE,OAAO,eAAe;EACtB,QAAQ;EACR,KAAK;EACL,MAAM;EACN,eAAe;EACf,UAAU;EACV,UAAU,IAAI;CAChB,CACF;CAGA,CACE,eAAe,cACf;EACE,OAAO,eAAe;EACtB,QAAQ;EACR,KAAK;EACL,MAAM;EACN,eAAe;EACf,UAAA;EACA,UAAU;CACZ,CACF;CACA,CACE,eAAe,WACf;EACE,OAAO,eAAe;EACtB,QAAQ;EACR,KAAK;EACL,MAAM;EACN,eAAe;EACf,UAAA;EACA,UAAU;CACZ,CACF;CACA,CACE,eAAe,WACf;EACE,OAAO,eAAe;EACtB,QAAQ;EACR,KAAK;EACL,MAAM;EACN,eAAe;EACf,UAAA;EACA,UAAU;CACZ,CACF;CACA,CACE,eAAe,YACf;EACE,OAAO,eAAe;EACtB,QAAQ;EACR,KAAK;EACL,MAAM;EACN,eAAe;EACf,UAAA;EACA,UAAU;CACZ,CACF;CACA,CACE,eAAe,mBACf;EACE,OAAO,eAAe;EACtB,QAAQ;EACR,KAAK;EACL,MAAM;EACN,eAAe;EACf,UAAA;EACA,UAAU;CACZ,CACF;CACA,CACE,eAAe,oBACf;EACE,OAAO,eAAe;EACtB,QAAQ;EACR,KAAK;EACL,MAAM;EACN,eAAe;EACf,UAAA;EACA,UAAU;CACZ,CACF;CACA,CACE,eAAe,gBACf;EACE,OAAO,eAAe;EACtB,QAAQ;EACR,KAAK;EACL,MAAM;EACN,eAAe;EACf,UAAA;EACA,UAAU;CACZ,CACF;CACA,CACE,eAAe,iBACf;EACE,OAAO,eAAe;EACtB,QAAQ;EACR,KAAK;EACL,MAAM;EACN,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"}
|
|
@@ -106,9 +106,7 @@ function calculateKickPowerFromHipRotation(hipState, kickType) {
|
|
|
106
106
|
case "hook":
|
|
107
107
|
powerFactor = normalizedFrontal * .4 + normalizedSagittal * .4 + normalizedTransverse * .2;
|
|
108
108
|
break;
|
|
109
|
-
case "axe":
|
|
110
|
-
powerFactor = normalizedSagittal * .8 + normalizedFrontal * .2;
|
|
111
|
-
break;
|
|
109
|
+
case "axe": powerFactor = normalizedSagittal * .8 + normalizedFrontal * .2;
|
|
112
110
|
}
|
|
113
111
|
return 1 + powerFactor * .4;
|
|
114
112
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"AdvancedJointMovements.js","names":[],"sources":["../../../../src/systems/animation/systems/AdvancedJointMovements.ts"],"sourcesContent":["/**\n * Advanced Joint Movement System for Korean Martial Arts\n * \n * Extends the base skeletal animation system with specialized joint movements\n * for authentic martial arts technique execution:\n * - Hip rotation for kicks (고관절 회전)\n * - Shoulder elevation/depression (어깨 들어올림)\n * - Ankle articulation (발목 관절)\n * - Wrist snap mechanics (손목 스냅)\n * - Knee drive positioning (무릎 밀어올림)\n * - Spinal flexion/extension (척추 굽힘)\n * \n * @module systems/animation/AdvancedJointMovements\n * @category Animation System\n * @korean 고급관절동작시스템\n */\n\nimport * as THREE from \"three\";\n\n/**\n * Body side for limb-specific movements\n * \n * @korean 신체측면\n */\nexport type BodySide = 'left' | 'right';\n\n/**\n * Kick technique types for hip rotation calculation\n * \n * Korean terminology:\n * - front: 앞차기 (Apchagi)\n * - roundhouse: 돌려차기 (Dollyeochagi)\n * - side: 옆차기 (Yeopchagi)\n * - hook: 후려차기 (Huryeochagi)\n * - axe: 내려차기 (Naeryeochagi)\n * \n * @korean 차기기술유형\n */\nexport type KickType = 'front' | 'roundhouse' | 'side' | 'hook' | 'axe';\n\n/**\n * Hand strike technique types for wrist snap calculation\n * \n * Korean terminology:\n * - backfist: 등주먹 (Deungjumeok)\n * - knife-hand: 수도 (Sudo)\n * - palm-heel: 장저 (Jangjeo)\n * - ridge-hand: 손날등 (Sonnaldeung)\n * - hammer-fist: 망치주먹 (Mangchijumeok)\n * \n * @korean 수격기술유형\n */\nexport type HandStrikeType = 'backfist' | 'knife-hand' | 'palm-heel' | 'ridge-hand' | 'hammer-fist';\n\n/**\n * Kick height levels for hip rotation scaling\n * \n * Korean terminology:\n * - 0: 하단 (Hadan) - Low section\n * - 1: 중단 (Jungdan) - Middle section\n * - 2: 상단 (Sangdan) - High section\n * \n * @korean 차기높이\n */\nexport type KickHeight = 0 | 1 | 2;\n\n/**\n * Technique execution phases\n * \n * Korean terminology:\n * - chamber: 준비 (Junbi) - Preparation/chamber\n * - extension: 확장 (Hwakjang) - Extension\n * - retraction: 회수 (Hoesu) - Retraction\n * \n * @korean 기술단계\n */\nexport type TechniquePhase = 'chamber' | 'extension' | 'retraction';\n\n/**\n * Strike execution phases\n * \n * Korean terminology:\n * - wind-up: 예비동작 (Yebi Dongjak) - Wind-up\n * - impact: 타격 (Tagyeok) - Impact\n * - follow-through: 후속동작 (Husok Dongjak) - Follow-through\n * \n * @korean 타격단계\n */\nexport type StrikePhase = 'wind-up' | 'impact' | 'follow-through';\n\n/**\n * Block and overhead technique types for shoulder elevation\n * \n * Korean terminology:\n * - high-block: 상단막기 (Sangdan Makgi)\n * - overhead-strike: 상단공격 (Sangdan Gonggyeok)\n * - rising-block: 올려막기 (Ollyeo Makgi)\n * - shrug: 어깨올리기 (Eokkae Olligi)\n * - neutral: 중립 (Jungnip)\n * \n * @korean 어깨기술유형\n */\nexport type ShoulderTechniqueType = 'high-block' | 'overhead-strike' | 'rising-block' | 'shrug' | 'neutral';\n\n/**\n * Shoulder and block technique execution phases\n * \n * Korean terminology:\n * - preparation: 준비 (Junbi)\n * - execution: 실행 (Silhaeng)\n * - recovery: 복귀 (Bokgwi)\n * \n * @korean 어깨단계\n */\nexport type ShoulderPhase = 'preparation' | 'execution' | 'recovery';\n\n/**\n * Spinal movement types for dodging and positioning\n * \n * Korean terminology:\n * - duck: 숙이기 (Sugigi) - Duck down\n * - lean-back: 뒤로기울기 (Dwiro Giulgi) - Lean back\n * - lean-left: 왼쪽기울기 (Oenjjok Giulgi) - Lean left\n * - lean-right: 오른쪽기울기 (Oreunjjok Giulgi) - Lean right\n * - low-attack: 낮은공격 (Najeun Gonggyeok) - Low attack\n * - neutral: 중립 (Jungnip) - Neutral\n * \n * @korean 척추동작유형\n */\nexport type SpinalMovementType = 'duck' | 'lean-back' | 'lean-left' | 'lean-right' | 'low-attack' | 'neutral';\n\n/**\n * Knee drive technique types for close-range combat\n * \n * Korean terminology:\n * - knee-strike: 무릎차기 (Mureup Chagi)\n * - clinch-control: 붙잡기제어 (Butjapgi Jeeo)\n * - push-kick: 밀어차기 (Mireo Chagi)\n * - neutral: 중립 (Jungnip)\n * \n * @korean 무릎기술유형\n */\nexport type KneeTechniqueType = 'knee-strike' | 'clinch-control' | 'push-kick' | 'neutral';\n\n/**\n * Knee technique execution phases\n * \n * Korean terminology:\n * - wind-up: 예비동작 (Yebi Dongjak)\n * - execution: 실행 (Silhaeng)\n * - recovery: 복귀 (Bokgwi)\n * \n * @korean 무릎단계\n */\nexport type KneePhase = 'wind-up' | 'execution' | 'recovery';\n\n/**\n * Hip rotation state for dynamic kick mechanics\n * \n * Tracks independent hip rotation in multiple planes for realistic\n * high kicks, roundhouse kicks, and hook kicks. Separates hip rotation\n * from pelvis rotation for authentic martial arts biomechanics.\n * \n * Korean: 고관절 회전 상태\n * \n */\nexport interface HipRotationState {\n /**\n * Frontal plane rotation (abduction/adduction) in radians\n * - Positive: Leg moves away from body centerline (abduction)\n * - Negative: Leg moves toward centerline (adduction)\n * Range: ±1.8 rad (±103°) for high kicks\n * \n * @korean 외전각도\n */\n readonly frontalRotation: number;\n\n /**\n * Sagittal plane rotation (flexion/extension) in radians\n * - Positive: Leg moves forward (flexion)\n * - Negative: Leg moves backward (extension)\n * Range: -1.8 to 1.8 rad for kicks\n * \n * @korean 굴곡각도\n */\n readonly sagittalRotation: number;\n\n /**\n * Transverse plane rotation (internal/external rotation) in radians\n * - Positive: Thigh rotates inward\n * - Negative: Thigh rotates outward\n * Range: ±0.8 rad (±46°)\n * \n * @korean 회전각도\n */\n readonly transverseRotation: number;\n\n /**\n * Which hip (left or right)\n * @korean 측면\n */\n readonly side: BodySide;\n}\n\n/**\n * Shoulder elevation/depression state for blocks and overhead strikes\n * \n * Tracks vertical shoulder movement independent of arm rotation.\n * Essential for authentic blocking mechanics and overhead techniques.\n * \n * Korean: 어깨 들어올림 상태\n * \n */\nexport interface ShoulderElevationState {\n /**\n * Vertical displacement in meters\n * - Positive: Shoulder elevated (shrug)\n * - Negative: Shoulder depressed (drop)\n * Range: ±0.05 m (±5cm)\n * \n * @korean 수직변위\n */\n readonly elevation: number;\n\n /**\n * Which shoulder (left or right)\n * @korean 측면\n */\n readonly side: BodySide;\n}\n\n/**\n * Ankle articulation state for kick chambers and pivots\n * \n * Tracks ankle joint positioning crucial for proper kick mechanics\n * and pivoting in Korean martial arts.\n * \n * Korean: 발목 관절 상태\n * \n */\nexport interface AnkleArticulationState {\n /**\n * Dorsiflexion/plantarflexion angle in radians\n * - Positive: Toes point up (dorsiflexion)\n * - Negative: Toes point down (plantarflexion)\n * Range: -0.8 to 0.5 rad\n * \n * @korean 발등굽힘각도\n */\n readonly flexion: number;\n\n /**\n * Inversion/eversion angle in radians\n * Range: ±0.4 rad (±23°)\n * \n * @korean 안쪽굽힘각도\n */\n readonly inversion: number;\n\n /**\n * Which ankle (left or right)\n * @korean 측면\n */\n readonly side: BodySide;\n}\n\n/**\n * Wrist snap state for rapid hand strikes\n * \n * Tracks rapid wrist rotation mechanics essential for backfist,\n * knife-hand, and other Korean martial arts hand techniques.\n * \n * Korean: 손목 스냅 상태\n * \n */\nexport interface WristSnapState {\n /**\n * Rotation angle in radians\n * Range: ±1.5 rad (±86°)\n * \n * @korean 회전각도\n */\n readonly rotation: number;\n\n /**\n * Snap velocity in rad/s (for power calculation)\n * @korean 회전속도\n */\n readonly velocity: number;\n\n /**\n * Which wrist (left or right)\n * @korean 측면\n */\n readonly side: BodySide;\n}\n\n/**\n * Knee drive state for clinch work and knee strikes\n * \n * Tracks independent knee positioning crucial for close-range\n * combat and knee strike techniques.\n * \n * Korean: 무릎 밀어올림 상태\n * \n */\nexport interface KneeDriveState {\n /**\n * Drive height in meters (vertical displacement)\n * Range: 0 to 0.8 m (80cm max lift)\n * \n * @korean 들어올림높이\n */\n readonly height: number;\n\n /**\n * Forward drive distance in meters\n * Range: 0 to 0.3 m (30cm)\n * \n * @korean 전방거리\n */\n readonly forward: number;\n\n /**\n * Which knee (left or right)\n * @korean 측면\n */\n readonly side: BodySide;\n}\n\n/**\n * Spinal flexion/extension state for dodges and low attacks\n * \n * Tracks forward/backward bending of spine for defensive\n * movements and low attack positioning.\n * \n * Korean: 척추 굽힘 상태\n * \n */\nexport interface SpinalFlexionState {\n /**\n * Flexion angle in radians (forward/backward bend)\n * - Positive: Forward bend\n * - Negative: Backward bend\n * Range: -0.5 to 0.8 rad\n * \n * @korean 굽힘각도\n */\n readonly flexion: number;\n\n /**\n * Lateral bend in radians (side to side)\n * Range: ±0.3 rad (±17°)\n * \n * @korean 측면굽힘각도\n */\n readonly lateralBend: number;\n}\n\n/**\n * Advanced joint movement constraints\n * \n * Defines anatomically correct limits for all advanced joint movements\n * based on human biomechanics and Korean martial arts techniques.\n * \n * @korean 고급관절제약조건\n */\nexport const ADVANCED_JOINT_CONSTRAINTS = {\n /**\n * Hip rotation constraints for kicks\n * @korean 고관절제약조건\n */\n HIP_ROTATION: {\n FRONTAL_MIN: -1.8, // -103° adduction\n FRONTAL_MAX: 1.8, // 103° abduction\n SAGITTAL_MIN: -1.8, // -103° extension\n SAGITTAL_MAX: 1.8, // 103° flexion\n TRANSVERSE_MIN: -0.8, // -46° external rotation\n TRANSVERSE_MAX: 0.8, // 46° internal rotation\n },\n\n /**\n * Shoulder elevation constraints\n * @korean 어깨들어올림제약조건\n */\n SHOULDER_ELEVATION: {\n MIN: -0.05, // -5cm depression\n MAX: 0.05, // 5cm elevation\n },\n\n /**\n * Ankle articulation constraints\n * @korean 발목관절제약조건\n */\n ANKLE_ARTICULATION: {\n FLEXION_MIN: -0.8, // -46° plantarflexion\n FLEXION_MAX: 0.5, // 29° dorsiflexion\n INVERSION_MIN: -0.4, // -23° eversion\n INVERSION_MAX: 0.4, // 23° inversion\n },\n\n /**\n * Wrist snap constraints\n * @korean 손목스냅제약조건\n */\n WRIST_SNAP: {\n ROTATION_MIN: -1.5, // -86°\n ROTATION_MAX: 1.5, // 86°\n MAX_VELOCITY: 30.0, // 30 rad/s for power strikes\n },\n\n /**\n * Knee drive constraints\n * @korean 무릎밀어올림제약조건\n */\n KNEE_DRIVE: {\n HEIGHT_MIN: 0, // Ground level\n HEIGHT_MAX: 0.8, // 80cm max lift\n FORWARD_MIN: 0,\n FORWARD_MAX: 0.3, // 30cm forward\n },\n\n /**\n * Spinal flexion constraints\n * @korean 척추굽힘제약조건\n */\n SPINAL_FLEXION: {\n FLEXION_MIN: -0.5, // -29° extension (backward)\n FLEXION_MAX: 0.8, // 46° flexion (forward)\n LATERAL_MIN: -0.3, // -17° left bend\n LATERAL_MAX: 0.3, // 17° right bend\n },\n} as const;\n\n/**\n * Calculate hip rotation for kick mechanics\n * \n * Determines optimal hip rotation angles for various kick types,\n * ensuring anatomically correct positioning and maximum power generation.\n * \n * @param kickType - Type of kick being performed\n * @param targetHeight - Target height for kick (0=low, 1=medium, 2=high)\n * @param side - Which leg is kicking\n * @returns Hip rotation state with all three plane rotations\n * \n * @example\n * ```typescript\n * const hipState = calculateHipRotationForKick('roundhouse', 2, 'right');\n * // Returns frontal: 1.5, sagittal: 1.3, transverse: 0.6 for high roundhouse\n * ```\n * \n * @korean 차기용고관절회전계산\n */\nexport function calculateHipRotationForKick(\n kickType: KickType,\n targetHeight: KickHeight,\n side: BodySide\n): HipRotationState {\n let frontalRotation = 0;\n let sagittalRotation = 0;\n let transverseRotation = 0;\n\n // Height multiplier (more rotation needed for higher kicks)\n const heightMultiplier = targetHeight === 0 ? 0.5 : targetHeight === 1 ? 0.75 : 1.0;\n\n switch (kickType) {\n case 'front':\n // Front kick: primarily sagittal plane (forward)\n sagittalRotation = 1.5 * heightMultiplier;\n frontalRotation = 0.2; // Slight abduction for balance\n transverseRotation = 0;\n break;\n\n case 'roundhouse':\n // Roundhouse: combination of frontal abduction and transverse rotation\n frontalRotation = 1.5 * heightMultiplier;\n sagittalRotation = 0.8 * heightMultiplier;\n transverseRotation = 0.6 * heightMultiplier; // Internal rotation for power\n break;\n\n case 'side':\n // Side kick: primarily frontal plane (abduction)\n frontalRotation = 1.6 * heightMultiplier;\n sagittalRotation = 0.3;\n transverseRotation = -0.4; // External rotation for proper foot alignment\n break;\n\n case 'hook':\n // Hook kick: high abduction with forward component\n frontalRotation = 1.7 * heightMultiplier;\n sagittalRotation = 1.2 * heightMultiplier;\n transverseRotation = 0.5;\n break;\n\n case 'axe':\n // Axe kick: maximum sagittal flexion, some abduction\n frontalRotation = 0.8 * heightMultiplier;\n sagittalRotation = 1.8 * heightMultiplier; // Maximum forward flexion\n transverseRotation = 0.2;\n break;\n }\n\n // Apply constraints\n frontalRotation = Math.max(\n ADVANCED_JOINT_CONSTRAINTS.HIP_ROTATION.FRONTAL_MIN,\n Math.min(ADVANCED_JOINT_CONSTRAINTS.HIP_ROTATION.FRONTAL_MAX, frontalRotation)\n );\n sagittalRotation = Math.max(\n ADVANCED_JOINT_CONSTRAINTS.HIP_ROTATION.SAGITTAL_MIN,\n Math.min(ADVANCED_JOINT_CONSTRAINTS.HIP_ROTATION.SAGITTAL_MAX, sagittalRotation)\n );\n transverseRotation = Math.max(\n ADVANCED_JOINT_CONSTRAINTS.HIP_ROTATION.TRANSVERSE_MIN,\n Math.min(ADVANCED_JOINT_CONSTRAINTS.HIP_ROTATION.TRANSVERSE_MAX, transverseRotation)\n );\n\n return {\n frontalRotation,\n sagittalRotation,\n transverseRotation,\n side,\n };\n}\n\n/**\n * Calculate kick power modifier from hip rotation\n * \n * Determines power bonus based on proper hip rotation technique.\n * Greater rotation in the correct planes generates more power.\n * \n * @param hipState - Current hip rotation state\n * @param kickType - Type of kick\n * @returns Power multiplier (1.0-1.40x, with kicks getting up to 40% bonus)\n * \n * @example\n * ```typescript\n * const hipState = calculateHipRotationForKick('roundhouse', 2, 'right');\n * const power = calculateKickPowerFromHipRotation(hipState, 'roundhouse');\n * // Returns ~1.35 for properly executed high roundhouse\n * ```\n * \n * @korean 고관절회전에서차기파워계산\n */\nexport function calculateKickPowerFromHipRotation(\n hipState: HipRotationState,\n kickType: KickType\n): number {\n // Normalize rotations to 0-1 range based on constraints\n const normalizedFrontal = Math.abs(hipState.frontalRotation) / \n ADVANCED_JOINT_CONSTRAINTS.HIP_ROTATION.FRONTAL_MAX;\n const normalizedSagittal = Math.abs(hipState.sagittalRotation) / \n ADVANCED_JOINT_CONSTRAINTS.HIP_ROTATION.SAGITTAL_MAX;\n const normalizedTransverse = Math.abs(hipState.transverseRotation) / \n ADVANCED_JOINT_CONSTRAINTS.HIP_ROTATION.TRANSVERSE_MAX;\n\n // Different kicks benefit from different rotation planes\n let powerFactor = 0;\n switch (kickType) {\n case 'front':\n powerFactor = normalizedSagittal * 0.8 + normalizedFrontal * 0.2;\n break;\n case 'roundhouse':\n powerFactor = normalizedFrontal * 0.5 + normalizedTransverse * 0.3 + normalizedSagittal * 0.2;\n break;\n case 'side':\n powerFactor = normalizedFrontal * 0.7 + normalizedTransverse * 0.3;\n break;\n case 'hook':\n powerFactor = normalizedFrontal * 0.4 + normalizedSagittal * 0.4 + normalizedTransverse * 0.2;\n break;\n case 'axe':\n powerFactor = normalizedSagittal * 0.8 + normalizedFrontal * 0.2;\n break;\n }\n\n // Kicks can get up to 40% power bonus from proper hip rotation\n return 1.0 + (powerFactor * 0.40);\n}\n\n/**\n * Apply hip rotation to Three.js Euler angles\n * \n * Converts hip rotation state into Three.js Euler angles for rendering.\n * \n * @param hipState - Hip rotation state\n * @returns Three.js Euler angles (X=sagittal, Y=transverse, Z=frontal)\n * \n * @korean 고관절회전을오일러각으로적용\n */\nexport function applyHipRotationToEuler(hipState: HipRotationState): THREE.Euler {\n return new THREE.Euler(\n hipState.sagittalRotation, // X-axis: forward/backward\n hipState.transverseRotation, // Y-axis: twist\n hipState.frontalRotation, // Z-axis: side to side\n 'XYZ'\n );\n}\n\n/**\n * Calculate ankle articulation for kick chamber\n * \n * Determines proper ankle positioning for kick chambering and extension,\n * crucial for proper foot alignment and power transfer in Korean martial arts.\n * \n * @param kickType - Type of kick being chambered\n * @param phase - Kick phase: 'chamber', 'extension', 'retraction'\n * @param side - Which ankle\n * @returns Ankle articulation state with flexion and inversion angles\n * \n * @example\n * ```typescript\n * const ankleState = calculateAnkleArticulation('front', 'extension', 'right');\n * // Returns: { flexion: 0.5, inversion: 0, side: 'right' }\n * ```\n * \n * @korean 차기챔버용발목관절계산\n */\nexport function calculateAnkleArticulation(\n kickType: KickType,\n phase: TechniquePhase,\n side: BodySide\n): AnkleArticulationState {\n let flexion = 0;\n let inversion = 0;\n\n switch (kickType) {\n case 'front':\n if (phase === 'chamber') {\n flexion = 0.2; // Slight dorsiflexion in chamber\n } else if (phase === 'extension') {\n flexion = 0.5; // Strong dorsiflexion for striking with ball of foot\n } else {\n flexion = 0.1; // Slight flexion during retraction\n }\n inversion = 0; // Neutral for front kicks\n break;\n\n case 'roundhouse':\n if (phase === 'chamber') {\n flexion = -0.3; // Plantarflexion in chamber\n } else if (phase === 'extension') {\n flexion = -0.5; // Strong plantarflexion for instep strike\n inversion = -0.2; // Slight eversion to align instep\n } else {\n flexion = -0.2;\n inversion = 0;\n }\n break;\n\n case 'side':\n if (phase === 'chamber') {\n flexion = 0.3; // Dorsiflexion in chamber\n } else if (phase === 'extension') {\n flexion = 0.4; // Dorsiflexion for heel/blade strike\n inversion = 0.3; // Inversion to present blade edge\n } else {\n flexion = 0.2;\n inversion = 0.1;\n }\n break;\n\n case 'hook':\n if (phase === 'chamber') {\n flexion = -0.2;\n } else if (phase === 'extension') {\n flexion = -0.4; // Plantarflexion for heel strike\n inversion = -0.15; // Slight eversion\n } else {\n flexion = -0.1;\n inversion = 0;\n }\n break;\n\n case 'axe':\n if (phase === 'chamber') {\n flexion = 0.1; // Minimal flexion in high chamber\n } else if (phase === 'extension') {\n flexion = 0.5; // Strong dorsiflexion for downward strike\n } else {\n flexion = 0.2;\n }\n inversion = 0; // Neutral for axe kicks\n break;\n }\n\n // Apply constraints\n flexion = Math.max(\n ADVANCED_JOINT_CONSTRAINTS.ANKLE_ARTICULATION.FLEXION_MIN,\n Math.min(ADVANCED_JOINT_CONSTRAINTS.ANKLE_ARTICULATION.FLEXION_MAX, flexion)\n );\n inversion = Math.max(\n ADVANCED_JOINT_CONSTRAINTS.ANKLE_ARTICULATION.INVERSION_MIN,\n Math.min(ADVANCED_JOINT_CONSTRAINTS.ANKLE_ARTICULATION.INVERSION_MAX, inversion)\n );\n\n return { flexion, inversion, side };\n}\n\n/**\n * Calculate wrist snap for hand strikes\n * \n * Determines wrist rotation and velocity for power generation in\n * backfist, knife-hand, and other Korean martial arts hand techniques.\n * \n * @param strikeType - Type of hand strike\n * @param phase - Strike phase: 'wind-up', 'impact', 'follow-through'\n * @param side - Which wrist\n * @returns Wrist snap state with rotation angle and velocity\n * \n * @example\n * ```typescript\n * const wristState = calculateWristSnap('backfist', 'impact', 'right');\n * // Returns: { rotation: 1.2, velocity: 25.0, side: 'right' }\n * ```\n * \n * @korean 수격용손목스냅계산\n */\nexport function calculateWristSnap(\n strikeType: HandStrikeType,\n phase: StrikePhase,\n side: BodySide\n): WristSnapState {\n let rotation = 0;\n let velocity = 0;\n\n switch (strikeType) {\n case 'backfist':\n if (phase === 'wind-up') {\n rotation = -0.8; // Cocked back\n velocity = 0;\n } else if (phase === 'impact') {\n rotation = 1.2; // Snapped forward\n velocity = 25.0; // High velocity snap\n } else {\n rotation = 0.5;\n velocity = 5.0;\n }\n break;\n\n case 'knife-hand':\n if (phase === 'wind-up') {\n rotation = -0.5;\n velocity = 0;\n } else if (phase === 'impact') {\n rotation = 0.8; // Moderate snap for precision\n velocity = 20.0;\n } else {\n rotation = 0.3;\n velocity = 3.0;\n }\n break;\n\n case 'palm-heel':\n if (phase === 'wind-up') {\n rotation = -0.3; // Minimal wind-up\n velocity = 0;\n } else if (phase === 'impact') {\n rotation = 0.5; // Push through\n velocity = 15.0; // Lower velocity, more push\n } else {\n rotation = 0.2;\n velocity = 2.0;\n }\n break;\n\n case 'ridge-hand':\n if (phase === 'wind-up') {\n rotation = 0.8; // Opposite direction\n velocity = 0;\n } else if (phase === 'impact') {\n rotation = -1.0; // Reverse snap\n velocity = 22.0;\n } else {\n rotation = -0.4;\n velocity = 4.0;\n }\n break;\n\n case 'hammer-fist':\n if (phase === 'wind-up') {\n rotation = 0.2; // Minimal rotation\n velocity = 0;\n } else if (phase === 'impact') {\n rotation = 0.3; // Downward strike, less rotation\n velocity = 18.0;\n } else {\n rotation = 0.1;\n velocity = 2.0;\n }\n break;\n }\n\n // Apply constraints\n rotation = Math.max(\n ADVANCED_JOINT_CONSTRAINTS.WRIST_SNAP.ROTATION_MIN,\n Math.min(ADVANCED_JOINT_CONSTRAINTS.WRIST_SNAP.ROTATION_MAX, rotation)\n );\n velocity = Math.min(ADVANCED_JOINT_CONSTRAINTS.WRIST_SNAP.MAX_VELOCITY, Math.abs(velocity));\n\n return { rotation, velocity, side };\n}\n\n/**\n * Calculate wrist snap power modifier\n * \n * Determines power bonus based on wrist snap velocity and rotation.\n * Higher velocity generates more power for whipping strikes.\n * \n * @param wristState - Current wrist snap state\n * @returns Power multiplier (1.0-1.25x, with hand strikes getting up to 25% bonus)\n * \n * @example\n * ```typescript\n * const wristState = calculateWristSnap('backfist', 'impact', 'right');\n * const power = calculateWristSnapPowerModifier(wristState);\n * // Returns ~1.20 for proper backfist snap\n * ```\n * \n * @korean 손목스냅파워배율계산\n */\nexport function calculateWristSnapPowerModifier(wristState: WristSnapState): number {\n // Normalize velocity to 0-1 range\n const normalizedVelocity = wristState.velocity / ADVANCED_JOINT_CONSTRAINTS.WRIST_SNAP.MAX_VELOCITY;\n \n // Normalize rotation magnitude to 0-1 range\n const normalizedRotation = Math.abs(wristState.rotation) / ADVANCED_JOINT_CONSTRAINTS.WRIST_SNAP.ROTATION_MAX;\n \n // Velocity contributes 70%, rotation contributes 30%\n const powerFactor = (normalizedVelocity * 0.7) + (normalizedRotation * 0.3);\n \n // Hand strikes get up to 25% power bonus from wrist snap\n return 1.0 + (powerFactor * 0.25);\n}\n\n/**\n * Calculate shoulder elevation for blocks and overhead strikes\n * \n * Determines vertical shoulder movement for defensive blocks and\n * overhead striking techniques in Korean martial arts.\n * \n * @param techniqueType - Type of technique requiring shoulder movement\n * @param phase - Technique phase\n * @param side - Which shoulder\n * @returns Shoulder elevation state with vertical displacement\n * \n * @example\n * ```typescript\n * const shoulderState = calculateShoulderElevation('high-block', 'execution', 'left');\n * // Returns: { elevation: 0.04, side: 'left' }\n * ```\n * \n * @korean 블록및상단공격용어깨들어올림계산\n */\nexport function calculateShoulderElevation(\n techniqueType: ShoulderTechniqueType,\n phase: ShoulderPhase,\n side: BodySide\n): ShoulderElevationState {\n let elevation = 0;\n\n switch (techniqueType) {\n case 'high-block':\n if (phase === 'preparation') {\n elevation = -0.02; // Slight drop before elevation\n } else if (phase === 'execution') {\n elevation = 0.04; // Elevate for high block\n } else {\n elevation = 0.01; // Slight elevation maintained\n }\n break;\n\n case 'overhead-strike':\n if (phase === 'preparation') {\n elevation = 0.03; // Pre-elevation for wind-up\n } else if (phase === 'execution') {\n elevation = 0.05; // Maximum elevation for overhead power\n } else {\n elevation = 0.01;\n }\n break;\n\n case 'rising-block':\n if (phase === 'preparation') {\n elevation = -0.03; // Lower for rising motion\n } else if (phase === 'execution') {\n elevation = 0.04; // Elevate as block rises\n } else {\n elevation = 0.02;\n }\n break;\n\n case 'shrug':\n if (phase === 'execution') {\n elevation = 0.05; // Maximum elevation for defensive shrug\n } else {\n elevation = 0;\n }\n break;\n\n case 'neutral':\n elevation = 0;\n break;\n }\n\n // Apply constraints\n elevation = Math.max(\n ADVANCED_JOINT_CONSTRAINTS.SHOULDER_ELEVATION.MIN,\n Math.min(ADVANCED_JOINT_CONSTRAINTS.SHOULDER_ELEVATION.MAX, elevation)\n );\n\n return { elevation, side };\n}\n\n/**\n * Calculate spinal flexion for dodges and low attacks\n * \n * Determines forward/backward and lateral bending of spine for\n * defensive movements and low attack positioning.\n * \n * @param movementType - Type of movement requiring spinal flexion\n * @param intensity - Movement intensity (0=minimal, 1=full)\n * @returns Spinal flexion state with flexion and lateral bend angles\n * \n * @example\n * ```typescript\n * const spineState = calculateSpinalFlexion('duck', 0.8);\n * // Returns: { flexion: 0.6, lateralBend: 0 }\n * ```\n * \n * @korean 회피및하단공격용척추굽힘계산\n */\nexport function calculateSpinalFlexion(\n movementType: SpinalMovementType,\n intensity: number = 1.0\n): SpinalFlexionState {\n let flexion = 0;\n let lateralBend = 0;\n\n // Clamp intensity to 0-1 range\n intensity = Math.max(0, Math.min(1, intensity));\n\n switch (movementType) {\n case 'duck':\n flexion = 0.7 * intensity; // Forward bend for ducking\n lateralBend = 0;\n break;\n\n case 'lean-back':\n flexion = -0.4 * intensity; // Backward bend for evasion\n lateralBend = 0;\n break;\n\n case 'lean-left':\n flexion = 0.1 * intensity; // Slight forward component\n lateralBend = -0.25 * intensity; // Left lateral bend\n break;\n\n case 'lean-right':\n flexion = 0.1 * intensity;\n lateralBend = 0.25 * intensity; // Right lateral bend\n break;\n\n case 'low-attack':\n flexion = 0.5 * intensity; // Forward bend for low strikes\n lateralBend = 0;\n break;\n\n case 'neutral':\n flexion = 0;\n lateralBend = 0;\n break;\n }\n\n // Apply constraints\n flexion = Math.max(\n ADVANCED_JOINT_CONSTRAINTS.SPINAL_FLEXION.FLEXION_MIN,\n Math.min(ADVANCED_JOINT_CONSTRAINTS.SPINAL_FLEXION.FLEXION_MAX, flexion)\n );\n lateralBend = Math.max(\n ADVANCED_JOINT_CONSTRAINTS.SPINAL_FLEXION.LATERAL_MIN,\n Math.min(ADVANCED_JOINT_CONSTRAINTS.SPINAL_FLEXION.LATERAL_MAX, lateralBend)\n );\n\n return { flexion, lateralBend };\n}\n\n/**\n * Calculate knee drive for knee strikes and clinch\n * \n * Determines independent knee positioning for close-range combat,\n * crucial for knee strike power and clinch work positioning.\n * \n * @param technique - Technique requiring knee drive\n * @param phase - Technique phase\n * @param side - Which knee\n * @returns Knee drive state with height and forward distance\n * \n * @example\n * ```typescript\n * const kneeState = calculateKneeDrive('knee-strike', 'execution', 'right');\n * // Returns: { height: 0.7, forward: 0.25, side: 'right' }\n * ```\n * \n * @korean 무릎차기및클린치용무릎밀어올림계산\n */\nexport function calculateKneeDrive(\n technique: KneeTechniqueType,\n phase: KneePhase,\n side: BodySide\n): KneeDriveState {\n let height = 0;\n let forward = 0;\n\n switch (technique) {\n case 'knee-strike':\n if (phase === 'wind-up') {\n height = 0.3; // Partial chamber\n forward = 0.1;\n } else if (phase === 'execution') {\n height = 0.7; // Drive upward\n forward = 0.25; // Drive forward for power\n } else {\n height = 0.2;\n forward = 0.05;\n }\n break;\n\n case 'clinch-control':\n if (phase === 'execution') {\n height = 0.4; // Moderate elevation for control\n forward = 0.15; // Forward pressure\n } else {\n height = 0.2;\n forward = 0.08;\n }\n break;\n\n case 'push-kick':\n if (phase === 'wind-up') {\n height = 0.5; // High chamber\n forward = 0.05;\n } else if (phase === 'execution') {\n height = 0.6; // Maintain height\n forward = 0.3; // Strong forward drive\n } else {\n height = 0.3;\n forward = 0.1;\n }\n break;\n\n case 'neutral':\n height = 0;\n forward = 0;\n break;\n }\n\n // Apply constraints\n height = Math.max(\n ADVANCED_JOINT_CONSTRAINTS.KNEE_DRIVE.HEIGHT_MIN,\n Math.min(ADVANCED_JOINT_CONSTRAINTS.KNEE_DRIVE.HEIGHT_MAX, height)\n );\n forward = Math.max(\n ADVANCED_JOINT_CONSTRAINTS.KNEE_DRIVE.FORWARD_MIN,\n Math.min(ADVANCED_JOINT_CONSTRAINTS.KNEE_DRIVE.FORWARD_MAX, forward)\n );\n\n return { height, forward, side };\n}\n\n/**\n * Calculate knee strike power modifier\n * \n * Determines power bonus based on knee drive height and forward momentum.\n * Greater drive generates more power through proper mechanics.\n * \n * @param kneeState - Current knee drive state\n * @returns Power multiplier (1.0-1.35x, with knee strikes getting up to 35% bonus)\n * \n * @example\n * ```typescript\n * const kneeState = calculateKneeDrive('knee-strike', 'execution', 'right');\n * const power = calculateKneeStrikePowerModifier(kneeState);\n * // Returns ~1.30 for properly executed knee strike\n * ```\n * \n * @korean 무릎차기파워배율계산\n */\nexport function calculateKneeStrikePowerModifier(kneeState: KneeDriveState): number {\n // Normalize height to 0-1 range\n const normalizedHeight = kneeState.height / ADVANCED_JOINT_CONSTRAINTS.KNEE_DRIVE.HEIGHT_MAX;\n \n // Normalize forward drive to 0-1 range\n const normalizedForward = kneeState.forward / ADVANCED_JOINT_CONSTRAINTS.KNEE_DRIVE.FORWARD_MAX;\n \n // Height contributes 60%, forward drive contributes 40%\n const powerFactor = (normalizedHeight * 0.6) + (normalizedForward * 0.4);\n \n // Knee strikes get up to 35% power bonus from proper drive\n return 1.0 + (powerFactor * 0.35);\n}\n\n"],"mappings":";;;;;;;;;;AA+WA,IAAa,6BAA6B;;;;;CAKxC,cAAc;EACZ,aAAa;EACb,aAAa;EACb,cAAc;EACd,cAAc;EACd,gBAAgB;EAChB,gBAAgB;CAClB;;;;;CAMA,oBAAoB;EAClB,KAAK;EACL,KAAK;CACP;;;;;CAMA,oBAAoB;EAClB,aAAa;EACb,aAAa;EACb,eAAe;EACf,eAAe;CACjB;;;;;CAMA,YAAY;EACV,cAAc;EACd,cAAc;EACd,cAAc;CAChB;;;;;CAMA,YAAY;EACV,YAAY;EACZ,YAAY;EACZ,aAAa;EACb,aAAa;CACf;;;;;CAMA,gBAAgB;EACd,aAAa;EACb,aAAa;EACb,aAAa;EACb,aAAa;CACf;AACF;;;;;;;;;;;;;;;;;;;;AA+GA,SAAgB,kCACd,UACA,UACQ;CAER,MAAM,oBAAoB,KAAK,IAAI,SAAS,eAAe,IACzD,2BAA2B,aAAa;CAC1C,MAAM,qBAAqB,KAAK,IAAI,SAAS,gBAAgB,IAC3D,2BAA2B,aAAa;CAC1C,MAAM,uBAAuB,KAAK,IAAI,SAAS,kBAAkB,IAC/D,2BAA2B,aAAa;CAG1C,IAAI,cAAc;CAClB,QAAQ,UAAR;EACE,KAAK;GACH,cAAc,qBAAqB,KAAM,oBAAoB;GAC7D;EACF,KAAK;GACH,cAAc,oBAAoB,KAAM,uBAAuB,KAAM,qBAAqB;GAC1F;EACF,KAAK;GACH,cAAc,oBAAoB,KAAM,uBAAuB;GAC/D;EACF,KAAK;GACH,cAAc,oBAAoB,KAAM,qBAAqB,KAAM,uBAAuB;GAC1F;EACF,KAAK;GACH,cAAc,qBAAqB,KAAM,oBAAoB;GAC7D;CACJ;CAGA,OAAO,IAAO,cAAc;AAC9B"}
|
|
1
|
+
{"version":3,"file":"AdvancedJointMovements.js","names":[],"sources":["../../../../src/systems/animation/systems/AdvancedJointMovements.ts"],"sourcesContent":["/**\n * Advanced Joint Movement System for Korean Martial Arts\n * \n * Extends the base skeletal animation system with specialized joint movements\n * for authentic martial arts technique execution:\n * - Hip rotation for kicks (고관절 회전)\n * - Shoulder elevation/depression (어깨 들어올림)\n * - Ankle articulation (발목 관절)\n * - Wrist snap mechanics (손목 스냅)\n * - Knee drive positioning (무릎 밀어올림)\n * - Spinal flexion/extension (척추 굽힘)\n * \n * @module systems/animation/AdvancedJointMovements\n * @category Animation System\n * @korean 고급관절동작시스템\n */\n\nimport * as THREE from \"three\";\n\n/**\n * Body side for limb-specific movements\n * \n * @korean 신체측면\n */\nexport type BodySide = 'left' | 'right';\n\n/**\n * Kick technique types for hip rotation calculation\n * \n * Korean terminology:\n * - front: 앞차기 (Apchagi)\n * - roundhouse: 돌려차기 (Dollyeochagi)\n * - side: 옆차기 (Yeopchagi)\n * - hook: 후려차기 (Huryeochagi)\n * - axe: 내려차기 (Naeryeochagi)\n * \n * @korean 차기기술유형\n */\nexport type KickType = 'front' | 'roundhouse' | 'side' | 'hook' | 'axe';\n\n/**\n * Hand strike technique types for wrist snap calculation\n * \n * Korean terminology:\n * - backfist: 등주먹 (Deungjumeok)\n * - knife-hand: 수도 (Sudo)\n * - palm-heel: 장저 (Jangjeo)\n * - ridge-hand: 손날등 (Sonnaldeung)\n * - hammer-fist: 망치주먹 (Mangchijumeok)\n * \n * @korean 수격기술유형\n */\nexport type HandStrikeType = 'backfist' | 'knife-hand' | 'palm-heel' | 'ridge-hand' | 'hammer-fist';\n\n/**\n * Kick height levels for hip rotation scaling\n * \n * Korean terminology:\n * - 0: 하단 (Hadan) - Low section\n * - 1: 중단 (Jungdan) - Middle section\n * - 2: 상단 (Sangdan) - High section\n * \n * @korean 차기높이\n */\nexport type KickHeight = 0 | 1 | 2;\n\n/**\n * Technique execution phases\n * \n * Korean terminology:\n * - chamber: 준비 (Junbi) - Preparation/chamber\n * - extension: 확장 (Hwakjang) - Extension\n * - retraction: 회수 (Hoesu) - Retraction\n * \n * @korean 기술단계\n */\nexport type TechniquePhase = 'chamber' | 'extension' | 'retraction';\n\n/**\n * Strike execution phases\n * \n * Korean terminology:\n * - wind-up: 예비동작 (Yebi Dongjak) - Wind-up\n * - impact: 타격 (Tagyeok) - Impact\n * - follow-through: 후속동작 (Husok Dongjak) - Follow-through\n * \n * @korean 타격단계\n */\nexport type StrikePhase = 'wind-up' | 'impact' | 'follow-through';\n\n/**\n * Block and overhead technique types for shoulder elevation\n * \n * Korean terminology:\n * - high-block: 상단막기 (Sangdan Makgi)\n * - overhead-strike: 상단공격 (Sangdan Gonggyeok)\n * - rising-block: 올려막기 (Ollyeo Makgi)\n * - shrug: 어깨올리기 (Eokkae Olligi)\n * - neutral: 중립 (Jungnip)\n * \n * @korean 어깨기술유형\n */\nexport type ShoulderTechniqueType = 'high-block' | 'overhead-strike' | 'rising-block' | 'shrug' | 'neutral';\n\n/**\n * Shoulder and block technique execution phases\n * \n * Korean terminology:\n * - preparation: 준비 (Junbi)\n * - execution: 실행 (Silhaeng)\n * - recovery: 복귀 (Bokgwi)\n * \n * @korean 어깨단계\n */\nexport type ShoulderPhase = 'preparation' | 'execution' | 'recovery';\n\n/**\n * Spinal movement types for dodging and positioning\n * \n * Korean terminology:\n * - duck: 숙이기 (Sugigi) - Duck down\n * - lean-back: 뒤로기울기 (Dwiro Giulgi) - Lean back\n * - lean-left: 왼쪽기울기 (Oenjjok Giulgi) - Lean left\n * - lean-right: 오른쪽기울기 (Oreunjjok Giulgi) - Lean right\n * - low-attack: 낮은공격 (Najeun Gonggyeok) - Low attack\n * - neutral: 중립 (Jungnip) - Neutral\n * \n * @korean 척추동작유형\n */\nexport type SpinalMovementType = 'duck' | 'lean-back' | 'lean-left' | 'lean-right' | 'low-attack' | 'neutral';\n\n/**\n * Knee drive technique types for close-range combat\n * \n * Korean terminology:\n * - knee-strike: 무릎차기 (Mureup Chagi)\n * - clinch-control: 붙잡기제어 (Butjapgi Jeeo)\n * - push-kick: 밀어차기 (Mireo Chagi)\n * - neutral: 중립 (Jungnip)\n * \n * @korean 무릎기술유형\n */\nexport type KneeTechniqueType = 'knee-strike' | 'clinch-control' | 'push-kick' | 'neutral';\n\n/**\n * Knee technique execution phases\n * \n * Korean terminology:\n * - wind-up: 예비동작 (Yebi Dongjak)\n * - execution: 실행 (Silhaeng)\n * - recovery: 복귀 (Bokgwi)\n * \n * @korean 무릎단계\n */\nexport type KneePhase = 'wind-up' | 'execution' | 'recovery';\n\n/**\n * Hip rotation state for dynamic kick mechanics\n * \n * Tracks independent hip rotation in multiple planes for realistic\n * high kicks, roundhouse kicks, and hook kicks. Separates hip rotation\n * from pelvis rotation for authentic martial arts biomechanics.\n * \n * Korean: 고관절 회전 상태\n * \n */\nexport interface HipRotationState {\n /**\n * Frontal plane rotation (abduction/adduction) in radians\n * - Positive: Leg moves away from body centerline (abduction)\n * - Negative: Leg moves toward centerline (adduction)\n * Range: ±1.8 rad (±103°) for high kicks\n * \n * @korean 외전각도\n */\n readonly frontalRotation: number;\n\n /**\n * Sagittal plane rotation (flexion/extension) in radians\n * - Positive: Leg moves forward (flexion)\n * - Negative: Leg moves backward (extension)\n * Range: -1.8 to 1.8 rad for kicks\n * \n * @korean 굴곡각도\n */\n readonly sagittalRotation: number;\n\n /**\n * Transverse plane rotation (internal/external rotation) in radians\n * - Positive: Thigh rotates inward\n * - Negative: Thigh rotates outward\n * Range: ±0.8 rad (±46°)\n * \n * @korean 회전각도\n */\n readonly transverseRotation: number;\n\n /**\n * Which hip (left or right)\n * @korean 측면\n */\n readonly side: BodySide;\n}\n\n/**\n * Shoulder elevation/depression state for blocks and overhead strikes\n * \n * Tracks vertical shoulder movement independent of arm rotation.\n * Essential for authentic blocking mechanics and overhead techniques.\n * \n * Korean: 어깨 들어올림 상태\n * \n */\nexport interface ShoulderElevationState {\n /**\n * Vertical displacement in meters\n * - Positive: Shoulder elevated (shrug)\n * - Negative: Shoulder depressed (drop)\n * Range: ±0.05 m (±5cm)\n * \n * @korean 수직변위\n */\n readonly elevation: number;\n\n /**\n * Which shoulder (left or right)\n * @korean 측면\n */\n readonly side: BodySide;\n}\n\n/**\n * Ankle articulation state for kick chambers and pivots\n * \n * Tracks ankle joint positioning crucial for proper kick mechanics\n * and pivoting in Korean martial arts.\n * \n * Korean: 발목 관절 상태\n * \n */\nexport interface AnkleArticulationState {\n /**\n * Dorsiflexion/plantarflexion angle in radians\n * - Positive: Toes point up (dorsiflexion)\n * - Negative: Toes point down (plantarflexion)\n * Range: -0.8 to 0.5 rad\n * \n * @korean 발등굽힘각도\n */\n readonly flexion: number;\n\n /**\n * Inversion/eversion angle in radians\n * Range: ±0.4 rad (±23°)\n * \n * @korean 안쪽굽힘각도\n */\n readonly inversion: number;\n\n /**\n * Which ankle (left or right)\n * @korean 측면\n */\n readonly side: BodySide;\n}\n\n/**\n * Wrist snap state for rapid hand strikes\n * \n * Tracks rapid wrist rotation mechanics essential for backfist,\n * knife-hand, and other Korean martial arts hand techniques.\n * \n * Korean: 손목 스냅 상태\n * \n */\nexport interface WristSnapState {\n /**\n * Rotation angle in radians\n * Range: ±1.5 rad (±86°)\n * \n * @korean 회전각도\n */\n readonly rotation: number;\n\n /**\n * Snap velocity in rad/s (for power calculation)\n * @korean 회전속도\n */\n readonly velocity: number;\n\n /**\n * Which wrist (left or right)\n * @korean 측면\n */\n readonly side: BodySide;\n}\n\n/**\n * Knee drive state for clinch work and knee strikes\n * \n * Tracks independent knee positioning crucial for close-range\n * combat and knee strike techniques.\n * \n * Korean: 무릎 밀어올림 상태\n * \n */\nexport interface KneeDriveState {\n /**\n * Drive height in meters (vertical displacement)\n * Range: 0 to 0.8 m (80cm max lift)\n * \n * @korean 들어올림높이\n */\n readonly height: number;\n\n /**\n * Forward drive distance in meters\n * Range: 0 to 0.3 m (30cm)\n * \n * @korean 전방거리\n */\n readonly forward: number;\n\n /**\n * Which knee (left or right)\n * @korean 측면\n */\n readonly side: BodySide;\n}\n\n/**\n * Spinal flexion/extension state for dodges and low attacks\n * \n * Tracks forward/backward bending of spine for defensive\n * movements and low attack positioning.\n * \n * Korean: 척추 굽힘 상태\n * \n */\nexport interface SpinalFlexionState {\n /**\n * Flexion angle in radians (forward/backward bend)\n * - Positive: Forward bend\n * - Negative: Backward bend\n * Range: -0.5 to 0.8 rad\n * \n * @korean 굽힘각도\n */\n readonly flexion: number;\n\n /**\n * Lateral bend in radians (side to side)\n * Range: ±0.3 rad (±17°)\n * \n * @korean 측면굽힘각도\n */\n readonly lateralBend: number;\n}\n\n/**\n * Advanced joint movement constraints\n * \n * Defines anatomically correct limits for all advanced joint movements\n * based on human biomechanics and Korean martial arts techniques.\n * \n * @korean 고급관절제약조건\n */\nexport const ADVANCED_JOINT_CONSTRAINTS = {\n /**\n * Hip rotation constraints for kicks\n * @korean 고관절제약조건\n */\n HIP_ROTATION: {\n FRONTAL_MIN: -1.8, // -103° adduction\n FRONTAL_MAX: 1.8, // 103° abduction\n SAGITTAL_MIN: -1.8, // -103° extension\n SAGITTAL_MAX: 1.8, // 103° flexion\n TRANSVERSE_MIN: -0.8, // -46° external rotation\n TRANSVERSE_MAX: 0.8, // 46° internal rotation\n },\n\n /**\n * Shoulder elevation constraints\n * @korean 어깨들어올림제약조건\n */\n SHOULDER_ELEVATION: {\n MIN: -0.05, // -5cm depression\n MAX: 0.05, // 5cm elevation\n },\n\n /**\n * Ankle articulation constraints\n * @korean 발목관절제약조건\n */\n ANKLE_ARTICULATION: {\n FLEXION_MIN: -0.8, // -46° plantarflexion\n FLEXION_MAX: 0.5, // 29° dorsiflexion\n INVERSION_MIN: -0.4, // -23° eversion\n INVERSION_MAX: 0.4, // 23° inversion\n },\n\n /**\n * Wrist snap constraints\n * @korean 손목스냅제약조건\n */\n WRIST_SNAP: {\n ROTATION_MIN: -1.5, // -86°\n ROTATION_MAX: 1.5, // 86°\n MAX_VELOCITY: 30.0, // 30 rad/s for power strikes\n },\n\n /**\n * Knee drive constraints\n * @korean 무릎밀어올림제약조건\n */\n KNEE_DRIVE: {\n HEIGHT_MIN: 0, // Ground level\n HEIGHT_MAX: 0.8, // 80cm max lift\n FORWARD_MIN: 0,\n FORWARD_MAX: 0.3, // 30cm forward\n },\n\n /**\n * Spinal flexion constraints\n * @korean 척추굽힘제약조건\n */\n SPINAL_FLEXION: {\n FLEXION_MIN: -0.5, // -29° extension (backward)\n FLEXION_MAX: 0.8, // 46° flexion (forward)\n LATERAL_MIN: -0.3, // -17° left bend\n LATERAL_MAX: 0.3, // 17° right bend\n },\n} as const;\n\n/**\n * Calculate hip rotation for kick mechanics\n * \n * Determines optimal hip rotation angles for various kick types,\n * ensuring anatomically correct positioning and maximum power generation.\n * \n * @param kickType - Type of kick being performed\n * @param targetHeight - Target height for kick (0=low, 1=medium, 2=high)\n * @param side - Which leg is kicking\n * @returns Hip rotation state with all three plane rotations\n * \n * @example\n * ```typescript\n * const hipState = calculateHipRotationForKick('roundhouse', 2, 'right');\n * // Returns frontal: 1.5, sagittal: 1.3, transverse: 0.6 for high roundhouse\n * ```\n * \n * @korean 차기용고관절회전계산\n */\nexport function calculateHipRotationForKick(\n kickType: KickType,\n targetHeight: KickHeight,\n side: BodySide\n): HipRotationState {\n let frontalRotation = 0;\n let sagittalRotation = 0;\n let transverseRotation = 0;\n\n // Height multiplier (more rotation needed for higher kicks)\n const heightMultiplier = targetHeight === 0 ? 0.5 : targetHeight === 1 ? 0.75 : 1.0;\n\n switch (kickType) {\n case 'front':\n // Front kick: primarily sagittal plane (forward)\n sagittalRotation = 1.5 * heightMultiplier;\n frontalRotation = 0.2; // Slight abduction for balance\n transverseRotation = 0;\n break;\n\n case 'roundhouse':\n // Roundhouse: combination of frontal abduction and transverse rotation\n frontalRotation = 1.5 * heightMultiplier;\n sagittalRotation = 0.8 * heightMultiplier;\n transverseRotation = 0.6 * heightMultiplier; // Internal rotation for power\n break;\n\n case 'side':\n // Side kick: primarily frontal plane (abduction)\n frontalRotation = 1.6 * heightMultiplier;\n sagittalRotation = 0.3;\n transverseRotation = -0.4; // External rotation for proper foot alignment\n break;\n\n case 'hook':\n // Hook kick: high abduction with forward component\n frontalRotation = 1.7 * heightMultiplier;\n sagittalRotation = 1.2 * heightMultiplier;\n transverseRotation = 0.5;\n break;\n\n case 'axe':\n // Axe kick: maximum sagittal flexion, some abduction\n frontalRotation = 0.8 * heightMultiplier;\n sagittalRotation = 1.8 * heightMultiplier; // Maximum forward flexion\n transverseRotation = 0.2;\n break;\n }\n\n // Apply constraints\n frontalRotation = Math.max(\n ADVANCED_JOINT_CONSTRAINTS.HIP_ROTATION.FRONTAL_MIN,\n Math.min(ADVANCED_JOINT_CONSTRAINTS.HIP_ROTATION.FRONTAL_MAX, frontalRotation)\n );\n sagittalRotation = Math.max(\n ADVANCED_JOINT_CONSTRAINTS.HIP_ROTATION.SAGITTAL_MIN,\n Math.min(ADVANCED_JOINT_CONSTRAINTS.HIP_ROTATION.SAGITTAL_MAX, sagittalRotation)\n );\n transverseRotation = Math.max(\n ADVANCED_JOINT_CONSTRAINTS.HIP_ROTATION.TRANSVERSE_MIN,\n Math.min(ADVANCED_JOINT_CONSTRAINTS.HIP_ROTATION.TRANSVERSE_MAX, transverseRotation)\n );\n\n return {\n frontalRotation,\n sagittalRotation,\n transverseRotation,\n side,\n };\n}\n\n/**\n * Calculate kick power modifier from hip rotation\n * \n * Determines power bonus based on proper hip rotation technique.\n * Greater rotation in the correct planes generates more power.\n * \n * @param hipState - Current hip rotation state\n * @param kickType - Type of kick\n * @returns Power multiplier (1.0-1.40x, with kicks getting up to 40% bonus)\n * \n * @example\n * ```typescript\n * const hipState = calculateHipRotationForKick('roundhouse', 2, 'right');\n * const power = calculateKickPowerFromHipRotation(hipState, 'roundhouse');\n * // Returns ~1.35 for properly executed high roundhouse\n * ```\n * \n * @korean 고관절회전에서차기파워계산\n */\nexport function calculateKickPowerFromHipRotation(\n hipState: HipRotationState,\n kickType: KickType\n): number {\n // Normalize rotations to 0-1 range based on constraints\n const normalizedFrontal = Math.abs(hipState.frontalRotation) / \n ADVANCED_JOINT_CONSTRAINTS.HIP_ROTATION.FRONTAL_MAX;\n const normalizedSagittal = Math.abs(hipState.sagittalRotation) / \n ADVANCED_JOINT_CONSTRAINTS.HIP_ROTATION.SAGITTAL_MAX;\n const normalizedTransverse = Math.abs(hipState.transverseRotation) / \n ADVANCED_JOINT_CONSTRAINTS.HIP_ROTATION.TRANSVERSE_MAX;\n\n // Different kicks benefit from different rotation planes\n let powerFactor = 0;\n switch (kickType) {\n case 'front':\n powerFactor = normalizedSagittal * 0.8 + normalizedFrontal * 0.2;\n break;\n case 'roundhouse':\n powerFactor = normalizedFrontal * 0.5 + normalizedTransverse * 0.3 + normalizedSagittal * 0.2;\n break;\n case 'side':\n powerFactor = normalizedFrontal * 0.7 + normalizedTransverse * 0.3;\n break;\n case 'hook':\n powerFactor = normalizedFrontal * 0.4 + normalizedSagittal * 0.4 + normalizedTransverse * 0.2;\n break;\n case 'axe':\n powerFactor = normalizedSagittal * 0.8 + normalizedFrontal * 0.2;\n break;\n }\n\n // Kicks can get up to 40% power bonus from proper hip rotation\n return 1.0 + (powerFactor * 0.40);\n}\n\n/**\n * Apply hip rotation to Three.js Euler angles\n * \n * Converts hip rotation state into Three.js Euler angles for rendering.\n * \n * @param hipState - Hip rotation state\n * @returns Three.js Euler angles (X=sagittal, Y=transverse, Z=frontal)\n * \n * @korean 고관절회전을오일러각으로적용\n */\nexport function applyHipRotationToEuler(hipState: HipRotationState): THREE.Euler {\n return new THREE.Euler(\n hipState.sagittalRotation, // X-axis: forward/backward\n hipState.transverseRotation, // Y-axis: twist\n hipState.frontalRotation, // Z-axis: side to side\n 'XYZ'\n );\n}\n\n/**\n * Calculate ankle articulation for kick chamber\n * \n * Determines proper ankle positioning for kick chambering and extension,\n * crucial for proper foot alignment and power transfer in Korean martial arts.\n * \n * @param kickType - Type of kick being chambered\n * @param phase - Kick phase: 'chamber', 'extension', 'retraction'\n * @param side - Which ankle\n * @returns Ankle articulation state with flexion and inversion angles\n * \n * @example\n * ```typescript\n * const ankleState = calculateAnkleArticulation('front', 'extension', 'right');\n * // Returns: { flexion: 0.5, inversion: 0, side: 'right' }\n * ```\n * \n * @korean 차기챔버용발목관절계산\n */\nexport function calculateAnkleArticulation(\n kickType: KickType,\n phase: TechniquePhase,\n side: BodySide\n): AnkleArticulationState {\n let flexion = 0;\n let inversion = 0;\n\n switch (kickType) {\n case 'front':\n if (phase === 'chamber') {\n flexion = 0.2; // Slight dorsiflexion in chamber\n } else if (phase === 'extension') {\n flexion = 0.5; // Strong dorsiflexion for striking with ball of foot\n } else {\n flexion = 0.1; // Slight flexion during retraction\n }\n inversion = 0; // Neutral for front kicks\n break;\n\n case 'roundhouse':\n if (phase === 'chamber') {\n flexion = -0.3; // Plantarflexion in chamber\n } else if (phase === 'extension') {\n flexion = -0.5; // Strong plantarflexion for instep strike\n inversion = -0.2; // Slight eversion to align instep\n } else {\n flexion = -0.2;\n inversion = 0;\n }\n break;\n\n case 'side':\n if (phase === 'chamber') {\n flexion = 0.3; // Dorsiflexion in chamber\n } else if (phase === 'extension') {\n flexion = 0.4; // Dorsiflexion for heel/blade strike\n inversion = 0.3; // Inversion to present blade edge\n } else {\n flexion = 0.2;\n inversion = 0.1;\n }\n break;\n\n case 'hook':\n if (phase === 'chamber') {\n flexion = -0.2;\n } else if (phase === 'extension') {\n flexion = -0.4; // Plantarflexion for heel strike\n inversion = -0.15; // Slight eversion\n } else {\n flexion = -0.1;\n inversion = 0;\n }\n break;\n\n case 'axe':\n if (phase === 'chamber') {\n flexion = 0.1; // Minimal flexion in high chamber\n } else if (phase === 'extension') {\n flexion = 0.5; // Strong dorsiflexion for downward strike\n } else {\n flexion = 0.2;\n }\n inversion = 0; // Neutral for axe kicks\n break;\n }\n\n // Apply constraints\n flexion = Math.max(\n ADVANCED_JOINT_CONSTRAINTS.ANKLE_ARTICULATION.FLEXION_MIN,\n Math.min(ADVANCED_JOINT_CONSTRAINTS.ANKLE_ARTICULATION.FLEXION_MAX, flexion)\n );\n inversion = Math.max(\n ADVANCED_JOINT_CONSTRAINTS.ANKLE_ARTICULATION.INVERSION_MIN,\n Math.min(ADVANCED_JOINT_CONSTRAINTS.ANKLE_ARTICULATION.INVERSION_MAX, inversion)\n );\n\n return { flexion, inversion, side };\n}\n\n/**\n * Calculate wrist snap for hand strikes\n * \n * Determines wrist rotation and velocity for power generation in\n * backfist, knife-hand, and other Korean martial arts hand techniques.\n * \n * @param strikeType - Type of hand strike\n * @param phase - Strike phase: 'wind-up', 'impact', 'follow-through'\n * @param side - Which wrist\n * @returns Wrist snap state with rotation angle and velocity\n * \n * @example\n * ```typescript\n * const wristState = calculateWristSnap('backfist', 'impact', 'right');\n * // Returns: { rotation: 1.2, velocity: 25.0, side: 'right' }\n * ```\n * \n * @korean 수격용손목스냅계산\n */\nexport function calculateWristSnap(\n strikeType: HandStrikeType,\n phase: StrikePhase,\n side: BodySide\n): WristSnapState {\n let rotation = 0;\n let velocity = 0;\n\n switch (strikeType) {\n case 'backfist':\n if (phase === 'wind-up') {\n rotation = -0.8; // Cocked back\n velocity = 0;\n } else if (phase === 'impact') {\n rotation = 1.2; // Snapped forward\n velocity = 25.0; // High velocity snap\n } else {\n rotation = 0.5;\n velocity = 5.0;\n }\n break;\n\n case 'knife-hand':\n if (phase === 'wind-up') {\n rotation = -0.5;\n velocity = 0;\n } else if (phase === 'impact') {\n rotation = 0.8; // Moderate snap for precision\n velocity = 20.0;\n } else {\n rotation = 0.3;\n velocity = 3.0;\n }\n break;\n\n case 'palm-heel':\n if (phase === 'wind-up') {\n rotation = -0.3; // Minimal wind-up\n velocity = 0;\n } else if (phase === 'impact') {\n rotation = 0.5; // Push through\n velocity = 15.0; // Lower velocity, more push\n } else {\n rotation = 0.2;\n velocity = 2.0;\n }\n break;\n\n case 'ridge-hand':\n if (phase === 'wind-up') {\n rotation = 0.8; // Opposite direction\n velocity = 0;\n } else if (phase === 'impact') {\n rotation = -1.0; // Reverse snap\n velocity = 22.0;\n } else {\n rotation = -0.4;\n velocity = 4.0;\n }\n break;\n\n case 'hammer-fist':\n if (phase === 'wind-up') {\n rotation = 0.2; // Minimal rotation\n velocity = 0;\n } else if (phase === 'impact') {\n rotation = 0.3; // Downward strike, less rotation\n velocity = 18.0;\n } else {\n rotation = 0.1;\n velocity = 2.0;\n }\n break;\n }\n\n // Apply constraints\n rotation = Math.max(\n ADVANCED_JOINT_CONSTRAINTS.WRIST_SNAP.ROTATION_MIN,\n Math.min(ADVANCED_JOINT_CONSTRAINTS.WRIST_SNAP.ROTATION_MAX, rotation)\n );\n velocity = Math.min(ADVANCED_JOINT_CONSTRAINTS.WRIST_SNAP.MAX_VELOCITY, Math.abs(velocity));\n\n return { rotation, velocity, side };\n}\n\n/**\n * Calculate wrist snap power modifier\n * \n * Determines power bonus based on wrist snap velocity and rotation.\n * Higher velocity generates more power for whipping strikes.\n * \n * @param wristState - Current wrist snap state\n * @returns Power multiplier (1.0-1.25x, with hand strikes getting up to 25% bonus)\n * \n * @example\n * ```typescript\n * const wristState = calculateWristSnap('backfist', 'impact', 'right');\n * const power = calculateWristSnapPowerModifier(wristState);\n * // Returns ~1.20 for proper backfist snap\n * ```\n * \n * @korean 손목스냅파워배율계산\n */\nexport function calculateWristSnapPowerModifier(wristState: WristSnapState): number {\n // Normalize velocity to 0-1 range\n const normalizedVelocity = wristState.velocity / ADVANCED_JOINT_CONSTRAINTS.WRIST_SNAP.MAX_VELOCITY;\n \n // Normalize rotation magnitude to 0-1 range\n const normalizedRotation = Math.abs(wristState.rotation) / ADVANCED_JOINT_CONSTRAINTS.WRIST_SNAP.ROTATION_MAX;\n \n // Velocity contributes 70%, rotation contributes 30%\n const powerFactor = (normalizedVelocity * 0.7) + (normalizedRotation * 0.3);\n \n // Hand strikes get up to 25% power bonus from wrist snap\n return 1.0 + (powerFactor * 0.25);\n}\n\n/**\n * Calculate shoulder elevation for blocks and overhead strikes\n * \n * Determines vertical shoulder movement for defensive blocks and\n * overhead striking techniques in Korean martial arts.\n * \n * @param techniqueType - Type of technique requiring shoulder movement\n * @param phase - Technique phase\n * @param side - Which shoulder\n * @returns Shoulder elevation state with vertical displacement\n * \n * @example\n * ```typescript\n * const shoulderState = calculateShoulderElevation('high-block', 'execution', 'left');\n * // Returns: { elevation: 0.04, side: 'left' }\n * ```\n * \n * @korean 블록및상단공격용어깨들어올림계산\n */\nexport function calculateShoulderElevation(\n techniqueType: ShoulderTechniqueType,\n phase: ShoulderPhase,\n side: BodySide\n): ShoulderElevationState {\n let elevation = 0;\n\n switch (techniqueType) {\n case 'high-block':\n if (phase === 'preparation') {\n elevation = -0.02; // Slight drop before elevation\n } else if (phase === 'execution') {\n elevation = 0.04; // Elevate for high block\n } else {\n elevation = 0.01; // Slight elevation maintained\n }\n break;\n\n case 'overhead-strike':\n if (phase === 'preparation') {\n elevation = 0.03; // Pre-elevation for wind-up\n } else if (phase === 'execution') {\n elevation = 0.05; // Maximum elevation for overhead power\n } else {\n elevation = 0.01;\n }\n break;\n\n case 'rising-block':\n if (phase === 'preparation') {\n elevation = -0.03; // Lower for rising motion\n } else if (phase === 'execution') {\n elevation = 0.04; // Elevate as block rises\n } else {\n elevation = 0.02;\n }\n break;\n\n case 'shrug':\n if (phase === 'execution') {\n elevation = 0.05; // Maximum elevation for defensive shrug\n } else {\n elevation = 0;\n }\n break;\n\n case 'neutral':\n elevation = 0;\n break;\n }\n\n // Apply constraints\n elevation = Math.max(\n ADVANCED_JOINT_CONSTRAINTS.SHOULDER_ELEVATION.MIN,\n Math.min(ADVANCED_JOINT_CONSTRAINTS.SHOULDER_ELEVATION.MAX, elevation)\n );\n\n return { elevation, side };\n}\n\n/**\n * Calculate spinal flexion for dodges and low attacks\n * \n * Determines forward/backward and lateral bending of spine for\n * defensive movements and low attack positioning.\n * \n * @param movementType - Type of movement requiring spinal flexion\n * @param intensity - Movement intensity (0=minimal, 1=full)\n * @returns Spinal flexion state with flexion and lateral bend angles\n * \n * @example\n * ```typescript\n * const spineState = calculateSpinalFlexion('duck', 0.8);\n * // Returns: { flexion: 0.6, lateralBend: 0 }\n * ```\n * \n * @korean 회피및하단공격용척추굽힘계산\n */\nexport function calculateSpinalFlexion(\n movementType: SpinalMovementType,\n intensity: number = 1.0\n): SpinalFlexionState {\n let flexion = 0;\n let lateralBend = 0;\n\n // Clamp intensity to 0-1 range\n intensity = Math.max(0, Math.min(1, intensity));\n\n switch (movementType) {\n case 'duck':\n flexion = 0.7 * intensity; // Forward bend for ducking\n lateralBend = 0;\n break;\n\n case 'lean-back':\n flexion = -0.4 * intensity; // Backward bend for evasion\n lateralBend = 0;\n break;\n\n case 'lean-left':\n flexion = 0.1 * intensity; // Slight forward component\n lateralBend = -0.25 * intensity; // Left lateral bend\n break;\n\n case 'lean-right':\n flexion = 0.1 * intensity;\n lateralBend = 0.25 * intensity; // Right lateral bend\n break;\n\n case 'low-attack':\n flexion = 0.5 * intensity; // Forward bend for low strikes\n lateralBend = 0;\n break;\n\n case 'neutral':\n flexion = 0;\n lateralBend = 0;\n break;\n }\n\n // Apply constraints\n flexion = Math.max(\n ADVANCED_JOINT_CONSTRAINTS.SPINAL_FLEXION.FLEXION_MIN,\n Math.min(ADVANCED_JOINT_CONSTRAINTS.SPINAL_FLEXION.FLEXION_MAX, flexion)\n );\n lateralBend = Math.max(\n ADVANCED_JOINT_CONSTRAINTS.SPINAL_FLEXION.LATERAL_MIN,\n Math.min(ADVANCED_JOINT_CONSTRAINTS.SPINAL_FLEXION.LATERAL_MAX, lateralBend)\n );\n\n return { flexion, lateralBend };\n}\n\n/**\n * Calculate knee drive for knee strikes and clinch\n * \n * Determines independent knee positioning for close-range combat,\n * crucial for knee strike power and clinch work positioning.\n * \n * @param technique - Technique requiring knee drive\n * @param phase - Technique phase\n * @param side - Which knee\n * @returns Knee drive state with height and forward distance\n * \n * @example\n * ```typescript\n * const kneeState = calculateKneeDrive('knee-strike', 'execution', 'right');\n * // Returns: { height: 0.7, forward: 0.25, side: 'right' }\n * ```\n * \n * @korean 무릎차기및클린치용무릎밀어올림계산\n */\nexport function calculateKneeDrive(\n technique: KneeTechniqueType,\n phase: KneePhase,\n side: BodySide\n): KneeDriveState {\n let height = 0;\n let forward = 0;\n\n switch (technique) {\n case 'knee-strike':\n if (phase === 'wind-up') {\n height = 0.3; // Partial chamber\n forward = 0.1;\n } else if (phase === 'execution') {\n height = 0.7; // Drive upward\n forward = 0.25; // Drive forward for power\n } else {\n height = 0.2;\n forward = 0.05;\n }\n break;\n\n case 'clinch-control':\n if (phase === 'execution') {\n height = 0.4; // Moderate elevation for control\n forward = 0.15; // Forward pressure\n } else {\n height = 0.2;\n forward = 0.08;\n }\n break;\n\n case 'push-kick':\n if (phase === 'wind-up') {\n height = 0.5; // High chamber\n forward = 0.05;\n } else if (phase === 'execution') {\n height = 0.6; // Maintain height\n forward = 0.3; // Strong forward drive\n } else {\n height = 0.3;\n forward = 0.1;\n }\n break;\n\n case 'neutral':\n height = 0;\n forward = 0;\n break;\n }\n\n // Apply constraints\n height = Math.max(\n ADVANCED_JOINT_CONSTRAINTS.KNEE_DRIVE.HEIGHT_MIN,\n Math.min(ADVANCED_JOINT_CONSTRAINTS.KNEE_DRIVE.HEIGHT_MAX, height)\n );\n forward = Math.max(\n ADVANCED_JOINT_CONSTRAINTS.KNEE_DRIVE.FORWARD_MIN,\n Math.min(ADVANCED_JOINT_CONSTRAINTS.KNEE_DRIVE.FORWARD_MAX, forward)\n );\n\n return { height, forward, side };\n}\n\n/**\n * Calculate knee strike power modifier\n * \n * Determines power bonus based on knee drive height and forward momentum.\n * Greater drive generates more power through proper mechanics.\n * \n * @param kneeState - Current knee drive state\n * @returns Power multiplier (1.0-1.35x, with knee strikes getting up to 35% bonus)\n * \n * @example\n * ```typescript\n * const kneeState = calculateKneeDrive('knee-strike', 'execution', 'right');\n * const power = calculateKneeStrikePowerModifier(kneeState);\n * // Returns ~1.30 for properly executed knee strike\n * ```\n * \n * @korean 무릎차기파워배율계산\n */\nexport function calculateKneeStrikePowerModifier(kneeState: KneeDriveState): number {\n // Normalize height to 0-1 range\n const normalizedHeight = kneeState.height / ADVANCED_JOINT_CONSTRAINTS.KNEE_DRIVE.HEIGHT_MAX;\n \n // Normalize forward drive to 0-1 range\n const normalizedForward = kneeState.forward / ADVANCED_JOINT_CONSTRAINTS.KNEE_DRIVE.FORWARD_MAX;\n \n // Height contributes 60%, forward drive contributes 40%\n const powerFactor = (normalizedHeight * 0.6) + (normalizedForward * 0.4);\n \n // Knee strikes get up to 35% power bonus from proper drive\n return 1.0 + (powerFactor * 0.35);\n}\n\n"],"mappings":";;;;;;;;;;AA+WA,IAAa,6BAA6B;;;;;CAKxC,cAAc;EACZ,aAAa;EACb,aAAa;EACb,cAAc;EACd,cAAc;EACd,gBAAgB;EAChB,gBAAgB;CAClB;;;;;CAMA,oBAAoB;EAClB,KAAK;EACL,KAAK;CACP;;;;;CAMA,oBAAoB;EAClB,aAAa;EACb,aAAa;EACb,eAAe;EACf,eAAe;CACjB;;;;;CAMA,YAAY;EACV,cAAc;EACd,cAAc;EACd,cAAc;CAChB;;;;;CAMA,YAAY;EACV,YAAY;EACZ,YAAY;EACZ,aAAa;EACb,aAAa;CACf;;;;;CAMA,gBAAgB;EACd,aAAa;EACb,aAAa;EACb,aAAa;EACb,aAAa;CACf;AACF;;;;;;;;;;;;;;;;;;;;AA+GA,SAAgB,kCACd,UACA,UACQ;CAER,MAAM,oBAAoB,KAAK,IAAI,SAAS,eAAe,IACzD,2BAA2B,aAAa;CAC1C,MAAM,qBAAqB,KAAK,IAAI,SAAS,gBAAgB,IAC3D,2BAA2B,aAAa;CAC1C,MAAM,uBAAuB,KAAK,IAAI,SAAS,kBAAkB,IAC/D,2BAA2B,aAAa;CAG1C,IAAI,cAAc;CAClB,QAAQ,UAAR;EACE,KAAK;GACH,cAAc,qBAAqB,KAAM,oBAAoB;GAC7D;EACF,KAAK;GACH,cAAc,oBAAoB,KAAM,uBAAuB,KAAM,qBAAqB;GAC1F;EACF,KAAK;GACH,cAAc,oBAAoB,KAAM,uBAAuB;GAC/D;EACF,KAAK;GACH,cAAc,oBAAoB,KAAM,qBAAqB,KAAM,uBAAuB;GAC1F;EACF,KAAK,OACH,cAAc,qBAAqB,KAAM,oBAAoB;CAEjE;CAGA,OAAO,IAAO,cAAc;AAC9B"}
|
|
@@ -50,7 +50,8 @@ var STANCE_TO_BIOMECH_KEY = {
|
|
|
50
50
|
*/
|
|
51
51
|
var getMuscleTensionForStance = (stance) => {
|
|
52
52
|
const activations = /* @__PURE__ */ new Map();
|
|
53
|
-
const
|
|
53
|
+
const biomechKey = STANCE_TO_BIOMECH_KEY[stance];
|
|
54
|
+
const biomech = KOREAN_STANCE_BIOMECHANICS[biomechKey];
|
|
54
55
|
if (!biomech) {
|
|
55
56
|
activations.set("QUAD_L", .2);
|
|
56
57
|
activations.set("QUAD_R", .2);
|
|
@@ -237,9 +238,7 @@ var getMuscleActivationForTechnique = (technique) => {
|
|
|
237
238
|
activations.set("CALF_L", .5);
|
|
238
239
|
activations.set("CALF_R", .5);
|
|
239
240
|
break;
|
|
240
|
-
default:
|
|
241
|
-
activations.set("CORE", .3);
|
|
242
|
-
break;
|
|
241
|
+
default: activations.set("CORE", .3);
|
|
243
242
|
}
|
|
244
243
|
return activations;
|
|
245
244
|
};
|