blacktrigram 0.7.39 → 0.7.40
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/App2.js.map +1 -1
- package/lib/audio/AudioAssetLoader.js.map +1 -1
- package/lib/audio/AudioAssetRegistry.js.map +1 -1
- package/lib/audio/AudioCache.js.map +1 -1
- package/lib/audio/AudioManager.js.map +1 -1
- package/lib/audio/AudioMonitor.js.map +1 -1
- package/lib/audio/AudioPool.js.map +1 -1
- package/lib/audio/AudioProvider.js.map +1 -1
- package/lib/audio/AudioUtils.js.map +1 -1
- package/lib/audio/BoneImpactAudioMap.js.map +1 -1
- package/lib/audio/VariantSelector.js.map +1 -1
- package/lib/audio/types.js.map +1 -1
- package/lib/components/screens/combat/CombatScreen3D.js.map +1 -1
- package/lib/components/screens/combat/components/controls/CombatButtons.js.map +1 -1
- package/lib/components/screens/combat/components/controls/CombatControlsPanel.js.map +1 -1
- package/lib/components/screens/combat/components/controls/ControlsGuide.js.map +1 -1
- package/lib/components/screens/combat/components/controls/KeyboardHints.js.map +1 -1
- package/lib/components/screens/combat/components/controls/PauseMenu.js.map +1 -1
- package/lib/components/screens/combat/components/controls/PauseMenuButton.js.map +1 -1
- package/lib/components/screens/combat/components/controls/QuickSettings.js.map +1 -1
- package/lib/components/screens/combat/components/effects/BloodDecals3D.js.map +1 -1
- package/lib/components/screens/combat/components/effects/BloodLossOverlayHtml.js.map +1 -1
- package/lib/components/screens/combat/components/effects/BloodParticles3D.js.map +1 -1
- package/lib/components/screens/combat/components/effects/BloodViscosity3D.js.map +1 -1
- package/lib/components/screens/combat/components/effects/CombatParticleEffects3D.js.map +1 -1
- package/lib/components/screens/combat/components/effects/ConsciousnessBlur.js.map +1 -1
- package/lib/components/screens/combat/components/effects/InternalDamage3D.js.map +1 -1
- package/lib/components/screens/combat/components/effects/PainVignette.js.map +1 -1
- package/lib/components/screens/combat/components/effects/ParticleAudio3D.js.map +1 -1
- package/lib/components/screens/combat/components/effects/TraumaOverlay3D.js.map +1 -1
- package/lib/components/screens/combat/components/feedback/MatchCountdown.js.map +1 -1
- package/lib/components/screens/combat/components/feedback/RoundAnnouncementOverlayHtml.js.map +1 -1
- package/lib/components/screens/combat/components/feedback/RoundDisplayStatus.js.map +1 -1
- package/lib/components/screens/combat/components/feedback/RoundStartAnnouncementOverlayHtml.js.map +1 -1
- package/lib/components/screens/combat/components/hud/CombatBottomHUD.js.map +1 -1
- package/lib/components/screens/combat/components/hud/CombatLeftHUD.js.map +1 -1
- package/lib/components/screens/combat/components/hud/CombatPortraitStatusStrip.js.map +1 -1
- package/lib/components/screens/combat/components/hud/CombatRightHUD.js.map +1 -1
- package/lib/components/screens/combat/components/hud/CombatTopHUD.js.map +1 -1
- package/lib/components/screens/combat/components/hud/DifficultyIndicator.js.map +1 -1
- package/lib/components/screens/combat/components/hud/FPSMonitor.js.map +1 -1
- package/lib/components/screens/combat/components/hud/MobileControlsWrapper.js.map +1 -1
- package/lib/components/screens/combat/components/hud/PlayerStateOverlayHtml.js.map +1 -1
- package/lib/components/screens/combat/components/indicators/BalanceIndicator.js.map +1 -1
- package/lib/components/screens/combat/components/indicators/InputBufferDisplay.js.map +1 -1
- package/lib/components/screens/combat/components/indicators/StaminaWarning.js.map +1 -1
- package/lib/components/screens/combat/components/indicators/TechniqueNameDisplay.js.map +1 -1
- package/lib/components/screens/combat/helpers/AnimationUpdater.js.map +1 -1
- package/lib/components/screens/combat/helpers/combatHelpers.js.map +1 -1
- package/lib/components/screens/combat/hooks/useAICombat.js.map +1 -1
- package/lib/components/screens/combat/hooks/useCombatActions.js.map +1 -1
- package/lib/components/screens/combat/hooks/useCombatAttackMovement.js.map +1 -1
- package/lib/components/screens/combat/hooks/useCombatAudio.js.map +1 -1
- package/lib/components/screens/combat/hooks/useCombatLayout.js.map +1 -1
- package/lib/components/screens/combat/hooks/useCombatState.js.map +1 -1
- package/lib/components/screens/controls/ControlsScreen3D.js.map +1 -1
- package/lib/components/screens/controls/components/ControlBindingsOverlayHtml.js.map +1 -1
- package/lib/components/screens/controls/components/ControlCategoryTabsOverlayHtml.js.map +1 -1
- package/lib/components/screens/controls/components/GamepadVisualization3D.js.map +1 -1
- package/lib/components/screens/controls/components/InteractiveControlDemoOverlayHtml.js.map +1 -1
- package/lib/components/screens/controls/components/Key3D.js.map +1 -1
- package/lib/components/screens/controls/components/VisualKeyboard3D.js.map +1 -1
- package/lib/components/screens/controls/constants/ControlsConstants.js.map +1 -1
- package/lib/components/screens/controls/hooks/useControlsState.js.map +1 -1
- package/lib/components/screens/endscreen/EndScreen3D.js.map +1 -1
- package/lib/components/screens/endscreen/components/DefeatAnimation3D.js.map +1 -1
- package/lib/components/screens/endscreen/components/MatchStatisticsDisplayOverlayHtml.js.map +1 -1
- package/lib/components/screens/endscreen/components/NavigationButtonsOverlayHtml.js.map +1 -1
- package/lib/components/screens/endscreen/components/PerformanceBreakdownOverlayHtml.js.map +1 -1
- package/lib/components/screens/endscreen/components/PerformanceRatingOverlayHtml.js.map +1 -1
- package/lib/components/screens/endscreen/components/VictoryAnimation3D.js.map +1 -1
- package/lib/components/screens/endscreen/components/WinnerDisplayOverlayHtml.js.map +1 -1
- package/lib/components/screens/intro/IntroScreen3D.js +1 -1
- package/lib/components/screens/intro/IntroScreen3D.js.map +1 -1
- package/lib/components/screens/intro/components/AbilityListOverlayHtml.js.map +1 -1
- package/lib/components/screens/intro/components/ArchetypeCardGridOverlayHtml.js.map +1 -1
- package/lib/components/screens/intro/components/ArchetypeCardOverlayHtml.js.map +1 -1
- package/lib/components/screens/intro/components/ArchetypeDisplayOverlayHtml.js.map +1 -1
- package/lib/components/screens/intro/components/EnhancedArchetypeDisplayOverlayHtml.js.map +1 -1
- package/lib/components/screens/intro/components/MenuButtonsOverlayHtml.js.map +1 -1
- package/lib/components/screens/intro/components/MenuSectionOverlayHtml.js.map +1 -1
- package/lib/components/screens/intro/components/StatBarOverlayHtml.js.map +1 -1
- package/lib/components/screens/philosophy/PhilosophyScreen3D.js.map +1 -1
- package/lib/components/screens/training/TrainingScreen3D.js.map +1 -1
- package/lib/components/screens/training/components/AnatomyControlsOverlayHtml.js.map +1 -1
- package/lib/components/screens/training/components/AnatomyOverlay3D.js.map +1 -1
- package/lib/components/screens/training/components/FootPlacementMarkers3D.js.map +1 -1
- package/lib/components/screens/training/components/FootworkDrillsOverlayHtml.js.map +1 -1
- package/lib/components/screens/training/components/HitFeedbackEffect3D.js.map +1 -1
- package/lib/components/screens/training/components/TrainingButtonsOverlayHtml.js.map +1 -1
- package/lib/components/screens/training/components/TrainingControlsOverlayHtml.js.map +1 -1
- package/lib/components/screens/training/components/TrainingDummy3D.js.map +1 -1
- package/lib/components/screens/training/components/TrainingFeedbackOverlayHtml.js.map +1 -1
- package/lib/components/screens/training/components/TrainingModeSelectorOverlayHtml.js.map +1 -1
- package/lib/components/screens/training/components/TrainingStatsOverlayHtml.js.map +1 -1
- package/lib/components/screens/training/components/VitalPointMarker3D.js.map +1 -1
- package/lib/components/screens/training/components/VitalPointTrainingOverlayHtml.js.map +1 -1
- package/lib/components/screens/training/components/hud/TrainingBottomHUD.js.map +1 -1
- package/lib/components/screens/training/components/hud/TrainingLeftHUD.js.map +1 -1
- package/lib/components/screens/training/components/hud/TrainingRightHUD.js.map +1 -1
- package/lib/components/screens/training/components/hud/TrainingTopHUD.js.map +1 -1
- package/lib/components/screens/training/hooks/useAttackMovement.js.map +1 -1
- package/lib/components/screens/training/hooks/useTrainingActions.js.map +1 -1
- package/lib/components/screens/training/hooks/useTrainingLayout.js.map +1 -1
- package/lib/components/screens/training/hooks/useTrainingState.js.map +1 -1
- package/lib/components/shared/base/BaseButton.js.map +1 -1
- package/lib/components/shared/base/BaseButtonOverlayHtml.js.map +1 -1
- package/lib/components/shared/base/BasePanel.js.map +1 -1
- package/lib/components/shared/base/BaseText.js.map +1 -1
- package/lib/components/shared/base/useKoreanTheme.js.map +1 -1
- package/lib/components/shared/debug/PerformanceDebugOverlayHtml.js.map +1 -1
- package/lib/components/shared/mobile/ActionButtons.js.map +1 -1
- package/lib/components/shared/mobile/GestureRecognizerPure.js.map +1 -1
- package/lib/components/shared/mobile/HapticController.js.map +1 -1
- package/lib/components/shared/mobile/MobileControlsPure.js.map +1 -1
- package/lib/components/shared/mobile/StanceWheelPure.js.map +1 -1
- package/lib/components/shared/mobile/TouchOptimizer.js.map +1 -1
- package/lib/components/shared/mobile/VirtualDPad.js.map +1 -1
- package/lib/components/shared/three/anatomy/BodySurface.js.map +1 -1
- package/lib/components/shared/three/anatomy/BoneAttachedMuscles.js.map +1 -1
- package/lib/components/shared/three/anatomy/BoneClothing.js.map +1 -1
- package/lib/components/shared/three/anatomy/BoneRenderer.js.map +1 -1
- package/lib/components/shared/three/anatomy/Face3D.js.map +1 -1
- package/lib/components/shared/three/anatomy/Foot3D.js.map +1 -1
- package/lib/components/shared/three/anatomy/Hand3D.js.map +1 -1
- package/lib/components/shared/three/effects/ActionFeedback.js.map +1 -1
- package/lib/components/shared/three/effects/DamageNumbers.js.map +1 -1
- package/lib/components/shared/three/effects/HitEffects3D.js.map +1 -1
- package/lib/components/shared/three/effects/PlayerStateIndicators.js.map +1 -1
- package/lib/components/shared/three/effects/StanceSymbol3D.js.map +1 -1
- package/lib/components/shared/three/effects/StanceTransitionEffect.js.map +1 -1
- package/lib/components/shared/three/effects/VitalPointMarkers3D.js.map +1 -1
- package/lib/components/shared/three/indicators/ElementalColorSystem.js.map +1 -1
- package/lib/components/shared/three/indicators/GuardIndicator.js.map +1 -1
- package/lib/components/shared/three/indicators/HapticFeedback.js.map +1 -1
- package/lib/components/shared/three/indicators/StanceChangeIndicator.js.map +1 -1
- package/lib/components/shared/three/models/Player3DWithTransitions.js.map +1 -1
- package/lib/components/shared/three/models/SkeletalPlayer3D.js.map +1 -1
- package/lib/components/shared/three/optimization/AdaptiveQuality.js.map +1 -1
- package/lib/components/shared/three/scene/AtmosphericParticles3D.js.map +1 -1
- package/lib/components/shared/three/scene/BackgroundScene3D.js.map +1 -1
- package/lib/components/shared/three/scene/CombatArena3D.js.map +1 -1
- package/lib/components/shared/three/scene/KoreanSignage3D.js.map +1 -1
- package/lib/components/shared/three/ui/ArchetypeCard.js.map +1 -1
- package/lib/components/shared/three/ui/BodyPartHealthDisplay.js.map +1 -1
- package/lib/components/shared/three/ui/BreathingIndicator2.js.map +1 -1
- package/lib/components/shared/three/ui/CombatReadinessBar.js.map +1 -1
- package/lib/components/shared/three/ui/ComboCounter.js.map +1 -1
- package/lib/components/shared/three/ui/HealthBar.js.map +1 -1
- package/lib/components/shared/three/ui/KoreanButton.js.map +1 -1
- package/lib/components/shared/three/ui/KoreanPanel.js.map +1 -1
- package/lib/components/shared/three/ui/KoreanText.js.map +1 -1
- package/lib/components/shared/three/ui/MenuList.js.map +1 -1
- package/lib/components/shared/three/ui/PlayerHUD.js.map +1 -1
- package/lib/components/shared/three/ui/ProgressBar.js.map +1 -1
- package/lib/components/shared/three/ui/SpeedIndicatorHUD.js.map +1 -1
- package/lib/components/shared/three/ui/StaminaBar.js.map +1 -1
- package/lib/components/shared/three/ui/TechniqueBar.js.map +1 -1
- package/lib/components/shared/three/ui/TechniqueCard.js.map +1 -1
- package/lib/components/shared/three/ui/VitalPointOverlayControlsHtml.js.map +1 -1
- package/lib/components/shared/ui/BackButton.js.map +1 -1
- package/lib/components/shared/ui/BaseHUDContainer.js.map +1 -1
- package/lib/components/shared/ui/CombatTimer.js.map +1 -1
- package/lib/components/shared/ui/ErrorModal.js.map +1 -1
- package/lib/components/shared/ui/LoadingState.js.map +1 -1
- package/lib/components/shared/ui/SplashScreen.js +2 -2
- package/lib/components/shared/ui/SplashScreen.js.map +1 -1
- package/lib/components/shared/ui/VitalPointOverlayControlsPure.js.map +1 -1
- package/lib/components/shared/ui/VolumeControl.js.map +1 -1
- package/lib/components/shared/ui/shared/ConfirmDialog.js.map +1 -1
- package/lib/components/ui/combat/BalanceIndicatorOverlayHtml.js.map +1 -1
- package/lib/constants/bodyDimensions.js.map +1 -1
- package/lib/constants/bodyRenderingConstants.js.map +1 -1
- package/lib/data/archetypeClothing.js.map +1 -1
- package/lib/data/archetypePhysicalAttributes.js.map +1 -1
- package/lib/data/techniqueMappings.js.map +1 -1
- package/lib/data/techniques.js.map +1 -1
- package/lib/hooks/useActionFeedback.js.map +1 -1
- package/lib/hooks/useBalanceAnimations.js.map +1 -1
- package/lib/hooks/useCombatTimer.js.map +1 -1
- package/lib/hooks/useDebounce.js.map +1 -1
- package/lib/hooks/useHUDLayout.js.map +1 -1
- package/lib/hooks/useHandPoseTransitions.js.map +1 -1
- package/lib/hooks/useKeyboardControls.js.map +1 -1
- package/lib/hooks/useMatchCountdown.js.map +1 -1
- package/lib/hooks/useMuscleActivation.js.map +1 -1
- package/lib/hooks/usePauseMenu.js.map +1 -1
- package/lib/hooks/usePlayerAnimation.js.map +1 -1
- package/lib/hooks/useResponsiveLayout.js.map +1 -1
- package/lib/hooks/useRoundTransition.js.map +1 -1
- package/lib/hooks/useSkeletalAnimation.js.map +1 -1
- package/lib/hooks/useTechniqueSelection.js.map +1 -1
- package/lib/hooks/useThrottle.js.map +1 -1
- package/lib/hooks/useTouchControls.js.map +1 -1
- package/lib/hooks/useWebGLContextLossHandler.js.map +1 -1
- package/lib/hooks/useWindowSize.js.map +1 -1
- package/lib/systems/CombatSystem.js.map +1 -1
- package/lib/systems/EffectCalculator.js.map +1 -1
- package/lib/systems/LayoutSystem.js.map +1 -1
- package/lib/systems/PlayerEffectManager.js.map +1 -1
- package/lib/systems/ResponsiveScaling.js.map +1 -1
- package/lib/systems/TrigramSystem.js.map +1 -1
- package/lib/systems/VitalPointSystem.js.map +1 -1
- package/lib/systems/ai/AIPersonality.js.map +1 -1
- package/lib/systems/ai/AdaptiveDifficulty.js.map +1 -1
- package/lib/systems/ai/ArchetypeEnforcer.js.map +1 -1
- package/lib/systems/ai/ComboSystem.js.map +1 -1
- package/lib/systems/ai/DecisionTree.js.map +1 -1
- package/lib/systems/ai/TrainingAI.js.map +1 -1
- package/lib/systems/ai/types.js.map +1 -1
- package/lib/systems/animation/builders/AnimationBuilder.js.map +1 -1
- package/lib/systems/animation/builders/HandPoseApplicator.js.map +1 -1
- package/lib/systems/animation/builders/HandPoses.js.map +1 -1
- package/lib/systems/animation/builders/KeyframeConfig.js.map +1 -1
- package/lib/systems/animation/builders/KeyframeInterpolation.js +3 -90
- package/lib/systems/animation/builders/KeyframeInterpolation.js.map +1 -1
- package/lib/systems/animation/builders/KickPhaseApplicator.js.map +1 -1
- package/lib/systems/animation/builders/KoreanGuardPositions.js.map +1 -1
- package/lib/systems/animation/builders/MartialArtsAnimationBuilder.js.map +1 -1
- package/lib/systems/animation/builders/MartialArtsConstants.js.map +1 -1
- package/lib/systems/animation/builders/MartialPoseApplicator.js.map +1 -1
- package/lib/systems/animation/builders/PunchPhaseApplicator.js.map +1 -1
- package/lib/systems/animation/builders/SkeletonRig.js.map +1 -1
- package/lib/systems/animation/builders/TrigramGuardApplicator.js.map +1 -1
- package/lib/systems/animation/catalogs/DefensiveAnimations.js.map +1 -1
- package/lib/systems/animation/catalogs/FootworkSkeletalAnimations.js.map +1 -1
- package/lib/systems/animation/catalogs/RecoveryAnimations.js.map +1 -1
- package/lib/systems/animation/catalogs/StanceAnimations.js.map +1 -1
- package/lib/systems/animation/catalogs/StanceAttackAnimations.js.map +1 -1
- package/lib/systems/animation/catalogs/StanceGuardPoses.js.map +1 -1
- package/lib/systems/animation/catalogs/StanceIdleAnimations.js.map +1 -1
- package/lib/systems/animation/catalogs/StanceLocomotionAnimations.js.map +1 -1
- package/lib/systems/animation/catalogs/StepSkeletalAnimations.js.map +1 -1
- package/lib/systems/animation/core/AnimationHitTiming.js.map +1 -1
- package/lib/systems/animation/core/AnimationOptimizations.js.map +1 -1
- package/lib/systems/animation/core/AnimationPriority.js.map +1 -1
- package/lib/systems/animation/core/AnimationRegistry.js.map +1 -1
- package/lib/systems/animation/core/AnimationStateMachine.js.map +1 -1
- package/lib/systems/animation/core/AnimationTransitions.js.map +1 -1
- package/lib/systems/animation/core/LateralityTransform.js.map +1 -1
- package/lib/systems/animation/core/RecoveryPhaseEnhancer.js.map +1 -1
- package/lib/systems/animation/core/TechniqueAnimationMapper.js.map +1 -1
- package/lib/systems/animation/core/TechniqueAnimationMapping.js.map +1 -1
- package/lib/systems/animation/core/types.js.map +1 -1
- package/lib/systems/animation/systems/AdvancedJointMovements.js.map +1 -1
- package/lib/systems/animation/systems/BodyFacingSystem.js.map +1 -1
- package/lib/systems/animation/systems/FacialExpressions.js.map +1 -1
- package/lib/systems/animation/systems/FallAnimations.js.map +1 -1
- package/lib/systems/animation/systems/MuscleActivation.js.map +1 -1
- package/lib/systems/bodypart/BodyPartDamageIntegration.js.map +1 -1
- package/lib/systems/bodypart/BodyPartHealthSystem.js.map +1 -1
- package/lib/systems/bodypart/BodyPartPositionMapping.js.map +1 -1
- package/lib/systems/bodypart/CombatInjuryIntegration.js.map +1 -1
- package/lib/systems/bodypart/InjuryIntegration.js.map +1 -1
- package/lib/systems/bodypart/InjuryTracker.js.map +1 -1
- package/lib/systems/bodypart/MovementPenaltySystem.js.map +1 -1
- package/lib/systems/bodypart/PlayerInjuryTrackingManager.js.map +1 -1
- package/lib/systems/bodypart/types.js.map +1 -1
- package/lib/systems/breathing/BreathingDisruptionSystem.js.map +1 -1
- package/lib/systems/breathing/feedback.js.map +1 -1
- package/lib/systems/breathing/integration.js.map +1 -1
- package/lib/systems/combat/BalanceSystem.js.map +1 -1
- package/lib/systems/combat/BreakingStatusEffects.js.map +1 -1
- package/lib/systems/combat/CombatStateSystem.js.map +1 -1
- package/lib/systems/combat/ConsciousnessSystem.js.map +1 -1
- package/lib/systems/combat/FallIntegration.js.map +1 -1
- package/lib/systems/combat/GrappleSystem.js.map +1 -1
- package/lib/systems/combat/LimbExposureSystem.js.map +1 -1
- package/lib/systems/combat/PainResponseSystem.js.map +1 -1
- package/lib/systems/combat/TrainingCombatSystem.js.map +1 -1
- package/lib/systems/combat/painConsciousnessUtils.js.map +1 -1
- package/lib/systems/combat/typeGuards.js.map +1 -1
- package/lib/systems/effects.js.map +1 -1
- package/lib/systems/game.js.map +1 -1
- package/lib/systems/movement/InjuryMovementModifier.js.map +1 -1
- package/lib/systems/movement/helpers/AccelerationUpdater.js.map +1 -1
- package/lib/systems/movement/helpers/accelerationUtils.js.map +1 -1
- package/lib/systems/movement/integration.js.map +1 -1
- package/lib/systems/physics/AttackMovementPhysics.js.map +1 -1
- package/lib/systems/physics/CollisionDetection.js.map +1 -1
- package/lib/systems/physics/CoordinateMapper.js.map +1 -1
- package/lib/systems/physics/KnockbackPhysics.js.map +1 -1
- package/lib/systems/physics/MovementPhysics.js.map +1 -1
- package/lib/systems/physics/PhysicalReachCalculator.js.map +1 -1
- package/lib/systems/physics/SpeedModifierSystem.js.map +1 -1
- package/lib/systems/trigram/KoreanCulture.js.map +1 -1
- package/lib/systems/trigram/KoreanTechniques.js.map +1 -1
- package/lib/systems/trigram/StanceManager.js.map +1 -1
- package/lib/systems/trigram/TransitionCalculator.js.map +1 -1
- package/lib/systems/trigram/TrigramCalculator.js.map +1 -1
- package/lib/systems/trigram/techniques/DarkOpsTechniques.js.map +1 -1
- package/lib/systems/trigram/techniques/GamTechniques.js.map +1 -1
- package/lib/systems/trigram/techniques/GanTechniques.js.map +1 -1
- package/lib/systems/trigram/techniques/GonTechniques.js.map +1 -1
- package/lib/systems/trigram/techniques/SonTechniques.js.map +1 -1
- package/lib/systems/trigram/techniques/TechniqueConfig.js.map +1 -1
- package/lib/systems/trigram/techniques/index.js.map +1 -1
- package/lib/systems/trigram/types/GonTechniqueExtensions.js.map +1 -1
- package/lib/systems/trigram/types.js.map +1 -1
- package/lib/systems/types.js.map +1 -1
- package/lib/systems/vitalpoint/DamageCalculator.js.map +1 -1
- package/lib/systems/vitalpoint/HitDetection.js.map +1 -1
- package/lib/systems/vitalpoint/KoreanAnatomy.js.map +1 -1
- package/lib/systems/vitalpoint/KoreanVitalPoints.js.map +1 -1
- package/lib/systems/vitalpoint/MeridianVitalPointMapping.js.map +1 -1
- package/lib/types/AccessibilityTypes.js.map +1 -1
- package/lib/types/PhysicsTypes.js.map +1 -1
- package/lib/types/common.js.map +1 -1
- package/lib/types/constants/colors.js.map +1 -1
- package/lib/types/constants/designSystem.js.map +1 -1
- package/lib/types/constants/layout.js.map +1 -1
- package/lib/types/constants/performance.js.map +1 -1
- package/lib/types/constants/typography.js.map +1 -1
- package/lib/types/facial.js.map +1 -1
- package/lib/types/hand-animation.js.map +1 -1
- package/lib/types/injury.js.map +1 -1
- package/lib/types/physics.js.map +1 -1
- package/lib/types/skeletal.js.map +1 -1
- package/lib/types/techniqueId.js.map +1 -1
- package/lib/utils/accessibility.js.map +1 -1
- package/lib/utils/arenaWorldDimensions.js.map +1 -1
- package/lib/utils/assetConfig.js.map +1 -1
- package/lib/utils/characterScaling.js.map +1 -1
- package/lib/utils/colorHelpers.js.map +1 -1
- package/lib/utils/colorUtils.js.map +1 -1
- package/lib/utils/combatReadiness.js.map +1 -1
- package/lib/utils/controlMapping.js.map +1 -1
- package/lib/utils/deviceDetection.js.map +1 -1
- package/lib/utils/effectUtils.js.map +1 -1
- package/lib/utils/fabricTextures.js.map +1 -1
- package/lib/utils/hapticFeedback.js.map +1 -1
- package/lib/utils/haptics.js.map +1 -1
- package/lib/utils/htmlOverlayHelpers.js.map +1 -1
- package/lib/utils/inputSystem.js.map +1 -1
- package/lib/utils/koreanThemeHelpers.js.map +1 -1
- package/lib/utils/math.js.map +1 -1
- package/lib/utils/mobileLayoutHelpers.js.map +1 -1
- package/lib/utils/mobileUIUtils.js.map +1 -1
- package/lib/utils/performance/PerformanceMonitor.js.map +1 -1
- package/lib/utils/performance/PerformanceOverlay3D.js.map +1 -1
- package/lib/utils/performance/usePerformanceMonitor.js.map +1 -1
- package/lib/utils/performanceOptimization.js.map +1 -1
- package/lib/utils/player3DHelpers.js.map +1 -1
- package/lib/utils/playerUtils.js.map +1 -1
- package/lib/utils/responsiveLayout.js.map +1 -1
- package/lib/utils/responsiveLayoutHelpers.js.map +1 -1
- package/lib/utils/responsiveOrientationConstants.js.map +1 -1
- package/lib/utils/safeAreaUtils.js.map +1 -1
- package/lib/utils/sharedPhysicsConfig.js.map +1 -1
- package/lib/utils/skeletonScaling.js.map +1 -1
- package/lib/utils/stanceHelpers.js.map +1 -1
- package/lib/utils/threeObjectPool.js.map +1 -1
- package/lib/utils/visualEffects.js.map +1 -1
- package/package.json +5 -5
package/lib/types/facial.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"facial.js","names":[],"sources":["../../src/types/facial.ts"],"sourcesContent":["/**\n * Facial animation types for realistic combat emotion and damage\n * \n * Defines facial expression system with damage tracking, eye tracking,\n * and smooth expression transitions for authentic human-like combat feedback.\n * \n * @module types/facial\n * @category Type Definitions\n * @korean 얼굴애니메이션타입\n */\n\nimport * as THREE from \"three\";\n\n/**\n * Facial expression states for combat\n * \n * Represents emotional and physical states visible on fighter's face.\n * Each expression corresponds to specific combat situations.\n * \n * @public\n * @category Facial Animation\n * @korean 얼굴표정\n */\nexport enum FacialExpression {\n /** Calm, ready state */\n NEUTRAL = \"neutral\",\n /** Concentrated, ready to attack */\n FOCUSED = \"focused\",\n /** Pain response after hit */\n PAINED = \"pained\",\n /** Low stamina, heavy breathing */\n EXHAUSTED = \"exhausted\",\n /** Brief satisfaction after successful strike */\n VICTORIOUS = \"victorious\",\n /** Knocked out, unconscious */\n DEFEATED = \"defeated\",\n}\n\n/**\n * Bilingual names for facial expressions\n * \n * @public\n * @category Facial Animation\n * @korean 표정이름\n */\nexport const FACIAL_EXPRESSION_NAMES: Record<\n FacialExpression,\n { korean: string; english: string; romanized: string }\n> = {\n [FacialExpression.NEUTRAL]: {\n korean: \"평온\",\n english: \"Neutral\",\n romanized: \"pyeong-on\",\n },\n [FacialExpression.FOCUSED]: {\n korean: \"집중\",\n english: \"Focused\",\n romanized: \"jipjung\",\n },\n [FacialExpression.PAINED]: {\n korean: \"고통\",\n english: \"Pained\",\n romanized: \"gotong\",\n },\n [FacialExpression.EXHAUSTED]: {\n korean: \"지침\",\n english: \"Exhausted\",\n romanized: \"jichim\",\n },\n [FacialExpression.VICTORIOUS]: {\n korean: \"승리\",\n english: \"Victorious\",\n romanized: \"seungri\",\n },\n [FacialExpression.DEFEATED]: {\n korean: \"패배\",\n english: \"Defeated\",\n romanized: \"paebae\",\n },\n};\n\n/**\n * Facial damage state tracking\n * \n * Tracks accumulated damage to face for visual feedback.\n * Includes bruising, swelling, and bleeding effects.\n * \n * @public\n * @category Facial Animation\n * @korean 얼굴손상상태\n */\nexport interface FacialDamageState {\n /** Left eye swelling (0-1, 0=none, 1=fully swollen) */\n readonly leftEyeSwelling: number;\n \n /** Right eye swelling (0-1, 0=none, 1=fully swollen) */\n readonly rightEyeSwelling: number;\n \n /** Mouth/lip bleeding intensity (0-1) */\n readonly mouthBleeding: number;\n \n /** Nose bleeding intensity (0-1) */\n readonly noseBleeding: number;\n \n /** Bruise intensity on left cheek (0-1) */\n readonly leftCheekBruise: number;\n \n /** Bruise intensity on right cheek (0-1) */\n readonly rightCheekBruise: number;\n \n /** Forehead bruise/cut intensity (0-1) */\n readonly foreheadBruise: number;\n \n /** Jaw bruise intensity (0-1) */\n readonly jawBruise: number;\n \n /** Total facial damage accumulation (0-100) */\n readonly totalFacialDamage: number;\n}\n\n/**\n * Expression state with transition timing\n * \n * Manages current expression and smooth transition to next expression.\n * \n * @public\n * @category Facial Animation\n * @korean 표정상태\n */\nexport interface ExpressionState {\n /** Current facial expression */\n readonly expression: FacialExpression;\n \n /** Expression intensity (0-1, affects degree of expression) */\n readonly intensity: number;\n \n /** Time to transition to new expression (seconds) */\n readonly transitionTime: number;\n \n /** Previous expression (for blending) */\n readonly previousExpression?: FacialExpression;\n \n /** Transition progress (0-1, 0=start, 1=complete) */\n readonly transitionProgress?: number;\n}\n\n/**\n * Eye openness values for expressions\n * \n * @public\n * @category Facial Animation\n * @korean 눈개방도\n */\nexport const EYE_OPENNESS: Record<FacialExpression, number> = {\n [FacialExpression.NEUTRAL]: 1.0, // Fully open\n [FacialExpression.FOCUSED]: 0.7, // Narrowed\n [FacialExpression.PAINED]: 0.3, // Squinted\n [FacialExpression.EXHAUSTED]: 0.5, // Half-closed\n [FacialExpression.VICTORIOUS]: 0.9, // Nearly fully open\n [FacialExpression.DEFEATED]: 0.0, // Closed\n};\n\n/**\n * Mouth openness values for expressions\n * \n * @public\n * @category Facial Animation\n * @korean 입개방도\n */\nexport const MOUTH_OPENNESS: Record<FacialExpression, number> = {\n [FacialExpression.NEUTRAL]: 0.0, // Closed\n [FacialExpression.FOCUSED]: 0.0, // Closed, determined\n [FacialExpression.PAINED]: 0.8, // Open, yelling\n [FacialExpression.EXHAUSTED]: 0.4, // Panting\n [FacialExpression.VICTORIOUS]: 0.2, // Slight smile\n [FacialExpression.DEFEATED]: 0.1, // Slack\n};\n\n/**\n * Head movement animation type\n * \n * Defines types of head movements for combat reactions.\n * \n * @public\n * @category Facial Animation\n * @korean 머리움직임타입\n */\nexport enum HeadMovementType {\n /** Head snaps back when hit */\n RECOIL = \"recoil\",\n /** Slight nod forward during attack */\n NOD = \"nod\",\n /** Head tilts to avoid strike */\n TILT = \"tilt\",\n /** Head turns to track opponent */\n TURN = \"turn\",\n /** Head shakes when stunned */\n SHAKE = \"shake\",\n /** Head drops when defeated */\n DROP = \"drop\",\n}\n\n/**\n * Head movement animation keyframes\n * \n * Sequence of Euler rotations for head movement animations.\n * \n * @public\n * @category Facial Animation\n * @korean 머리움직임키프레임\n */\nexport interface HeadMovementKeyframes {\n /** Movement type identifier */\n readonly type: HeadMovementType;\n \n /** Sequence of rotation keyframes */\n readonly rotations: readonly THREE.Euler[];\n \n /** Duration of each keyframe in seconds */\n readonly frameDuration: number;\n \n /** Total animation duration in seconds */\n readonly totalDuration: number;\n \n /** Whether animation loops */\n readonly loop: boolean;\n}\n\n/**\n * Eye tracking state\n * \n * Manages eye direction and pupil position for opponent tracking.\n * \n * @public\n * @category Facial Animation\n * @korean 눈추적상태\n */\nexport interface EyeTrackingState {\n /** Target position to look at (opponent position) */\n readonly targetPosition: THREE.Vector3;\n \n /** Current look direction (normalized) */\n readonly lookDirection: THREE.Vector3;\n \n /** Pupil offset from center (-1 to 1 for each axis) */\n readonly pupilOffset: { x: number; y: number };\n \n /** Tracking speed (smoothing factor) */\n readonly trackingSpeed: number;\n \n /** Whether tracking is enabled */\n readonly enabled: boolean;\n}\n\n/**\n * Default facial damage state (no damage)\n * \n * @public\n * @category Facial Animation\n * @korean 기본얼굴손상상태\n */\nexport const DEFAULT_FACIAL_DAMAGE: FacialDamageState = {\n leftEyeSwelling: 0,\n rightEyeSwelling: 0,\n mouthBleeding: 0,\n noseBleeding: 0,\n leftCheekBruise: 0,\n rightCheekBruise: 0,\n foreheadBruise: 0,\n jawBruise: 0,\n totalFacialDamage: 0,\n};\n\n/**\n * Default expression state (neutral)\n * \n * @public\n * @category Facial Animation\n * @korean 기본표정상태\n */\nexport const DEFAULT_EXPRESSION_STATE: ExpressionState = {\n expression: FacialExpression.NEUTRAL,\n intensity: 1.0,\n transitionTime: 0.2,\n};\n\n/**\n * Default eye tracking state\n * \n * @public\n * @category Facial Animation\n * @korean 기본눈추적상태\n */\nexport const DEFAULT_EYE_TRACKING: EyeTrackingState = {\n targetPosition: new THREE.Vector3(0, 1.9, 5), // Looking forward\n lookDirection: new THREE.Vector3(0, 0, 1), // Forward\n pupilOffset: { x: 0, y: 0 },\n trackingSpeed: 0.1,\n enabled: true,\n};\n\n/**\n * Face3D component props\n * \n * @public\n * @category Facial Animation\n * @korean 얼굴3D속성\n */\nexport interface Face3DProps {\n /** Current facial expression */\n readonly expression: FacialExpression;\n \n /** Facial damage state */\n readonly damage: FacialDamageState;\n \n /** Opponent position for eye tracking */\n readonly opponentPosition: THREE.Vector3;\n \n /** Current head rotation (Euler angles) */\n readonly headRotation: THREE.Euler;\n \n /** Enable eye tracking (default: true) */\n readonly enableEyeTracking?: boolean;\n \n /** Enable damage visualization (default: true) */\n readonly enableDamageVisuals?: boolean;\n \n /** Mobile mode (simplified rendering) */\n readonly isMobile?: boolean;\n \n /** Skin tone color (default: 0xffdbac) */\n readonly skinColor?: number;\n}\n\n/**\n * Eye component props\n * \n * @public\n * @category Facial Animation\n * @korean 눈3D속성\n */\nexport interface EyeProps {\n /** Eye position relative to head */\n readonly position: [number, number, number];\n \n /** Current facial expression */\n readonly expression: FacialExpression;\n \n /** Look direction for pupil tracking */\n readonly lookDirection: THREE.Vector3;\n \n /** Eye swelling amount (0-1) */\n readonly swelling: number;\n \n /** Whether this is the left or right eye */\n readonly side: \"left\" | \"right\";\n}\n\n/**\n * Mouth component props\n * \n * @public\n * @category Facial Animation\n * @korean 입3D속성\n */\nexport interface MouthProps {\n /** Mouth position relative to head */\n readonly position: [number, number, number];\n \n /** Current facial expression */\n readonly expression: FacialExpression;\n \n /** Bleeding intensity (0-1) */\n readonly bleeding: number;\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;AAuBA,IAAY,mBAAL,yBAAA,kBAAA;;AAEL,kBAAA,aAAU;;AAEV,kBAAA,aAAU;;AAEV,kBAAA,YAAS;;AAET,kBAAA,eAAY;;AAEZ,kBAAA,gBAAa;;AAEb,kBAAA,cAAW;;KACZ;;;;;;;;AASD,IAAa,0BAGT;EACD,iBAAiB,UAAU;EAC1B,QAAQ;EACR,SAAS;EACT,WAAW;EACZ;EACA,iBAAiB,UAAU;EAC1B,QAAQ;EACR,SAAS;EACT,WAAW;EACZ;EACA,iBAAiB,SAAS;EACzB,QAAQ;EACR,SAAS;EACT,WAAW;EACZ;EACA,iBAAiB,YAAY;EAC5B,QAAQ;EACR,SAAS;EACT,WAAW;EACZ;EACA,iBAAiB,aAAa;EAC7B,QAAQ;EACR,SAAS;EACT,WAAW;EACZ;EACA,iBAAiB,WAAW;EAC3B,QAAQ;EACR,SAAS;EACT,WAAW;EACZ;CACF;;;;;;;;AA0ED,IAAa,eAAiD;EAC3D,iBAAiB,UAAU;EAC3B,iBAAiB,UAAU;EAC3B,iBAAiB,SAAS;EAC1B,iBAAiB,YAAY;EAC7B,iBAAiB,aAAa;EAC9B,iBAAiB,WAAW;CAC9B;;;;;;;;AASD,IAAa,iBAAmD;EAC7D,iBAAiB,UAAU;EAC3B,iBAAiB,UAAU;EAC3B,iBAAiB,SAAS;EAC1B,iBAAiB,YAAY;EAC7B,iBAAiB,aAAa;EAC9B,iBAAiB,WAAW;CAC9B;;;;;;;;;;AAWD,IAAY,mBAAL,yBAAA,kBAAA;;AAEL,kBAAA,YAAS;;AAET,kBAAA,SAAM;;AAEN,kBAAA,UAAO;;AAEP,kBAAA,UAAO;;AAEP,kBAAA,WAAQ;;AAER,kBAAA,UAAO;;KACR;;;;;;;;AA6DD,IAAa,wBAA2C;CACtD,iBAAiB;CACjB,kBAAkB;CAClB,eAAe;CACf,cAAc;CACd,iBAAiB;CACjB,kBAAkB;CAClB,gBAAgB;CAChB,WAAW;CACX,mBAAmB;CACpB;;;;;;;;AASD,IAAa,2BAA4C;CACvD,YAAY,iBAAiB;CAC7B,WAAW;CACX,gBAAgB;CACjB;;;;;;;;AASD,IAAa,uBAAyC;CACpD,gBAAgB,IAAI,MAAM,QAAQ,GAAG,KAAK,EAAE;CAC5C,eAAe,IAAI,MAAM,QAAQ,GAAG,GAAG,EAAE;CACzC,aAAa;EAAE,GAAG;EAAG,GAAG;EAAG;CAC3B,eAAe;CACf,SAAS;CACV"}
|
|
1
|
+
{"version":3,"file":"facial.js","names":[],"sources":["../../src/types/facial.ts"],"sourcesContent":["/**\n * Facial animation types for realistic combat emotion and damage\n * \n * Defines facial expression system with damage tracking, eye tracking,\n * and smooth expression transitions for authentic human-like combat feedback.\n * \n * @module types/facial\n * @category Type Definitions\n * @korean 얼굴애니메이션타입\n */\n\nimport * as THREE from \"three\";\n\n/**\n * Facial expression states for combat\n * \n * Represents emotional and physical states visible on fighter's face.\n * Each expression corresponds to specific combat situations.\n * \n * @public\n * @category Facial Animation\n * @korean 얼굴표정\n */\nexport enum FacialExpression {\n /** Calm, ready state */\n NEUTRAL = \"neutral\",\n /** Concentrated, ready to attack */\n FOCUSED = \"focused\",\n /** Pain response after hit */\n PAINED = \"pained\",\n /** Low stamina, heavy breathing */\n EXHAUSTED = \"exhausted\",\n /** Brief satisfaction after successful strike */\n VICTORIOUS = \"victorious\",\n /** Knocked out, unconscious */\n DEFEATED = \"defeated\",\n}\n\n/**\n * Bilingual names for facial expressions\n * \n * @public\n * @category Facial Animation\n * @korean 표정이름\n */\nexport const FACIAL_EXPRESSION_NAMES: Record<\n FacialExpression,\n { korean: string; english: string; romanized: string }\n> = {\n [FacialExpression.NEUTRAL]: {\n korean: \"평온\",\n english: \"Neutral\",\n romanized: \"pyeong-on\",\n },\n [FacialExpression.FOCUSED]: {\n korean: \"집중\",\n english: \"Focused\",\n romanized: \"jipjung\",\n },\n [FacialExpression.PAINED]: {\n korean: \"고통\",\n english: \"Pained\",\n romanized: \"gotong\",\n },\n [FacialExpression.EXHAUSTED]: {\n korean: \"지침\",\n english: \"Exhausted\",\n romanized: \"jichim\",\n },\n [FacialExpression.VICTORIOUS]: {\n korean: \"승리\",\n english: \"Victorious\",\n romanized: \"seungri\",\n },\n [FacialExpression.DEFEATED]: {\n korean: \"패배\",\n english: \"Defeated\",\n romanized: \"paebae\",\n },\n};\n\n/**\n * Facial damage state tracking\n * \n * Tracks accumulated damage to face for visual feedback.\n * Includes bruising, swelling, and bleeding effects.\n * \n * @public\n * @category Facial Animation\n * @korean 얼굴손상상태\n */\nexport interface FacialDamageState {\n /** Left eye swelling (0-1, 0=none, 1=fully swollen) */\n readonly leftEyeSwelling: number;\n \n /** Right eye swelling (0-1, 0=none, 1=fully swollen) */\n readonly rightEyeSwelling: number;\n \n /** Mouth/lip bleeding intensity (0-1) */\n readonly mouthBleeding: number;\n \n /** Nose bleeding intensity (0-1) */\n readonly noseBleeding: number;\n \n /** Bruise intensity on left cheek (0-1) */\n readonly leftCheekBruise: number;\n \n /** Bruise intensity on right cheek (0-1) */\n readonly rightCheekBruise: number;\n \n /** Forehead bruise/cut intensity (0-1) */\n readonly foreheadBruise: number;\n \n /** Jaw bruise intensity (0-1) */\n readonly jawBruise: number;\n \n /** Total facial damage accumulation (0-100) */\n readonly totalFacialDamage: number;\n}\n\n/**\n * Expression state with transition timing\n * \n * Manages current expression and smooth transition to next expression.\n * \n * @public\n * @category Facial Animation\n * @korean 표정상태\n */\nexport interface ExpressionState {\n /** Current facial expression */\n readonly expression: FacialExpression;\n \n /** Expression intensity (0-1, affects degree of expression) */\n readonly intensity: number;\n \n /** Time to transition to new expression (seconds) */\n readonly transitionTime: number;\n \n /** Previous expression (for blending) */\n readonly previousExpression?: FacialExpression;\n \n /** Transition progress (0-1, 0=start, 1=complete) */\n readonly transitionProgress?: number;\n}\n\n/**\n * Eye openness values for expressions\n * \n * @public\n * @category Facial Animation\n * @korean 눈개방도\n */\nexport const EYE_OPENNESS: Record<FacialExpression, number> = {\n [FacialExpression.NEUTRAL]: 1.0, // Fully open\n [FacialExpression.FOCUSED]: 0.7, // Narrowed\n [FacialExpression.PAINED]: 0.3, // Squinted\n [FacialExpression.EXHAUSTED]: 0.5, // Half-closed\n [FacialExpression.VICTORIOUS]: 0.9, // Nearly fully open\n [FacialExpression.DEFEATED]: 0.0, // Closed\n};\n\n/**\n * Mouth openness values for expressions\n * \n * @public\n * @category Facial Animation\n * @korean 입개방도\n */\nexport const MOUTH_OPENNESS: Record<FacialExpression, number> = {\n [FacialExpression.NEUTRAL]: 0.0, // Closed\n [FacialExpression.FOCUSED]: 0.0, // Closed, determined\n [FacialExpression.PAINED]: 0.8, // Open, yelling\n [FacialExpression.EXHAUSTED]: 0.4, // Panting\n [FacialExpression.VICTORIOUS]: 0.2, // Slight smile\n [FacialExpression.DEFEATED]: 0.1, // Slack\n};\n\n/**\n * Head movement animation type\n * \n * Defines types of head movements for combat reactions.\n * \n * @public\n * @category Facial Animation\n * @korean 머리움직임타입\n */\nexport enum HeadMovementType {\n /** Head snaps back when hit */\n RECOIL = \"recoil\",\n /** Slight nod forward during attack */\n NOD = \"nod\",\n /** Head tilts to avoid strike */\n TILT = \"tilt\",\n /** Head turns to track opponent */\n TURN = \"turn\",\n /** Head shakes when stunned */\n SHAKE = \"shake\",\n /** Head drops when defeated */\n DROP = \"drop\",\n}\n\n/**\n * Head movement animation keyframes\n * \n * Sequence of Euler rotations for head movement animations.\n * \n * @public\n * @category Facial Animation\n * @korean 머리움직임키프레임\n */\nexport interface HeadMovementKeyframes {\n /** Movement type identifier */\n readonly type: HeadMovementType;\n \n /** Sequence of rotation keyframes */\n readonly rotations: readonly THREE.Euler[];\n \n /** Duration of each keyframe in seconds */\n readonly frameDuration: number;\n \n /** Total animation duration in seconds */\n readonly totalDuration: number;\n \n /** Whether animation loops */\n readonly loop: boolean;\n}\n\n/**\n * Eye tracking state\n * \n * Manages eye direction and pupil position for opponent tracking.\n * \n * @public\n * @category Facial Animation\n * @korean 눈추적상태\n */\nexport interface EyeTrackingState {\n /** Target position to look at (opponent position) */\n readonly targetPosition: THREE.Vector3;\n \n /** Current look direction (normalized) */\n readonly lookDirection: THREE.Vector3;\n \n /** Pupil offset from center (-1 to 1 for each axis) */\n readonly pupilOffset: { x: number; y: number };\n \n /** Tracking speed (smoothing factor) */\n readonly trackingSpeed: number;\n \n /** Whether tracking is enabled */\n readonly enabled: boolean;\n}\n\n/**\n * Default facial damage state (no damage)\n * \n * @public\n * @category Facial Animation\n * @korean 기본얼굴손상상태\n */\nexport const DEFAULT_FACIAL_DAMAGE: FacialDamageState = {\n leftEyeSwelling: 0,\n rightEyeSwelling: 0,\n mouthBleeding: 0,\n noseBleeding: 0,\n leftCheekBruise: 0,\n rightCheekBruise: 0,\n foreheadBruise: 0,\n jawBruise: 0,\n totalFacialDamage: 0,\n};\n\n/**\n * Default expression state (neutral)\n * \n * @public\n * @category Facial Animation\n * @korean 기본표정상태\n */\nexport const DEFAULT_EXPRESSION_STATE: ExpressionState = {\n expression: FacialExpression.NEUTRAL,\n intensity: 1.0,\n transitionTime: 0.2,\n};\n\n/**\n * Default eye tracking state\n * \n * @public\n * @category Facial Animation\n * @korean 기본눈추적상태\n */\nexport const DEFAULT_EYE_TRACKING: EyeTrackingState = {\n targetPosition: new THREE.Vector3(0, 1.9, 5), // Looking forward\n lookDirection: new THREE.Vector3(0, 0, 1), // Forward\n pupilOffset: { x: 0, y: 0 },\n trackingSpeed: 0.1,\n enabled: true,\n};\n\n/**\n * Face3D component props\n * \n * @public\n * @category Facial Animation\n * @korean 얼굴3D속성\n */\nexport interface Face3DProps {\n /** Current facial expression */\n readonly expression: FacialExpression;\n \n /** Facial damage state */\n readonly damage: FacialDamageState;\n \n /** Opponent position for eye tracking */\n readonly opponentPosition: THREE.Vector3;\n \n /** Current head rotation (Euler angles) */\n readonly headRotation: THREE.Euler;\n \n /** Enable eye tracking (default: true) */\n readonly enableEyeTracking?: boolean;\n \n /** Enable damage visualization (default: true) */\n readonly enableDamageVisuals?: boolean;\n \n /** Mobile mode (simplified rendering) */\n readonly isMobile?: boolean;\n \n /** Skin tone color (default: 0xffdbac) */\n readonly skinColor?: number;\n}\n\n/**\n * Eye component props\n * \n * @public\n * @category Facial Animation\n * @korean 눈3D속성\n */\nexport interface EyeProps {\n /** Eye position relative to head */\n readonly position: [number, number, number];\n \n /** Current facial expression */\n readonly expression: FacialExpression;\n \n /** Look direction for pupil tracking */\n readonly lookDirection: THREE.Vector3;\n \n /** Eye swelling amount (0-1) */\n readonly swelling: number;\n \n /** Whether this is the left or right eye */\n readonly side: \"left\" | \"right\";\n}\n\n/**\n * Mouth component props\n * \n * @public\n * @category Facial Animation\n * @korean 입3D속성\n */\nexport interface MouthProps {\n /** Mouth position relative to head */\n readonly position: [number, number, number];\n \n /** Current facial expression */\n readonly expression: FacialExpression;\n \n /** Bleeding intensity (0-1) */\n readonly bleeding: number;\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;AAuBA,IAAY,mBAAL,yBAAA,kBAAA;;CAEL,iBAAA,aAAU;;CAEV,iBAAA,aAAU;;CAEV,iBAAA,YAAS;;CAET,iBAAA,eAAY;;CAEZ,iBAAA,gBAAa;;CAEb,iBAAA,cAAW;;KACZ;;;;;;;;AASD,IAAa,0BAGT;EACD,iBAAiB,UAAU;EAC1B,QAAQ;EACR,SAAS;EACT,WAAW;EACZ;EACA,iBAAiB,UAAU;EAC1B,QAAQ;EACR,SAAS;EACT,WAAW;EACZ;EACA,iBAAiB,SAAS;EACzB,QAAQ;EACR,SAAS;EACT,WAAW;EACZ;EACA,iBAAiB,YAAY;EAC5B,QAAQ;EACR,SAAS;EACT,WAAW;EACZ;EACA,iBAAiB,aAAa;EAC7B,QAAQ;EACR,SAAS;EACT,WAAW;EACZ;EACA,iBAAiB,WAAW;EAC3B,QAAQ;EACR,SAAS;EACT,WAAW;EACZ;CACF;;;;;;;;AA0ED,IAAa,eAAiD;EAC3D,iBAAiB,UAAU;EAC3B,iBAAiB,UAAU;EAC3B,iBAAiB,SAAS;EAC1B,iBAAiB,YAAY;EAC7B,iBAAiB,aAAa;EAC9B,iBAAiB,WAAW;CAC9B;;;;;;;;AASD,IAAa,iBAAmD;EAC7D,iBAAiB,UAAU;EAC3B,iBAAiB,UAAU;EAC3B,iBAAiB,SAAS;EAC1B,iBAAiB,YAAY;EAC7B,iBAAiB,aAAa;EAC9B,iBAAiB,WAAW;CAC9B;;;;;;;;;;AAWD,IAAY,mBAAL,yBAAA,kBAAA;;CAEL,iBAAA,YAAS;;CAET,iBAAA,SAAM;;CAEN,iBAAA,UAAO;;CAEP,iBAAA,UAAO;;CAEP,iBAAA,WAAQ;;CAER,iBAAA,UAAO;;KACR;;;;;;;;AA6DD,IAAa,wBAA2C;CACtD,iBAAiB;CACjB,kBAAkB;CAClB,eAAe;CACf,cAAc;CACd,iBAAiB;CACjB,kBAAkB;CAClB,gBAAgB;CAChB,WAAW;CACX,mBAAmB;CACpB;;;;;;;;AASD,IAAa,2BAA4C;CACvD,YAAY,iBAAiB;CAC7B,WAAW;CACX,gBAAgB;CACjB;;;;;;;;AASD,IAAa,uBAAyC;CACpD,gBAAgB,IAAI,MAAM,QAAQ,GAAG,KAAK,EAAE;CAC5C,eAAe,IAAI,MAAM,QAAQ,GAAG,GAAG,EAAE;CACzC,aAAa;EAAE,GAAG;EAAG,GAAG;EAAG;CAC3B,eAAe;CACf,SAAS;CACV"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"hand-animation.js","names":[],"sources":["../../src/types/hand-animation.ts"],"sourcesContent":["/**\n * Hand animation types for Korean martial arts techniques\n *\n * Defines hand poses, finger positions, and animation states for realistic\n * martial arts hand techniques including strikes, grappling, and precise\n * vital point targeting.\n *\n * @module types/hand-animation\n * @category Type Definitions\n * @korean 손애니메이션타입\n */\n\nimport * as THREE from \"three\";\n\n/**\n * Martial arts hand pose types\n *\n * Traditional Korean martial arts hand formations:\n * - FIST (주먹): Closed fist for punching\n * - KNIFE_HAND (수도): Extended fingers, rigid hand edge for strikes\n * - SPEAR_HAND (관수): Extended fingers together, pointed thrust\n * - PALM_HEEL (장력): Palm-heel strike position with curled fingers\n * - GRAPPLING (잡기): Fingers curved for grabs and control\n * - OPEN (펴기): Neutral open hand position\n *\n * @public\n * @korean 손자세타입\n */\nexport enum HandPoseType {\n /** 주먹 - Closed fist for punching */\n FIST = \"fist\",\n /** 수도 - Knife-hand strike with rigid edge */\n KNIFE_HAND = \"knife_hand\",\n /** 관수 - Spear-hand thrust with pointed fingers */\n SPEAR_HAND = \"spear_hand\",\n /** 장력 - Palm-heel strike with curled fingers */\n PALM_HEEL = \"palm_heel\",\n /** 잡기 - Grappling hand for grabs */\n GRAPPLING = \"grappling\",\n /** 펴기 - Open hand neutral position */\n OPEN = \"open\",\n /** 휴식 - Relaxed natural hand position for walking/idle */\n RELAXED = \"relaxed\",\n}\n\n/**\n * Finger identification\n *\n * @public\n * @korean 손가락\n */\nexport enum FingerType {\n /** 엄지 - Thumb */\n THUMB = \"thumb\",\n /** 검지 - Index finger */\n INDEX = \"index\",\n /** 중지 - Middle finger */\n MIDDLE = \"middle\",\n /** 약지 - Ring finger */\n RING = \"ring\",\n /** 새끼 - Pinky finger */\n PINKY = \"pinky\",\n}\n\n/**\n * Finger curl amount (0 = fully extended, 1 = fully curled)\n *\n * Normalized values for finger joint angles:\n * - 0.0: Fully extended (straight)\n * - 0.5: Half curled (slightly bent)\n * - 1.0: Fully curled (tight fist)\n *\n * @public\n * @korean 손가락구부림량\n */\nexport interface FingerCurl {\n /** Thumb curl amount (0-1) */\n readonly thumb: number;\n /** Index finger curl amount (0-1) */\n readonly index: number;\n /** Middle finger curl amount (0-1) */\n readonly middle: number;\n /** Ring finger curl amount (0-1) */\n readonly ring: number;\n /** Pinky finger curl amount (0-1) */\n readonly pinky: number;\n}\n\n/**\n * Finger spread amount (0 = together, 1 = spread apart)\n *\n * Controls the lateral spacing between fingers.\n *\n * @public\n * @korean 손가락벌림량\n */\nexport interface FingerSpread {\n /** Spread between thumb and index (0-1) */\n readonly thumbIndex: number;\n /** Spread between index and middle (0-1) */\n readonly indexMiddle: number;\n /** Spread between middle and ring (0-1) */\n readonly middleRing: number;\n /** Spread between ring and pinky (0-1) */\n readonly ringPinky: number;\n}\n\n/**\n * Hand pose definition for martial arts techniques\n *\n * Complete hand configuration including finger positions and wrist rotation\n * for authentic Korean martial arts hand techniques.\n *\n * @public\n * @korean 손자세정의\n */\nexport interface HandPose {\n /**\n * Pose identifier\n * @korean 자세ID\n */\n readonly type: HandPoseType;\n\n /**\n * Korean name for the pose\n * @korean 한글이름\n */\n readonly nameKorean: string;\n\n /**\n * English name for the pose\n * @korean 영어이름\n */\n readonly nameEnglish: string;\n\n /**\n * Romanized Korean name\n * @korean 로마자이름\n */\n readonly romanized: string;\n\n /**\n * Finger curl amounts (0-1 per finger)\n * @korean 손가락구부림\n */\n readonly fingerCurl: FingerCurl;\n\n /**\n * Finger spread amounts (0-1 between fingers)\n * @korean 손가락벌림\n */\n readonly fingerSpread: FingerSpread;\n\n /**\n * Wrist rotation for the technique\n * @korean 손목회전\n */\n readonly wristRotation: THREE.Euler;\n\n /**\n * Description of the technique\n * @korean 설명\n */\n readonly description: {\n readonly korean: string;\n readonly english: string;\n };\n\n /**\n * Which martial art this pose comes from\n * @korean 무술출처\n */\n readonly martialArtOrigin:\n | \"taekwondo\"\n | \"hapkido\"\n | \"taekyon\"\n | \"traditional\";\n\n /**\n * Primary striking surface\n * @korean 타격면\n */\n readonly strikingSurface:\n | \"knuckles\"\n | \"palm_heel\"\n | \"knife_edge\"\n | \"fingertips\"\n | \"whole_hand\";\n}\n\n/**\n * Hand animation state\n *\n * Current state of hand animation including pose transition progress.\n *\n * @public\n * @korean 손애니메이션상태\n */\nexport interface HandAnimationState {\n /**\n * Current hand pose\n * @korean 현재자세\n */\n readonly currentPose: HandPoseType;\n\n /**\n * Target hand pose (during transition)\n * @korean 목표자세\n */\n readonly targetPose: HandPoseType | null;\n\n /**\n * Transition progress (0-1)\n * @korean 전환진행률\n */\n readonly transitionProgress: number;\n\n /**\n * Current finger curl values (interpolated)\n * @korean 현재손가락구부림\n */\n readonly currentFingerCurl: FingerCurl;\n\n /**\n * Current finger spread values (interpolated)\n * @korean 현재손가락벌림\n */\n readonly currentFingerSpread: FingerSpread;\n\n /**\n * Current wrist rotation (interpolated)\n * @korean 현재손목회전\n */\n readonly currentWristRotation: THREE.Euler;\n\n /**\n * Whether hand is highlighted for vital point targeting\n * @korean 급소표시여부\n */\n readonly isHighlighted: boolean;\n\n /**\n * Highlight mode for different striking surfaces\n * @korean 표시모드\n */\n readonly highlightMode:\n | \"none\"\n | \"knuckles\"\n | \"palm\"\n | \"knife_edge\"\n | \"fingertips\"\n | null;\n}\n\n/**\n * Hand side identification\n *\n * @public\n * @korean 손쪽\n */\nexport type HandSide = \"left\" | \"right\";\n\n/**\n * Hand pose configuration for attack techniques\n *\n * Maps attack technique names to appropriate hand poses.\n *\n * @public\n * @korean 공격기술손자세\n */\nexport interface TechniqueHandPose {\n /**\n * Technique name (e.g., \"jab\", \"cross\", \"knife_hand_strike\")\n * @korean 기술이름\n */\n readonly techniqueName: string;\n\n /**\n * Hand pose for left hand\n * @korean 왼손자세\n */\n readonly leftHandPose: HandPoseType;\n\n /**\n * Hand pose for right hand\n * @korean 오른손자세\n */\n readonly rightHandPose: HandPoseType;\n\n /**\n * Transition duration in seconds\n * @korean 전환시간\n */\n readonly transitionDuration: number;\n}\n\n/**\n * Hand level of detail (LOD) settings\n *\n * Performance optimization by adjusting hand detail based on camera distance.\n *\n * @public\n * @korean 손상세도설정\n */\nexport interface HandLODConfig {\n /**\n * Detail level\n * - high: Full finger geometry (4 bones per finger)\n * - medium: Simplified fingers (3 bones per finger)\n * - low: No finger detail (hand as single unit)\n * @korean 상세도\n */\n readonly detailLevel: \"high\" | \"medium\" | \"low\";\n\n /**\n * Distance thresholds for LOD switching\n * @korean 거리기준\n */\n readonly distanceThresholds: {\n readonly high: number; // Camera distance for high detail (< 5 units)\n readonly medium: number; // Camera distance for medium detail (< 15 units)\n readonly low: number; // Camera distance for low detail (>= 15 units)\n };\n\n /**\n * Whether to render individual fingers\n * @korean 손가락렌더링여부\n */\n readonly renderFingers: boolean;\n\n /**\n * Number of segments per finger\n * @korean 손가락세그먼트수\n */\n readonly fingerSegments: number;\n}\n\n/**\n * Finger bone segments\n *\n * Anatomically correct finger bone structure:\n * - Metacarpal: Knuckle base (hand to finger connection)\n * - Proximal: First joint (knuckle joint)\n * - Intermediate: Middle joint\n * - Distal: Fingertip\n *\n * Note: Thumb has no intermediate phalanx (2 bones instead of 3)\n *\n * @public\n * @korean 손가락뼈세그먼트\n */\nexport interface FingerSegments {\n /**\n * Metacarpal bone (knuckle base)\n * @korean 중수골\n */\n readonly metacarpal: THREE.Vector3;\n\n /**\n * Proximal phalanx (first joint)\n * @korean 근위지골\n */\n readonly proximal: THREE.Vector3;\n\n /**\n * Intermediate phalanx (middle joint)\n * Note: Thumb does not have this bone\n * @korean 중위지골\n */\n readonly intermediate: THREE.Vector3 | null;\n\n /**\n * Distal phalanx (fingertip)\n * @korean 원위지골\n */\n readonly distal: THREE.Vector3;\n}\n\n/**\n * Complete hand structure with all finger bones\n *\n * @public\n * @korean 손뼈구조\n */\nexport interface HandStructure {\n /**\n * Palm base position\n * @korean 손바닥위치\n */\n readonly palm: THREE.Vector3;\n\n /**\n * Wrist position\n * @korean 손목위치\n */\n readonly wrist: THREE.Vector3;\n\n /**\n * Thumb segments (2 bones: no intermediate)\n * @korean 엄지뼈\n */\n readonly thumb: FingerSegments;\n\n /**\n * Index finger segments (3 bones)\n * @korean 검지뼈\n */\n readonly index: FingerSegments;\n\n /**\n * Middle finger segments (3 bones)\n * @korean 중지뼈\n */\n readonly middle: FingerSegments;\n\n /**\n * Ring finger segments (3 bones)\n * @korean 약지뼈\n */\n readonly ring: FingerSegments;\n\n /**\n * Pinky finger segments (3 bones)\n * @korean 새끼뼈\n */\n readonly pinky: FingerSegments;\n}\n"],"mappings":";;;;;;;;;;;;;;;AA4BA,IAAY,eAAL,yBAAA,cAAA;;
|
|
1
|
+
{"version":3,"file":"hand-animation.js","names":[],"sources":["../../src/types/hand-animation.ts"],"sourcesContent":["/**\n * Hand animation types for Korean martial arts techniques\n *\n * Defines hand poses, finger positions, and animation states for realistic\n * martial arts hand techniques including strikes, grappling, and precise\n * vital point targeting.\n *\n * @module types/hand-animation\n * @category Type Definitions\n * @korean 손애니메이션타입\n */\n\nimport * as THREE from \"three\";\n\n/**\n * Martial arts hand pose types\n *\n * Traditional Korean martial arts hand formations:\n * - FIST (주먹): Closed fist for punching\n * - KNIFE_HAND (수도): Extended fingers, rigid hand edge for strikes\n * - SPEAR_HAND (관수): Extended fingers together, pointed thrust\n * - PALM_HEEL (장력): Palm-heel strike position with curled fingers\n * - GRAPPLING (잡기): Fingers curved for grabs and control\n * - OPEN (펴기): Neutral open hand position\n *\n * @public\n * @korean 손자세타입\n */\nexport enum HandPoseType {\n /** 주먹 - Closed fist for punching */\n FIST = \"fist\",\n /** 수도 - Knife-hand strike with rigid edge */\n KNIFE_HAND = \"knife_hand\",\n /** 관수 - Spear-hand thrust with pointed fingers */\n SPEAR_HAND = \"spear_hand\",\n /** 장력 - Palm-heel strike with curled fingers */\n PALM_HEEL = \"palm_heel\",\n /** 잡기 - Grappling hand for grabs */\n GRAPPLING = \"grappling\",\n /** 펴기 - Open hand neutral position */\n OPEN = \"open\",\n /** 휴식 - Relaxed natural hand position for walking/idle */\n RELAXED = \"relaxed\",\n}\n\n/**\n * Finger identification\n *\n * @public\n * @korean 손가락\n */\nexport enum FingerType {\n /** 엄지 - Thumb */\n THUMB = \"thumb\",\n /** 검지 - Index finger */\n INDEX = \"index\",\n /** 중지 - Middle finger */\n MIDDLE = \"middle\",\n /** 약지 - Ring finger */\n RING = \"ring\",\n /** 새끼 - Pinky finger */\n PINKY = \"pinky\",\n}\n\n/**\n * Finger curl amount (0 = fully extended, 1 = fully curled)\n *\n * Normalized values for finger joint angles:\n * - 0.0: Fully extended (straight)\n * - 0.5: Half curled (slightly bent)\n * - 1.0: Fully curled (tight fist)\n *\n * @public\n * @korean 손가락구부림량\n */\nexport interface FingerCurl {\n /** Thumb curl amount (0-1) */\n readonly thumb: number;\n /** Index finger curl amount (0-1) */\n readonly index: number;\n /** Middle finger curl amount (0-1) */\n readonly middle: number;\n /** Ring finger curl amount (0-1) */\n readonly ring: number;\n /** Pinky finger curl amount (0-1) */\n readonly pinky: number;\n}\n\n/**\n * Finger spread amount (0 = together, 1 = spread apart)\n *\n * Controls the lateral spacing between fingers.\n *\n * @public\n * @korean 손가락벌림량\n */\nexport interface FingerSpread {\n /** Spread between thumb and index (0-1) */\n readonly thumbIndex: number;\n /** Spread between index and middle (0-1) */\n readonly indexMiddle: number;\n /** Spread between middle and ring (0-1) */\n readonly middleRing: number;\n /** Spread between ring and pinky (0-1) */\n readonly ringPinky: number;\n}\n\n/**\n * Hand pose definition for martial arts techniques\n *\n * Complete hand configuration including finger positions and wrist rotation\n * for authentic Korean martial arts hand techniques.\n *\n * @public\n * @korean 손자세정의\n */\nexport interface HandPose {\n /**\n * Pose identifier\n * @korean 자세ID\n */\n readonly type: HandPoseType;\n\n /**\n * Korean name for the pose\n * @korean 한글이름\n */\n readonly nameKorean: string;\n\n /**\n * English name for the pose\n * @korean 영어이름\n */\n readonly nameEnglish: string;\n\n /**\n * Romanized Korean name\n * @korean 로마자이름\n */\n readonly romanized: string;\n\n /**\n * Finger curl amounts (0-1 per finger)\n * @korean 손가락구부림\n */\n readonly fingerCurl: FingerCurl;\n\n /**\n * Finger spread amounts (0-1 between fingers)\n * @korean 손가락벌림\n */\n readonly fingerSpread: FingerSpread;\n\n /**\n * Wrist rotation for the technique\n * @korean 손목회전\n */\n readonly wristRotation: THREE.Euler;\n\n /**\n * Description of the technique\n * @korean 설명\n */\n readonly description: {\n readonly korean: string;\n readonly english: string;\n };\n\n /**\n * Which martial art this pose comes from\n * @korean 무술출처\n */\n readonly martialArtOrigin:\n | \"taekwondo\"\n | \"hapkido\"\n | \"taekyon\"\n | \"traditional\";\n\n /**\n * Primary striking surface\n * @korean 타격면\n */\n readonly strikingSurface:\n | \"knuckles\"\n | \"palm_heel\"\n | \"knife_edge\"\n | \"fingertips\"\n | \"whole_hand\";\n}\n\n/**\n * Hand animation state\n *\n * Current state of hand animation including pose transition progress.\n *\n * @public\n * @korean 손애니메이션상태\n */\nexport interface HandAnimationState {\n /**\n * Current hand pose\n * @korean 현재자세\n */\n readonly currentPose: HandPoseType;\n\n /**\n * Target hand pose (during transition)\n * @korean 목표자세\n */\n readonly targetPose: HandPoseType | null;\n\n /**\n * Transition progress (0-1)\n * @korean 전환진행률\n */\n readonly transitionProgress: number;\n\n /**\n * Current finger curl values (interpolated)\n * @korean 현재손가락구부림\n */\n readonly currentFingerCurl: FingerCurl;\n\n /**\n * Current finger spread values (interpolated)\n * @korean 현재손가락벌림\n */\n readonly currentFingerSpread: FingerSpread;\n\n /**\n * Current wrist rotation (interpolated)\n * @korean 현재손목회전\n */\n readonly currentWristRotation: THREE.Euler;\n\n /**\n * Whether hand is highlighted for vital point targeting\n * @korean 급소표시여부\n */\n readonly isHighlighted: boolean;\n\n /**\n * Highlight mode for different striking surfaces\n * @korean 표시모드\n */\n readonly highlightMode:\n | \"none\"\n | \"knuckles\"\n | \"palm\"\n | \"knife_edge\"\n | \"fingertips\"\n | null;\n}\n\n/**\n * Hand side identification\n *\n * @public\n * @korean 손쪽\n */\nexport type HandSide = \"left\" | \"right\";\n\n/**\n * Hand pose configuration for attack techniques\n *\n * Maps attack technique names to appropriate hand poses.\n *\n * @public\n * @korean 공격기술손자세\n */\nexport interface TechniqueHandPose {\n /**\n * Technique name (e.g., \"jab\", \"cross\", \"knife_hand_strike\")\n * @korean 기술이름\n */\n readonly techniqueName: string;\n\n /**\n * Hand pose for left hand\n * @korean 왼손자세\n */\n readonly leftHandPose: HandPoseType;\n\n /**\n * Hand pose for right hand\n * @korean 오른손자세\n */\n readonly rightHandPose: HandPoseType;\n\n /**\n * Transition duration in seconds\n * @korean 전환시간\n */\n readonly transitionDuration: number;\n}\n\n/**\n * Hand level of detail (LOD) settings\n *\n * Performance optimization by adjusting hand detail based on camera distance.\n *\n * @public\n * @korean 손상세도설정\n */\nexport interface HandLODConfig {\n /**\n * Detail level\n * - high: Full finger geometry (4 bones per finger)\n * - medium: Simplified fingers (3 bones per finger)\n * - low: No finger detail (hand as single unit)\n * @korean 상세도\n */\n readonly detailLevel: \"high\" | \"medium\" | \"low\";\n\n /**\n * Distance thresholds for LOD switching\n * @korean 거리기준\n */\n readonly distanceThresholds: {\n readonly high: number; // Camera distance for high detail (< 5 units)\n readonly medium: number; // Camera distance for medium detail (< 15 units)\n readonly low: number; // Camera distance for low detail (>= 15 units)\n };\n\n /**\n * Whether to render individual fingers\n * @korean 손가락렌더링여부\n */\n readonly renderFingers: boolean;\n\n /**\n * Number of segments per finger\n * @korean 손가락세그먼트수\n */\n readonly fingerSegments: number;\n}\n\n/**\n * Finger bone segments\n *\n * Anatomically correct finger bone structure:\n * - Metacarpal: Knuckle base (hand to finger connection)\n * - Proximal: First joint (knuckle joint)\n * - Intermediate: Middle joint\n * - Distal: Fingertip\n *\n * Note: Thumb has no intermediate phalanx (2 bones instead of 3)\n *\n * @public\n * @korean 손가락뼈세그먼트\n */\nexport interface FingerSegments {\n /**\n * Metacarpal bone (knuckle base)\n * @korean 중수골\n */\n readonly metacarpal: THREE.Vector3;\n\n /**\n * Proximal phalanx (first joint)\n * @korean 근위지골\n */\n readonly proximal: THREE.Vector3;\n\n /**\n * Intermediate phalanx (middle joint)\n * Note: Thumb does not have this bone\n * @korean 중위지골\n */\n readonly intermediate: THREE.Vector3 | null;\n\n /**\n * Distal phalanx (fingertip)\n * @korean 원위지골\n */\n readonly distal: THREE.Vector3;\n}\n\n/**\n * Complete hand structure with all finger bones\n *\n * @public\n * @korean 손뼈구조\n */\nexport interface HandStructure {\n /**\n * Palm base position\n * @korean 손바닥위치\n */\n readonly palm: THREE.Vector3;\n\n /**\n * Wrist position\n * @korean 손목위치\n */\n readonly wrist: THREE.Vector3;\n\n /**\n * Thumb segments (2 bones: no intermediate)\n * @korean 엄지뼈\n */\n readonly thumb: FingerSegments;\n\n /**\n * Index finger segments (3 bones)\n * @korean 검지뼈\n */\n readonly index: FingerSegments;\n\n /**\n * Middle finger segments (3 bones)\n * @korean 중지뼈\n */\n readonly middle: FingerSegments;\n\n /**\n * Ring finger segments (3 bones)\n * @korean 약지뼈\n */\n readonly ring: FingerSegments;\n\n /**\n * Pinky finger segments (3 bones)\n * @korean 새끼뼈\n */\n readonly pinky: FingerSegments;\n}\n"],"mappings":";;;;;;;;;;;;;;;AA4BA,IAAY,eAAL,yBAAA,cAAA;;CAEL,aAAA,UAAO;;CAEP,aAAA,gBAAa;;CAEb,aAAA,gBAAa;;CAEb,aAAA,eAAY;;CAEZ,aAAA,eAAY;;CAEZ,aAAA,UAAO;;CAEP,aAAA,aAAU;;KACX;;;;;;;AAQD,IAAY,aAAL,yBAAA,YAAA;;CAEL,WAAA,WAAQ;;CAER,WAAA,WAAQ;;CAER,WAAA,YAAS;;CAET,WAAA,UAAO;;CAEP,WAAA,WAAQ;;KACT"}
|
package/lib/types/injury.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"injury.js","names":[],"sources":["../../src/types/injury.ts"],"sourcesContent":["/**\n * Shared Injury Types\n * \n * **Korean**: 공유 부상 타입\n * \n * Common injury types and data structures used across the trauma visualization\n * system. Separated from React components to avoid coupling system logic to UI.\n * \n * @module types/injury\n * @category Types\n * @korean 부상타입\n */\n\nimport { BodyRegion } from \"./common\";\n\n/**\n * Injury type classification\n * \n * **Korean**: 부상 유형 분류\n * \n * @public\n */\nexport enum InjuryType {\n /** Blunt force trauma */\n BRUISE = \"bruise\",\n /** Sharp weapon/strike */\n CUT = \"cut\",\n /** Deep cut with blood trail */\n LACERATION = \"laceration\",\n /** Bone damage indicator */\n FRACTURE = \"fracture\",\n}\n\n/**\n * Individual injury data for visualization\n * \n * **Korean**: 시각화를 위한 개별 부상 데이터\n * \n * Used by both the injury tracking system and trauma visualization components.\n * \n * @public\n */\nexport interface Injury {\n /** Unique identifier */\n readonly id: string;\n /** Body region affected */\n readonly region: BodyRegion;\n /** Type of injury */\n readonly type: InjuryType;\n /** Position on body [x, y, z] relative to character center */\n readonly position: [number, number, number];\n /** Severity (0.0 to 1.0) */\n readonly severity: number;\n /** Number of hits to same location (for progressive bruising) */\n readonly hitCount: number;\n /** Timestamp when injury was created */\n readonly timestamp: number;\n /** Optional player ID for multi-player scenarios */\n readonly playerId?: string | number;\n}\n"],"mappings":";;;;;;;;AAsBA,IAAY,aAAL,yBAAA,YAAA;;
|
|
1
|
+
{"version":3,"file":"injury.js","names":[],"sources":["../../src/types/injury.ts"],"sourcesContent":["/**\n * Shared Injury Types\n * \n * **Korean**: 공유 부상 타입\n * \n * Common injury types and data structures used across the trauma visualization\n * system. Separated from React components to avoid coupling system logic to UI.\n * \n * @module types/injury\n * @category Types\n * @korean 부상타입\n */\n\nimport { BodyRegion } from \"./common\";\n\n/**\n * Injury type classification\n * \n * **Korean**: 부상 유형 분류\n * \n * @public\n */\nexport enum InjuryType {\n /** Blunt force trauma */\n BRUISE = \"bruise\",\n /** Sharp weapon/strike */\n CUT = \"cut\",\n /** Deep cut with blood trail */\n LACERATION = \"laceration\",\n /** Bone damage indicator */\n FRACTURE = \"fracture\",\n}\n\n/**\n * Individual injury data for visualization\n * \n * **Korean**: 시각화를 위한 개별 부상 데이터\n * \n * Used by both the injury tracking system and trauma visualization components.\n * \n * @public\n */\nexport interface Injury {\n /** Unique identifier */\n readonly id: string;\n /** Body region affected */\n readonly region: BodyRegion;\n /** Type of injury */\n readonly type: InjuryType;\n /** Position on body [x, y, z] relative to character center */\n readonly position: [number, number, number];\n /** Severity (0.0 to 1.0) */\n readonly severity: number;\n /** Number of hits to same location (for progressive bruising) */\n readonly hitCount: number;\n /** Timestamp when injury was created */\n readonly timestamp: number;\n /** Optional player ID for multi-player scenarios */\n readonly playerId?: string | number;\n}\n"],"mappings":";;;;;;;;AAsBA,IAAY,aAAL,yBAAA,YAAA;;CAEL,WAAA,YAAS;;CAET,WAAA,SAAM;;CAEN,WAAA,gBAAa;;CAEb,WAAA,cAAW;;KACZ"}
|
package/lib/types/physics.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"physics.js","names":[],"sources":["../../src/types/physics.ts"],"sourcesContent":["/**\n * Physics and collision detection type definitions for Black Trigram combat system.\n *\n * **Korean**: 물리 및 충돌 타입 정의\n *\n * This module provides type definitions for the collision detection system, including\n * bounding boxes, attack reach calculations, and raycasting results.\n *\n * @module types/physics\n * @category Type Definitions\n * @korean 물리타입\n */\n\nimport type {\n Position,\n TrigramStance,\n VitalPointCategory,\n VitalPointSeverity,\n} from \"./common\";\n\n/**\n * Anatomical regions for collision detection.\n *\n * **Korean**: 해부학적 영역\n *\n * The five main body regions used for bounding box collision detection.\n * Each region contains multiple vital points.\n *\n * @public\n * @category Collision Types\n * @korean 해부영역\n */\nexport type AnatomicalRegionPhysics =\n | \"head\" // 머리 (Meori)\n | \"neck\" // 목 (Mok)\n | \"torso\" // 몸통 (Momtong)\n | \"arms\" // 팔 (Pal)\n | \"legs\"; // 다리 (Dari)\n\n/**\n * Bounding box shape types for collision detection.\n *\n * **Korean**: 경계 상자 형태\n *\n * @public\n * @category Collision Types\n * @korean 경계형태\n */\nexport type BoundingBoxType = \"sphere\" | \"box\" | \"capsule\";\n\n/**\n * Body part source for reach calculation.\n *\n * **Korean**: 신체 부위\n *\n * Defines which body part's length is used for reach calculation.\n *\n * @public\n * @category Combat Types\n * @korean 신체부위\n */\nexport type ReachBodyPart = \"arm\" | \"leg\" | \"torso\";\n\n/**\n * Specific limb types for exposure tracking.\n *\n * **Korean**: 노출된 사지 부위\n *\n * Granular limb identification for counter-attack and breaking techniques.\n * Each limb can be targeted during its extension phase.\n *\n * @public\n * @category Combat Types\n * @korean 사지부위\n */\nexport type ExposedLimbType =\n | \"left_arm\" // 왼팔 (Oenpal)\n | \"right_arm\" // 오른팔 (Oreunpal)\n | \"left_leg\" // 왼다리 (Oendari)\n | \"right_leg\" // 오른다리 (Oreundari)\n | \"left_ankle\" // 왼발목 (Oenbalmok)\n | \"right_ankle\" // 오른발목 (Oreunbalmok)\n | \"left_knee\" // 왼무릎 (Oenmureup)\n | \"right_knee\" // 오른무릎 (Oreunmureup)\n | \"left_elbow\" // 왼팔꿈치 (Oenpalkkumchi)\n | \"right_elbow\" // 오른팔꿈치 (Oreunpalkkumchi)\n | \"left_wrist\" // 왼손목 (Oensomok)\n | \"right_wrist\"; // 오른손목 (Oreunsomok)\n\n/**\n * Limb exposure window during technique execution.\n *\n * **Korean**: 사지 노출 시간\n *\n * Defines when and how a limb becomes vulnerable during attack execution.\n * Used for counter-attacks, breaking techniques, and defensive opportunities.\n *\n * @public\n * @category Combat Types\n * @korean 사지노출시간\n */\nexport interface LimbExposureWindow {\n /**\n * Which limb is exposed during the technique.\n * @korean 노출된사지\n */\n readonly exposedLimb: ExposedLimbType;\n\n /**\n * Start time of exposure as fraction of execution time (0.0-1.0).\n * Example: 0.3 means exposure starts at 30% into the animation.\n * @korean 노출시작시간\n */\n readonly startTime: number;\n\n /**\n * Duration of exposure window in milliseconds.\n * This is the vulnerable period where counter-attacks can target the limb.\n * @korean 노출지속시간\n */\n readonly duration: number;\n\n /**\n * Vulnerability multiplier for damage to this limb (1.0-3.0).\n * Higher values indicate greater vulnerability:\n * - 1.0-1.3: Minor exposure (quick jabs)\n * - 1.4-1.8: Moderate exposure (standard strikes)\n * - 1.9-3.0: Critical exposure (overextended kicks, spinning techniques)\n * @korean 취약성배수\n */\n readonly vulnerabilityMultiplier: number;\n\n /**\n * Whether this exposure allows breaking techniques.\n * True for fully extended limbs (kicks, lunging punches).\n * @korean 파쇄기술가능\n */\n readonly allowsBreaking: boolean;\n}\n\n/**\n * Physical reach configuration for techniques.\n *\n * **Korean**: 물리적 도달 설정\n *\n * Defines how a technique's reach is calculated using physical attributes\n * rather than a fixed distance value. The actual reach depends on:\n * - Body part length from archetype physical attributes\n * - Animation extension multiplier from hit timing\n * - Stance modifiers from Eight Trigrams\n *\n * **NEW**: Now includes limb exposure tracking for counter-attack opportunities.\n *\n * Formula: `effectiveReach = (limbLength/100) × extensionMultiplier × stanceModifier`\n *\n * @example\n * ```typescript\n * // Punch technique using arm length with exposure\n * const punchReach: PhysicalReachConfig = {\n * bodyPart: \"arm\",\n * techniqueType: \"punch\",\n * baseExtension: 0.95, // 95% arm extension at peak\n * exposureWindow: {\n * exposedLimb: \"right_arm\",\n * startTime: 0.4,\n * duration: 300,\n * vulnerabilityMultiplier: 1.3,\n * allowsBreaking: false\n * }\n * };\n *\n * // Kick using leg length - high vulnerability\n * const kickReach: PhysicalReachConfig = {\n * bodyPart: \"leg\",\n * techniqueType: \"kick\",\n * baseExtension: 1.1, // 110% leg extension (high reach)\n * exposureWindow: {\n * exposedLimb: \"right_leg\",\n * startTime: 0.5,\n * duration: 400,\n * vulnerabilityMultiplier: 2.2,\n * allowsBreaking: true\n * }\n * };\n * ```\n *\n * @public\n * @category Combat Types\n * @korean 물리적도달설정\n */\nexport interface PhysicalReachConfig {\n /**\n * Body part used for reach calculation.\n * Determines which physical attribute length is used.\n * @korean 신체부위\n */\n readonly bodyPart: ReachBodyPart;\n\n /**\n * Technique type for classification.\n * @korean 기술유형\n */\n readonly techniqueType: TechniqueType;\n\n /**\n * Base extension multiplier (0.0 - 1.5).\n * - 0.4-0.5: Close range (elbows, knees)\n * - 0.9-1.0: Standard reach (punches)\n * - 1.1-1.5: Extended reach (kicks, spinning techniques)\n * @korean 기본확장배수\n */\n readonly baseExtension: number;\n\n /**\n * Optional limb exposure window for counter-attack opportunities.\n * Defines when and how the attacking limb becomes vulnerable.\n * @korean 사지노출설정\n */\n readonly exposureWindow?: LimbExposureWindow;\n}\n\n/**\n * Runtime validation helper for base extension multiplier.\n *\n * Ensures that the provided baseExtension value respects the documented range\n * of 0.0 to 1.5. This should be used when loading or constructing technique\n * configurations from dynamic sources (e.g. JSON, network, editors).\n *\n * **Korean**: 기본 확장 배수 검증\n *\n * @param baseExtension - The base extension multiplier to validate.\n * @returns `true` if the value is within the inclusive range [0.0, 1.5], otherwise `false`.\n * @public\n * @category Combat Types\n * @korean 기본확장배수검증\n */\nexport function isValidBaseExtension(baseExtension: number): boolean {\n return baseExtension >= 0.0 && baseExtension <= 1.5;\n}\n\n/**\n * Asserts that a PhysicalReachConfig has a valid baseExtension value.\n *\n * Throws a RangeError if the configuration's baseExtension is outside the\n * allowed range of 0.0 to 1.5. This provides a canonical runtime check that\n * can be used by reach calculators or technique loaders.\n *\n * **Korean**: 물리적 도달 설정 검증\n *\n * @param config - The PhysicalReachConfig instance to validate.\n * @throws RangeError If baseExtension is outside [0.0, 1.5].\n * @public\n * @category Combat Types\n * @korean 물리적도달설정검증\n */\nexport function assertValidPhysicalReachConfig(\n config: PhysicalReachConfig\n): void {\n if (!isValidBaseExtension(config.baseExtension)) {\n throw new RangeError(\n `Invalid baseExtension ${config.baseExtension}. ` +\n \"Expected a value between 0.0 and 1.5 inclusive.\"\n );\n }\n}\n\n/**\n * Technique types for attack reach calculation.\n *\n * **Korean**: 기술 유형\n *\n * @public\n * @category Combat Types\n * @korean 기술유형\n */\nexport type TechniqueType =\n | \"punch\" // 주먹 (Jumeok)\n | \"kick\" // 발차기 (Balchagi)\n | \"elbow\" // 팔꿈치 (Palkkumchi)\n | \"knee\" // 무릎 (Mureup)\n | \"pressure_point\"; // 급소 (Geupso)\n\n/**\n * Bounding box definition for an anatomical region.\n *\n * **Korean**: 경계 상자\n *\n * Defines the collision volume for an anatomical region using either a sphere,\n * box, or capsule shape. Used for broad-phase collision detection.\n *\n * @example\n * ```typescript\n * const headBox: BoundingBox = {\n * type: \"sphere\",\n * center: { x: 0, y: 1.7, z: 0 },\n * dimensions: { x: 0.125, y: 0, z: 0 }, // radius only\n * region: \"head\"\n * };\n * ```\n *\n * @public\n * @category Collision Types\n * @korean 경계상자\n */\nexport interface BoundingBox {\n /** Shape type of the bounding box */\n readonly type: BoundingBoxType;\n\n /** Center position of the bounding box in 3D space */\n readonly center: Position3D;\n\n /** Dimensions: radius for sphere, width/height/depth for box, radius/height for capsule */\n readonly dimensions: Position3D;\n\n /** Anatomical region this bounding box represents */\n readonly region: AnatomicalRegionPhysics;\n}\n\n/**\n * Position in 3D space.\n *\n * **Korean**: 3D 위치\n *\n * Extends the 2D Position type with a z-coordinate for Three.js integration.\n *\n * @public\n * @category Core Types\n * @korean 3D위치\n */\nexport interface Position3D {\n /** X coordinate in meters */\n readonly x: number;\n\n /** Y coordinate in meters */\n readonly y: number;\n\n /** Z coordinate in meters */\n readonly z: number;\n}\n\n/**\n * Attack reach calculation result.\n *\n * **Korean**: 공격 범위\n *\n * Contains the effective attack range considering technique type and stance modifiers.\n *\n * @example\n * ```typescript\n * const kickReach: AttackReach = {\n * technique: \"kick\",\n * baseReach: 1.0,\n * stance: TrigramStance.LI,\n * stanceModifier: 1.20,\n * effectiveReach: 1.2\n * };\n * ```\n *\n * @public\n * @category Combat Types\n * @korean 공격범위\n */\nexport interface AttackReach {\n /** Type of technique being used */\n readonly technique: TechniqueType;\n\n /** Base reach in meters without modifiers */\n readonly baseReach: number;\n\n /** Current trigram stance */\n readonly stance: TrigramStance;\n\n /** Stance-specific reach modifier (0.9 - 1.2) */\n readonly stanceModifier: number;\n\n /** Final effective reach: baseReach × stanceModifier */\n readonly effectiveReach: number;\n}\n\n/**\n * Collision detection result.\n *\n * **Korean**: 충돌 결과\n *\n * Contains comprehensive information about a collision, including whether it hit,\n * which region and vital point were struck, distance, and accuracy.\n *\n * @example\n * ```typescript\n * const result: CollisionResult = {\n * hit: true,\n * region: \"head\",\n * vitalPoint: templePoint,\n * distance: 0.8,\n * accuracy: 0.95\n * };\n * ```\n *\n * @public\n * @category Collision Types\n * @korean 충돌결과\n */\nexport interface CollisionResult {\n /** Whether the attack hit */\n readonly hit: boolean;\n\n /** Anatomical region hit (if any) */\n readonly region?: AnatomicalRegionPhysics;\n\n /** Specific vital point hit (if any) */\n readonly vitalPoint?: CollisionVitalPoint;\n\n /** Actual distance from attacker to target in meters */\n readonly distance: number;\n\n /** Hit accuracy (0-1), based on targeting precision */\n readonly accuracy: number;\n\n /** 3D point of intersection (if hit) */\n readonly hitPoint?: Position3D;\n}\n\n/**\n * Minimal vital point shape for collision results to avoid deep cross-module\n * dependencies. Full vital point data lives in the vitalpoint system.\n */\nexport interface CollisionVitalPoint {\n readonly id: string;\n readonly names: {\n readonly korean: string;\n readonly english: string;\n readonly romanized: string;\n };\n readonly position: Position;\n readonly category: VitalPointCategory;\n readonly severity: VitalPointSeverity;\n}\n\n/**\n * Counter-attack opportunity during opponent's technique execution.\n *\n * **Korean**: 반격 기회\n *\n * Represents a window of opportunity to counter-attack when the opponent\n * has exposed a limb during their technique execution.\n *\n * @example\n * ```typescript\n * const counterOpportunity: CounterOpportunity = {\n * exposedLimb: \"right_leg\",\n * windowStart: 450, // ms into opponent's kick\n * windowDuration: 300, // 300ms counter window\n * vulnerabilityMultiplier: 2.0,\n * allowsBreaking: true,\n * recommendedCounters: [\"ankle_break\", \"knee_strike\", \"leg_sweep\"]\n * };\n * ```\n *\n * @public\n * @category Combat Types\n * @korean 반격기회\n */\nexport interface CounterOpportunity {\n /**\n * The exposed limb that can be targeted.\n * @korean 노출된사지\n */\n readonly exposedLimb: ExposedLimbType;\n\n /**\n * Start time of counter window in milliseconds (from technique start).\n * @korean 반격시작시간\n */\n readonly windowStart: number;\n\n /**\n * Duration of counter window in milliseconds.\n * @korean 반격지속시간\n */\n readonly windowDuration: number;\n\n /**\n * Vulnerability multiplier for damage during this window.\n * @korean 취약성배수\n */\n readonly vulnerabilityMultiplier: number;\n\n /**\n * Whether limb breaking techniques are effective during this window.\n * @korean 파쇄기술가능\n */\n readonly allowsBreaking: boolean;\n\n /**\n * Recommended counter-technique IDs for this opportunity.\n * @korean 추천반격기술\n */\n readonly recommendedCounters?: readonly string[];\n}\n\n/**\n * Breaking technique target types.\n *\n * **Korean**: 파쇄 기술 목표\n *\n * Specific joint and bone targets for breaking techniques.\n * Used for limb-breaking counter-attacks.\n *\n * @public\n * @category Combat Types\n * @korean 파쇄목표\n */\nexport type BreakingTarget =\n | \"ankle\" // 발목 (Balmok)\n | \"knee\" // 무릎 (Mureup)\n | \"elbow\" // 팔꿈치 (Palkkumchi)\n | \"wrist\" // 손목 (Sonmok)\n | \"shoulder\" // 어깨 (Eokkae)\n | \"hip\"; // 엉덩이/골반 (Eongdeongi)\n\n/**\n * Breaking technique result.\n *\n * **Korean**: 파쇄 결과\n *\n * Result of a breaking technique attempt, including injury severity\n * and status effects applied to the broken limb.\n *\n * @public\n * @category Combat Types\n * @korean 파쇄결과\n */\nexport interface BreakingResult {\n /**\n * Whether the breaking technique succeeded.\n * @korean 파쇄성공\n */\n readonly success: boolean;\n\n /**\n * Target limb/joint that was broken.\n * @korean 파쇄목표\n */\n readonly target: BreakingTarget;\n\n /**\n * Severity of the break (fracture, dislocation, etc.).\n * Range: 0.0 (failed) to 1.0 (complete break).\n * @korean 파쇄심각도\n */\n readonly severity: number;\n\n /**\n * Damage dealt to the limb.\n * @korean 피해량\n */\n readonly damage: number;\n\n /**\n * Mobility reduction percentage (0.0-1.0).\n * Affects movement speed and technique execution.\n * @korean 이동력감소\n */\n readonly mobilityReduction: number;\n\n /**\n * IDs of status effects applied (pain, bleeding, disabled_limb, etc.).\n * @korean 상태효과\n */\n readonly statusEffects: readonly string[];\n}\n\n/**\n * Raycast query parameters.\n *\n * **Korean**: 레이캐스트 쿼리\n *\n * Defines the parameters for a raycasting operation from attacker to target.\n *\n * @public\n * @category Collision Types\n * @korean 레이캐스트쿼리\n */\nexport interface RaycastQuery {\n /** Origin point of the ray (attacker position) */\n readonly origin: Position3D;\n\n /** Direction vector of the ray (normalized) */\n readonly direction: Position3D;\n\n /** Maximum distance to check in meters */\n readonly maxDistance: number;\n\n /** Target anatomical region (optional, for filtering) */\n readonly targetRegion?: AnatomicalRegionPhysics;\n}\n\n/**\n * Base reach values for different technique types.\n *\n * **Korean**: 기본 범위 값\n *\n * @public\n * @category Combat Constants\n * @korean 기본범위값\n */\nexport const BASE_REACH: Record<TechniqueType, number> = {\n punch: 0.7, // 70cm - Standard punch range\n kick: 1.0, // 100cm - Longer leg reach\n elbow: 0.4, // 40cm - Close-range strike\n knee: 0.4, // 40cm - Close-range strike\n pressure_point: 0.5, // 50cm - Precise targeting\n};\n\n/**\n * Stance reach modifiers for the Eight Trigrams.\n *\n * **Korean**: 팔괘 범위 수정자\n *\n * Each stance affects attack range differently based on its combat philosophy:\n * - Aggressive stances (Fire, Thunder) extend reach\n * - Defensive stances (Mountain) reduce reach\n * - Balanced stances (Heaven, Earth) have minor adjustments\n *\n * @public\n * @category Combat Constants\n * @korean 팔괘범위수정자\n */\nexport const STANCE_REACH_MODIFIERS: Record<TrigramStance, number> = {\n li: 1.2, // ☲ Fire (리): +20% reach (aggressive)\n jin: 1.15, // ☳ Thunder (진): +15% reach (explosive)\n geon: 1.1, // ☰ Heaven (건): +10% reach (balanced)\n son: 1.05, // ☴ Wind (손): +5% reach (continuous)\n tae: 1.0, // ☱ Lake (태): neutral reach\n gam: 1.0, // ☵ Water (감): neutral reach (adaptive)\n gon: 0.95, // ☷ Earth (곤): -5% reach (grounded)\n gan: 0.9, // ☶ Mountain (간): -10% reach (defensive)\n};\n\n/**\n * Korean terminology for collision detection concepts.\n *\n * **Korean**: 충돌 감지 용어\n *\n * @public\n * @category Korean Terms\n * @korean 충돌용어\n */\nexport const COLLISION_KOREAN_TERMS = {\n collisionDetection: \"충돌감지\", // Chungdol Gamji\n attackRange: \"타격범위\", // Tagyeok Beomwi\n preciseHit: \"정밀타격\", // Jeongmil Tagyeok\n boundingBox: \"경계상자\", // Gyeonggye Sangja\n raycast: \"광선투사\", // Gwangseon Tusa\n hitAccuracy: \"타격정확도\", // Tagyeok Jeonghwakdo\n effectiveReach: \"유효범위\", // Yuhyo Beomwi\n} as const;\n\n/**\n * Anatomical region dimensions in meters.\n *\n * **Korean**: 해부 영역 치수\n *\n * Standard dimensions for adult human anatomical regions used for bounding box creation.\n *\n * @public\n * @category Collision Constants\n * @korean 해부영역치수\n */\nexport const ANATOMICAL_DIMENSIONS = {\n head: {\n type: \"sphere\" as const,\n radius: 0.125, // 머리 구 반경 (Meori sphere radius) – 12.5cm radius for head sphere\n center: { x: 0, y: 1.7, z: 0 }, // 머리 중심 높이 (Meori center height) – Average adult head height\n },\n neck: {\n type: \"capsule\" as const,\n radius: 0.075, // 목 캡슐 반경 (Mok capsule radius) – 7.5cm radius for neck cylinder\n height: 0.15, // 목 캡슐 높이 (Mok capsule height) – 15cm height\n center: { x: 0, y: 1.5, z: 0 }, // 목 중심 높이 (Mok center height)\n },\n torso: {\n type: \"box\" as const,\n width: 0.4, // 몸통 상자 가로 (Momtong box width) – 40cm width (shoulder to shoulder)\n height: 0.6, // 몸통 상자 세로 (Momtong box height) – 60cm height (neck to waist)\n depth: 0.25, // 몸통 상자 깊이 (Momtong box depth) – 25cm depth (front to back)\n center: { x: 0, y: 1.1, z: 0 }, // 몸통 중심 높이 (Momtong center height)\n },\n arms: {\n type: \"capsule\" as const,\n radius: 0.05, // 팔 캡슐 반경 (Pal capsule radius) – 5cm radius for arm cylinder\n height: 0.6, // 팔 캡슐 길이 (Pal capsule length) – 60cm length (shoulder to hand)\n center: { x: 0.3, y: 1.1, z: 0 }, // 팔 위치 오프셋 (Pal position offset) – Offset for arm position\n },\n legs: {\n type: \"capsule\" as const,\n radius: 0.06, // 다리 캡슐 반경 (Dari capsule radius) – 6cm radius for leg cylinder\n height: 0.8, // 다리 캡슐 길이 (Dari capsule length) – 80cm length (hip to foot)\n center: { x: 0.15, y: 0.4, z: 0 }, // 다리 위치 오프셋 (Dari position offset) – Offset for leg position\n },\n} as const;\n"],"mappings":";;;;;;;;;;;;;;;;AA4OA,SAAgB,qBAAqB,eAAgC;AACnE,QAAO,iBAAiB,KAAO,iBAAiB;;;;;;;;;;;;;;;;;AAkBlD,SAAgB,+BACd,QACM;AACN,KAAI,CAAC,qBAAqB,OAAO,cAAc,CAC7C,OAAM,IAAI,WACR,yBAAyB,OAAO,cAAc,mDAE/C;;;;;;;;;;;AAwVL,IAAa,aAA4C;CACvD,OAAO;CACP,MAAM;CACN,OAAO;CACP,MAAM;CACN,gBAAgB;CACjB;;;;;;;;;;;;;;;AAgBD,IAAa,yBAAwD;CACnE,IAAI;CACJ,KAAK;CACL,MAAM;CACN,KAAK;CACL,KAAK;CACL,KAAK;CACL,KAAK;CACL,KAAK;CACN;;;;;;;;;;AAWD,IAAa,yBAAyB;CACpC,oBAAoB;CACpB,aAAa;CACb,YAAY;CACZ,aAAa;CACb,SAAS;CACT,aAAa;CACb,gBAAgB;CACjB;;;;;;;;;;;;AAaD,IAAa,wBAAwB;CACnC,MAAM;EACJ,MAAM;EACN,QAAQ;EACR,QAAQ;GAAE,GAAG;GAAG,GAAG;GAAK,GAAG;GAAG;EAC/B;CACD,MAAM;EACJ,MAAM;EACN,QAAQ;EACR,QAAQ;EACR,QAAQ;GAAE,GAAG;GAAG,GAAG;GAAK,GAAG;GAAG;EAC/B;CACD,OAAO;EACL,MAAM;EACN,OAAO;EACP,QAAQ;EACR,OAAO;EACP,QAAQ;GAAE,GAAG;GAAG,GAAG;GAAK,GAAG;GAAG;EAC/B;CACD,MAAM;EACJ,MAAM;EACN,QAAQ;EACR,QAAQ;EACR,QAAQ;GAAE,GAAG;GAAK,GAAG;GAAK,GAAG;GAAG;EACjC;CACD,MAAM;EACJ,MAAM;EACN,QAAQ;EACR,QAAQ;EACR,QAAQ;GAAE,GAAG;GAAM,GAAG;GAAK,GAAG;GAAG;EAClC;CACF"}
|
|
1
|
+
{"version":3,"file":"physics.js","names":[],"sources":["../../src/types/physics.ts"],"sourcesContent":["/**\n * Physics and collision detection type definitions for Black Trigram combat system.\n *\n * **Korean**: 물리 및 충돌 타입 정의\n *\n * This module provides type definitions for the collision detection system, including\n * bounding boxes, attack reach calculations, and raycasting results.\n *\n * @module types/physics\n * @category Type Definitions\n * @korean 물리타입\n */\n\nimport type {\n Position,\n TrigramStance,\n VitalPointCategory,\n VitalPointSeverity,\n} from \"./common\";\n\n/**\n * Anatomical regions for collision detection.\n *\n * **Korean**: 해부학적 영역\n *\n * The five main body regions used for bounding box collision detection.\n * Each region contains multiple vital points.\n *\n * @public\n * @category Collision Types\n * @korean 해부영역\n */\nexport type AnatomicalRegionPhysics =\n | \"head\" // 머리 (Meori)\n | \"neck\" // 목 (Mok)\n | \"torso\" // 몸통 (Momtong)\n | \"arms\" // 팔 (Pal)\n | \"legs\"; // 다리 (Dari)\n\n/**\n * Bounding box shape types for collision detection.\n *\n * **Korean**: 경계 상자 형태\n *\n * @public\n * @category Collision Types\n * @korean 경계형태\n */\nexport type BoundingBoxType = \"sphere\" | \"box\" | \"capsule\";\n\n/**\n * Body part source for reach calculation.\n *\n * **Korean**: 신체 부위\n *\n * Defines which body part's length is used for reach calculation.\n *\n * @public\n * @category Combat Types\n * @korean 신체부위\n */\nexport type ReachBodyPart = \"arm\" | \"leg\" | \"torso\";\n\n/**\n * Specific limb types for exposure tracking.\n *\n * **Korean**: 노출된 사지 부위\n *\n * Granular limb identification for counter-attack and breaking techniques.\n * Each limb can be targeted during its extension phase.\n *\n * @public\n * @category Combat Types\n * @korean 사지부위\n */\nexport type ExposedLimbType =\n | \"left_arm\" // 왼팔 (Oenpal)\n | \"right_arm\" // 오른팔 (Oreunpal)\n | \"left_leg\" // 왼다리 (Oendari)\n | \"right_leg\" // 오른다리 (Oreundari)\n | \"left_ankle\" // 왼발목 (Oenbalmok)\n | \"right_ankle\" // 오른발목 (Oreunbalmok)\n | \"left_knee\" // 왼무릎 (Oenmureup)\n | \"right_knee\" // 오른무릎 (Oreunmureup)\n | \"left_elbow\" // 왼팔꿈치 (Oenpalkkumchi)\n | \"right_elbow\" // 오른팔꿈치 (Oreunpalkkumchi)\n | \"left_wrist\" // 왼손목 (Oensomok)\n | \"right_wrist\"; // 오른손목 (Oreunsomok)\n\n/**\n * Limb exposure window during technique execution.\n *\n * **Korean**: 사지 노출 시간\n *\n * Defines when and how a limb becomes vulnerable during attack execution.\n * Used for counter-attacks, breaking techniques, and defensive opportunities.\n *\n * @public\n * @category Combat Types\n * @korean 사지노출시간\n */\nexport interface LimbExposureWindow {\n /**\n * Which limb is exposed during the technique.\n * @korean 노출된사지\n */\n readonly exposedLimb: ExposedLimbType;\n\n /**\n * Start time of exposure as fraction of execution time (0.0-1.0).\n * Example: 0.3 means exposure starts at 30% into the animation.\n * @korean 노출시작시간\n */\n readonly startTime: number;\n\n /**\n * Duration of exposure window in milliseconds.\n * This is the vulnerable period where counter-attacks can target the limb.\n * @korean 노출지속시간\n */\n readonly duration: number;\n\n /**\n * Vulnerability multiplier for damage to this limb (1.0-3.0).\n * Higher values indicate greater vulnerability:\n * - 1.0-1.3: Minor exposure (quick jabs)\n * - 1.4-1.8: Moderate exposure (standard strikes)\n * - 1.9-3.0: Critical exposure (overextended kicks, spinning techniques)\n * @korean 취약성배수\n */\n readonly vulnerabilityMultiplier: number;\n\n /**\n * Whether this exposure allows breaking techniques.\n * True for fully extended limbs (kicks, lunging punches).\n * @korean 파쇄기술가능\n */\n readonly allowsBreaking: boolean;\n}\n\n/**\n * Physical reach configuration for techniques.\n *\n * **Korean**: 물리적 도달 설정\n *\n * Defines how a technique's reach is calculated using physical attributes\n * rather than a fixed distance value. The actual reach depends on:\n * - Body part length from archetype physical attributes\n * - Animation extension multiplier from hit timing\n * - Stance modifiers from Eight Trigrams\n *\n * **NEW**: Now includes limb exposure tracking for counter-attack opportunities.\n *\n * Formula: `effectiveReach = (limbLength/100) × extensionMultiplier × stanceModifier`\n *\n * @example\n * ```typescript\n * // Punch technique using arm length with exposure\n * const punchReach: PhysicalReachConfig = {\n * bodyPart: \"arm\",\n * techniqueType: \"punch\",\n * baseExtension: 0.95, // 95% arm extension at peak\n * exposureWindow: {\n * exposedLimb: \"right_arm\",\n * startTime: 0.4,\n * duration: 300,\n * vulnerabilityMultiplier: 1.3,\n * allowsBreaking: false\n * }\n * };\n *\n * // Kick using leg length - high vulnerability\n * const kickReach: PhysicalReachConfig = {\n * bodyPart: \"leg\",\n * techniqueType: \"kick\",\n * baseExtension: 1.1, // 110% leg extension (high reach)\n * exposureWindow: {\n * exposedLimb: \"right_leg\",\n * startTime: 0.5,\n * duration: 400,\n * vulnerabilityMultiplier: 2.2,\n * allowsBreaking: true\n * }\n * };\n * ```\n *\n * @public\n * @category Combat Types\n * @korean 물리적도달설정\n */\nexport interface PhysicalReachConfig {\n /**\n * Body part used for reach calculation.\n * Determines which physical attribute length is used.\n * @korean 신체부위\n */\n readonly bodyPart: ReachBodyPart;\n\n /**\n * Technique type for classification.\n * @korean 기술유형\n */\n readonly techniqueType: TechniqueType;\n\n /**\n * Base extension multiplier (0.0 - 1.5).\n * - 0.4-0.5: Close range (elbows, knees)\n * - 0.9-1.0: Standard reach (punches)\n * - 1.1-1.5: Extended reach (kicks, spinning techniques)\n * @korean 기본확장배수\n */\n readonly baseExtension: number;\n\n /**\n * Optional limb exposure window for counter-attack opportunities.\n * Defines when and how the attacking limb becomes vulnerable.\n * @korean 사지노출설정\n */\n readonly exposureWindow?: LimbExposureWindow;\n}\n\n/**\n * Runtime validation helper for base extension multiplier.\n *\n * Ensures that the provided baseExtension value respects the documented range\n * of 0.0 to 1.5. This should be used when loading or constructing technique\n * configurations from dynamic sources (e.g. JSON, network, editors).\n *\n * **Korean**: 기본 확장 배수 검증\n *\n * @param baseExtension - The base extension multiplier to validate.\n * @returns `true` if the value is within the inclusive range [0.0, 1.5], otherwise `false`.\n * @public\n * @category Combat Types\n * @korean 기본확장배수검증\n */\nexport function isValidBaseExtension(baseExtension: number): boolean {\n return baseExtension >= 0.0 && baseExtension <= 1.5;\n}\n\n/**\n * Asserts that a PhysicalReachConfig has a valid baseExtension value.\n *\n * Throws a RangeError if the configuration's baseExtension is outside the\n * allowed range of 0.0 to 1.5. This provides a canonical runtime check that\n * can be used by reach calculators or technique loaders.\n *\n * **Korean**: 물리적 도달 설정 검증\n *\n * @param config - The PhysicalReachConfig instance to validate.\n * @throws RangeError If baseExtension is outside [0.0, 1.5].\n * @public\n * @category Combat Types\n * @korean 물리적도달설정검증\n */\nexport function assertValidPhysicalReachConfig(\n config: PhysicalReachConfig\n): void {\n if (!isValidBaseExtension(config.baseExtension)) {\n throw new RangeError(\n `Invalid baseExtension ${config.baseExtension}. ` +\n \"Expected a value between 0.0 and 1.5 inclusive.\"\n );\n }\n}\n\n/**\n * Technique types for attack reach calculation.\n *\n * **Korean**: 기술 유형\n *\n * @public\n * @category Combat Types\n * @korean 기술유형\n */\nexport type TechniqueType =\n | \"punch\" // 주먹 (Jumeok)\n | \"kick\" // 발차기 (Balchagi)\n | \"elbow\" // 팔꿈치 (Palkkumchi)\n | \"knee\" // 무릎 (Mureup)\n | \"pressure_point\"; // 급소 (Geupso)\n\n/**\n * Bounding box definition for an anatomical region.\n *\n * **Korean**: 경계 상자\n *\n * Defines the collision volume for an anatomical region using either a sphere,\n * box, or capsule shape. Used for broad-phase collision detection.\n *\n * @example\n * ```typescript\n * const headBox: BoundingBox = {\n * type: \"sphere\",\n * center: { x: 0, y: 1.7, z: 0 },\n * dimensions: { x: 0.125, y: 0, z: 0 }, // radius only\n * region: \"head\"\n * };\n * ```\n *\n * @public\n * @category Collision Types\n * @korean 경계상자\n */\nexport interface BoundingBox {\n /** Shape type of the bounding box */\n readonly type: BoundingBoxType;\n\n /** Center position of the bounding box in 3D space */\n readonly center: Position3D;\n\n /** Dimensions: radius for sphere, width/height/depth for box, radius/height for capsule */\n readonly dimensions: Position3D;\n\n /** Anatomical region this bounding box represents */\n readonly region: AnatomicalRegionPhysics;\n}\n\n/**\n * Position in 3D space.\n *\n * **Korean**: 3D 위치\n *\n * Extends the 2D Position type with a z-coordinate for Three.js integration.\n *\n * @public\n * @category Core Types\n * @korean 3D위치\n */\nexport interface Position3D {\n /** X coordinate in meters */\n readonly x: number;\n\n /** Y coordinate in meters */\n readonly y: number;\n\n /** Z coordinate in meters */\n readonly z: number;\n}\n\n/**\n * Attack reach calculation result.\n *\n * **Korean**: 공격 범위\n *\n * Contains the effective attack range considering technique type and stance modifiers.\n *\n * @example\n * ```typescript\n * const kickReach: AttackReach = {\n * technique: \"kick\",\n * baseReach: 1.0,\n * stance: TrigramStance.LI,\n * stanceModifier: 1.20,\n * effectiveReach: 1.2\n * };\n * ```\n *\n * @public\n * @category Combat Types\n * @korean 공격범위\n */\nexport interface AttackReach {\n /** Type of technique being used */\n readonly technique: TechniqueType;\n\n /** Base reach in meters without modifiers */\n readonly baseReach: number;\n\n /** Current trigram stance */\n readonly stance: TrigramStance;\n\n /** Stance-specific reach modifier (0.9 - 1.2) */\n readonly stanceModifier: number;\n\n /** Final effective reach: baseReach × stanceModifier */\n readonly effectiveReach: number;\n}\n\n/**\n * Collision detection result.\n *\n * **Korean**: 충돌 결과\n *\n * Contains comprehensive information about a collision, including whether it hit,\n * which region and vital point were struck, distance, and accuracy.\n *\n * @example\n * ```typescript\n * const result: CollisionResult = {\n * hit: true,\n * region: \"head\",\n * vitalPoint: templePoint,\n * distance: 0.8,\n * accuracy: 0.95\n * };\n * ```\n *\n * @public\n * @category Collision Types\n * @korean 충돌결과\n */\nexport interface CollisionResult {\n /** Whether the attack hit */\n readonly hit: boolean;\n\n /** Anatomical region hit (if any) */\n readonly region?: AnatomicalRegionPhysics;\n\n /** Specific vital point hit (if any) */\n readonly vitalPoint?: CollisionVitalPoint;\n\n /** Actual distance from attacker to target in meters */\n readonly distance: number;\n\n /** Hit accuracy (0-1), based on targeting precision */\n readonly accuracy: number;\n\n /** 3D point of intersection (if hit) */\n readonly hitPoint?: Position3D;\n}\n\n/**\n * Minimal vital point shape for collision results to avoid deep cross-module\n * dependencies. Full vital point data lives in the vitalpoint system.\n */\nexport interface CollisionVitalPoint {\n readonly id: string;\n readonly names: {\n readonly korean: string;\n readonly english: string;\n readonly romanized: string;\n };\n readonly position: Position;\n readonly category: VitalPointCategory;\n readonly severity: VitalPointSeverity;\n}\n\n/**\n * Counter-attack opportunity during opponent's technique execution.\n *\n * **Korean**: 반격 기회\n *\n * Represents a window of opportunity to counter-attack when the opponent\n * has exposed a limb during their technique execution.\n *\n * @example\n * ```typescript\n * const counterOpportunity: CounterOpportunity = {\n * exposedLimb: \"right_leg\",\n * windowStart: 450, // ms into opponent's kick\n * windowDuration: 300, // 300ms counter window\n * vulnerabilityMultiplier: 2.0,\n * allowsBreaking: true,\n * recommendedCounters: [\"ankle_break\", \"knee_strike\", \"leg_sweep\"]\n * };\n * ```\n *\n * @public\n * @category Combat Types\n * @korean 반격기회\n */\nexport interface CounterOpportunity {\n /**\n * The exposed limb that can be targeted.\n * @korean 노출된사지\n */\n readonly exposedLimb: ExposedLimbType;\n\n /**\n * Start time of counter window in milliseconds (from technique start).\n * @korean 반격시작시간\n */\n readonly windowStart: number;\n\n /**\n * Duration of counter window in milliseconds.\n * @korean 반격지속시간\n */\n readonly windowDuration: number;\n\n /**\n * Vulnerability multiplier for damage during this window.\n * @korean 취약성배수\n */\n readonly vulnerabilityMultiplier: number;\n\n /**\n * Whether limb breaking techniques are effective during this window.\n * @korean 파쇄기술가능\n */\n readonly allowsBreaking: boolean;\n\n /**\n * Recommended counter-technique IDs for this opportunity.\n * @korean 추천반격기술\n */\n readonly recommendedCounters?: readonly string[];\n}\n\n/**\n * Breaking technique target types.\n *\n * **Korean**: 파쇄 기술 목표\n *\n * Specific joint and bone targets for breaking techniques.\n * Used for limb-breaking counter-attacks.\n *\n * @public\n * @category Combat Types\n * @korean 파쇄목표\n */\nexport type BreakingTarget =\n | \"ankle\" // 발목 (Balmok)\n | \"knee\" // 무릎 (Mureup)\n | \"elbow\" // 팔꿈치 (Palkkumchi)\n | \"wrist\" // 손목 (Sonmok)\n | \"shoulder\" // 어깨 (Eokkae)\n | \"hip\"; // 엉덩이/골반 (Eongdeongi)\n\n/**\n * Breaking technique result.\n *\n * **Korean**: 파쇄 결과\n *\n * Result of a breaking technique attempt, including injury severity\n * and status effects applied to the broken limb.\n *\n * @public\n * @category Combat Types\n * @korean 파쇄결과\n */\nexport interface BreakingResult {\n /**\n * Whether the breaking technique succeeded.\n * @korean 파쇄성공\n */\n readonly success: boolean;\n\n /**\n * Target limb/joint that was broken.\n * @korean 파쇄목표\n */\n readonly target: BreakingTarget;\n\n /**\n * Severity of the break (fracture, dislocation, etc.).\n * Range: 0.0 (failed) to 1.0 (complete break).\n * @korean 파쇄심각도\n */\n readonly severity: number;\n\n /**\n * Damage dealt to the limb.\n * @korean 피해량\n */\n readonly damage: number;\n\n /**\n * Mobility reduction percentage (0.0-1.0).\n * Affects movement speed and technique execution.\n * @korean 이동력감소\n */\n readonly mobilityReduction: number;\n\n /**\n * IDs of status effects applied (pain, bleeding, disabled_limb, etc.).\n * @korean 상태효과\n */\n readonly statusEffects: readonly string[];\n}\n\n/**\n * Raycast query parameters.\n *\n * **Korean**: 레이캐스트 쿼리\n *\n * Defines the parameters for a raycasting operation from attacker to target.\n *\n * @public\n * @category Collision Types\n * @korean 레이캐스트쿼리\n */\nexport interface RaycastQuery {\n /** Origin point of the ray (attacker position) */\n readonly origin: Position3D;\n\n /** Direction vector of the ray (normalized) */\n readonly direction: Position3D;\n\n /** Maximum distance to check in meters */\n readonly maxDistance: number;\n\n /** Target anatomical region (optional, for filtering) */\n readonly targetRegion?: AnatomicalRegionPhysics;\n}\n\n/**\n * Base reach values for different technique types.\n *\n * **Korean**: 기본 범위 값\n *\n * @public\n * @category Combat Constants\n * @korean 기본범위값\n */\nexport const BASE_REACH: Record<TechniqueType, number> = {\n punch: 0.7, // 70cm - Standard punch range\n kick: 1.0, // 100cm - Longer leg reach\n elbow: 0.4, // 40cm - Close-range strike\n knee: 0.4, // 40cm - Close-range strike\n pressure_point: 0.5, // 50cm - Precise targeting\n};\n\n/**\n * Stance reach modifiers for the Eight Trigrams.\n *\n * **Korean**: 팔괘 범위 수정자\n *\n * Each stance affects attack range differently based on its combat philosophy:\n * - Aggressive stances (Fire, Thunder) extend reach\n * - Defensive stances (Mountain) reduce reach\n * - Balanced stances (Heaven, Earth) have minor adjustments\n *\n * @public\n * @category Combat Constants\n * @korean 팔괘범위수정자\n */\nexport const STANCE_REACH_MODIFIERS: Record<TrigramStance, number> = {\n li: 1.2, // ☲ Fire (리): +20% reach (aggressive)\n jin: 1.15, // ☳ Thunder (진): +15% reach (explosive)\n geon: 1.1, // ☰ Heaven (건): +10% reach (balanced)\n son: 1.05, // ☴ Wind (손): +5% reach (continuous)\n tae: 1.0, // ☱ Lake (태): neutral reach\n gam: 1.0, // ☵ Water (감): neutral reach (adaptive)\n gon: 0.95, // ☷ Earth (곤): -5% reach (grounded)\n gan: 0.9, // ☶ Mountain (간): -10% reach (defensive)\n};\n\n/**\n * Korean terminology for collision detection concepts.\n *\n * **Korean**: 충돌 감지 용어\n *\n * @public\n * @category Korean Terms\n * @korean 충돌용어\n */\nexport const COLLISION_KOREAN_TERMS = {\n collisionDetection: \"충돌감지\", // Chungdol Gamji\n attackRange: \"타격범위\", // Tagyeok Beomwi\n preciseHit: \"정밀타격\", // Jeongmil Tagyeok\n boundingBox: \"경계상자\", // Gyeonggye Sangja\n raycast: \"광선투사\", // Gwangseon Tusa\n hitAccuracy: \"타격정확도\", // Tagyeok Jeonghwakdo\n effectiveReach: \"유효범위\", // Yuhyo Beomwi\n} as const;\n\n/**\n * Anatomical region dimensions in meters.\n *\n * **Korean**: 해부 영역 치수\n *\n * Standard dimensions for adult human anatomical regions used for bounding box creation.\n *\n * @public\n * @category Collision Constants\n * @korean 해부영역치수\n */\nexport const ANATOMICAL_DIMENSIONS = {\n head: {\n type: \"sphere\" as const,\n radius: 0.125, // 머리 구 반경 (Meori sphere radius) – 12.5cm radius for head sphere\n center: { x: 0, y: 1.7, z: 0 }, // 머리 중심 높이 (Meori center height) – Average adult head height\n },\n neck: {\n type: \"capsule\" as const,\n radius: 0.075, // 목 캡슐 반경 (Mok capsule radius) – 7.5cm radius for neck cylinder\n height: 0.15, // 목 캡슐 높이 (Mok capsule height) – 15cm height\n center: { x: 0, y: 1.5, z: 0 }, // 목 중심 높이 (Mok center height)\n },\n torso: {\n type: \"box\" as const,\n width: 0.4, // 몸통 상자 가로 (Momtong box width) – 40cm width (shoulder to shoulder)\n height: 0.6, // 몸통 상자 세로 (Momtong box height) – 60cm height (neck to waist)\n depth: 0.25, // 몸통 상자 깊이 (Momtong box depth) – 25cm depth (front to back)\n center: { x: 0, y: 1.1, z: 0 }, // 몸통 중심 높이 (Momtong center height)\n },\n arms: {\n type: \"capsule\" as const,\n radius: 0.05, // 팔 캡슐 반경 (Pal capsule radius) – 5cm radius for arm cylinder\n height: 0.6, // 팔 캡슐 길이 (Pal capsule length) – 60cm length (shoulder to hand)\n center: { x: 0.3, y: 1.1, z: 0 }, // 팔 위치 오프셋 (Pal position offset) – Offset for arm position\n },\n legs: {\n type: \"capsule\" as const,\n radius: 0.06, // 다리 캡슐 반경 (Dari capsule radius) – 6cm radius for leg cylinder\n height: 0.8, // 다리 캡슐 길이 (Dari capsule length) – 80cm length (hip to foot)\n center: { x: 0.15, y: 0.4, z: 0 }, // 다리 위치 오프셋 (Dari position offset) – Offset for leg position\n },\n} as const;\n"],"mappings":";;;;;;;;;;;;;;;;AA4OA,SAAgB,qBAAqB,eAAgC;CACnE,OAAO,iBAAiB,KAAO,iBAAiB;;;;;;;;;;;;;;;;;AAkBlD,SAAgB,+BACd,QACM;CACN,IAAI,CAAC,qBAAqB,OAAO,cAAc,EAC7C,MAAM,IAAI,WACR,yBAAyB,OAAO,cAAc,mDAE/C;;;;;;;;;;;AAwVL,IAAa,aAA4C;CACvD,OAAO;CACP,MAAM;CACN,OAAO;CACP,MAAM;CACN,gBAAgB;CACjB;;;;;;;;;;;;;;;AAgBD,IAAa,yBAAwD;CACnE,IAAI;CACJ,KAAK;CACL,MAAM;CACN,KAAK;CACL,KAAK;CACL,KAAK;CACL,KAAK;CACL,KAAK;CACN;;;;;;;;;;AAWD,IAAa,yBAAyB;CACpC,oBAAoB;CACpB,aAAa;CACb,YAAY;CACZ,aAAa;CACb,SAAS;CACT,aAAa;CACb,gBAAgB;CACjB;;;;;;;;;;;;AAaD,IAAa,wBAAwB;CACnC,MAAM;EACJ,MAAM;EACN,QAAQ;EACR,QAAQ;GAAE,GAAG;GAAG,GAAG;GAAK,GAAG;GAAG;EAC/B;CACD,MAAM;EACJ,MAAM;EACN,QAAQ;EACR,QAAQ;EACR,QAAQ;GAAE,GAAG;GAAG,GAAG;GAAK,GAAG;GAAG;EAC/B;CACD,OAAO;EACL,MAAM;EACN,OAAO;EACP,QAAQ;EACR,OAAO;EACP,QAAQ;GAAE,GAAG;GAAG,GAAG;GAAK,GAAG;GAAG;EAC/B;CACD,MAAM;EACJ,MAAM;EACN,QAAQ;EACR,QAAQ;EACR,QAAQ;GAAE,GAAG;GAAK,GAAG;GAAK,GAAG;GAAG;EACjC;CACD,MAAM;EACJ,MAAM;EACN,QAAQ;EACR,QAAQ;EACR,QAAQ;GAAE,GAAG;GAAM,GAAG;GAAK,GAAG;GAAG;EAClC;CACF"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"skeletal.js","names":[],"sources":["../../src/types/skeletal.ts"],"sourcesContent":["/**\n * Skeletal rigging types for articulated body model\n *\n * Defines bone hierarchy, skeletal rig structure, and animation keyframes\n * for realistic human-like fighter animations with independent limb movement.\n *\n * @module types/skeletal\n * @category Type Definitions\n * @korean 골격타입\n */\n\nimport * as THREE from \"three\";\n\n/**\n * Bone in skeletal rig hierarchy\n *\n * Represents a single bone with position, rotation, scale, and parent-child relationships.\n * Bones form a tree structure for realistic articulated body movement.\n *\n * @public\n * @category Skeletal System\n * @korean 뼈\n */\nexport interface Bone {\n /**\n * Unique identifier for the bone\n * @korean 이름\n */\n readonly name: string;\n\n /**\n * Parent bone (null for root bone)\n * @korean 부모뼈\n */\n parent: Bone | null;\n\n /**\n * Local position relative to parent\n * @korean 위치\n */\n position: THREE.Vector3;\n\n /**\n * Local rotation in Euler angles\n * @korean 회전\n */\n rotation: THREE.Euler;\n\n /**\n * Local scale\n * @korean 크기\n */\n scale: THREE.Vector3;\n\n /**\n * Child bones\n * @korean 자식뼈들\n */\n children: Bone[];\n\n /**\n * Length of the bone (for rendering)\n * @korean 길이\n */\n readonly length: number;\n\n /**\n * Rest pose position (default pose)\n * @korean 기본위치\n */\n readonly restPosition: THREE.Vector3;\n\n /**\n * Rest pose rotation (default pose)\n * @korean 기본회전\n */\n readonly restRotation: THREE.Euler;\n}\n\n/**\n * Skeletal rig with complete bone hierarchy\n *\n * Contains root bone and map of all bones for efficient lookup.\n * Maximum 30 bones for 60fps performance.\n *\n * @public\n * @category Skeletal System\n * @korean 골격\n */\nexport interface SkeletalRig {\n /**\n * Root bone (pelvis/center)\n * @korean 뿌리뼈\n */\n readonly root: Bone;\n\n /**\n * Map of bone name to bone for fast lookup\n * @korean 뼈맵\n */\n readonly bones: Map<string, Bone>;\n\n /**\n * Total number of bones in rig\n * @korean 뼈개수\n */\n readonly boneCount: number;\n}\n\n/**\n * Animation keyframe for skeletal animation\n *\n * Defines bone transformations at a specific time in the animation.\n * Keyframes are interpolated for smooth animation between poses.\n * Now includes integrated anatomy state for hands, feet, and facial expressions.\n *\n * @public\n * @category Animation\n * @korean 애니메이션키프레임\n */\nexport interface AnimationKeyframe {\n /**\n * Time in seconds from animation start\n * @korean 시간\n */\n readonly time: number;\n\n /**\n * Bone rotations at this keyframe\n * Map of bone name to rotation\n * @korean 뼈회전들\n */\n readonly boneRotations: Map<string, THREE.Euler>;\n\n /**\n * Bone positions at this keyframe (optional, for IK or special moves)\n * Map of bone name to position offset from rest pose\n * @korean 뼈위치들\n */\n readonly bonePositions: Map<string, THREE.Vector3>;\n\n /**\n * Optional easing function name for interpolation\n * @korean 이징함수\n */\n readonly easing?: \"linear\" | \"ease-in\" | \"ease-out\" | \"ease-in-out\";\n\n // ═══════════════════════════════════════════════════════════════════════════\n // ANATOMY STATE (해부학 상태) - Integrated hand, foot, and facial animation\n // ═══════════════════════════════════════════════════════════════════════════\n\n /**\n * Left hand pose type at this keyframe\n * @korean 왼손자세\n */\n readonly leftHandPose?: string;\n\n /**\n * Right hand pose type at this keyframe\n * @korean 오른손자세\n */\n readonly rightHandPose?: string;\n\n /**\n * Whether left foot is highlighted (e.g., during kicks)\n * @korean 왼발강조\n */\n readonly leftFootHighlight?: boolean;\n\n /**\n * Whether right foot is highlighted (e.g., during kicks)\n * @korean 오른발강조\n */\n readonly rightFootHighlight?: boolean;\n\n /**\n * Left hand highlight mode for striking surface\n * @korean 왼손강조모드\n */\n readonly leftHandHighlightMode?:\n | \"none\"\n | \"knuckles\"\n | \"palm\"\n | \"knife_edge\"\n | \"fingertips\";\n\n /**\n * Right hand highlight mode for striking surface\n * @korean 오른손강조모드\n */\n readonly rightHandHighlightMode?:\n | \"none\"\n | \"knuckles\"\n | \"palm\"\n | \"knife_edge\"\n | \"fingertips\";\n\n /**\n * Facial expression at this keyframe\n * @korean 얼굴표정\n */\n readonly facialExpression?: string;\n\n /**\n * Muscle activation targets at this keyframe\n * Map of muscle group name to tension level (0-1)\n * @korean 근육활성화\n */\n readonly muscleActivations?: Map<string, number>;\n}\n\n/**\n * Attack animation type categories\n *\n * Defines the 5 base categories of attack animations with variants.\n * Each technique maps to one of these animation types.\n *\n * @public\n * @category Animation\n * @korean 공격애니메이션타입\n */\nexport enum AttackAnimationType {\n // Punch category (주먹 타격)\n PUNCH_HIGH = \"punch_high\",\n PUNCH_MID = \"punch_mid\",\n PUNCH_LOW = \"punch_low\",\n\n // Kick category (발차기)\n KICK_FRONT = \"kick_front\",\n KICK_SIDE = \"kick_side\",\n KICK_ROUNDHOUSE = \"kick_round\",\n\n // Elbow category (팔꿈치 타격)\n ELBOW_STRIKE = \"elbow_strike\",\n ELBOW_UPPERCUT = \"elbow_uppercut\",\n\n // Knee category (무릎 타격)\n KNEE_STRIKE = \"knee_strike\",\n KNEE_CLINCH = \"knee_clinch\",\n\n // Pressure point category (급소 타격)\n PRESSURE_POINT = \"pressure_point\",\n PRESSURE_POINT_RAPID = \"pressure_point_rapid\",\n}\n\n/**\n * Animation configuration for a technique\n *\n * Links a technique to its specific attack animation with speed modifier.\n *\n * @public\n * @category Animation\n * @korean 기술애니메이션설정\n */\nexport interface TechniqueAnimationConfig {\n /**\n * Type of attack animation to play\n * @korean 애니메이션타입\n */\n readonly type: AttackAnimationType;\n\n /**\n * Speed modifier (0.8-1.2)\n * - Light techniques: 1.2x speed\n * - Normal techniques: 1.0x speed\n * - Heavy techniques: 0.8x speed\n * @korean 속도배율\n */\n readonly speedModifier: number;\n}\n\n/**\n * Complete skeletal animation sequence\n *\n * Sequence of keyframes defining a complete animation\n * (e.g., jab, cross, roundhouse kick).\n *\n * @public\n * @category Animation\n * @korean 골격애니메이션\n */\nexport interface SkeletalAnimation {\n /**\n * Animation identifier\n * @korean 이름\n */\n readonly name: string;\n\n /**\n * Korean name for animation\n * @korean 한글이름\n */\n readonly koreanName: string;\n\n /**\n * Animation keyframes in chronological order\n * @korean 키프레임들\n */\n readonly keyframes: AnimationKeyframe[];\n\n /**\n * Total duration in seconds\n * @korean 지속시간\n */\n readonly duration: number;\n\n /**\n * Whether animation loops\n * @korean 반복여부\n */\n readonly loop: boolean;\n\n /**\n * Animation type (attack, defense, stance change, movement, etc.)\n * @korean 애니메이션타입\n */\n readonly type: \"attack\" | \"defense\" | \"stance\" | \"walk\" | \"idle\" | \"movement\";\n}\n\n/**\n * Bone chain definition for IK (Inverse Kinematics)\n *\n * Defines a chain of bones for IK solving (e.g., arm chain, leg chain).\n * Used for realistic limb positioning and movement.\n *\n * @public\n * @category Skeletal System\n * @korean 뼈체인\n */\nexport interface BoneChain {\n /**\n * Chain identifier\n * @korean 이름\n */\n readonly name: string;\n\n /**\n * Start bone (e.g., shoulder for arm chain)\n * @korean 시작뼈\n */\n readonly startBone: string;\n\n /**\n * End bone (e.g., hand for arm chain)\n * @korean 끝뼈\n */\n readonly endBone: string;\n\n /**\n * Bones in chain from start to end\n * @korean 뼈들\n */\n readonly bones: string[];\n\n /**\n * IK target position (for end effector)\n * @korean IK목표\n */\n ikTarget?: THREE.Vector3;\n}\n\n/**\n * Hand bone structure with 5 fingers\n *\n * Simplified hand model with palm and 5 fingers.\n * Each finger has 3 segments (proximal, middle, distal).\n *\n * @public\n * @category Skeletal System\n * @korean 손뼈\n */\nexport interface HandBones {\n /**\n * Palm bone\n * @korean 손바닥\n */\n readonly palm: Bone;\n\n /**\n * Thumb bones (3 segments)\n * @korean 엄지손가락\n */\n readonly thumb: [Bone, Bone, Bone];\n\n /**\n * Index finger bones (3 segments)\n * @korean 검지손가락\n */\n readonly index: [Bone, Bone, Bone];\n\n /**\n * Middle finger bones (3 segments)\n * @korean 중지손가락\n */\n readonly middle: [Bone, Bone, Bone];\n\n /**\n * Ring finger bones (3 segments)\n * @korean 약지손가락\n */\n readonly ring: [Bone, Bone, Bone];\n\n /**\n * Pinky finger bones (3 segments)\n * @korean 새끼손가락\n */\n readonly pinky: [Bone, Bone, Bone];\n}\n\n/**\n * Joint constraint for realistic movement\n *\n * Defines rotation limits for joints (e.g., elbow can't bend backward).\n * Ensures anatomically correct movement.\n *\n * @public\n * @category Skeletal System\n * @korean 관절제약\n */\nexport interface JointConstraint {\n /**\n * Bone name this constraint applies to\n * @korean 뼈이름\n */\n readonly boneName: string;\n\n /**\n * Minimum rotation angles (X, Y, Z) in radians\n * @korean 최소회전\n */\n readonly minRotation: THREE.Vector3;\n\n /**\n * Maximum rotation angles (X, Y, Z) in radians\n * @korean 최대회전\n */\n readonly maxRotation: THREE.Vector3;\n\n /**\n * Whether this joint can twist (rotate around its length)\n * @korean 비틀기가능\n */\n readonly canTwist: boolean;\n}\n\n/**\n * Bone names for humanoid rig\n *\n * Standard bone naming convention for humanoid skeleton.\n * Total: 28 bones base + optional 38 hand bones (19 per hand) = 66 bones max.\n *\n * Hand bones are optional and can be excluded for performance (LOD system).\n *\n * @public\n * @category Skeletal System\n * @korean 뼈이름들\n */\nexport enum BoneName {\n // Core (1 bone)\n PELVIS = \"pelvis\",\n\n // Spine (3 bones)\n SPINE_LOWER = \"spine_lower\",\n SPINE_MIDDLE = \"spine_middle\",\n SPINE_UPPER = \"spine_upper\",\n\n // Head (2 bones)\n NECK = \"neck\",\n HEAD = \"head\",\n\n // Left Arm (6 bones)\n SHOULDER_L = \"shoulder_L\",\n UPPER_ARM_L = \"upper_arm_L\",\n ELBOW_L = \"elbow_L\",\n FOREARM_L = \"forearm_L\",\n WRIST_L = \"wrist_L\",\n HAND_L = \"hand_L\",\n\n // Right Arm (6 bones)\n SHOULDER_R = \"shoulder_R\",\n UPPER_ARM_R = \"upper_arm_R\",\n ELBOW_R = \"elbow_R\",\n FOREARM_R = \"forearm_R\",\n WRIST_R = \"wrist_R\",\n HAND_R = \"hand_R\",\n\n // Left Leg (5 bones)\n HIP_L = \"hip_L\",\n THIGH_L = \"thigh_L\",\n KNEE_L = \"knee_L\",\n SHIN_L = \"shin_L\",\n FOOT_L = \"foot_L\",\n\n // Right Leg (5 bones)\n HIP_R = \"hip_R\",\n THIGH_R = \"thigh_R\",\n KNEE_R = \"knee_R\",\n SHIN_R = \"shin_R\",\n FOOT_R = \"foot_R\",\n\n // Left Hand Fingers (19 bones - optional for LOD)\n THUMB_META_L = \"thumb_meta_L\",\n THUMB_PROX_L = \"thumb_prox_L\",\n THUMB_DIST_L = \"thumb_dist_L\",\n INDEX_META_L = \"index_meta_L\",\n INDEX_PROX_L = \"index_prox_L\",\n INDEX_INTER_L = \"index_inter_L\",\n INDEX_DIST_L = \"index_dist_L\",\n MIDDLE_META_L = \"middle_meta_L\",\n MIDDLE_PROX_L = \"middle_prox_L\",\n MIDDLE_INTER_L = \"middle_inter_L\",\n MIDDLE_DIST_L = \"middle_dist_L\",\n RING_META_L = \"ring_meta_L\",\n RING_PROX_L = \"ring_prox_L\",\n RING_INTER_L = \"ring_inter_L\",\n RING_DIST_L = \"ring_dist_L\",\n PINKY_META_L = \"pinky_meta_L\",\n PINKY_PROX_L = \"pinky_prox_L\",\n PINKY_INTER_L = \"pinky_inter_L\",\n PINKY_DIST_L = \"pinky_dist_L\",\n\n // Right Hand Fingers (19 bones - optional for LOD)\n THUMB_META_R = \"thumb_meta_R\",\n THUMB_PROX_R = \"thumb_prox_R\",\n THUMB_DIST_R = \"thumb_dist_R\",\n INDEX_META_R = \"index_meta_R\",\n INDEX_PROX_R = \"index_prox_R\",\n INDEX_INTER_R = \"index_inter_R\",\n INDEX_DIST_R = \"index_dist_R\",\n MIDDLE_META_R = \"middle_meta_R\",\n MIDDLE_PROX_R = \"middle_prox_R\",\n MIDDLE_INTER_R = \"middle_inter_R\",\n MIDDLE_DIST_R = \"middle_dist_R\",\n RING_META_R = \"ring_meta_R\",\n RING_PROX_R = \"ring_prox_R\",\n RING_INTER_R = \"ring_inter_R\",\n RING_DIST_R = \"ring_dist_R\",\n PINKY_META_R = \"pinky_meta_R\",\n PINKY_PROX_R = \"pinky_prox_R\",\n PINKY_INTER_R = \"pinky_inter_R\",\n PINKY_DIST_R = \"pinky_dist_R\",\n}\n\n/**\n * Animation state for skeletal player\n *\n * Tracks current animation playback state for skeletal animations.\n *\n * @public\n * @category Animation\n * @korean 애니메이션상태\n */\nexport interface SkeletalAnimationState {\n /**\n * Current animation being played\n * @korean 현재애니메이션\n */\n currentAnimation: SkeletalAnimation | null;\n\n /**\n * Current time in animation (seconds)\n * @korean 현재시간\n */\n currentTime: number;\n\n /**\n * Whether animation is playing\n * @korean 재생중\n */\n isPlaying: boolean;\n\n /**\n * Animation playback speed multiplier\n * @korean 재생속도\n */\n playbackSpeed: number;\n\n /**\n * Previous keyframe index\n * @korean 이전키프레임\n */\n previousKeyframeIndex: number;\n\n /**\n * Next keyframe index\n * @korean 다음키프레임\n */\n nextKeyframeIndex: number;\n}\n\n/**\n * Fighting stance guard pose configuration\n *\n * Defines complete body positioning for authentic Korean martial arts stances.\n * Includes arms, torso, legs, and pelvis rotations based on traditional Taekwondo/Hapkido.\n *\n * Each stance corresponds to a real martial arts position with unique leg positioning.\n * Used for stance-specific idle animations at 60fps.\n *\n * @public\n * @category Animation\n * @korean 자세방어포즈\n */\nexport interface StanceGuardPose {\n /**\n * Left arm bone rotations (shoulder, elbow, wrist)\n * @korean 왼팔\n */\n readonly leftArm: {\n readonly shoulder: THREE.Euler;\n readonly elbow: THREE.Euler;\n readonly wrist: THREE.Euler;\n };\n\n /**\n * Right arm bone rotations (shoulder, elbow, wrist)\n * @korean 오른팔\n */\n readonly rightArm: {\n readonly shoulder: THREE.Euler;\n readonly elbow: THREE.Euler;\n readonly wrist: THREE.Euler;\n };\n\n /**\n * Torso rotation (spine upper bone)\n * @korean 몸통회전\n */\n readonly torso: THREE.Euler;\n\n /**\n * Left leg bone rotations (hip, knee, ankle)\n * NEW: For authentic Taekwondo stance positioning\n * @korean 왼다리\n */\n readonly leftLeg: {\n readonly hip: THREE.Euler; // Hip rotation/abduction\n readonly knee: THREE.Euler; // Knee bend angle\n readonly ankle: THREE.Euler; // Ankle dorsiflexion/plantarflexion\n };\n\n /**\n * Right leg bone rotations (hip, knee, ankle)\n * NEW: For authentic Taekwondo stance positioning\n * @korean 오른다리\n */\n readonly rightLeg: {\n readonly hip: THREE.Euler; // Hip rotation/abduction\n readonly knee: THREE.Euler; // Knee bend angle\n readonly ankle: THREE.Euler; // Ankle dorsiflexion/plantarflexion\n };\n\n /**\n * Pelvis rotation (hip tilt and rotation)\n * NEW: For proper stance base\n * @korean 골반회전\n */\n readonly pelvis: THREE.Euler;\n\n /**\n * Stance width (foot spacing in meters)\n * NEW: Defines lateral distance between feet\n * Range: 0.3 (narrow) to 1.5 (wide horse stance)\n *\n * This value describes the intended lateral spacing between the feet for this\n * stance configuration. Consumers (e.g. animation systems or positioning\n * overlays) may use it to drive foot placement, validation, or visualization\n * as needed.\n *\n * @korean 자세너비\n */\n readonly stanceWidth: number;\n\n /**\n * Stance depth (front-to-back foot spacing in meters)\n * Optional: Defaults to 0 for parallel stances (Juchum Seogi, Narani Seogi)\n *\n * For forward stances (Ap Koobi Seogi): positive value (~0.6-0.9m)\n * For back stances (Dwi Seogi): negative value (~-0.3m with back foot forward)\n *\n * Left foot gets -depth/2, right foot gets +depth/2 on Z-axis.\n * Combined with stanceWidth, this defines the full 2D foot placement.\n *\n * @korean 자세깊이\n */\n readonly stanceDepth?: number;\n\n /**\n * Pelvis height offset (vertical drop in meters)\n * Optional: Defaults to 0 for normal standing height\n *\n * For deep stances (Juchum Seogi, Joong Ha Seogi): negative value (~-0.15 to -0.3m)\n * This lowers the center of gravity for power and stability.\n *\n * @korean 골반높이\n */\n readonly pelvisHeight?: number;\n\n /**\n * Weight distribution (forward/neutral/back)\n * @korean 무게중심\n */\n readonly weight: \"forward\" | \"neutral\" | \"back\";\n\n /**\n * Breathing animation range (min/max for chest movement)\n * @korean 호흡범위\n */\n readonly breathingRange: {\n readonly min: number;\n readonly max: number;\n };\n}\n\n/**\n * Stance guard animation configuration\n *\n * Extends base AnimationConfig with stance-specific guard pose data.\n * Includes 4-6 frame breathing animation for realistic idle behavior.\n *\n * @public\n * @category Animation\n * @korean 자세방어애니메이션설정\n */\nexport interface StanceGuardAnimationConfig {\n /**\n * Trigram stance identifier\n * @korean 괘\n */\n readonly stance: string;\n\n /**\n * Korean name of stance\n * @korean 한글이름\n */\n readonly koreanName: string;\n\n /**\n * English name of stance\n * @korean 영어이름\n */\n readonly englishName: string;\n\n /**\n * Guard pose keyframe (default position)\n * @korean 방어포즈\n */\n readonly guardPose: StanceGuardPose;\n\n /**\n * Breathing animation frames (4-6 frames)\n * @korean 호흡프레임\n */\n readonly breathingFrames: number;\n\n /**\n * Target frames per second (60fps)\n * @korean 초당프레임\n */\n readonly fps: number;\n\n /**\n * Breathing cycle loop enabled\n * @korean 반복여부\n */\n readonly loop: boolean;\n\n /**\n * Animation priority (0 for idle guards)\n * @korean 우선순위\n */\n readonly priority: number;\n}\n\n/**\n * Mirror a guard pose for left/right stance laterality.\n *\n * **Korean**: 자세 좌우 대칭\n *\n * Creates a mirror-image guard pose by swapping left and right limb positions\n * and negating lateral (Y-axis and Z-axis) rotations. This enables authentic\n * left/right stance differentiation in Korean martial arts.\n *\n * Key transformations:\n * - Swap leftArm ↔ rightArm bone rotations\n * - Negate Y rotation (lateral twist)\n * - Negate Z rotation (roll)\n * - Preserve X rotation (forward/back bend)\n * - Keep weight distribution and breathing range unchanged\n *\n * @param pose - Original guard pose to mirror\n * @returns Mirrored guard pose with swapped and negated rotations\n *\n * @example\n * ```typescript\n * // Create right-handed version of a left-handed guard\n * const leftGeonGuard = GEON_HIGH_GUARD_POSE;\n * const rightGeonGuard = mirrorGuardPose(leftGeonGuard);\n *\n * // leftGeonGuard has left hand forward\n * // rightGeonGuard has right hand forward (mirrored)\n * ```\n *\n * @public\n * @category Animation\n * @korean 방어포즈대칭\n */\nexport function mirrorGuardPose(pose: StanceGuardPose): StanceGuardPose {\n // Helper to negate Y and Z rotations while preserving X\n const mirrorEuler = (euler: THREE.Euler): THREE.Euler => {\n return new THREE.Euler(\n euler.x, // Preserve forward/back bend\n -euler.y, // Negate lateral twist\n -euler.z // Negate roll\n );\n };\n\n return {\n // Swap left and right arms with mirrored rotations\n leftArm: {\n shoulder: mirrorEuler(pose.rightArm.shoulder),\n elbow: mirrorEuler(pose.rightArm.elbow),\n wrist: mirrorEuler(pose.rightArm.wrist),\n },\n rightArm: {\n shoulder: mirrorEuler(pose.leftArm.shoulder),\n elbow: mirrorEuler(pose.leftArm.elbow),\n wrist: mirrorEuler(pose.leftArm.wrist),\n },\n // Mirror torso rotation\n torso: mirrorEuler(pose.torso),\n // NEW: Swap and mirror leg positions\n leftLeg: {\n hip: mirrorEuler(pose.rightLeg.hip),\n knee: mirrorEuler(pose.rightLeg.knee),\n ankle: mirrorEuler(pose.rightLeg.ankle),\n },\n rightLeg: {\n hip: mirrorEuler(pose.leftLeg.hip),\n knee: mirrorEuler(pose.leftLeg.knee),\n ankle: mirrorEuler(pose.leftLeg.ankle),\n },\n // Mirror pelvis rotation\n pelvis: mirrorEuler(pose.pelvis),\n // Stance width remains the same\n stanceWidth: pose.stanceWidth,\n // Stance depth: negate for mirrored stance (front foot becomes back foot)\n stanceDepth: pose.stanceDepth !== undefined ? -pose.stanceDepth : undefined,\n // Pelvis height remains unchanged (not affected by laterality)\n pelvisHeight: pose.pelvisHeight,\n // Weight distribution remains the same\n weight: pose.weight,\n // Breathing range unchanged (not affected by laterality, reuse original object)\n breathingRange: pose.breathingRange,\n };\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;AA6NA,IAAY,sBAAL,yBAAA,qBAAA;AAEL,qBAAA,gBAAa;AACb,qBAAA,eAAY;AACZ,qBAAA,eAAY;AAGZ,qBAAA,gBAAa;AACb,qBAAA,eAAY;AACZ,qBAAA,qBAAkB;AAGlB,qBAAA,kBAAe;AACf,qBAAA,oBAAiB;AAGjB,qBAAA,iBAAc;AACd,qBAAA,iBAAc;AAGd,qBAAA,oBAAiB;AACjB,qBAAA,0BAAuB;;KACxB;;;;;;;;;;;;;AAsND,IAAY,WAAL,yBAAA,UAAA;AAEL,UAAA,YAAS;AAGT,UAAA,iBAAc;AACd,UAAA,kBAAe;AACf,UAAA,iBAAc;AAGd,UAAA,UAAO;AACP,UAAA,UAAO;AAGP,UAAA,gBAAa;AACb,UAAA,iBAAc;AACd,UAAA,aAAU;AACV,UAAA,eAAY;AACZ,UAAA,aAAU;AACV,UAAA,YAAS;AAGT,UAAA,gBAAa;AACb,UAAA,iBAAc;AACd,UAAA,aAAU;AACV,UAAA,eAAY;AACZ,UAAA,aAAU;AACV,UAAA,YAAS;AAGT,UAAA,WAAQ;AACR,UAAA,aAAU;AACV,UAAA,YAAS;AACT,UAAA,YAAS;AACT,UAAA,YAAS;AAGT,UAAA,WAAQ;AACR,UAAA,aAAU;AACV,UAAA,YAAS;AACT,UAAA,YAAS;AACT,UAAA,YAAS;AAGT,UAAA,kBAAe;AACf,UAAA,kBAAe;AACf,UAAA,kBAAe;AACf,UAAA,kBAAe;AACf,UAAA,kBAAe;AACf,UAAA,mBAAgB;AAChB,UAAA,kBAAe;AACf,UAAA,mBAAgB;AAChB,UAAA,mBAAgB;AAChB,UAAA,oBAAiB;AACjB,UAAA,mBAAgB;AAChB,UAAA,iBAAc;AACd,UAAA,iBAAc;AACd,UAAA,kBAAe;AACf,UAAA,iBAAc;AACd,UAAA,kBAAe;AACf,UAAA,kBAAe;AACf,UAAA,mBAAgB;AAChB,UAAA,kBAAe;AAGf,UAAA,kBAAe;AACf,UAAA,kBAAe;AACf,UAAA,kBAAe;AACf,UAAA,kBAAe;AACf,UAAA,kBAAe;AACf,UAAA,mBAAgB;AAChB,UAAA,kBAAe;AACf,UAAA,mBAAgB;AAChB,UAAA,mBAAgB;AAChB,UAAA,oBAAiB;AACjB,UAAA,mBAAgB;AAChB,UAAA,iBAAc;AACd,UAAA,iBAAc;AACd,UAAA,kBAAe;AACf,UAAA,iBAAc;AACd,UAAA,kBAAe;AACf,UAAA,kBAAe;AACf,UAAA,mBAAgB;AAChB,UAAA,kBAAe;;KAChB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA0QD,SAAgB,gBAAgB,MAAwC;CAEtE,MAAM,eAAe,UAAoC;AACvD,SAAO,IAAI,MAAM,MACf,MAAM,GACN,CAAC,MAAM,GACP,CAAC,MAAM,EACR;;AAGH,QAAO;EAEL,SAAS;GACP,UAAU,YAAY,KAAK,SAAS,SAAS;GAC7C,OAAO,YAAY,KAAK,SAAS,MAAM;GACvC,OAAO,YAAY,KAAK,SAAS,MAAM;GACxC;EACD,UAAU;GACR,UAAU,YAAY,KAAK,QAAQ,SAAS;GAC5C,OAAO,YAAY,KAAK,QAAQ,MAAM;GACtC,OAAO,YAAY,KAAK,QAAQ,MAAM;GACvC;EAED,OAAO,YAAY,KAAK,MAAM;EAE9B,SAAS;GACP,KAAK,YAAY,KAAK,SAAS,IAAI;GACnC,MAAM,YAAY,KAAK,SAAS,KAAK;GACrC,OAAO,YAAY,KAAK,SAAS,MAAM;GACxC;EACD,UAAU;GACR,KAAK,YAAY,KAAK,QAAQ,IAAI;GAClC,MAAM,YAAY,KAAK,QAAQ,KAAK;GACpC,OAAO,YAAY,KAAK,QAAQ,MAAM;GACvC;EAED,QAAQ,YAAY,KAAK,OAAO;EAEhC,aAAa,KAAK;EAElB,aAAa,KAAK,gBAAgB,KAAA,IAAY,CAAC,KAAK,cAAc,KAAA;EAElE,cAAc,KAAK;EAEnB,QAAQ,KAAK;EAEb,gBAAgB,KAAK;EACtB"}
|
|
1
|
+
{"version":3,"file":"skeletal.js","names":[],"sources":["../../src/types/skeletal.ts"],"sourcesContent":["/**\n * Skeletal rigging types for articulated body model\n *\n * Defines bone hierarchy, skeletal rig structure, and animation keyframes\n * for realistic human-like fighter animations with independent limb movement.\n *\n * @module types/skeletal\n * @category Type Definitions\n * @korean 골격타입\n */\n\nimport * as THREE from \"three\";\n\n/**\n * Bone in skeletal rig hierarchy\n *\n * Represents a single bone with position, rotation, scale, and parent-child relationships.\n * Bones form a tree structure for realistic articulated body movement.\n *\n * @public\n * @category Skeletal System\n * @korean 뼈\n */\nexport interface Bone {\n /**\n * Unique identifier for the bone\n * @korean 이름\n */\n readonly name: string;\n\n /**\n * Parent bone (null for root bone)\n * @korean 부모뼈\n */\n parent: Bone | null;\n\n /**\n * Local position relative to parent\n * @korean 위치\n */\n position: THREE.Vector3;\n\n /**\n * Local rotation in Euler angles\n * @korean 회전\n */\n rotation: THREE.Euler;\n\n /**\n * Local scale\n * @korean 크기\n */\n scale: THREE.Vector3;\n\n /**\n * Child bones\n * @korean 자식뼈들\n */\n children: Bone[];\n\n /**\n * Length of the bone (for rendering)\n * @korean 길이\n */\n readonly length: number;\n\n /**\n * Rest pose position (default pose)\n * @korean 기본위치\n */\n readonly restPosition: THREE.Vector3;\n\n /**\n * Rest pose rotation (default pose)\n * @korean 기본회전\n */\n readonly restRotation: THREE.Euler;\n}\n\n/**\n * Skeletal rig with complete bone hierarchy\n *\n * Contains root bone and map of all bones for efficient lookup.\n * Maximum 30 bones for 60fps performance.\n *\n * @public\n * @category Skeletal System\n * @korean 골격\n */\nexport interface SkeletalRig {\n /**\n * Root bone (pelvis/center)\n * @korean 뿌리뼈\n */\n readonly root: Bone;\n\n /**\n * Map of bone name to bone for fast lookup\n * @korean 뼈맵\n */\n readonly bones: Map<string, Bone>;\n\n /**\n * Total number of bones in rig\n * @korean 뼈개수\n */\n readonly boneCount: number;\n}\n\n/**\n * Animation keyframe for skeletal animation\n *\n * Defines bone transformations at a specific time in the animation.\n * Keyframes are interpolated for smooth animation between poses.\n * Now includes integrated anatomy state for hands, feet, and facial expressions.\n *\n * @public\n * @category Animation\n * @korean 애니메이션키프레임\n */\nexport interface AnimationKeyframe {\n /**\n * Time in seconds from animation start\n * @korean 시간\n */\n readonly time: number;\n\n /**\n * Bone rotations at this keyframe\n * Map of bone name to rotation\n * @korean 뼈회전들\n */\n readonly boneRotations: Map<string, THREE.Euler>;\n\n /**\n * Bone positions at this keyframe (optional, for IK or special moves)\n * Map of bone name to position offset from rest pose\n * @korean 뼈위치들\n */\n readonly bonePositions: Map<string, THREE.Vector3>;\n\n /**\n * Optional easing function name for interpolation\n * @korean 이징함수\n */\n readonly easing?: \"linear\" | \"ease-in\" | \"ease-out\" | \"ease-in-out\";\n\n // ═══════════════════════════════════════════════════════════════════════════\n // ANATOMY STATE (해부학 상태) - Integrated hand, foot, and facial animation\n // ═══════════════════════════════════════════════════════════════════════════\n\n /**\n * Left hand pose type at this keyframe\n * @korean 왼손자세\n */\n readonly leftHandPose?: string;\n\n /**\n * Right hand pose type at this keyframe\n * @korean 오른손자세\n */\n readonly rightHandPose?: string;\n\n /**\n * Whether left foot is highlighted (e.g., during kicks)\n * @korean 왼발강조\n */\n readonly leftFootHighlight?: boolean;\n\n /**\n * Whether right foot is highlighted (e.g., during kicks)\n * @korean 오른발강조\n */\n readonly rightFootHighlight?: boolean;\n\n /**\n * Left hand highlight mode for striking surface\n * @korean 왼손강조모드\n */\n readonly leftHandHighlightMode?:\n | \"none\"\n | \"knuckles\"\n | \"palm\"\n | \"knife_edge\"\n | \"fingertips\";\n\n /**\n * Right hand highlight mode for striking surface\n * @korean 오른손강조모드\n */\n readonly rightHandHighlightMode?:\n | \"none\"\n | \"knuckles\"\n | \"palm\"\n | \"knife_edge\"\n | \"fingertips\";\n\n /**\n * Facial expression at this keyframe\n * @korean 얼굴표정\n */\n readonly facialExpression?: string;\n\n /**\n * Muscle activation targets at this keyframe\n * Map of muscle group name to tension level (0-1)\n * @korean 근육활성화\n */\n readonly muscleActivations?: Map<string, number>;\n}\n\n/**\n * Attack animation type categories\n *\n * Defines the 5 base categories of attack animations with variants.\n * Each technique maps to one of these animation types.\n *\n * @public\n * @category Animation\n * @korean 공격애니메이션타입\n */\nexport enum AttackAnimationType {\n // Punch category (주먹 타격)\n PUNCH_HIGH = \"punch_high\",\n PUNCH_MID = \"punch_mid\",\n PUNCH_LOW = \"punch_low\",\n\n // Kick category (발차기)\n KICK_FRONT = \"kick_front\",\n KICK_SIDE = \"kick_side\",\n KICK_ROUNDHOUSE = \"kick_round\",\n\n // Elbow category (팔꿈치 타격)\n ELBOW_STRIKE = \"elbow_strike\",\n ELBOW_UPPERCUT = \"elbow_uppercut\",\n\n // Knee category (무릎 타격)\n KNEE_STRIKE = \"knee_strike\",\n KNEE_CLINCH = \"knee_clinch\",\n\n // Pressure point category (급소 타격)\n PRESSURE_POINT = \"pressure_point\",\n PRESSURE_POINT_RAPID = \"pressure_point_rapid\",\n}\n\n/**\n * Animation configuration for a technique\n *\n * Links a technique to its specific attack animation with speed modifier.\n *\n * @public\n * @category Animation\n * @korean 기술애니메이션설정\n */\nexport interface TechniqueAnimationConfig {\n /**\n * Type of attack animation to play\n * @korean 애니메이션타입\n */\n readonly type: AttackAnimationType;\n\n /**\n * Speed modifier (0.8-1.2)\n * - Light techniques: 1.2x speed\n * - Normal techniques: 1.0x speed\n * - Heavy techniques: 0.8x speed\n * @korean 속도배율\n */\n readonly speedModifier: number;\n}\n\n/**\n * Complete skeletal animation sequence\n *\n * Sequence of keyframes defining a complete animation\n * (e.g., jab, cross, roundhouse kick).\n *\n * @public\n * @category Animation\n * @korean 골격애니메이션\n */\nexport interface SkeletalAnimation {\n /**\n * Animation identifier\n * @korean 이름\n */\n readonly name: string;\n\n /**\n * Korean name for animation\n * @korean 한글이름\n */\n readonly koreanName: string;\n\n /**\n * Animation keyframes in chronological order\n * @korean 키프레임들\n */\n readonly keyframes: AnimationKeyframe[];\n\n /**\n * Total duration in seconds\n * @korean 지속시간\n */\n readonly duration: number;\n\n /**\n * Whether animation loops\n * @korean 반복여부\n */\n readonly loop: boolean;\n\n /**\n * Animation type (attack, defense, stance change, movement, etc.)\n * @korean 애니메이션타입\n */\n readonly type: \"attack\" | \"defense\" | \"stance\" | \"walk\" | \"idle\" | \"movement\";\n}\n\n/**\n * Bone chain definition for IK (Inverse Kinematics)\n *\n * Defines a chain of bones for IK solving (e.g., arm chain, leg chain).\n * Used for realistic limb positioning and movement.\n *\n * @public\n * @category Skeletal System\n * @korean 뼈체인\n */\nexport interface BoneChain {\n /**\n * Chain identifier\n * @korean 이름\n */\n readonly name: string;\n\n /**\n * Start bone (e.g., shoulder for arm chain)\n * @korean 시작뼈\n */\n readonly startBone: string;\n\n /**\n * End bone (e.g., hand for arm chain)\n * @korean 끝뼈\n */\n readonly endBone: string;\n\n /**\n * Bones in chain from start to end\n * @korean 뼈들\n */\n readonly bones: string[];\n\n /**\n * IK target position (for end effector)\n * @korean IK목표\n */\n ikTarget?: THREE.Vector3;\n}\n\n/**\n * Hand bone structure with 5 fingers\n *\n * Simplified hand model with palm and 5 fingers.\n * Each finger has 3 segments (proximal, middle, distal).\n *\n * @public\n * @category Skeletal System\n * @korean 손뼈\n */\nexport interface HandBones {\n /**\n * Palm bone\n * @korean 손바닥\n */\n readonly palm: Bone;\n\n /**\n * Thumb bones (3 segments)\n * @korean 엄지손가락\n */\n readonly thumb: [Bone, Bone, Bone];\n\n /**\n * Index finger bones (3 segments)\n * @korean 검지손가락\n */\n readonly index: [Bone, Bone, Bone];\n\n /**\n * Middle finger bones (3 segments)\n * @korean 중지손가락\n */\n readonly middle: [Bone, Bone, Bone];\n\n /**\n * Ring finger bones (3 segments)\n * @korean 약지손가락\n */\n readonly ring: [Bone, Bone, Bone];\n\n /**\n * Pinky finger bones (3 segments)\n * @korean 새끼손가락\n */\n readonly pinky: [Bone, Bone, Bone];\n}\n\n/**\n * Joint constraint for realistic movement\n *\n * Defines rotation limits for joints (e.g., elbow can't bend backward).\n * Ensures anatomically correct movement.\n *\n * @public\n * @category Skeletal System\n * @korean 관절제약\n */\nexport interface JointConstraint {\n /**\n * Bone name this constraint applies to\n * @korean 뼈이름\n */\n readonly boneName: string;\n\n /**\n * Minimum rotation angles (X, Y, Z) in radians\n * @korean 최소회전\n */\n readonly minRotation: THREE.Vector3;\n\n /**\n * Maximum rotation angles (X, Y, Z) in radians\n * @korean 최대회전\n */\n readonly maxRotation: THREE.Vector3;\n\n /**\n * Whether this joint can twist (rotate around its length)\n * @korean 비틀기가능\n */\n readonly canTwist: boolean;\n}\n\n/**\n * Bone names for humanoid rig\n *\n * Standard bone naming convention for humanoid skeleton.\n * Total: 28 bones base + optional 38 hand bones (19 per hand) = 66 bones max.\n *\n * Hand bones are optional and can be excluded for performance (LOD system).\n *\n * @public\n * @category Skeletal System\n * @korean 뼈이름들\n */\nexport enum BoneName {\n // Core (1 bone)\n PELVIS = \"pelvis\",\n\n // Spine (3 bones)\n SPINE_LOWER = \"spine_lower\",\n SPINE_MIDDLE = \"spine_middle\",\n SPINE_UPPER = \"spine_upper\",\n\n // Head (2 bones)\n NECK = \"neck\",\n HEAD = \"head\",\n\n // Left Arm (6 bones)\n SHOULDER_L = \"shoulder_L\",\n UPPER_ARM_L = \"upper_arm_L\",\n ELBOW_L = \"elbow_L\",\n FOREARM_L = \"forearm_L\",\n WRIST_L = \"wrist_L\",\n HAND_L = \"hand_L\",\n\n // Right Arm (6 bones)\n SHOULDER_R = \"shoulder_R\",\n UPPER_ARM_R = \"upper_arm_R\",\n ELBOW_R = \"elbow_R\",\n FOREARM_R = \"forearm_R\",\n WRIST_R = \"wrist_R\",\n HAND_R = \"hand_R\",\n\n // Left Leg (5 bones)\n HIP_L = \"hip_L\",\n THIGH_L = \"thigh_L\",\n KNEE_L = \"knee_L\",\n SHIN_L = \"shin_L\",\n FOOT_L = \"foot_L\",\n\n // Right Leg (5 bones)\n HIP_R = \"hip_R\",\n THIGH_R = \"thigh_R\",\n KNEE_R = \"knee_R\",\n SHIN_R = \"shin_R\",\n FOOT_R = \"foot_R\",\n\n // Left Hand Fingers (19 bones - optional for LOD)\n THUMB_META_L = \"thumb_meta_L\",\n THUMB_PROX_L = \"thumb_prox_L\",\n THUMB_DIST_L = \"thumb_dist_L\",\n INDEX_META_L = \"index_meta_L\",\n INDEX_PROX_L = \"index_prox_L\",\n INDEX_INTER_L = \"index_inter_L\",\n INDEX_DIST_L = \"index_dist_L\",\n MIDDLE_META_L = \"middle_meta_L\",\n MIDDLE_PROX_L = \"middle_prox_L\",\n MIDDLE_INTER_L = \"middle_inter_L\",\n MIDDLE_DIST_L = \"middle_dist_L\",\n RING_META_L = \"ring_meta_L\",\n RING_PROX_L = \"ring_prox_L\",\n RING_INTER_L = \"ring_inter_L\",\n RING_DIST_L = \"ring_dist_L\",\n PINKY_META_L = \"pinky_meta_L\",\n PINKY_PROX_L = \"pinky_prox_L\",\n PINKY_INTER_L = \"pinky_inter_L\",\n PINKY_DIST_L = \"pinky_dist_L\",\n\n // Right Hand Fingers (19 bones - optional for LOD)\n THUMB_META_R = \"thumb_meta_R\",\n THUMB_PROX_R = \"thumb_prox_R\",\n THUMB_DIST_R = \"thumb_dist_R\",\n INDEX_META_R = \"index_meta_R\",\n INDEX_PROX_R = \"index_prox_R\",\n INDEX_INTER_R = \"index_inter_R\",\n INDEX_DIST_R = \"index_dist_R\",\n MIDDLE_META_R = \"middle_meta_R\",\n MIDDLE_PROX_R = \"middle_prox_R\",\n MIDDLE_INTER_R = \"middle_inter_R\",\n MIDDLE_DIST_R = \"middle_dist_R\",\n RING_META_R = \"ring_meta_R\",\n RING_PROX_R = \"ring_prox_R\",\n RING_INTER_R = \"ring_inter_R\",\n RING_DIST_R = \"ring_dist_R\",\n PINKY_META_R = \"pinky_meta_R\",\n PINKY_PROX_R = \"pinky_prox_R\",\n PINKY_INTER_R = \"pinky_inter_R\",\n PINKY_DIST_R = \"pinky_dist_R\",\n}\n\n/**\n * Animation state for skeletal player\n *\n * Tracks current animation playback state for skeletal animations.\n *\n * @public\n * @category Animation\n * @korean 애니메이션상태\n */\nexport interface SkeletalAnimationState {\n /**\n * Current animation being played\n * @korean 현재애니메이션\n */\n currentAnimation: SkeletalAnimation | null;\n\n /**\n * Current time in animation (seconds)\n * @korean 현재시간\n */\n currentTime: number;\n\n /**\n * Whether animation is playing\n * @korean 재생중\n */\n isPlaying: boolean;\n\n /**\n * Animation playback speed multiplier\n * @korean 재생속도\n */\n playbackSpeed: number;\n\n /**\n * Previous keyframe index\n * @korean 이전키프레임\n */\n previousKeyframeIndex: number;\n\n /**\n * Next keyframe index\n * @korean 다음키프레임\n */\n nextKeyframeIndex: number;\n}\n\n/**\n * Fighting stance guard pose configuration\n *\n * Defines complete body positioning for authentic Korean martial arts stances.\n * Includes arms, torso, legs, and pelvis rotations based on traditional Taekwondo/Hapkido.\n *\n * Each stance corresponds to a real martial arts position with unique leg positioning.\n * Used for stance-specific idle animations at 60fps.\n *\n * @public\n * @category Animation\n * @korean 자세방어포즈\n */\nexport interface StanceGuardPose {\n /**\n * Left arm bone rotations (shoulder, elbow, wrist)\n * @korean 왼팔\n */\n readonly leftArm: {\n readonly shoulder: THREE.Euler;\n readonly elbow: THREE.Euler;\n readonly wrist: THREE.Euler;\n };\n\n /**\n * Right arm bone rotations (shoulder, elbow, wrist)\n * @korean 오른팔\n */\n readonly rightArm: {\n readonly shoulder: THREE.Euler;\n readonly elbow: THREE.Euler;\n readonly wrist: THREE.Euler;\n };\n\n /**\n * Torso rotation (spine upper bone)\n * @korean 몸통회전\n */\n readonly torso: THREE.Euler;\n\n /**\n * Left leg bone rotations (hip, knee, ankle)\n * NEW: For authentic Taekwondo stance positioning\n * @korean 왼다리\n */\n readonly leftLeg: {\n readonly hip: THREE.Euler; // Hip rotation/abduction\n readonly knee: THREE.Euler; // Knee bend angle\n readonly ankle: THREE.Euler; // Ankle dorsiflexion/plantarflexion\n };\n\n /**\n * Right leg bone rotations (hip, knee, ankle)\n * NEW: For authentic Taekwondo stance positioning\n * @korean 오른다리\n */\n readonly rightLeg: {\n readonly hip: THREE.Euler; // Hip rotation/abduction\n readonly knee: THREE.Euler; // Knee bend angle\n readonly ankle: THREE.Euler; // Ankle dorsiflexion/plantarflexion\n };\n\n /**\n * Pelvis rotation (hip tilt and rotation)\n * NEW: For proper stance base\n * @korean 골반회전\n */\n readonly pelvis: THREE.Euler;\n\n /**\n * Stance width (foot spacing in meters)\n * NEW: Defines lateral distance between feet\n * Range: 0.3 (narrow) to 1.5 (wide horse stance)\n *\n * This value describes the intended lateral spacing between the feet for this\n * stance configuration. Consumers (e.g. animation systems or positioning\n * overlays) may use it to drive foot placement, validation, or visualization\n * as needed.\n *\n * @korean 자세너비\n */\n readonly stanceWidth: number;\n\n /**\n * Stance depth (front-to-back foot spacing in meters)\n * Optional: Defaults to 0 for parallel stances (Juchum Seogi, Narani Seogi)\n *\n * For forward stances (Ap Koobi Seogi): positive value (~0.6-0.9m)\n * For back stances (Dwi Seogi): negative value (~-0.3m with back foot forward)\n *\n * Left foot gets -depth/2, right foot gets +depth/2 on Z-axis.\n * Combined with stanceWidth, this defines the full 2D foot placement.\n *\n * @korean 자세깊이\n */\n readonly stanceDepth?: number;\n\n /**\n * Pelvis height offset (vertical drop in meters)\n * Optional: Defaults to 0 for normal standing height\n *\n * For deep stances (Juchum Seogi, Joong Ha Seogi): negative value (~-0.15 to -0.3m)\n * This lowers the center of gravity for power and stability.\n *\n * @korean 골반높이\n */\n readonly pelvisHeight?: number;\n\n /**\n * Weight distribution (forward/neutral/back)\n * @korean 무게중심\n */\n readonly weight: \"forward\" | \"neutral\" | \"back\";\n\n /**\n * Breathing animation range (min/max for chest movement)\n * @korean 호흡범위\n */\n readonly breathingRange: {\n readonly min: number;\n readonly max: number;\n };\n}\n\n/**\n * Stance guard animation configuration\n *\n * Extends base AnimationConfig with stance-specific guard pose data.\n * Includes 4-6 frame breathing animation for realistic idle behavior.\n *\n * @public\n * @category Animation\n * @korean 자세방어애니메이션설정\n */\nexport interface StanceGuardAnimationConfig {\n /**\n * Trigram stance identifier\n * @korean 괘\n */\n readonly stance: string;\n\n /**\n * Korean name of stance\n * @korean 한글이름\n */\n readonly koreanName: string;\n\n /**\n * English name of stance\n * @korean 영어이름\n */\n readonly englishName: string;\n\n /**\n * Guard pose keyframe (default position)\n * @korean 방어포즈\n */\n readonly guardPose: StanceGuardPose;\n\n /**\n * Breathing animation frames (4-6 frames)\n * @korean 호흡프레임\n */\n readonly breathingFrames: number;\n\n /**\n * Target frames per second (60fps)\n * @korean 초당프레임\n */\n readonly fps: number;\n\n /**\n * Breathing cycle loop enabled\n * @korean 반복여부\n */\n readonly loop: boolean;\n\n /**\n * Animation priority (0 for idle guards)\n * @korean 우선순위\n */\n readonly priority: number;\n}\n\n/**\n * Mirror a guard pose for left/right stance laterality.\n *\n * **Korean**: 자세 좌우 대칭\n *\n * Creates a mirror-image guard pose by swapping left and right limb positions\n * and negating lateral (Y-axis and Z-axis) rotations. This enables authentic\n * left/right stance differentiation in Korean martial arts.\n *\n * Key transformations:\n * - Swap leftArm ↔ rightArm bone rotations\n * - Negate Y rotation (lateral twist)\n * - Negate Z rotation (roll)\n * - Preserve X rotation (forward/back bend)\n * - Keep weight distribution and breathing range unchanged\n *\n * @param pose - Original guard pose to mirror\n * @returns Mirrored guard pose with swapped and negated rotations\n *\n * @example\n * ```typescript\n * // Create right-handed version of a left-handed guard\n * const leftGeonGuard = GEON_HIGH_GUARD_POSE;\n * const rightGeonGuard = mirrorGuardPose(leftGeonGuard);\n *\n * // leftGeonGuard has left hand forward\n * // rightGeonGuard has right hand forward (mirrored)\n * ```\n *\n * @public\n * @category Animation\n * @korean 방어포즈대칭\n */\nexport function mirrorGuardPose(pose: StanceGuardPose): StanceGuardPose {\n // Helper to negate Y and Z rotations while preserving X\n const mirrorEuler = (euler: THREE.Euler): THREE.Euler => {\n return new THREE.Euler(\n euler.x, // Preserve forward/back bend\n -euler.y, // Negate lateral twist\n -euler.z // Negate roll\n );\n };\n\n return {\n // Swap left and right arms with mirrored rotations\n leftArm: {\n shoulder: mirrorEuler(pose.rightArm.shoulder),\n elbow: mirrorEuler(pose.rightArm.elbow),\n wrist: mirrorEuler(pose.rightArm.wrist),\n },\n rightArm: {\n shoulder: mirrorEuler(pose.leftArm.shoulder),\n elbow: mirrorEuler(pose.leftArm.elbow),\n wrist: mirrorEuler(pose.leftArm.wrist),\n },\n // Mirror torso rotation\n torso: mirrorEuler(pose.torso),\n // NEW: Swap and mirror leg positions\n leftLeg: {\n hip: mirrorEuler(pose.rightLeg.hip),\n knee: mirrorEuler(pose.rightLeg.knee),\n ankle: mirrorEuler(pose.rightLeg.ankle),\n },\n rightLeg: {\n hip: mirrorEuler(pose.leftLeg.hip),\n knee: mirrorEuler(pose.leftLeg.knee),\n ankle: mirrorEuler(pose.leftLeg.ankle),\n },\n // Mirror pelvis rotation\n pelvis: mirrorEuler(pose.pelvis),\n // Stance width remains the same\n stanceWidth: pose.stanceWidth,\n // Stance depth: negate for mirrored stance (front foot becomes back foot)\n stanceDepth: pose.stanceDepth !== undefined ? -pose.stanceDepth : undefined,\n // Pelvis height remains unchanged (not affected by laterality)\n pelvisHeight: pose.pelvisHeight,\n // Weight distribution remains the same\n weight: pose.weight,\n // Breathing range unchanged (not affected by laterality, reuse original object)\n breathingRange: pose.breathingRange,\n };\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;AA6NA,IAAY,sBAAL,yBAAA,qBAAA;CAEL,oBAAA,gBAAa;CACb,oBAAA,eAAY;CACZ,oBAAA,eAAY;CAGZ,oBAAA,gBAAa;CACb,oBAAA,eAAY;CACZ,oBAAA,qBAAkB;CAGlB,oBAAA,kBAAe;CACf,oBAAA,oBAAiB;CAGjB,oBAAA,iBAAc;CACd,oBAAA,iBAAc;CAGd,oBAAA,oBAAiB;CACjB,oBAAA,0BAAuB;;KACxB;;;;;;;;;;;;;AAsND,IAAY,WAAL,yBAAA,UAAA;CAEL,SAAA,YAAS;CAGT,SAAA,iBAAc;CACd,SAAA,kBAAe;CACf,SAAA,iBAAc;CAGd,SAAA,UAAO;CACP,SAAA,UAAO;CAGP,SAAA,gBAAa;CACb,SAAA,iBAAc;CACd,SAAA,aAAU;CACV,SAAA,eAAY;CACZ,SAAA,aAAU;CACV,SAAA,YAAS;CAGT,SAAA,gBAAa;CACb,SAAA,iBAAc;CACd,SAAA,aAAU;CACV,SAAA,eAAY;CACZ,SAAA,aAAU;CACV,SAAA,YAAS;CAGT,SAAA,WAAQ;CACR,SAAA,aAAU;CACV,SAAA,YAAS;CACT,SAAA,YAAS;CACT,SAAA,YAAS;CAGT,SAAA,WAAQ;CACR,SAAA,aAAU;CACV,SAAA,YAAS;CACT,SAAA,YAAS;CACT,SAAA,YAAS;CAGT,SAAA,kBAAe;CACf,SAAA,kBAAe;CACf,SAAA,kBAAe;CACf,SAAA,kBAAe;CACf,SAAA,kBAAe;CACf,SAAA,mBAAgB;CAChB,SAAA,kBAAe;CACf,SAAA,mBAAgB;CAChB,SAAA,mBAAgB;CAChB,SAAA,oBAAiB;CACjB,SAAA,mBAAgB;CAChB,SAAA,iBAAc;CACd,SAAA,iBAAc;CACd,SAAA,kBAAe;CACf,SAAA,iBAAc;CACd,SAAA,kBAAe;CACf,SAAA,kBAAe;CACf,SAAA,mBAAgB;CAChB,SAAA,kBAAe;CAGf,SAAA,kBAAe;CACf,SAAA,kBAAe;CACf,SAAA,kBAAe;CACf,SAAA,kBAAe;CACf,SAAA,kBAAe;CACf,SAAA,mBAAgB;CAChB,SAAA,kBAAe;CACf,SAAA,mBAAgB;CAChB,SAAA,mBAAgB;CAChB,SAAA,oBAAiB;CACjB,SAAA,mBAAgB;CAChB,SAAA,iBAAc;CACd,SAAA,iBAAc;CACd,SAAA,kBAAe;CACf,SAAA,iBAAc;CACd,SAAA,kBAAe;CACf,SAAA,kBAAe;CACf,SAAA,mBAAgB;CAChB,SAAA,kBAAe;;KAChB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA0QD,SAAgB,gBAAgB,MAAwC;CAEtE,MAAM,eAAe,UAAoC;EACvD,OAAO,IAAI,MAAM,MACf,MAAM,GACN,CAAC,MAAM,GACP,CAAC,MAAM,EACR;;CAGH,OAAO;EAEL,SAAS;GACP,UAAU,YAAY,KAAK,SAAS,SAAS;GAC7C,OAAO,YAAY,KAAK,SAAS,MAAM;GACvC,OAAO,YAAY,KAAK,SAAS,MAAM;GACxC;EACD,UAAU;GACR,UAAU,YAAY,KAAK,QAAQ,SAAS;GAC5C,OAAO,YAAY,KAAK,QAAQ,MAAM;GACtC,OAAO,YAAY,KAAK,QAAQ,MAAM;GACvC;EAED,OAAO,YAAY,KAAK,MAAM;EAE9B,SAAS;GACP,KAAK,YAAY,KAAK,SAAS,IAAI;GACnC,MAAM,YAAY,KAAK,SAAS,KAAK;GACrC,OAAO,YAAY,KAAK,SAAS,MAAM;GACxC;EACD,UAAU;GACR,KAAK,YAAY,KAAK,QAAQ,IAAI;GAClC,MAAM,YAAY,KAAK,QAAQ,KAAK;GACpC,OAAO,YAAY,KAAK,QAAQ,MAAM;GACvC;EAED,QAAQ,YAAY,KAAK,OAAO;EAEhC,aAAa,KAAK;EAElB,aAAa,KAAK,gBAAgB,KAAA,IAAY,CAAC,KAAK,cAAc,KAAA;EAElE,cAAc,KAAK;EAEnB,QAAQ,KAAK;EAEb,gBAAgB,KAAK;EACtB"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"techniqueId.js","names":[],"sources":["../../src/types/techniqueId.ts"],"sourcesContent":["/**\n * Technique ID enum for type-safe technique references\n *\n * **Korean**: 기술 ID 열거형\n *\n * Provides compile-time safety for all technique references across the codebase.\n * Every technique in the game has a unique ID defined here.\n *\n * @module types/techniqueId\n * @category Combat System\n * @korean 기술ID\n */\n\n/**\n * All technique IDs in the game system\n *\n * Each archetype has unique techniques:\n * - 무사 (Musa) - 4 techniques: musa_thunder_strike, musa_iron_defense, musa_dragon_fist, musa_mountain_breaker\n * - 암살자 (Amsalja) - 4 techniques: amsalja_shadow_strike, amsalja_nerve_strike, amsalja_deadly_precision, amsalja_silent_death\n * - 해커 (Hacker) - 4 techniques: hacker_electric_shock, hacker_data_strike, hacker_cyber_overdrive, hacker_system_crash\n * - 정보요원 (Jeongbo) - 5 techniques: jeongbo_tactical_strike, jeongbo_counter_intelligence, jeongbo_psychological_warfare, jeongbo_precision_takedown, jeongbo_intelligence_strike\n * - 조직폭력배 (Jojik) - 4 techniques: jojik_street_brawl, jojik_improvised_weapon, jojik_ruthless_assault, jojik_brutal_takedown\n *\n * @public\n * @category Combat System\n * @korean 기술ID열거형\n */\nexport enum TechniqueId {\n // 무사 (Musa) - Traditional Warrior\n MUSA_THUNDER_STRIKE = \"musa_thunder_strike\",\n MUSA_IRON_DEFENSE = \"musa_iron_defense\",\n MUSA_DRAGON_FIST = \"musa_dragon_fist\",\n MUSA_MOUNTAIN_BREAKER = \"musa_mountain_breaker\",\n\n // 암살자 (Amsalja) - Shadow Assassin\n AMSALJA_SHADOW_STRIKE = \"amsalja_shadow_strike\",\n AMSALJA_NERVE_STRIKE = \"amsalja_nerve_strike\",\n AMSALJA_DEADLY_PRECISION = \"amsalja_deadly_precision\",\n AMSALJA_SILENT_DEATH = \"amsalja_silent_death\",\n\n // 해커 (Hacker) - Cyber Warrior\n HACKER_ELECTRIC_SHOCK = \"hacker_electric_shock\",\n HACKER_DATA_STRIKE = \"hacker_data_strike\",\n HACKER_CYBER_OVERDRIVE = \"hacker_cyber_overdrive\",\n HACKER_SYSTEM_CRASH = \"hacker_system_crash\",\n\n // 정보요원 (Jeongbo Yowon) - Intelligence Operative\n JEONGBO_TACTICAL_STRIKE = \"jeongbo_tactical_strike\",\n JEONGBO_COUNTER_INTELLIGENCE = \"jeongbo_counter_intelligence\",\n JEONGBO_PSYCHOLOGICAL_WARFARE = \"jeongbo_psychological_warfare\",\n JEONGBO_PRECISION_TAKEDOWN = \"jeongbo_precision_takedown\",\n JEONGBO_INTELLIGENCE_STRIKE = \"jeongbo_intelligence_strike\",\n\n // 조직폭력배 (Jojik Pokryeokbae) - Organized Crime\n JOJIK_STREET_BRAWL = \"jojik_street_brawl\",\n JOJIK_IMPROVISED_WEAPON = \"jojik_improvised_weapon\",\n JOJIK_RUTHLESS_ASSAULT = \"jojik_ruthless_assault\",\n JOJIK_BRUTAL_TAKEDOWN = \"jojik_brutal_takedown\",\n}\n\nexport default TechniqueId;\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;AA2BA,IAAY,cAAL,yBAAA,aAAA;
|
|
1
|
+
{"version":3,"file":"techniqueId.js","names":[],"sources":["../../src/types/techniqueId.ts"],"sourcesContent":["/**\n * Technique ID enum for type-safe technique references\n *\n * **Korean**: 기술 ID 열거형\n *\n * Provides compile-time safety for all technique references across the codebase.\n * Every technique in the game has a unique ID defined here.\n *\n * @module types/techniqueId\n * @category Combat System\n * @korean 기술ID\n */\n\n/**\n * All technique IDs in the game system\n *\n * Each archetype has unique techniques:\n * - 무사 (Musa) - 4 techniques: musa_thunder_strike, musa_iron_defense, musa_dragon_fist, musa_mountain_breaker\n * - 암살자 (Amsalja) - 4 techniques: amsalja_shadow_strike, amsalja_nerve_strike, amsalja_deadly_precision, amsalja_silent_death\n * - 해커 (Hacker) - 4 techniques: hacker_electric_shock, hacker_data_strike, hacker_cyber_overdrive, hacker_system_crash\n * - 정보요원 (Jeongbo) - 5 techniques: jeongbo_tactical_strike, jeongbo_counter_intelligence, jeongbo_psychological_warfare, jeongbo_precision_takedown, jeongbo_intelligence_strike\n * - 조직폭력배 (Jojik) - 4 techniques: jojik_street_brawl, jojik_improvised_weapon, jojik_ruthless_assault, jojik_brutal_takedown\n *\n * @public\n * @category Combat System\n * @korean 기술ID열거형\n */\nexport enum TechniqueId {\n // 무사 (Musa) - Traditional Warrior\n MUSA_THUNDER_STRIKE = \"musa_thunder_strike\",\n MUSA_IRON_DEFENSE = \"musa_iron_defense\",\n MUSA_DRAGON_FIST = \"musa_dragon_fist\",\n MUSA_MOUNTAIN_BREAKER = \"musa_mountain_breaker\",\n\n // 암살자 (Amsalja) - Shadow Assassin\n AMSALJA_SHADOW_STRIKE = \"amsalja_shadow_strike\",\n AMSALJA_NERVE_STRIKE = \"amsalja_nerve_strike\",\n AMSALJA_DEADLY_PRECISION = \"amsalja_deadly_precision\",\n AMSALJA_SILENT_DEATH = \"amsalja_silent_death\",\n\n // 해커 (Hacker) - Cyber Warrior\n HACKER_ELECTRIC_SHOCK = \"hacker_electric_shock\",\n HACKER_DATA_STRIKE = \"hacker_data_strike\",\n HACKER_CYBER_OVERDRIVE = \"hacker_cyber_overdrive\",\n HACKER_SYSTEM_CRASH = \"hacker_system_crash\",\n\n // 정보요원 (Jeongbo Yowon) - Intelligence Operative\n JEONGBO_TACTICAL_STRIKE = \"jeongbo_tactical_strike\",\n JEONGBO_COUNTER_INTELLIGENCE = \"jeongbo_counter_intelligence\",\n JEONGBO_PSYCHOLOGICAL_WARFARE = \"jeongbo_psychological_warfare\",\n JEONGBO_PRECISION_TAKEDOWN = \"jeongbo_precision_takedown\",\n JEONGBO_INTELLIGENCE_STRIKE = \"jeongbo_intelligence_strike\",\n\n // 조직폭력배 (Jojik Pokryeokbae) - Organized Crime\n JOJIK_STREET_BRAWL = \"jojik_street_brawl\",\n JOJIK_IMPROVISED_WEAPON = \"jojik_improvised_weapon\",\n JOJIK_RUTHLESS_ASSAULT = \"jojik_ruthless_assault\",\n JOJIK_BRUTAL_TAKEDOWN = \"jojik_brutal_takedown\",\n}\n\nexport default TechniqueId;\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;AA2BA,IAAY,cAAL,yBAAA,aAAA;CAEL,YAAA,yBAAsB;CACtB,YAAA,uBAAoB;CACpB,YAAA,sBAAmB;CACnB,YAAA,2BAAwB;CAGxB,YAAA,2BAAwB;CACxB,YAAA,0BAAuB;CACvB,YAAA,8BAA2B;CAC3B,YAAA,0BAAuB;CAGvB,YAAA,2BAAwB;CACxB,YAAA,wBAAqB;CACrB,YAAA,4BAAyB;CACzB,YAAA,yBAAsB;CAGtB,YAAA,6BAA0B;CAC1B,YAAA,kCAA+B;CAC/B,YAAA,mCAAgC;CAChC,YAAA,gCAA6B;CAC7B,YAAA,iCAA8B;CAG9B,YAAA,wBAAqB;CACrB,YAAA,6BAA0B;CAC1B,YAAA,4BAAyB;CACzB,YAAA,2BAAwB;;KACzB"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"accessibility.js","names":[],"sources":["../../src/utils/accessibility.ts"],"sourcesContent":["/**\n * Accessibility utility functions for WCAG 2.1 Level AA compliance\n * Provides keyboard navigation, focus management, color contrast checking,\n * and screen reader support utilities\n *\n * @module utils/accessibility\n * @category Accessibility\n * @korean 접근성 유틸리티\n */\n\nimport React from \"react\";\nimport {\n AriaAttributes,\n ColorContrastConfig,\n createBilingualLabel,\n FocusIndicatorStyle,\n KeyboardActions,\n ScreenReaderAnnouncement,\n WCAGComplianceResult,\n WCAGLevel,\n} from \"../types/AccessibilityTypes\";\nimport { KOREAN_COLORS } from \"@/types/constants\";\nimport { EventManager } from \"./EventManager\";\n\n/**\n * Handle keyboard navigation events with WCAG compliance\n * Supports Tab, Enter, Space, Escape, Arrow keys, Home, End\n *\n * @param event - Keyboard event\n * @param actions - Actions to perform for different keys\n *\n * @example\n * ```tsx\n * <div onKeyDown={(e) => handleKeyboardNav(e, {\n * onActivate: () => handleClick(),\n * onCancel: () => handleClose(),\n * onNavigate: (dir) => handleMove(dir),\n * })}>\n * ```\n */\nexport function handleKeyboardNav(\n event: KeyboardEvent,\n actions: KeyboardActions,\n): void {\n const { key, shiftKey } = event;\n\n switch (key) {\n case \"Enter\":\n case \" \":\n // Activate element (button, link, etc.)\n event.preventDefault();\n actions.onActivate?.();\n break;\n\n case \"Escape\":\n // Cancel or close action\n event.preventDefault();\n actions.onCancel?.();\n break;\n\n case \"Tab\":\n // Tab navigation (forward or backward with Shift)\n actions.onTab?.(shiftKey);\n break;\n\n case \"ArrowUp\":\n // Navigate up\n event.preventDefault();\n actions.onNavigate?.(\"up\");\n break;\n\n case \"ArrowDown\":\n // Navigate down\n event.preventDefault();\n actions.onNavigate?.(\"down\");\n break;\n\n case \"ArrowLeft\":\n // Navigate left\n event.preventDefault();\n actions.onNavigate?.(\"left\");\n break;\n\n case \"ArrowRight\":\n // Navigate right\n event.preventDefault();\n actions.onNavigate?.(\"right\");\n break;\n\n case \"Home\":\n // Jump to start\n event.preventDefault();\n actions.onJump?.(\"start\");\n break;\n\n case \"End\":\n // Jump to end\n event.preventDefault();\n actions.onJump?.(\"end\");\n break;\n }\n}\n\n/**\n * Get default focus indicator style (WCAG 2.1 Level AA compliant)\n * Uses 2px solid outline with high contrast cyan color\n * Computed lazily to avoid initialization order dependencies\n */\nfunction getDefaultFocusStyle(): FocusIndicatorStyle {\n const rgb = hexToRgb(KOREAN_COLORS.PRIMARY_CYAN);\n return {\n outlineWidth: 2,\n outlineColor: KOREAN_COLORS.PRIMARY_CYAN,\n outlineOffset: 2,\n outlineStyle: \"solid\",\n boxShadow: `0 0 0 2px rgba(${rgb.r}, ${rgb.g}, ${rgb.b}, 0.5)`,\n transitionDuration: 0.2,\n };\n}\n\n/**\n * Get focus indicator CSS style object\n *\n * @param isFocused - Whether element is currently focused\n * @param customStyle - Optional custom focus style overrides\n * @returns CSS style object for focus indicator\n *\n * @example\n * ```tsx\n * const [isFocused, setIsFocused] = useState(false);\n * <button\n * style={getFocusStyle(isFocused)}\n * onFocus={() => setIsFocused(true)}\n * onBlur={() => setIsFocused(false)}\n * >\n * ```\n */\nexport function getFocusStyle(\n isFocused: boolean,\n customStyle?: Partial<FocusIndicatorStyle>,\n): React.CSSProperties {\n const defaultStyle = getDefaultFocusStyle();\n\n if (!isFocused) {\n return {\n // Preserve browser default focus indicator instead of 'none'\n transition: `all ${defaultStyle.transitionDuration}s ease`,\n };\n }\n\n const style = { ...defaultStyle, ...customStyle };\n const rgb = hexToRgb(style.outlineColor ?? KOREAN_COLORS.PRIMARY_CYAN);\n\n return {\n outline: `${style.outlineWidth}px ${style.outlineStyle} rgb(${rgb.r}, ${rgb.g}, ${rgb.b})`,\n outlineOffset: `${style.outlineOffset}px`,\n boxShadow:\n style.boxShadow ?? `0 0 0 2px rgba(${rgb.r}, ${rgb.g}, ${rgb.b}, 0.5)`,\n transition: `all ${style.transitionDuration}s ease`,\n };\n}\n\n/**\n * Convert hex color to RGB components\n *\n * @param hex - Hex color code (0xRRGGBB)\n * @returns RGB components\n */\nexport function hexToRgb(hex: number): { r: number; g: number; b: number } {\n return {\n r: (hex >> 16) & 255,\n g: (hex >> 8) & 255,\n b: hex & 255,\n };\n}\n\n/**\n * Calculate contrast ratio between two colors (WCAG 2.1)\n *\n * @param foreground - Foreground color (hex)\n * @param background - Background color (hex)\n * @returns Contrast ratio (1-21)\n *\n * @example\n * ```typescript\n * const ratio = getContrastRatio(KOREAN_COLORS.TEXT_PRIMARY, KOREAN_COLORS.UI_BACKGROUND_DARK);\n * console.log(`Contrast ratio: ${ratio.toFixed(2)}:1`); // Should be >= 4.5:1 for WCAG AA\n * ```\n */\nexport function getContrastRatio(\n foreground: number,\n background: number,\n): number {\n const fg = hexToRgb(foreground);\n const bg = hexToRgb(background);\n\n // Calculate relative luminance (WCAG formula)\n const getLuminance = (r: number, g: number, b: number): number => {\n const [rs, gs, bs] = [r, g, b].map((c) => {\n const sRGB = c / 255;\n return sRGB <= 0.03928\n ? sRGB / 12.92\n : Math.pow((sRGB + 0.055) / 1.055, 2.4);\n });\n return 0.2126 * rs + 0.7152 * gs + 0.0722 * bs;\n };\n\n const l1 = getLuminance(fg.r, fg.g, fg.b);\n const l2 = getLuminance(bg.r, bg.g, bg.b);\n\n // Contrast ratio formula\n const lighter = Math.max(l1, l2);\n const darker = Math.min(l1, l2);\n return (lighter + 0.05) / (darker + 0.05);\n}\n\n/**\n * Check if color combination meets WCAG contrast requirements\n *\n * @param config - Color contrast configuration\n * @returns Whether contrast meets WCAG requirements\n *\n * @example\n * ```typescript\n * const meetsWCAG = meetsContrastRequirement({\n * foreground: KOREAN_COLORS.TEXT_PRIMARY,\n * background: KOREAN_COLORS.UI_BACKGROUND_DARK,\n * targetRatio: 4.5,\n * });\n * ```\n */\nexport function meetsContrastRequirement(config: ColorContrastConfig): boolean {\n const ratio = getContrastRatio(config.foreground, config.background);\n return ratio >= config.targetRatio;\n}\n\n/**\n * Get WCAG-compliant foreground color for given background\n * Returns white or black depending on which provides better contrast\n *\n * @param background - Background color (hex)\n * @returns Foreground color (hex) that meets WCAG AA\n */\nexport function getAccessibleForeground(background: number): number {\n const whiteRatio = getContrastRatio(KOREAN_COLORS.TEXT_PRIMARY, background);\n const blackRatio = getContrastRatio(KOREAN_COLORS.BLACK_SOLID, background);\n\n // Return color with better contrast\n return whiteRatio >= blackRatio\n ? KOREAN_COLORS.TEXT_PRIMARY\n : KOREAN_COLORS.BLACK_SOLID;\n}\n\n/**\n * Announce message to screen readers\n * Creates a live region announcement with configurable politeness\n *\n * @param announcement - Screen reader announcement configuration\n *\n * @example\n * ```typescript\n * announceToScreenReader({\n * message: '공격 성공 | Attack successful',\n * politeness: 'polite',\n * delay: 100,\n * });\n * ```\n */\nlet liveRegionInstance: HTMLElement | null = null;\n\nexport function announceToScreenReader(\n announcement: ScreenReaderAnnouncement,\n): void {\n const { message, politeness = \"polite\", delay = 0 } = announcement;\n\n setTimeout(() => {\n // Create or get existing live region\n if (!liveRegionInstance) {\n liveRegionInstance = document.createElement(\"div\");\n liveRegionInstance.id = \"sr-live-region\";\n liveRegionInstance.setAttribute(\"aria-live\", politeness);\n liveRegionInstance.setAttribute(\"aria-atomic\", \"true\");\n liveRegionInstance.style.cssText = `\n position: absolute;\n left: -10000px;\n width: 1px;\n height: 1px;\n overflow: hidden;\n `;\n document.body.appendChild(liveRegionInstance);\n }\n\n // Update politeness level if changed\n if (liveRegionInstance.getAttribute(\"aria-live\") !== politeness) {\n liveRegionInstance.setAttribute(\"aria-live\", politeness);\n }\n\n // Update message\n liveRegionInstance.textContent = message;\n\n // Clear message after 3 seconds\n setTimeout(() => {\n if (liveRegionInstance) {\n liveRegionInstance.textContent = \"\";\n }\n }, 3000);\n }, delay);\n}\n\n/**\n * Clean up screen reader live region (for testing or app unmount)\n */\nexport function cleanupScreenReaderRegion(): void {\n if (liveRegionInstance?.parentNode) {\n liveRegionInstance.parentNode.removeChild(liveRegionInstance);\n liveRegionInstance = null;\n }\n}\n\n/**\n * Trap focus within a container element\n * Prevents focus from leaving the container (e.g., for modals)\n *\n * @param container - Container element to trap focus within\n * @returns Cleanup function to remove focus trap\n *\n * @example\n * ```typescript\n * useEffect(() => {\n * const cleanup = trapFocus(modalRef.current);\n * return cleanup;\n * }, []);\n * ```\n */\nexport function trapFocus(container: HTMLElement | null): () => void {\n if (!container) return () => {};\n\n const focusableElements = getFocusableElements(container);\n const firstElement = focusableElements[0];\n const lastElement = focusableElements[focusableElements.length - 1];\n\n const handleKeyDown = (event: KeyboardEvent) => {\n if (event.key !== \"Tab\") return;\n\n if (event.shiftKey) {\n // Shift + Tab: Move focus backward\n if (document.activeElement === firstElement) {\n event.preventDefault();\n lastElement?.focus();\n }\n } else {\n // Tab: Move focus forward\n if (document.activeElement === lastElement) {\n event.preventDefault();\n firstElement?.focus();\n }\n }\n };\n\n container.addEventListener(\"keydown\", handleKeyDown);\n\n // Set initial focus\n firstElement?.focus();\n\n // Return cleanup function\n return () => {\n container.removeEventListener(\"keydown\", handleKeyDown);\n };\n}\n\n/**\n * Get all focusable elements within a container\n *\n * @param container - Container element\n * @returns Array of focusable elements\n */\nexport function getFocusableElements(container: HTMLElement): HTMLElement[] {\n const selector = [\n \"a[href]\",\n \"button:not([disabled])\",\n \"input:not([disabled])\",\n \"select:not([disabled])\",\n \"textarea:not([disabled])\",\n '[tabindex]:not([tabindex=\"-1\"])',\n ].join(\", \");\n\n return Array.from(container.querySelectorAll<HTMLElement>(selector));\n}\n\n/**\n * Validate WCAG 2.1 Level AA compliance for a component\n *\n * Note: Color contrast checking requires access to computed styles,\n * which is not available in all testing environments. This validation\n * focuses on structural accessibility attributes.\n *\n * @param element - Element to validate\n * @param config - Validation configuration\n * @returns Compliance validation result\n */\nexport function validateWCAGCompliance(\n element: HTMLElement | null,\n config?: {\n checkKeyboard?: boolean;\n checkFocusVisible?: boolean;\n checkAria?: boolean;\n },\n): WCAGComplianceResult {\n const {\n checkKeyboard = true,\n checkFocusVisible = true,\n checkAria = true,\n } = config ?? {};\n\n const issues: string[] = [];\n let keyboardAccessible = true;\n const colorContrast = true; // Note: Requires computed styles - use getContrastRatio() directly for color validation\n let focusVisible = true;\n let ariaLabels = true;\n\n if (!element) {\n issues.push(\"Element not found\");\n return {\n level: WCAGLevel.A,\n compliant: false,\n criteria: {\n keyboardAccessible: false,\n colorContrast: false,\n focusVisible: false,\n ariaLabels: false,\n semanticHTML: false,\n errorIdentification: false,\n },\n issues,\n };\n }\n\n // Check keyboard accessibility\n if (checkKeyboard) {\n const isInteractive =\n element.tagName === \"BUTTON\" ||\n element.tagName === \"A\" ||\n element.tagName === \"INPUT\" ||\n element.hasAttribute(\"onclick\");\n\n if (isInteractive && element.getAttribute(\"tabindex\") === \"-1\") {\n keyboardAccessible = false;\n issues.push(\"Interactive element not keyboard accessible\");\n }\n }\n\n // Check ARIA labels\n if (checkAria) {\n const hasAriaLabel =\n element.hasAttribute(\"aria-label\") ||\n element.hasAttribute(\"aria-labelledby\");\n\n const hasTextContent = element.textContent?.trim();\n\n if (!hasAriaLabel && !hasTextContent) {\n ariaLabels = false;\n issues.push(\"Missing ARIA label or text content\");\n }\n }\n\n // Check focus indicator\n if (checkFocusVisible) {\n const computedStyle = window.getComputedStyle(element);\n const outline = computedStyle.outline;\n\n if (outline === \"none\" || outline === \"\") {\n focusVisible = false;\n issues.push(\"Missing visible focus indicator\");\n }\n }\n\n const allPass =\n keyboardAccessible && colorContrast && focusVisible && ariaLabels;\n\n return {\n level: allPass ? WCAGLevel.AA : WCAGLevel.A,\n compliant: allPass,\n criteria: {\n keyboardAccessible,\n colorContrast, // Note: Always true - use getContrastRatio() for actual color validation\n focusVisible,\n ariaLabels,\n semanticHTML: true, // Requires manual verification\n errorIdentification: true, // Requires manual verification\n },\n issues,\n };\n}\n\n/**\n * Create comprehensive ARIA attributes for a component\n *\n * @param label - Bilingual label (Korean | English)\n * @param role - ARIA role\n * @param additionalAttrs - Additional ARIA attributes\n * @returns Complete ARIA attributes object\n *\n * @example\n * ```tsx\n * const ariaProps = createAriaAttributes(\n * createBilingualLabel('공격', 'Attack'),\n * 'button',\n * { 'aria-pressed': isPressed }\n * );\n * <button {...ariaProps}>\n * ```\n */\nexport function createAriaAttributes(\n label: string | { korean: string; english: string },\n role?: AriaAttributes[\"role\"],\n additionalAttrs?: Partial<AriaAttributes>,\n): AriaAttributes {\n const bilingualLabel =\n typeof label === \"string\"\n ? label\n : createBilingualLabel(label.korean, label.english).label;\n\n return {\n role,\n \"aria-label\": bilingualLabel,\n ...additionalAttrs,\n };\n}\n\n/**\n * Check if element is currently visible\n * Useful for skip-to-content and focus management\n *\n * @param element - Element to check\n * @returns Whether element is visible\n */\nexport function isElementVisible(element: HTMLElement | null): boolean {\n if (!element) return false;\n\n const style = window.getComputedStyle(element);\n return (\n style.display !== \"none\" &&\n style.visibility !== \"hidden\" &&\n style.opacity !== \"0\" &&\n element.offsetParent !== null\n );\n}\n\n/**\n * Focus first error element in a form\n * Useful for form validation accessibility\n *\n * @param container - Form container\n */\nexport function focusFirstError(container: HTMLElement): void {\n const errorElements = container.querySelectorAll<HTMLElement>(\n '[aria-invalid=\"true\"]',\n );\n\n if (errorElements.length > 0) {\n errorElements[0].focus();\n announceToScreenReader({\n message: \"오류가 발견되었습니다 | Errors found\",\n politeness: \"assertive\",\n });\n }\n}\n\n/**\n * Create skip to content link for keyboard navigation\n * Returns a link that allows users to skip to main content\n *\n * Returns an object with the link element and a cleanup function to properly\n * remove event listeners when the component is no longer needed.\n *\n * **Breaking Change**: This function now returns `{ element, cleanup }` instead of\n * just `HTMLAnchorElement`. Update usage from:\n * ```typescript\n * const link = createSkipLink(id);\n * ```\n * to:\n * ```typescript\n * const { element, cleanup } = createSkipLink(id);\n * ```\n *\n * 컴포넌트가 더 이상 필요하지 않을 때 이벤트 리스너를 적절히 제거하기 위한\n * 링크 요소와 정리 함수가 포함된 객체를 반환합니다.\n *\n * @param targetId - ID of main content element\n * @returns Object with skip link element and cleanup function\n *\n * @example\n * ```typescript\n * const { element, cleanup } = createSkipLink(\"main-content\");\n * document.body.prepend(element);\n * // Later, when component unmounts\n * cleanup();\n * element.remove();\n * ```\n */\nexport function createSkipLink(targetId: string): {\n element: HTMLAnchorElement;\n cleanup: () => void;\n} {\n const skipLink = document.createElement(\"a\");\n skipLink.href = `#${targetId}`;\n skipLink.textContent = \"본문으로 건너뛰기 | Skip to content\";\n skipLink.className = \"skip-link\";\n skipLink.style.cssText = `\n position: absolute;\n top: -40px;\n left: 0;\n background: #00e6e6;\n color: #000;\n padding: 8px;\n text-decoration: none;\n z-index: 10000;\n font-weight: bold;\n `;\n\n // Use EventManager for consistent event handling across the codebase\n const eventManager = new EventManager();\n\n // Event handlers\n const handleFocus = () => {\n skipLink.style.top = \"0\";\n };\n\n const handleBlur = () => {\n skipLink.style.top = \"-40px\";\n };\n\n // Add event listeners via EventManager\n eventManager.add(skipLink, \"focus\", handleFocus);\n eventManager.add(skipLink, \"blur\", handleBlur);\n\n // Cleanup function to remove all event listeners\n const cleanup = () => {\n eventManager.cleanup();\n };\n\n return { element: skipLink, cleanup };\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;AAwCA,SAAgB,kBACd,OACA,SACM;CACN,MAAM,EAAE,KAAK,aAAa;AAE1B,SAAQ,KAAR;EACE,KAAK;EACL,KAAK;AAEH,SAAM,gBAAgB;AACtB,WAAQ,cAAc;AACtB;EAEF,KAAK;AAEH,SAAM,gBAAgB;AACtB,WAAQ,YAAY;AACpB;EAEF,KAAK;AAEH,WAAQ,QAAQ,SAAS;AACzB;EAEF,KAAK;AAEH,SAAM,gBAAgB;AACtB,WAAQ,aAAa,KAAK;AAC1B;EAEF,KAAK;AAEH,SAAM,gBAAgB;AACtB,WAAQ,aAAa,OAAO;AAC5B;EAEF,KAAK;AAEH,SAAM,gBAAgB;AACtB,WAAQ,aAAa,OAAO;AAC5B;EAEF,KAAK;AAEH,SAAM,gBAAgB;AACtB,WAAQ,aAAa,QAAQ;AAC7B;EAEF,KAAK;AAEH,SAAM,gBAAgB;AACtB,WAAQ,SAAS,QAAQ;AACzB;EAEF,KAAK;AAEH,SAAM,gBAAgB;AACtB,WAAQ,SAAS,MAAM;AACvB;;;;;;;;AASN,SAAS,uBAA4C;CACnD,MAAM,MAAM,SAAS,cAAc,aAAa;AAChD,QAAO;EACL,cAAc;EACd,cAAc,cAAc;EAC5B,eAAe;EACf,cAAc;EACd,WAAW,kBAAkB,IAAI,EAAE,IAAI,IAAI,EAAE,IAAI,IAAI,EAAE;EACvD,oBAAoB;EACrB;;;;;;;;;;;;;;;;;;;AAoBH,SAAgB,cACd,WACA,aACqB;CACrB,MAAM,eAAe,sBAAsB;AAE3C,KAAI,CAAC,UACH,QAAO,EAEL,YAAY,OAAO,aAAa,mBAAmB,SACpD;CAGH,MAAM,QAAQ;EAAE,GAAG;EAAc,GAAG;EAAa;CACjD,MAAM,MAAM,SAAS,MAAM,gBAAgB,cAAc,aAAa;AAEtE,QAAO;EACL,SAAS,GAAG,MAAM,aAAa,KAAK,MAAM,aAAa,OAAO,IAAI,EAAE,IAAI,IAAI,EAAE,IAAI,IAAI,EAAE;EACxF,eAAe,GAAG,MAAM,cAAc;EACtC,WACE,MAAM,aAAa,kBAAkB,IAAI,EAAE,IAAI,IAAI,EAAE,IAAI,IAAI,EAAE;EACjE,YAAY,OAAO,MAAM,mBAAmB;EAC7C;;;;;;;;AASH,SAAgB,SAAS,KAAkD;AACzE,QAAO;EACL,GAAI,OAAO,KAAM;EACjB,GAAI,OAAO,IAAK;EAChB,GAAG,MAAM;EACV;;;;;;;;;;;;;;;;;AA+FH,IAAI,qBAAyC;AAE7C,SAAgB,uBACd,cACM;CACN,MAAM,EAAE,SAAS,aAAa,UAAU,QAAQ,MAAM;AAEtD,kBAAiB;AAEf,MAAI,CAAC,oBAAoB;AACvB,wBAAqB,SAAS,cAAc,MAAM;AAClD,sBAAmB,KAAK;AACxB,sBAAmB,aAAa,aAAa,WAAW;AACxD,sBAAmB,aAAa,eAAe,OAAO;AACtD,sBAAmB,MAAM,UAAU;;;;;;;AAOnC,YAAS,KAAK,YAAY,mBAAmB;;AAI/C,MAAI,mBAAmB,aAAa,YAAY,KAAK,WACnD,oBAAmB,aAAa,aAAa,WAAW;AAI1D,qBAAmB,cAAc;AAGjC,mBAAiB;AACf,OAAI,mBACF,oBAAmB,cAAc;KAElC,IAAK;IACP,MAAM"}
|
|
1
|
+
{"version":3,"file":"accessibility.js","names":[],"sources":["../../src/utils/accessibility.ts"],"sourcesContent":["/**\n * Accessibility utility functions for WCAG 2.1 Level AA compliance\n * Provides keyboard navigation, focus management, color contrast checking,\n * and screen reader support utilities\n *\n * @module utils/accessibility\n * @category Accessibility\n * @korean 접근성 유틸리티\n */\n\nimport React from \"react\";\nimport {\n AriaAttributes,\n ColorContrastConfig,\n createBilingualLabel,\n FocusIndicatorStyle,\n KeyboardActions,\n ScreenReaderAnnouncement,\n WCAGComplianceResult,\n WCAGLevel,\n} from \"../types/AccessibilityTypes\";\nimport { KOREAN_COLORS } from \"@/types/constants\";\nimport { EventManager } from \"./EventManager\";\n\n/**\n * Handle keyboard navigation events with WCAG compliance\n * Supports Tab, Enter, Space, Escape, Arrow keys, Home, End\n *\n * @param event - Keyboard event\n * @param actions - Actions to perform for different keys\n *\n * @example\n * ```tsx\n * <div onKeyDown={(e) => handleKeyboardNav(e, {\n * onActivate: () => handleClick(),\n * onCancel: () => handleClose(),\n * onNavigate: (dir) => handleMove(dir),\n * })}>\n * ```\n */\nexport function handleKeyboardNav(\n event: KeyboardEvent,\n actions: KeyboardActions,\n): void {\n const { key, shiftKey } = event;\n\n switch (key) {\n case \"Enter\":\n case \" \":\n // Activate element (button, link, etc.)\n event.preventDefault();\n actions.onActivate?.();\n break;\n\n case \"Escape\":\n // Cancel or close action\n event.preventDefault();\n actions.onCancel?.();\n break;\n\n case \"Tab\":\n // Tab navigation (forward or backward with Shift)\n actions.onTab?.(shiftKey);\n break;\n\n case \"ArrowUp\":\n // Navigate up\n event.preventDefault();\n actions.onNavigate?.(\"up\");\n break;\n\n case \"ArrowDown\":\n // Navigate down\n event.preventDefault();\n actions.onNavigate?.(\"down\");\n break;\n\n case \"ArrowLeft\":\n // Navigate left\n event.preventDefault();\n actions.onNavigate?.(\"left\");\n break;\n\n case \"ArrowRight\":\n // Navigate right\n event.preventDefault();\n actions.onNavigate?.(\"right\");\n break;\n\n case \"Home\":\n // Jump to start\n event.preventDefault();\n actions.onJump?.(\"start\");\n break;\n\n case \"End\":\n // Jump to end\n event.preventDefault();\n actions.onJump?.(\"end\");\n break;\n }\n}\n\n/**\n * Get default focus indicator style (WCAG 2.1 Level AA compliant)\n * Uses 2px solid outline with high contrast cyan color\n * Computed lazily to avoid initialization order dependencies\n */\nfunction getDefaultFocusStyle(): FocusIndicatorStyle {\n const rgb = hexToRgb(KOREAN_COLORS.PRIMARY_CYAN);\n return {\n outlineWidth: 2,\n outlineColor: KOREAN_COLORS.PRIMARY_CYAN,\n outlineOffset: 2,\n outlineStyle: \"solid\",\n boxShadow: `0 0 0 2px rgba(${rgb.r}, ${rgb.g}, ${rgb.b}, 0.5)`,\n transitionDuration: 0.2,\n };\n}\n\n/**\n * Get focus indicator CSS style object\n *\n * @param isFocused - Whether element is currently focused\n * @param customStyle - Optional custom focus style overrides\n * @returns CSS style object for focus indicator\n *\n * @example\n * ```tsx\n * const [isFocused, setIsFocused] = useState(false);\n * <button\n * style={getFocusStyle(isFocused)}\n * onFocus={() => setIsFocused(true)}\n * onBlur={() => setIsFocused(false)}\n * >\n * ```\n */\nexport function getFocusStyle(\n isFocused: boolean,\n customStyle?: Partial<FocusIndicatorStyle>,\n): React.CSSProperties {\n const defaultStyle = getDefaultFocusStyle();\n\n if (!isFocused) {\n return {\n // Preserve browser default focus indicator instead of 'none'\n transition: `all ${defaultStyle.transitionDuration}s ease`,\n };\n }\n\n const style = { ...defaultStyle, ...customStyle };\n const rgb = hexToRgb(style.outlineColor ?? KOREAN_COLORS.PRIMARY_CYAN);\n\n return {\n outline: `${style.outlineWidth}px ${style.outlineStyle} rgb(${rgb.r}, ${rgb.g}, ${rgb.b})`,\n outlineOffset: `${style.outlineOffset}px`,\n boxShadow:\n style.boxShadow ?? `0 0 0 2px rgba(${rgb.r}, ${rgb.g}, ${rgb.b}, 0.5)`,\n transition: `all ${style.transitionDuration}s ease`,\n };\n}\n\n/**\n * Convert hex color to RGB components\n *\n * @param hex - Hex color code (0xRRGGBB)\n * @returns RGB components\n */\nexport function hexToRgb(hex: number): { r: number; g: number; b: number } {\n return {\n r: (hex >> 16) & 255,\n g: (hex >> 8) & 255,\n b: hex & 255,\n };\n}\n\n/**\n * Calculate contrast ratio between two colors (WCAG 2.1)\n *\n * @param foreground - Foreground color (hex)\n * @param background - Background color (hex)\n * @returns Contrast ratio (1-21)\n *\n * @example\n * ```typescript\n * const ratio = getContrastRatio(KOREAN_COLORS.TEXT_PRIMARY, KOREAN_COLORS.UI_BACKGROUND_DARK);\n * console.log(`Contrast ratio: ${ratio.toFixed(2)}:1`); // Should be >= 4.5:1 for WCAG AA\n * ```\n */\nexport function getContrastRatio(\n foreground: number,\n background: number,\n): number {\n const fg = hexToRgb(foreground);\n const bg = hexToRgb(background);\n\n // Calculate relative luminance (WCAG formula)\n const getLuminance = (r: number, g: number, b: number): number => {\n const [rs, gs, bs] = [r, g, b].map((c) => {\n const sRGB = c / 255;\n return sRGB <= 0.03928\n ? sRGB / 12.92\n : Math.pow((sRGB + 0.055) / 1.055, 2.4);\n });\n return 0.2126 * rs + 0.7152 * gs + 0.0722 * bs;\n };\n\n const l1 = getLuminance(fg.r, fg.g, fg.b);\n const l2 = getLuminance(bg.r, bg.g, bg.b);\n\n // Contrast ratio formula\n const lighter = Math.max(l1, l2);\n const darker = Math.min(l1, l2);\n return (lighter + 0.05) / (darker + 0.05);\n}\n\n/**\n * Check if color combination meets WCAG contrast requirements\n *\n * @param config - Color contrast configuration\n * @returns Whether contrast meets WCAG requirements\n *\n * @example\n * ```typescript\n * const meetsWCAG = meetsContrastRequirement({\n * foreground: KOREAN_COLORS.TEXT_PRIMARY,\n * background: KOREAN_COLORS.UI_BACKGROUND_DARK,\n * targetRatio: 4.5,\n * });\n * ```\n */\nexport function meetsContrastRequirement(config: ColorContrastConfig): boolean {\n const ratio = getContrastRatio(config.foreground, config.background);\n return ratio >= config.targetRatio;\n}\n\n/**\n * Get WCAG-compliant foreground color for given background\n * Returns white or black depending on which provides better contrast\n *\n * @param background - Background color (hex)\n * @returns Foreground color (hex) that meets WCAG AA\n */\nexport function getAccessibleForeground(background: number): number {\n const whiteRatio = getContrastRatio(KOREAN_COLORS.TEXT_PRIMARY, background);\n const blackRatio = getContrastRatio(KOREAN_COLORS.BLACK_SOLID, background);\n\n // Return color with better contrast\n return whiteRatio >= blackRatio\n ? KOREAN_COLORS.TEXT_PRIMARY\n : KOREAN_COLORS.BLACK_SOLID;\n}\n\n/**\n * Announce message to screen readers\n * Creates a live region announcement with configurable politeness\n *\n * @param announcement - Screen reader announcement configuration\n *\n * @example\n * ```typescript\n * announceToScreenReader({\n * message: '공격 성공 | Attack successful',\n * politeness: 'polite',\n * delay: 100,\n * });\n * ```\n */\nlet liveRegionInstance: HTMLElement | null = null;\n\nexport function announceToScreenReader(\n announcement: ScreenReaderAnnouncement,\n): void {\n const { message, politeness = \"polite\", delay = 0 } = announcement;\n\n setTimeout(() => {\n // Create or get existing live region\n if (!liveRegionInstance) {\n liveRegionInstance = document.createElement(\"div\");\n liveRegionInstance.id = \"sr-live-region\";\n liveRegionInstance.setAttribute(\"aria-live\", politeness);\n liveRegionInstance.setAttribute(\"aria-atomic\", \"true\");\n liveRegionInstance.style.cssText = `\n position: absolute;\n left: -10000px;\n width: 1px;\n height: 1px;\n overflow: hidden;\n `;\n document.body.appendChild(liveRegionInstance);\n }\n\n // Update politeness level if changed\n if (liveRegionInstance.getAttribute(\"aria-live\") !== politeness) {\n liveRegionInstance.setAttribute(\"aria-live\", politeness);\n }\n\n // Update message\n liveRegionInstance.textContent = message;\n\n // Clear message after 3 seconds\n setTimeout(() => {\n if (liveRegionInstance) {\n liveRegionInstance.textContent = \"\";\n }\n }, 3000);\n }, delay);\n}\n\n/**\n * Clean up screen reader live region (for testing or app unmount)\n */\nexport function cleanupScreenReaderRegion(): void {\n if (liveRegionInstance?.parentNode) {\n liveRegionInstance.parentNode.removeChild(liveRegionInstance);\n liveRegionInstance = null;\n }\n}\n\n/**\n * Trap focus within a container element\n * Prevents focus from leaving the container (e.g., for modals)\n *\n * @param container - Container element to trap focus within\n * @returns Cleanup function to remove focus trap\n *\n * @example\n * ```typescript\n * useEffect(() => {\n * const cleanup = trapFocus(modalRef.current);\n * return cleanup;\n * }, []);\n * ```\n */\nexport function trapFocus(container: HTMLElement | null): () => void {\n if (!container) return () => {};\n\n const focusableElements = getFocusableElements(container);\n const firstElement = focusableElements[0];\n const lastElement = focusableElements[focusableElements.length - 1];\n\n const handleKeyDown = (event: KeyboardEvent) => {\n if (event.key !== \"Tab\") return;\n\n if (event.shiftKey) {\n // Shift + Tab: Move focus backward\n if (document.activeElement === firstElement) {\n event.preventDefault();\n lastElement?.focus();\n }\n } else {\n // Tab: Move focus forward\n if (document.activeElement === lastElement) {\n event.preventDefault();\n firstElement?.focus();\n }\n }\n };\n\n container.addEventListener(\"keydown\", handleKeyDown);\n\n // Set initial focus\n firstElement?.focus();\n\n // Return cleanup function\n return () => {\n container.removeEventListener(\"keydown\", handleKeyDown);\n };\n}\n\n/**\n * Get all focusable elements within a container\n *\n * @param container - Container element\n * @returns Array of focusable elements\n */\nexport function getFocusableElements(container: HTMLElement): HTMLElement[] {\n const selector = [\n \"a[href]\",\n \"button:not([disabled])\",\n \"input:not([disabled])\",\n \"select:not([disabled])\",\n \"textarea:not([disabled])\",\n '[tabindex]:not([tabindex=\"-1\"])',\n ].join(\", \");\n\n return Array.from(container.querySelectorAll<HTMLElement>(selector));\n}\n\n/**\n * Validate WCAG 2.1 Level AA compliance for a component\n *\n * Note: Color contrast checking requires access to computed styles,\n * which is not available in all testing environments. This validation\n * focuses on structural accessibility attributes.\n *\n * @param element - Element to validate\n * @param config - Validation configuration\n * @returns Compliance validation result\n */\nexport function validateWCAGCompliance(\n element: HTMLElement | null,\n config?: {\n checkKeyboard?: boolean;\n checkFocusVisible?: boolean;\n checkAria?: boolean;\n },\n): WCAGComplianceResult {\n const {\n checkKeyboard = true,\n checkFocusVisible = true,\n checkAria = true,\n } = config ?? {};\n\n const issues: string[] = [];\n let keyboardAccessible = true;\n const colorContrast = true; // Note: Requires computed styles - use getContrastRatio() directly for color validation\n let focusVisible = true;\n let ariaLabels = true;\n\n if (!element) {\n issues.push(\"Element not found\");\n return {\n level: WCAGLevel.A,\n compliant: false,\n criteria: {\n keyboardAccessible: false,\n colorContrast: false,\n focusVisible: false,\n ariaLabels: false,\n semanticHTML: false,\n errorIdentification: false,\n },\n issues,\n };\n }\n\n // Check keyboard accessibility\n if (checkKeyboard) {\n const isInteractive =\n element.tagName === \"BUTTON\" ||\n element.tagName === \"A\" ||\n element.tagName === \"INPUT\" ||\n element.hasAttribute(\"onclick\");\n\n if (isInteractive && element.getAttribute(\"tabindex\") === \"-1\") {\n keyboardAccessible = false;\n issues.push(\"Interactive element not keyboard accessible\");\n }\n }\n\n // Check ARIA labels\n if (checkAria) {\n const hasAriaLabel =\n element.hasAttribute(\"aria-label\") ||\n element.hasAttribute(\"aria-labelledby\");\n\n const hasTextContent = element.textContent?.trim();\n\n if (!hasAriaLabel && !hasTextContent) {\n ariaLabels = false;\n issues.push(\"Missing ARIA label or text content\");\n }\n }\n\n // Check focus indicator\n if (checkFocusVisible) {\n const computedStyle = window.getComputedStyle(element);\n const outline = computedStyle.outline;\n\n if (outline === \"none\" || outline === \"\") {\n focusVisible = false;\n issues.push(\"Missing visible focus indicator\");\n }\n }\n\n const allPass =\n keyboardAccessible && colorContrast && focusVisible && ariaLabels;\n\n return {\n level: allPass ? WCAGLevel.AA : WCAGLevel.A,\n compliant: allPass,\n criteria: {\n keyboardAccessible,\n colorContrast, // Note: Always true - use getContrastRatio() for actual color validation\n focusVisible,\n ariaLabels,\n semanticHTML: true, // Requires manual verification\n errorIdentification: true, // Requires manual verification\n },\n issues,\n };\n}\n\n/**\n * Create comprehensive ARIA attributes for a component\n *\n * @param label - Bilingual label (Korean | English)\n * @param role - ARIA role\n * @param additionalAttrs - Additional ARIA attributes\n * @returns Complete ARIA attributes object\n *\n * @example\n * ```tsx\n * const ariaProps = createAriaAttributes(\n * createBilingualLabel('공격', 'Attack'),\n * 'button',\n * { 'aria-pressed': isPressed }\n * );\n * <button {...ariaProps}>\n * ```\n */\nexport function createAriaAttributes(\n label: string | { korean: string; english: string },\n role?: AriaAttributes[\"role\"],\n additionalAttrs?: Partial<AriaAttributes>,\n): AriaAttributes {\n const bilingualLabel =\n typeof label === \"string\"\n ? label\n : createBilingualLabel(label.korean, label.english).label;\n\n return {\n role,\n \"aria-label\": bilingualLabel,\n ...additionalAttrs,\n };\n}\n\n/**\n * Check if element is currently visible\n * Useful for skip-to-content and focus management\n *\n * @param element - Element to check\n * @returns Whether element is visible\n */\nexport function isElementVisible(element: HTMLElement | null): boolean {\n if (!element) return false;\n\n const style = window.getComputedStyle(element);\n return (\n style.display !== \"none\" &&\n style.visibility !== \"hidden\" &&\n style.opacity !== \"0\" &&\n element.offsetParent !== null\n );\n}\n\n/**\n * Focus first error element in a form\n * Useful for form validation accessibility\n *\n * @param container - Form container\n */\nexport function focusFirstError(container: HTMLElement): void {\n const errorElements = container.querySelectorAll<HTMLElement>(\n '[aria-invalid=\"true\"]',\n );\n\n if (errorElements.length > 0) {\n errorElements[0].focus();\n announceToScreenReader({\n message: \"오류가 발견되었습니다 | Errors found\",\n politeness: \"assertive\",\n });\n }\n}\n\n/**\n * Create skip to content link for keyboard navigation\n * Returns a link that allows users to skip to main content\n *\n * Returns an object with the link element and a cleanup function to properly\n * remove event listeners when the component is no longer needed.\n *\n * **Breaking Change**: This function now returns `{ element, cleanup }` instead of\n * just `HTMLAnchorElement`. Update usage from:\n * ```typescript\n * const link = createSkipLink(id);\n * ```\n * to:\n * ```typescript\n * const { element, cleanup } = createSkipLink(id);\n * ```\n *\n * 컴포넌트가 더 이상 필요하지 않을 때 이벤트 리스너를 적절히 제거하기 위한\n * 링크 요소와 정리 함수가 포함된 객체를 반환합니다.\n *\n * @param targetId - ID of main content element\n * @returns Object with skip link element and cleanup function\n *\n * @example\n * ```typescript\n * const { element, cleanup } = createSkipLink(\"main-content\");\n * document.body.prepend(element);\n * // Later, when component unmounts\n * cleanup();\n * element.remove();\n * ```\n */\nexport function createSkipLink(targetId: string): {\n element: HTMLAnchorElement;\n cleanup: () => void;\n} {\n const skipLink = document.createElement(\"a\");\n skipLink.href = `#${targetId}`;\n skipLink.textContent = \"본문으로 건너뛰기 | Skip to content\";\n skipLink.className = \"skip-link\";\n skipLink.style.cssText = `\n position: absolute;\n top: -40px;\n left: 0;\n background: #00e6e6;\n color: #000;\n padding: 8px;\n text-decoration: none;\n z-index: 10000;\n font-weight: bold;\n `;\n\n // Use EventManager for consistent event handling across the codebase\n const eventManager = new EventManager();\n\n // Event handlers\n const handleFocus = () => {\n skipLink.style.top = \"0\";\n };\n\n const handleBlur = () => {\n skipLink.style.top = \"-40px\";\n };\n\n // Add event listeners via EventManager\n eventManager.add(skipLink, \"focus\", handleFocus);\n eventManager.add(skipLink, \"blur\", handleBlur);\n\n // Cleanup function to remove all event listeners\n const cleanup = () => {\n eventManager.cleanup();\n };\n\n return { element: skipLink, cleanup };\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;AAwCA,SAAgB,kBACd,OACA,SACM;CACN,MAAM,EAAE,KAAK,aAAa;CAE1B,QAAQ,KAAR;EACE,KAAK;EACL,KAAK;GAEH,MAAM,gBAAgB;GACtB,QAAQ,cAAc;GACtB;EAEF,KAAK;GAEH,MAAM,gBAAgB;GACtB,QAAQ,YAAY;GACpB;EAEF,KAAK;GAEH,QAAQ,QAAQ,SAAS;GACzB;EAEF,KAAK;GAEH,MAAM,gBAAgB;GACtB,QAAQ,aAAa,KAAK;GAC1B;EAEF,KAAK;GAEH,MAAM,gBAAgB;GACtB,QAAQ,aAAa,OAAO;GAC5B;EAEF,KAAK;GAEH,MAAM,gBAAgB;GACtB,QAAQ,aAAa,OAAO;GAC5B;EAEF,KAAK;GAEH,MAAM,gBAAgB;GACtB,QAAQ,aAAa,QAAQ;GAC7B;EAEF,KAAK;GAEH,MAAM,gBAAgB;GACtB,QAAQ,SAAS,QAAQ;GACzB;EAEF,KAAK;GAEH,MAAM,gBAAgB;GACtB,QAAQ,SAAS,MAAM;GACvB;;;;;;;;AASN,SAAS,uBAA4C;CACnD,MAAM,MAAM,SAAS,cAAc,aAAa;CAChD,OAAO;EACL,cAAc;EACd,cAAc,cAAc;EAC5B,eAAe;EACf,cAAc;EACd,WAAW,kBAAkB,IAAI,EAAE,IAAI,IAAI,EAAE,IAAI,IAAI,EAAE;EACvD,oBAAoB;EACrB;;;;;;;;;;;;;;;;;;;AAoBH,SAAgB,cACd,WACA,aACqB;CACrB,MAAM,eAAe,sBAAsB;CAE3C,IAAI,CAAC,WACH,OAAO,EAEL,YAAY,OAAO,aAAa,mBAAmB,SACpD;CAGH,MAAM,QAAQ;EAAE,GAAG;EAAc,GAAG;EAAa;CACjD,MAAM,MAAM,SAAS,MAAM,gBAAgB,cAAc,aAAa;CAEtE,OAAO;EACL,SAAS,GAAG,MAAM,aAAa,KAAK,MAAM,aAAa,OAAO,IAAI,EAAE,IAAI,IAAI,EAAE,IAAI,IAAI,EAAE;EACxF,eAAe,GAAG,MAAM,cAAc;EACtC,WACE,MAAM,aAAa,kBAAkB,IAAI,EAAE,IAAI,IAAI,EAAE,IAAI,IAAI,EAAE;EACjE,YAAY,OAAO,MAAM,mBAAmB;EAC7C;;;;;;;;AASH,SAAgB,SAAS,KAAkD;CACzE,OAAO;EACL,GAAI,OAAO,KAAM;EACjB,GAAI,OAAO,IAAK;EAChB,GAAG,MAAM;EACV;;;;;;;;;;;;;;;;;AA+FH,IAAI,qBAAyC;AAE7C,SAAgB,uBACd,cACM;CACN,MAAM,EAAE,SAAS,aAAa,UAAU,QAAQ,MAAM;CAEtD,iBAAiB;EAEf,IAAI,CAAC,oBAAoB;GACvB,qBAAqB,SAAS,cAAc,MAAM;GAClD,mBAAmB,KAAK;GACxB,mBAAmB,aAAa,aAAa,WAAW;GACxD,mBAAmB,aAAa,eAAe,OAAO;GACtD,mBAAmB,MAAM,UAAU;;;;;;;GAOnC,SAAS,KAAK,YAAY,mBAAmB;;EAI/C,IAAI,mBAAmB,aAAa,YAAY,KAAK,YACnD,mBAAmB,aAAa,aAAa,WAAW;EAI1D,mBAAmB,cAAc;EAGjC,iBAAiB;GACf,IAAI,oBACF,mBAAmB,cAAc;KAElC,IAAK;IACP,MAAM"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"arenaWorldDimensions.js","names":[],"sources":["../../src/utils/arenaWorldDimensions.ts"],"sourcesContent":["/**\n * Arena world dimension calculations for physics-first coordinate system.\n *\n * Determines the physical size of the combat arena in meters based on\n * screen **resolution** (not device type), ensuring appropriate gameplay\n * space for different display capabilities.\n *\n * **Important**: Arena size is determined by RESOLUTION, not device type.\n * A mobile device with a 4K screen gets a larger arena than a desktop with\n * 1080p resolution, because arena size should match display capability.\n *\n * **4:3 Aspect Ratio Philosophy**:\n * All arenas use 4:3 aspect ratio (width > height) for optimal screen usage.\n * Width sizes: 6m, 8m, 10m, 12m, 14m based on resolution.\n * Height is calculated as width × 0.75 (3/4).\n * This ensures:\n * - Efficient use of horizontal screen space\n * - Consistent physics across all devices\n * - Traditional fighting game proportions\n *\n * **Korean**: 경기장세계크기 (Arena World Size)\n *\n * @module utils/arenaWorldDimensions\n * @category Physics\n */\n\n/**\n * Screen size category based on RESOLUTION (width in pixels).\n *\n * **Korean**: 화면크기범주 (Screen Size Category)\n *\n * @public\n */\nexport type ScreenSizeCategory =\n | \"small\"\n | \"medium\"\n | \"large\"\n | \"xlarge\"\n | \"ultra\";\n\n/**\n * Standard arena widths in meters.\n * Arenas use 4:3 aspect ratio (height = width × 0.75).\n *\n * @public\n */\nexport const ARENA_SIZES = {\n /** Small arena for compact screens (6m × 4.5m) */\n SMALL: 6,\n /** Medium arena for tablets and small laptops (8m × 6m) */\n MEDIUM: 8,\n /** Large arena for standard desktops (10m × 7.5m) */\n LARGE: 10,\n /** Extra-large arena for high-res displays (12m × 9m) */\n XLARGE: 12,\n /** Ultra arena for 4K+ displays (14m × 10.5m) */\n ULTRA: 14,\n} as const;\n\n/**\n * Resolution breakpoints for arena size determination.\n * Based on common display widths.\n *\n * @public\n */\nexport const RESOLUTION_BREAKPOINTS = {\n /** Small screens: < 768px (phones, small tablets) */\n SMALL_MAX: 768,\n /** Medium screens: 768-1199px (tablets, small laptops) */\n MEDIUM_MAX: 1200,\n /** Large screens: 1200-1919px (laptops, standard monitors) */\n LARGE_MAX: 1920,\n /** XLarge screens: 1920-2559px (large monitors, some 4K) */\n XLARGE_MAX: 2560,\n /** Ultra screens: ≥ 2560px (4K, ultrawide) */\n} as const;\n\n/**\n * World dimensions for an arena in meters.\n *\n * **Korean**: 세계크기 (World Dimensions)\n *\n * @public\n */\nexport interface WorldDimensions {\n /** Arena width in meters */\n readonly widthMeters: number;\n /** Arena depth in meters (height = width × 0.75 for 4:3 ratio) */\n readonly depthMeters: number;\n /** Arena width in meters (same as widthMeters, for convenience) */\n readonly sizeMeters: number;\n /** Screen category used to determine arena size */\n readonly screenCategory: ScreenSizeCategory;\n}\n\n/**\n * Complete arena configuration including pixel and meter dimensions.\n *\n * @public\n */\nexport interface ArenaConfiguration {\n /** Arena position X in pixels */\n readonly x: number;\n /** Arena position Y in pixels */\n readonly y: number;\n /** Arena width in pixels */\n readonly width: number;\n /** Arena height in pixels */\n readonly height: number;\n /** Arena world width in meters */\n readonly worldWidthMeters: number;\n /** Arena world depth in meters */\n readonly worldDepthMeters: number;\n /** Pixels per meter ratio for this arena */\n readonly pixelsPerMeter: number;\n /** Scale factor (1.0 = reference desktop, <1.0 = smaller screens) */\n readonly scale: number;\n}\n\n/**\n * Categorize screen based on resolution (width in pixels).\n *\n * This is used for arena sizing. Device type (mobile/desktop) is determined\n * separately for UI controls only.\n *\n * **Korean**: 화면크기범주결정 (Determine Screen Size Category)\n *\n * @param screenWidth - Screen width in pixels\n * @returns Screen size category for arena dimensions\n *\n * @example\n * ```typescript\n * getScreenSizeCategory(640); // \"small\" - small phone\n * getScreenSizeCategory(1200); // \"large\" - standard laptop\n * getScreenSizeCategory(3840); // \"ultra\" - 4K display\n * ```\n *\n * @public\n */\nexport function getScreenSizeCategory(screenWidth: number): ScreenSizeCategory {\n if (screenWidth < RESOLUTION_BREAKPOINTS.SMALL_MAX) return \"small\";\n if (screenWidth < RESOLUTION_BREAKPOINTS.MEDIUM_MAX) return \"medium\";\n if (screenWidth < RESOLUTION_BREAKPOINTS.LARGE_MAX) return \"large\";\n if (screenWidth < RESOLUTION_BREAKPOINTS.XLARGE_MAX) return \"xlarge\";\n return \"ultra\";\n}\n\n/**\n * Get arena size in meters for a given screen category.\n *\n * @param category - Screen size category\n * @returns Arena size in meters (square dimension)\n *\n * @public\n */\nexport function getArenaSizeForCategory(category: ScreenSizeCategory): number {\n switch (category) {\n case \"small\":\n return ARENA_SIZES.SMALL;\n case \"medium\":\n return ARENA_SIZES.MEDIUM;\n case \"large\":\n return ARENA_SIZES.LARGE;\n case \"xlarge\":\n return ARENA_SIZES.XLARGE;\n case \"ultra\":\n return ARENA_SIZES.ULTRA;\n default:\n return ARENA_SIZES.LARGE;\n }\n}\n\n/**\n * Calculate appropriate arena world dimensions based on screen resolution.\n *\n * Larger resolution displays get larger arenas for better gameplay, while\n * maintaining the same physical combat mechanics. Movement speed in m/s\n * remains constant, but larger arenas provide more tactical space.\n *\n * **Arenas use 4:3 aspect ratio** (width > depth):\n * - < 768px: 6m × 4.5m (compact, fast-paced combat)\n * - 768-1199px: 8m × 6m (balanced gameplay)\n * - 1200-1919px: 10m × 7.5m (tactical combat)\n * - 1920-2559px: 12m × 9m (advanced positioning)\n * - ≥ 2560px: 14m × 10.5m (maximum tactical space)\n *\n * **Korean**: 경기장크기계산 (Calculate Arena Size)\n *\n * @param screenWidth - Screen width in pixels (resolution, not device type)\n * @returns World dimensions in meters (4:3 aspect ratio)\n *\n * @example\n * ```typescript\n * // Mobile phone 640px width\n * const smallPhone = calculateArenaWorldDimensions(640);\n * // Result: { widthMeters: 6, depthMeters: 4.5, sizeMeters: 6, screenCategory: \"small\" }\n *\n * // Desktop 1920×1080\n * const desktop = calculateArenaWorldDimensions(1920);\n * // Result: { widthMeters: 12, depthMeters: 9, sizeMeters: 12, screenCategory: \"xlarge\" }\n * ```\n *\n * @public\n */\nexport function calculateArenaWorldDimensions(\n screenWidth: number,\n): WorldDimensions {\n const category = getScreenSizeCategory(screenWidth);\n const sizeMeters = getArenaSizeForCategory(category);\n // 4:3 aspect ratio: depth = width × 0.75\n const depthMeters = sizeMeters * 0.75;\n\n return {\n widthMeters: sizeMeters,\n depthMeters,\n sizeMeters,\n screenCategory: category,\n };\n}\n\n/**\n * Calculate complete arena configuration including pixel and meter dimensions.\n *\n * This is the primary function for setting up arena bounds. It calculates:\n * - Pixel dimensions based on available screen space\n * - Meter dimensions based on screen resolution category\n * - Pixels-per-meter ratio for physics conversion\n *\n * @param screenWidth - Screen width in pixels\n * @param screenHeight - Screen height in pixels\n * @param topOffset - Pixels reserved at top (HUD, headers)\n * @param bottomOffset - Pixels reserved at bottom (controls, footer)\n * @param horizontalMargin - Pixels to leave on sides (as ratio 0-1)\n * @returns Complete arena configuration\n *\n * @public\n */\nexport function calculateArenaConfiguration(\n screenWidth: number,\n screenHeight: number,\n topOffset: number,\n bottomOffset: number,\n horizontalMargin: number = 0.1,\n): ArenaConfiguration {\n // Get world dimensions based on resolution\n const worldDimensions = calculateArenaWorldDimensions(screenWidth);\n\n // Calculate available pixel space\n const availableWidth = screenWidth * (1 - 2 * horizontalMargin);\n const availableHeight = screenHeight - topOffset - bottomOffset;\n\n // Use the smaller dimension to ensure square arena fits\n const arenaSizePixels = Math.min(availableWidth, availableHeight);\n\n // Calculate arena position (centered horizontally)\n const arenaX = (screenWidth - arenaSizePixels) / 2;\n const arenaY = topOffset;\n\n // Calculate pixels per meter for this configuration\n const pixelsPerMeter = arenaSizePixels / worldDimensions.sizeMeters;\n\n // Calculate scale relative to reference (1000px / 10m = 100 px/m)\n const referencePixelsPerMeter = 100;\n const scale = pixelsPerMeter / referencePixelsPerMeter;\n\n return {\n x: arenaX,\n y: arenaY,\n width: arenaSizePixels,\n height: arenaSizePixels,\n worldWidthMeters: worldDimensions.widthMeters,\n worldDepthMeters: worldDimensions.depthMeters,\n pixelsPerMeter,\n scale,\n };\n}\n\n/**\n * Calculate pixels-per-meter ratio from arena dimensions.\n *\n * @param arenaWidthPixels - Arena width in pixels\n * @param arenaWidthMeters - Arena width in meters\n * @returns Pixels per meter ratio\n *\n * @public\n */\nexport function calculatePixelsPerMeter(\n arenaWidthPixels: number,\n arenaWidthMeters: number,\n): number {\n if (arenaWidthMeters <= 0) {\n throw new Error(`arenaWidthMeters must be positive: ${arenaWidthMeters}`);\n }\n return arenaWidthPixels / arenaWidthMeters;\n}\n\n/**\n * Get player height in meters from archetype physical attributes.\n *\n * Converts archetype totalHeight (in centimeters) to meters for physics calculations.\n *\n * **Korean**: 플레이어키 (Player Height)\n *\n * @param archetypeHeightCm - Player height in centimeters from PhysicalAttributes\n * @returns Height in meters\n *\n * @example\n * ```typescript\n * // Musa archetype: 180cm\n * const height = getPlayerHeightMeters(180);\n * // Result: 1.8 meters\n * ```\n *\n * @public\n */\nexport function getPlayerHeightMeters(archetypeHeightCm: number): number {\n return archetypeHeightCm / 100;\n}\n"],"mappings":";;;;;;;AA8CA,IAAa,cAAc;;CAEzB,OAAO;;CAEP,QAAQ;;CAER,OAAO;;CAEP,QAAQ;;CAER,OAAO;CACR;;;;;;;AAQD,IAAa,yBAAyB;;CAEpC,WAAW;;CAEX,YAAY;;CAEZ,WAAW;;CAEX,YAAY;CAEb;;;;;;;;;;;;;;;;;;;;;AAgED,SAAgB,sBAAsB,aAAyC;AAC7E,KAAI,cAAc,uBAAuB,UAAW,QAAO;AAC3D,KAAI,cAAc,uBAAuB,WAAY,QAAO;AAC5D,KAAI,cAAc,uBAAuB,UAAW,QAAO;AAC3D,KAAI,cAAc,uBAAuB,WAAY,QAAO;AAC5D,QAAO;;;;;;;;;;AAWT,SAAgB,wBAAwB,UAAsC;AAC5E,SAAQ,UAAR;EACE,KAAK,QACH,QAAO,YAAY;EACrB,KAAK,SACH,QAAO,YAAY;EACrB,KAAK,QACH,QAAO,YAAY;EACrB,KAAK,SACH,QAAO,YAAY;EACrB,KAAK,QACH,QAAO,YAAY;EACrB,QACE,QAAO,YAAY;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAoCzB,SAAgB,8BACd,aACiB;CACjB,MAAM,WAAW,sBAAsB,YAAY;CACnD,MAAM,aAAa,wBAAwB,SAAS;AAIpD,QAAO;EACL,aAAa;EACb,aAJkB,aAAa;EAK/B;EACA,gBAAgB;EACjB;;;;;;;;;;;;;;;;;;;AAoBH,SAAgB,4BACd,aACA,cACA,WACA,cACA,mBAA2B,IACP;CAEpB,MAAM,kBAAkB,8BAA8B,YAAY;CAGlE,MAAM,iBAAiB,eAAe,IAAI,IAAI;CAC9C,MAAM,kBAAkB,eAAe,YAAY;CAGnD,MAAM,kBAAkB,KAAK,IAAI,gBAAgB,gBAAgB;CAGjE,MAAM,UAAU,cAAc,mBAAmB;CACjD,MAAM,SAAS;CAGf,MAAM,iBAAiB,kBAAkB,gBAAgB;CAIzD,MAAM,QAAQ,iBAAiB;AAE/B,QAAO;EACL,GAAG;EACH,GAAG;EACH,OAAO;EACP,QAAQ;EACR,kBAAkB,gBAAgB;EAClC,kBAAkB,gBAAgB;EAClC;EACA;EACD"}
|
|
1
|
+
{"version":3,"file":"arenaWorldDimensions.js","names":[],"sources":["../../src/utils/arenaWorldDimensions.ts"],"sourcesContent":["/**\n * Arena world dimension calculations for physics-first coordinate system.\n *\n * Determines the physical size of the combat arena in meters based on\n * screen **resolution** (not device type), ensuring appropriate gameplay\n * space for different display capabilities.\n *\n * **Important**: Arena size is determined by RESOLUTION, not device type.\n * A mobile device with a 4K screen gets a larger arena than a desktop with\n * 1080p resolution, because arena size should match display capability.\n *\n * **4:3 Aspect Ratio Philosophy**:\n * All arenas use 4:3 aspect ratio (width > height) for optimal screen usage.\n * Width sizes: 6m, 8m, 10m, 12m, 14m based on resolution.\n * Height is calculated as width × 0.75 (3/4).\n * This ensures:\n * - Efficient use of horizontal screen space\n * - Consistent physics across all devices\n * - Traditional fighting game proportions\n *\n * **Korean**: 경기장세계크기 (Arena World Size)\n *\n * @module utils/arenaWorldDimensions\n * @category Physics\n */\n\n/**\n * Screen size category based on RESOLUTION (width in pixels).\n *\n * **Korean**: 화면크기범주 (Screen Size Category)\n *\n * @public\n */\nexport type ScreenSizeCategory =\n | \"small\"\n | \"medium\"\n | \"large\"\n | \"xlarge\"\n | \"ultra\";\n\n/**\n * Standard arena widths in meters.\n * Arenas use 4:3 aspect ratio (height = width × 0.75).\n *\n * @public\n */\nexport const ARENA_SIZES = {\n /** Small arena for compact screens (6m × 4.5m) */\n SMALL: 6,\n /** Medium arena for tablets and small laptops (8m × 6m) */\n MEDIUM: 8,\n /** Large arena for standard desktops (10m × 7.5m) */\n LARGE: 10,\n /** Extra-large arena for high-res displays (12m × 9m) */\n XLARGE: 12,\n /** Ultra arena for 4K+ displays (14m × 10.5m) */\n ULTRA: 14,\n} as const;\n\n/**\n * Resolution breakpoints for arena size determination.\n * Based on common display widths.\n *\n * @public\n */\nexport const RESOLUTION_BREAKPOINTS = {\n /** Small screens: < 768px (phones, small tablets) */\n SMALL_MAX: 768,\n /** Medium screens: 768-1199px (tablets, small laptops) */\n MEDIUM_MAX: 1200,\n /** Large screens: 1200-1919px (laptops, standard monitors) */\n LARGE_MAX: 1920,\n /** XLarge screens: 1920-2559px (large monitors, some 4K) */\n XLARGE_MAX: 2560,\n /** Ultra screens: ≥ 2560px (4K, ultrawide) */\n} as const;\n\n/**\n * World dimensions for an arena in meters.\n *\n * **Korean**: 세계크기 (World Dimensions)\n *\n * @public\n */\nexport interface WorldDimensions {\n /** Arena width in meters */\n readonly widthMeters: number;\n /** Arena depth in meters (height = width × 0.75 for 4:3 ratio) */\n readonly depthMeters: number;\n /** Arena width in meters (same as widthMeters, for convenience) */\n readonly sizeMeters: number;\n /** Screen category used to determine arena size */\n readonly screenCategory: ScreenSizeCategory;\n}\n\n/**\n * Complete arena configuration including pixel and meter dimensions.\n *\n * @public\n */\nexport interface ArenaConfiguration {\n /** Arena position X in pixels */\n readonly x: number;\n /** Arena position Y in pixels */\n readonly y: number;\n /** Arena width in pixels */\n readonly width: number;\n /** Arena height in pixels */\n readonly height: number;\n /** Arena world width in meters */\n readonly worldWidthMeters: number;\n /** Arena world depth in meters */\n readonly worldDepthMeters: number;\n /** Pixels per meter ratio for this arena */\n readonly pixelsPerMeter: number;\n /** Scale factor (1.0 = reference desktop, <1.0 = smaller screens) */\n readonly scale: number;\n}\n\n/**\n * Categorize screen based on resolution (width in pixels).\n *\n * This is used for arena sizing. Device type (mobile/desktop) is determined\n * separately for UI controls only.\n *\n * **Korean**: 화면크기범주결정 (Determine Screen Size Category)\n *\n * @param screenWidth - Screen width in pixels\n * @returns Screen size category for arena dimensions\n *\n * @example\n * ```typescript\n * getScreenSizeCategory(640); // \"small\" - small phone\n * getScreenSizeCategory(1200); // \"large\" - standard laptop\n * getScreenSizeCategory(3840); // \"ultra\" - 4K display\n * ```\n *\n * @public\n */\nexport function getScreenSizeCategory(screenWidth: number): ScreenSizeCategory {\n if (screenWidth < RESOLUTION_BREAKPOINTS.SMALL_MAX) return \"small\";\n if (screenWidth < RESOLUTION_BREAKPOINTS.MEDIUM_MAX) return \"medium\";\n if (screenWidth < RESOLUTION_BREAKPOINTS.LARGE_MAX) return \"large\";\n if (screenWidth < RESOLUTION_BREAKPOINTS.XLARGE_MAX) return \"xlarge\";\n return \"ultra\";\n}\n\n/**\n * Get arena size in meters for a given screen category.\n *\n * @param category - Screen size category\n * @returns Arena size in meters (square dimension)\n *\n * @public\n */\nexport function getArenaSizeForCategory(category: ScreenSizeCategory): number {\n switch (category) {\n case \"small\":\n return ARENA_SIZES.SMALL;\n case \"medium\":\n return ARENA_SIZES.MEDIUM;\n case \"large\":\n return ARENA_SIZES.LARGE;\n case \"xlarge\":\n return ARENA_SIZES.XLARGE;\n case \"ultra\":\n return ARENA_SIZES.ULTRA;\n default:\n return ARENA_SIZES.LARGE;\n }\n}\n\n/**\n * Calculate appropriate arena world dimensions based on screen resolution.\n *\n * Larger resolution displays get larger arenas for better gameplay, while\n * maintaining the same physical combat mechanics. Movement speed in m/s\n * remains constant, but larger arenas provide more tactical space.\n *\n * **Arenas use 4:3 aspect ratio** (width > depth):\n * - < 768px: 6m × 4.5m (compact, fast-paced combat)\n * - 768-1199px: 8m × 6m (balanced gameplay)\n * - 1200-1919px: 10m × 7.5m (tactical combat)\n * - 1920-2559px: 12m × 9m (advanced positioning)\n * - ≥ 2560px: 14m × 10.5m (maximum tactical space)\n *\n * **Korean**: 경기장크기계산 (Calculate Arena Size)\n *\n * @param screenWidth - Screen width in pixels (resolution, not device type)\n * @returns World dimensions in meters (4:3 aspect ratio)\n *\n * @example\n * ```typescript\n * // Mobile phone 640px width\n * const smallPhone = calculateArenaWorldDimensions(640);\n * // Result: { widthMeters: 6, depthMeters: 4.5, sizeMeters: 6, screenCategory: \"small\" }\n *\n * // Desktop 1920×1080\n * const desktop = calculateArenaWorldDimensions(1920);\n * // Result: { widthMeters: 12, depthMeters: 9, sizeMeters: 12, screenCategory: \"xlarge\" }\n * ```\n *\n * @public\n */\nexport function calculateArenaWorldDimensions(\n screenWidth: number,\n): WorldDimensions {\n const category = getScreenSizeCategory(screenWidth);\n const sizeMeters = getArenaSizeForCategory(category);\n // 4:3 aspect ratio: depth = width × 0.75\n const depthMeters = sizeMeters * 0.75;\n\n return {\n widthMeters: sizeMeters,\n depthMeters,\n sizeMeters,\n screenCategory: category,\n };\n}\n\n/**\n * Calculate complete arena configuration including pixel and meter dimensions.\n *\n * This is the primary function for setting up arena bounds. It calculates:\n * - Pixel dimensions based on available screen space\n * - Meter dimensions based on screen resolution category\n * - Pixels-per-meter ratio for physics conversion\n *\n * @param screenWidth - Screen width in pixels\n * @param screenHeight - Screen height in pixels\n * @param topOffset - Pixels reserved at top (HUD, headers)\n * @param bottomOffset - Pixels reserved at bottom (controls, footer)\n * @param horizontalMargin - Pixels to leave on sides (as ratio 0-1)\n * @returns Complete arena configuration\n *\n * @public\n */\nexport function calculateArenaConfiguration(\n screenWidth: number,\n screenHeight: number,\n topOffset: number,\n bottomOffset: number,\n horizontalMargin: number = 0.1,\n): ArenaConfiguration {\n // Get world dimensions based on resolution\n const worldDimensions = calculateArenaWorldDimensions(screenWidth);\n\n // Calculate available pixel space\n const availableWidth = screenWidth * (1 - 2 * horizontalMargin);\n const availableHeight = screenHeight - topOffset - bottomOffset;\n\n // Use the smaller dimension to ensure square arena fits\n const arenaSizePixels = Math.min(availableWidth, availableHeight);\n\n // Calculate arena position (centered horizontally)\n const arenaX = (screenWidth - arenaSizePixels) / 2;\n const arenaY = topOffset;\n\n // Calculate pixels per meter for this configuration\n const pixelsPerMeter = arenaSizePixels / worldDimensions.sizeMeters;\n\n // Calculate scale relative to reference (1000px / 10m = 100 px/m)\n const referencePixelsPerMeter = 100;\n const scale = pixelsPerMeter / referencePixelsPerMeter;\n\n return {\n x: arenaX,\n y: arenaY,\n width: arenaSizePixels,\n height: arenaSizePixels,\n worldWidthMeters: worldDimensions.widthMeters,\n worldDepthMeters: worldDimensions.depthMeters,\n pixelsPerMeter,\n scale,\n };\n}\n\n/**\n * Calculate pixels-per-meter ratio from arena dimensions.\n *\n * @param arenaWidthPixels - Arena width in pixels\n * @param arenaWidthMeters - Arena width in meters\n * @returns Pixels per meter ratio\n *\n * @public\n */\nexport function calculatePixelsPerMeter(\n arenaWidthPixels: number,\n arenaWidthMeters: number,\n): number {\n if (arenaWidthMeters <= 0) {\n throw new Error(`arenaWidthMeters must be positive: ${arenaWidthMeters}`);\n }\n return arenaWidthPixels / arenaWidthMeters;\n}\n\n/**\n * Get player height in meters from archetype physical attributes.\n *\n * Converts archetype totalHeight (in centimeters) to meters for physics calculations.\n *\n * **Korean**: 플레이어키 (Player Height)\n *\n * @param archetypeHeightCm - Player height in centimeters from PhysicalAttributes\n * @returns Height in meters\n *\n * @example\n * ```typescript\n * // Musa archetype: 180cm\n * const height = getPlayerHeightMeters(180);\n * // Result: 1.8 meters\n * ```\n *\n * @public\n */\nexport function getPlayerHeightMeters(archetypeHeightCm: number): number {\n return archetypeHeightCm / 100;\n}\n"],"mappings":";;;;;;;AA8CA,IAAa,cAAc;;CAEzB,OAAO;;CAEP,QAAQ;;CAER,OAAO;;CAEP,QAAQ;;CAER,OAAO;CACR;;;;;;;AAQD,IAAa,yBAAyB;;CAEpC,WAAW;;CAEX,YAAY;;CAEZ,WAAW;;CAEX,YAAY;CAEb;;;;;;;;;;;;;;;;;;;;;AAgED,SAAgB,sBAAsB,aAAyC;CAC7E,IAAI,cAAc,uBAAuB,WAAW,OAAO;CAC3D,IAAI,cAAc,uBAAuB,YAAY,OAAO;CAC5D,IAAI,cAAc,uBAAuB,WAAW,OAAO;CAC3D,IAAI,cAAc,uBAAuB,YAAY,OAAO;CAC5D,OAAO;;;;;;;;;;AAWT,SAAgB,wBAAwB,UAAsC;CAC5E,QAAQ,UAAR;EACE,KAAK,SACH,OAAO,YAAY;EACrB,KAAK,UACH,OAAO,YAAY;EACrB,KAAK,SACH,OAAO,YAAY;EACrB,KAAK,UACH,OAAO,YAAY;EACrB,KAAK,SACH,OAAO,YAAY;EACrB,SACE,OAAO,YAAY;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAoCzB,SAAgB,8BACd,aACiB;CACjB,MAAM,WAAW,sBAAsB,YAAY;CACnD,MAAM,aAAa,wBAAwB,SAAS;CAIpD,OAAO;EACL,aAAa;EACb,aAJkB,aAAa;EAK/B;EACA,gBAAgB;EACjB;;;;;;;;;;;;;;;;;;;AAoBH,SAAgB,4BACd,aACA,cACA,WACA,cACA,mBAA2B,IACP;CAEpB,MAAM,kBAAkB,8BAA8B,YAAY;CAGlE,MAAM,iBAAiB,eAAe,IAAI,IAAI;CAC9C,MAAM,kBAAkB,eAAe,YAAY;CAGnD,MAAM,kBAAkB,KAAK,IAAI,gBAAgB,gBAAgB;CAGjE,MAAM,UAAU,cAAc,mBAAmB;CACjD,MAAM,SAAS;CAGf,MAAM,iBAAiB,kBAAkB,gBAAgB;CAIzD,MAAM,QAAQ,iBAAiB;CAE/B,OAAO;EACL,GAAG;EACH,GAAG;EACH,OAAO;EACP,QAAQ;EACR,kBAAkB,gBAAgB;EAClC,kBAAkB,gBAAgB;EAClC;EACA;EACD"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"assetConfig.js","names":[],"sources":["../../src/utils/assetConfig.ts"],"sourcesContent":["/**\n * Asset path configuration for Black Trigram library consumers.\n *\n * By default, all asset URLs are root-relative (e.g. /assets/audio/*).\n * Library consumers who host assets at a different path can call\n * {@link setAssetBasePath} to prefix all asset URLs.\n *\n * @example\n * ```ts\n * import { setAssetBasePath } from 'blacktrigram';\n * // Serve assets from a CDN\n * setAssetBasePath('https://cdn.example.com');\n * ```\n *\n * @module utils\n * @category Utilities\n */\n\nlet assetBasePath = \"\";\n\n/**\n * Set the base path for all game assets.\n * Call this before initializing any game components.\n *\n * @param basePath - The base URL/path prefix (e.g. `\"https://cdn.example.com\"` or `\"/my-app\"`)\n */\nexport function setAssetBasePath(basePath: string): void {\n // Remove trailing slash for consistency\n assetBasePath = basePath.replace(/\\/+$/, \"\");\n}\n\n/**\n * Get the current asset base path.\n */\nexport function getAssetBasePath(): string {\n return assetBasePath;\n}\n\n/**\n * Resolve an asset path by prepending the configured base path.\n *\n * Accepts paths with or without a leading slash — both are normalized\n * to root-relative before the base path (if any) is prepended.\n *\n * @param path - Asset path, e.g. /assets/audio/music/intro_theme.mp3 or assets/audio/music/intro_theme.mp3\n * @returns The resolved URL with the configured base path prepended\n */\nexport function resolveAssetPath(path: string): string {\n // Ensure path starts with / for correct concatenation\n const normalizedPath = path.startsWith(\"/\") ? path : `/${path}`;\n\n if (!assetBasePath) {\n return normalizedPath;\n }\n return `${assetBasePath}${normalizedPath}`;\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;AAkBA,IAAI,gBAAgB;;;;;;;AAQpB,SAAgB,iBAAiB,UAAwB;
|
|
1
|
+
{"version":3,"file":"assetConfig.js","names":[],"sources":["../../src/utils/assetConfig.ts"],"sourcesContent":["/**\n * Asset path configuration for Black Trigram library consumers.\n *\n * By default, all asset URLs are root-relative (e.g. /assets/audio/*).\n * Library consumers who host assets at a different path can call\n * {@link setAssetBasePath} to prefix all asset URLs.\n *\n * @example\n * ```ts\n * import { setAssetBasePath } from 'blacktrigram';\n * // Serve assets from a CDN\n * setAssetBasePath('https://cdn.example.com');\n * ```\n *\n * @module utils\n * @category Utilities\n */\n\nlet assetBasePath = \"\";\n\n/**\n * Set the base path for all game assets.\n * Call this before initializing any game components.\n *\n * @param basePath - The base URL/path prefix (e.g. `\"https://cdn.example.com\"` or `\"/my-app\"`)\n */\nexport function setAssetBasePath(basePath: string): void {\n // Remove trailing slash for consistency\n assetBasePath = basePath.replace(/\\/+$/, \"\");\n}\n\n/**\n * Get the current asset base path.\n */\nexport function getAssetBasePath(): string {\n return assetBasePath;\n}\n\n/**\n * Resolve an asset path by prepending the configured base path.\n *\n * Accepts paths with or without a leading slash — both are normalized\n * to root-relative before the base path (if any) is prepended.\n *\n * @param path - Asset path, e.g. /assets/audio/music/intro_theme.mp3 or assets/audio/music/intro_theme.mp3\n * @returns The resolved URL with the configured base path prepended\n */\nexport function resolveAssetPath(path: string): string {\n // Ensure path starts with / for correct concatenation\n const normalizedPath = path.startsWith(\"/\") ? path : `/${path}`;\n\n if (!assetBasePath) {\n return normalizedPath;\n }\n return `${assetBasePath}${normalizedPath}`;\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;AAkBA,IAAI,gBAAgB;;;;;;;AAQpB,SAAgB,iBAAiB,UAAwB;CAEvD,gBAAgB,SAAS,QAAQ,QAAQ,GAAG;;;;;AAM9C,SAAgB,mBAA2B;CACzC,OAAO;;;;;;;;;;;AAYT,SAAgB,iBAAiB,MAAsB;CAErD,MAAM,iBAAiB,KAAK,WAAW,IAAI,GAAG,OAAO,IAAI;CAEzD,IAAI,CAAC,eACH,OAAO;CAET,OAAO,GAAG,gBAAgB"}
|