blacktrigram 0.7.47 → 0.7.49
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/App2.js.map +1 -1
- package/lib/audio/AudioAssetLoader.js.map +1 -1
- package/lib/audio/AudioAssetRegistry.js.map +1 -1
- package/lib/audio/AudioCache.js.map +1 -1
- package/lib/audio/AudioManager.js.map +1 -1
- package/lib/audio/AudioMonitor.js.map +1 -1
- package/lib/audio/AudioPool.js.map +1 -1
- package/lib/audio/AudioProvider.js.map +1 -1
- package/lib/audio/AudioUtils.js.map +1 -1
- package/lib/audio/BoneImpactAudioMap.js.map +1 -1
- package/lib/audio/VariantSelector.js.map +1 -1
- package/lib/audio/types.js.map +1 -1
- package/lib/components/screens/combat/CombatScreen3D.d.ts.map +1 -1
- package/lib/components/screens/combat/CombatScreen3D.js +29 -25
- package/lib/components/screens/combat/CombatScreen3D.js.map +1 -1
- package/lib/components/screens/combat/components/controls/CombatButtons.js.map +1 -1
- package/lib/components/screens/combat/components/controls/CombatControlsPanel.js.map +1 -1
- package/lib/components/screens/combat/components/controls/ControlsGuide.js.map +1 -1
- package/lib/components/screens/combat/components/controls/KeyboardHints.js.map +1 -1
- package/lib/components/screens/combat/components/controls/PauseMenu.js.map +1 -1
- package/lib/components/screens/combat/components/controls/PauseMenuButton.js.map +1 -1
- package/lib/components/screens/combat/components/controls/QuickSettings.js.map +1 -1
- package/lib/components/screens/combat/components/effects/BloodDecals3D.js.map +1 -1
- package/lib/components/screens/combat/components/effects/BloodLossOverlayHtml.js.map +1 -1
- package/lib/components/screens/combat/components/effects/BloodParticles3D.js.map +1 -1
- package/lib/components/screens/combat/components/effects/BloodViscosity3D.js.map +1 -1
- package/lib/components/screens/combat/components/effects/CombatParticleEffects3D.js.map +1 -1
- package/lib/components/screens/combat/components/effects/ConsciousnessBlur.js.map +1 -1
- package/lib/components/screens/combat/components/effects/InternalDamage3D.js.map +1 -1
- package/lib/components/screens/combat/components/effects/PainVignette.js.map +1 -1
- package/lib/components/screens/combat/components/effects/ParticleAudio3D.js.map +1 -1
- package/lib/components/screens/combat/components/effects/TraumaOverlay3D.js.map +1 -1
- package/lib/components/screens/combat/components/feedback/MatchCountdown.js.map +1 -1
- package/lib/components/screens/combat/components/feedback/RoundAnnouncementOverlayHtml.js.map +1 -1
- package/lib/components/screens/combat/components/feedback/RoundDisplayStatus.js.map +1 -1
- package/lib/components/screens/combat/components/feedback/RoundStartAnnouncementOverlayHtml.js.map +1 -1
- package/lib/components/screens/combat/components/hud/CombatBottomHUD.d.ts.map +1 -1
- package/lib/components/screens/combat/components/hud/CombatBottomHUD.js +2 -2
- package/lib/components/screens/combat/components/hud/CombatBottomHUD.js.map +1 -1
- package/lib/components/screens/combat/components/hud/CombatLeftHUD.js.map +1 -1
- package/lib/components/screens/combat/components/hud/CombatPortraitStatusStrip.js +1 -1
- package/lib/components/screens/combat/components/hud/CombatPortraitStatusStrip.js.map +1 -1
- package/lib/components/screens/combat/components/hud/CombatRightHUD.js.map +1 -1
- package/lib/components/screens/combat/components/hud/CombatTopHUD.d.ts.map +1 -1
- package/lib/components/screens/combat/components/hud/CombatTopHUD.js +2 -1
- package/lib/components/screens/combat/components/hud/CombatTopHUD.js.map +1 -1
- package/lib/components/screens/combat/components/hud/DifficultyIndicator.js.map +1 -1
- package/lib/components/screens/combat/components/hud/FPSMonitor.js.map +1 -1
- package/lib/components/screens/combat/components/hud/MobileControlsWrapper.js.map +1 -1
- package/lib/components/screens/combat/components/hud/PlayerStateOverlayHtml.js.map +1 -1
- package/lib/components/screens/combat/components/indicators/BalanceIndicator.js.map +1 -1
- package/lib/components/screens/combat/components/indicators/InputBufferDisplay.js.map +1 -1
- package/lib/components/screens/combat/components/indicators/StaminaWarning.js.map +1 -1
- package/lib/components/screens/combat/components/indicators/TechniqueNameDisplay.js.map +1 -1
- package/lib/components/screens/combat/helpers/AnimationUpdater.d.ts.map +1 -1
- package/lib/components/screens/combat/helpers/AnimationUpdater.js +4 -2
- package/lib/components/screens/combat/helpers/AnimationUpdater.js.map +1 -1
- package/lib/components/screens/combat/helpers/combatHelpers.js.map +1 -1
- package/lib/components/screens/combat/hooks/useAICombat.js.map +1 -1
- package/lib/components/screens/combat/hooks/useCombatActions.js.map +1 -1
- package/lib/components/screens/combat/hooks/useCombatAttackMovement.js.map +1 -1
- package/lib/components/screens/combat/hooks/useCombatAudio.js.map +1 -1
- package/lib/components/screens/combat/hooks/useCombatLayout.d.ts.map +1 -1
- package/lib/components/screens/combat/hooks/useCombatLayout.js +11 -5
- package/lib/components/screens/combat/hooks/useCombatLayout.js.map +1 -1
- package/lib/components/screens/combat/hooks/useCombatState.js.map +1 -1
- package/lib/components/screens/controls/ControlsScreen3D.js +1 -1
- package/lib/components/screens/controls/ControlsScreen3D.js.map +1 -1
- package/lib/components/screens/controls/components/ControlBindingsOverlayHtml.js.map +1 -1
- package/lib/components/screens/controls/components/ControlCategoryTabsOverlayHtml.js.map +1 -1
- package/lib/components/screens/controls/components/GamepadVisualization3D.js.map +1 -1
- package/lib/components/screens/controls/components/InteractiveControlDemoOverlayHtml.js.map +1 -1
- package/lib/components/screens/controls/components/Key3D.js.map +1 -1
- package/lib/components/screens/controls/components/VisualKeyboard3D.js.map +1 -1
- package/lib/components/screens/controls/constants/ControlsConstants.js.map +1 -1
- package/lib/components/screens/controls/hooks/useControlsState.js.map +1 -1
- package/lib/components/screens/endscreen/EndScreen3D.js.map +1 -1
- package/lib/components/screens/endscreen/components/DefeatAnimation3D.js.map +1 -1
- package/lib/components/screens/endscreen/components/MatchStatisticsDisplayOverlayHtml.js.map +1 -1
- package/lib/components/screens/endscreen/components/NavigationButtonsOverlayHtml.js.map +1 -1
- package/lib/components/screens/endscreen/components/PerformanceBreakdownOverlayHtml.js.map +1 -1
- package/lib/components/screens/endscreen/components/PerformanceRatingOverlayHtml.js.map +1 -1
- package/lib/components/screens/endscreen/components/VictoryAnimation3D.js.map +1 -1
- package/lib/components/screens/endscreen/components/WinnerDisplayOverlayHtml.js.map +1 -1
- package/lib/components/screens/intro/IntroScreen3D.js +1 -1
- package/lib/components/screens/intro/IntroScreen3D.js.map +1 -1
- package/lib/components/screens/intro/components/AbilityListOverlayHtml.js.map +1 -1
- package/lib/components/screens/intro/components/ArchetypeCardGridOverlayHtml.js.map +1 -1
- package/lib/components/screens/intro/components/ArchetypeCardOverlayHtml.js.map +1 -1
- package/lib/components/screens/intro/components/ArchetypeDisplayOverlayHtml.js.map +1 -1
- package/lib/components/screens/intro/components/EnhancedArchetypeDisplayOverlayHtml.js.map +1 -1
- package/lib/components/screens/intro/components/MenuButtonsOverlayHtml.js.map +1 -1
- package/lib/components/screens/intro/components/MenuSectionOverlayHtml.js.map +1 -1
- package/lib/components/screens/intro/components/StatBarOverlayHtml.js.map +1 -1
- package/lib/components/screens/philosophy/PhilosophyScreen3D.js +1 -1
- package/lib/components/screens/philosophy/PhilosophyScreen3D.js.map +1 -1
- package/lib/components/screens/training/TrainingScreen3D.d.ts.map +1 -1
- package/lib/components/screens/training/TrainingScreen3D.js +3 -11
- package/lib/components/screens/training/TrainingScreen3D.js.map +1 -1
- package/lib/components/screens/training/components/AnatomyControlsOverlayHtml.js.map +1 -1
- package/lib/components/screens/training/components/AnatomyOverlay3D.js.map +1 -1
- package/lib/components/screens/training/components/FootPlacementMarkers3D.js.map +1 -1
- package/lib/components/screens/training/components/FootworkDrillsOverlayHtml.js.map +1 -1
- package/lib/components/screens/training/components/HitFeedbackEffect3D.js.map +1 -1
- package/lib/components/screens/training/components/TrainingButtonsOverlayHtml.js.map +1 -1
- package/lib/components/screens/training/components/TrainingControlsOverlayHtml.js.map +1 -1
- package/lib/components/screens/training/components/TrainingDummy3D.js.map +1 -1
- package/lib/components/screens/training/components/TrainingFeedbackOverlayHtml.js.map +1 -1
- package/lib/components/screens/training/components/TrainingModeSelectorOverlayHtml.js.map +1 -1
- package/lib/components/screens/training/components/TrainingStatsOverlayHtml.js.map +1 -1
- package/lib/components/screens/training/components/VitalPointMarker3D.js.map +1 -1
- package/lib/components/screens/training/components/VitalPointTrainingOverlayHtml.js.map +1 -1
- package/lib/components/screens/training/components/hud/TrainingBottomHUD.d.ts.map +1 -1
- package/lib/components/screens/training/components/hud/TrainingBottomHUD.js +2 -2
- package/lib/components/screens/training/components/hud/TrainingBottomHUD.js.map +1 -1
- package/lib/components/screens/training/components/hud/TrainingLeftHUD.js.map +1 -1
- package/lib/components/screens/training/components/hud/TrainingRightHUD.js.map +1 -1
- package/lib/components/screens/training/components/hud/TrainingTopHUD.js.map +1 -1
- package/lib/components/screens/training/hooks/useAttackMovement.js.map +1 -1
- package/lib/components/screens/training/hooks/useTrainingActions.d.ts +1 -0
- package/lib/components/screens/training/hooks/useTrainingActions.d.ts.map +1 -1
- package/lib/components/screens/training/hooks/useTrainingActions.js +6 -4
- package/lib/components/screens/training/hooks/useTrainingActions.js.map +1 -1
- package/lib/components/screens/training/hooks/useTrainingLayout.d.ts.map +1 -1
- package/lib/components/screens/training/hooks/useTrainingLayout.js +11 -5
- package/lib/components/screens/training/hooks/useTrainingLayout.js.map +1 -1
- package/lib/components/screens/training/hooks/useTrainingState.js.map +1 -1
- package/lib/components/shared/base/BaseButton.js.map +1 -1
- package/lib/components/shared/base/BaseButtonOverlayHtml.js.map +1 -1
- package/lib/components/shared/base/BasePanel.js.map +1 -1
- package/lib/components/shared/base/BaseText.js.map +1 -1
- package/lib/components/shared/base/useKoreanTheme.js.map +1 -1
- package/lib/components/shared/debug/PerformanceDebugOverlayHtml.js.map +1 -1
- package/lib/components/shared/mobile/ActionButtons.js.map +1 -1
- package/lib/components/shared/mobile/GestureRecognizerPure.js.map +1 -1
- package/lib/components/shared/mobile/HapticController.js.map +1 -1
- package/lib/components/shared/mobile/MobileControlsPure.js.map +1 -1
- package/lib/components/shared/mobile/StanceWheelPure.js.map +1 -1
- package/lib/components/shared/mobile/TouchOptimizer.js.map +1 -1
- package/lib/components/shared/mobile/VirtualDPad.js.map +1 -1
- package/lib/components/shared/three/anatomy/BodySurface.js.map +1 -1
- package/lib/components/shared/three/anatomy/BoneAttachedMuscles.js.map +1 -1
- package/lib/components/shared/three/anatomy/BoneClothing.js.map +1 -1
- package/lib/components/shared/three/anatomy/BoneRenderer.js.map +1 -1
- package/lib/components/shared/three/anatomy/Face3D.js.map +1 -1
- package/lib/components/shared/three/anatomy/Foot3D.js.map +1 -1
- package/lib/components/shared/three/anatomy/Hand3D.js.map +1 -1
- package/lib/components/shared/three/effects/ActionFeedback.js.map +1 -1
- package/lib/components/shared/three/effects/DamageNumbers.js.map +1 -1
- package/lib/components/shared/three/effects/HitEffects3D.js.map +1 -1
- package/lib/components/shared/three/effects/PlayerStateIndicators.js.map +1 -1
- package/lib/components/shared/three/effects/StanceSymbol3D.js.map +1 -1
- package/lib/components/shared/three/effects/StanceTransitionEffect.js.map +1 -1
- package/lib/components/shared/three/effects/VitalPointMarkers3D.js.map +1 -1
- package/lib/components/shared/three/indicators/ElementalColorSystem.js.map +1 -1
- package/lib/components/shared/three/indicators/GuardIndicator.js.map +1 -1
- package/lib/components/shared/three/indicators/HapticFeedback.js.map +1 -1
- package/lib/components/shared/three/indicators/StanceChangeIndicator.js.map +1 -1
- package/lib/components/shared/three/models/Player3DWithTransitions.js.map +1 -1
- package/lib/components/shared/three/models/SkeletalPlayer3D.d.ts.map +1 -1
- package/lib/components/shared/three/models/SkeletalPlayer3D.js +7 -5
- package/lib/components/shared/three/models/SkeletalPlayer3D.js.map +1 -1
- package/lib/components/shared/three/optimization/AdaptiveQuality.js.map +1 -1
- package/lib/components/shared/three/scene/AtmosphericParticles3D.js.map +1 -1
- package/lib/components/shared/three/scene/BackgroundScene3D.js.map +1 -1
- package/lib/components/shared/three/scene/CombatArena3D.js.map +1 -1
- package/lib/components/shared/three/scene/KoreanSignage3D.js.map +1 -1
- package/lib/components/shared/three/ui/ArchetypeCard.js.map +1 -1
- package/lib/components/shared/three/ui/BodyPartHealthDisplay.js.map +1 -1
- package/lib/components/shared/three/ui/BreathingIndicator2.js.map +1 -1
- package/lib/components/shared/three/ui/CombatReadinessBar.js.map +1 -1
- package/lib/components/shared/three/ui/ComboCounter.js.map +1 -1
- package/lib/components/shared/three/ui/HealthBar.js.map +1 -1
- package/lib/components/shared/three/ui/KoreanButton.js.map +1 -1
- package/lib/components/shared/three/ui/KoreanPanel.js.map +1 -1
- package/lib/components/shared/three/ui/KoreanText.js.map +1 -1
- package/lib/components/shared/three/ui/MenuList.js.map +1 -1
- package/lib/components/shared/three/ui/PlayerHUD.js.map +1 -1
- package/lib/components/shared/three/ui/ProgressBar.js.map +1 -1
- package/lib/components/shared/three/ui/SpeedIndicatorHUD.js.map +1 -1
- package/lib/components/shared/three/ui/StaminaBar.js.map +1 -1
- package/lib/components/shared/three/ui/TechniqueBar.js.map +1 -1
- package/lib/components/shared/three/ui/TechniqueCard.js.map +1 -1
- package/lib/components/shared/three/ui/VitalPointOverlayControlsHtml.js.map +1 -1
- package/lib/components/shared/ui/BackButton.js.map +1 -1
- package/lib/components/shared/ui/BaseHUDContainer.js.map +1 -1
- package/lib/components/shared/ui/CombatTimer.js.map +1 -1
- package/lib/components/shared/ui/ErrorModal.js.map +1 -1
- package/lib/components/shared/ui/LoadingState.js.map +1 -1
- package/lib/components/shared/ui/SplashScreen.js +2 -2
- package/lib/components/shared/ui/SplashScreen.js.map +1 -1
- package/lib/components/shared/ui/VitalPointOverlayControlsPure.js.map +1 -1
- package/lib/components/shared/ui/VolumeControl.js.map +1 -1
- package/lib/components/shared/ui/shared/ConfirmDialog.js.map +1 -1
- package/lib/components/ui/combat/BalanceIndicatorOverlayHtml.js.map +1 -1
- package/lib/constants/bodyDimensions.js.map +1 -1
- package/lib/constants/bodyRenderingConstants.js.map +1 -1
- package/lib/data/archetypeClothing.js.map +1 -1
- package/lib/data/archetypePhysicalAttributes.js.map +1 -1
- package/lib/data/techniqueMappings.js.map +1 -1
- package/lib/data/techniques.js.map +1 -1
- package/lib/hooks/useActionFeedback.js.map +1 -1
- package/lib/hooks/useBalanceAnimations.js.map +1 -1
- package/lib/hooks/useCombatTimer.js.map +1 -1
- package/lib/hooks/useDebounce.js.map +1 -1
- package/lib/hooks/useHUDLayout.d.ts.map +1 -1
- package/lib/hooks/useHUDLayout.js +3 -2
- package/lib/hooks/useHUDLayout.js.map +1 -1
- package/lib/hooks/useHandPoseTransitions.js.map +1 -1
- package/lib/hooks/useKeyboardControls.js.map +1 -1
- package/lib/hooks/useMatchCountdown.js.map +1 -1
- package/lib/hooks/useMuscleActivation.js.map +1 -1
- package/lib/hooks/usePauseMenu.js.map +1 -1
- package/lib/hooks/usePlayerAnimation.js.map +1 -1
- package/lib/hooks/useResponsiveLayout.js.map +1 -1
- package/lib/hooks/useRoundTransition.js.map +1 -1
- package/lib/hooks/useSkeletalAnimation.d.ts.map +1 -1
- package/lib/hooks/useSkeletalAnimation.js +1 -1
- package/lib/hooks/useSkeletalAnimation.js.map +1 -1
- package/lib/hooks/useTechniqueSelection.js.map +1 -1
- package/lib/hooks/useThrottle.js.map +1 -1
- package/lib/hooks/useTouchControls.js.map +1 -1
- package/lib/hooks/useWebGLContextLossHandler.js.map +1 -1
- package/lib/hooks/useWindowSize.js.map +1 -1
- package/lib/systems/CombatSystem.js.map +1 -1
- package/lib/systems/EffectCalculator.js.map +1 -1
- package/lib/systems/LayoutSystem.js.map +1 -1
- package/lib/systems/PlayerEffectManager.js.map +1 -1
- package/lib/systems/ResponsiveScaling.js.map +1 -1
- package/lib/systems/TrigramSystem.js.map +1 -1
- package/lib/systems/VitalPointSystem.js.map +1 -1
- package/lib/systems/ai/AIPersonality.js.map +1 -1
- package/lib/systems/ai/AdaptiveDifficulty.js +16 -16
- package/lib/systems/ai/AdaptiveDifficulty.js.map +1 -1
- package/lib/systems/ai/ArchetypeEnforcer.js.map +1 -1
- package/lib/systems/ai/ComboSystem.js.map +1 -1
- package/lib/systems/ai/DecisionTree.js.map +1 -1
- package/lib/systems/ai/TrainingAI.js.map +1 -1
- package/lib/systems/ai/types.js.map +1 -1
- package/lib/systems/animation/builders/AnimationBuilder.js.map +1 -1
- package/lib/systems/animation/builders/HandPoseApplicator.js.map +1 -1
- package/lib/systems/animation/builders/HandPoses.js.map +1 -1
- package/lib/systems/animation/builders/KeyframeConfig.js.map +1 -1
- package/lib/systems/animation/builders/KeyframeInterpolation.js.map +1 -1
- package/lib/systems/animation/builders/KickPhaseApplicator.d.ts +6 -0
- package/lib/systems/animation/builders/KickPhaseApplicator.d.ts.map +1 -1
- package/lib/systems/animation/builders/KickPhaseApplicator.js +16 -9
- package/lib/systems/animation/builders/KickPhaseApplicator.js.map +1 -1
- package/lib/systems/animation/builders/KoreanGuardPositions.d.ts +4 -4
- package/lib/systems/animation/builders/KoreanGuardPositions.js.map +1 -1
- package/lib/systems/animation/builders/MartialArtsAnimationBuilder.d.ts +1 -1
- package/lib/systems/animation/builders/MartialArtsAnimationBuilder.d.ts.map +1 -1
- package/lib/systems/animation/builders/MartialArtsAnimationBuilder.js +5 -5
- package/lib/systems/animation/builders/MartialArtsAnimationBuilder.js.map +1 -1
- package/lib/systems/animation/builders/MartialArtsConstants.d.ts +112 -71
- package/lib/systems/animation/builders/MartialArtsConstants.d.ts.map +1 -1
- package/lib/systems/animation/builders/MartialArtsConstants.js +113 -72
- package/lib/systems/animation/builders/MartialArtsConstants.js.map +1 -1
- package/lib/systems/animation/builders/MartialPoseApplicator.js.map +1 -1
- package/lib/systems/animation/builders/PunchPhaseApplicator.js.map +1 -1
- package/lib/systems/animation/builders/SkeletonRig.js.map +1 -1
- package/lib/systems/animation/builders/TrigramGuardApplicator.js.map +1 -1
- package/lib/systems/animation/catalogs/AttackAnimations.js.map +1 -1
- package/lib/systems/animation/catalogs/BasicAnimations.js.map +1 -1
- package/lib/systems/animation/catalogs/ComboAnimations.js.map +1 -1
- package/lib/systems/animation/catalogs/DarkOpsAnimations.js.map +1 -1
- package/lib/systems/animation/catalogs/DefensiveAnimations.js.map +1 -1
- package/lib/systems/animation/catalogs/ElbowKneeAnimations.js.map +1 -1
- package/lib/systems/animation/catalogs/EnhancedAttackAnimations.js.map +1 -1
- package/lib/systems/animation/catalogs/EnhancedElbowKneeAnimations.js.map +1 -1
- package/lib/systems/animation/catalogs/FootworkSkeletalAnimations.js.map +1 -1
- package/lib/systems/animation/catalogs/GamRedirectionAnimations.js.map +1 -1
- package/lib/systems/animation/catalogs/GamStanceAnimations.js +21 -0
- package/lib/systems/animation/catalogs/GamStanceAnimations.js.map +1 -0
- package/lib/systems/animation/catalogs/GamTechniqueAnimations.js +34 -2
- package/lib/systems/animation/catalogs/GamTechniqueAnimations.js.map +1 -1
- package/lib/systems/animation/catalogs/GanStanceAnimations.js.map +1 -1
- package/lib/systems/animation/catalogs/GanTechniqueAnimations.js.map +1 -1
- package/lib/systems/animation/catalogs/GeonStanceAnimations.js.map +1 -1
- package/lib/systems/animation/catalogs/GonTechniqueAnimations.d.ts +9 -0
- package/lib/systems/animation/catalogs/GonTechniqueAnimations.d.ts.map +1 -1
- package/lib/systems/animation/catalogs/GonTechniqueAnimations.js +288 -0
- package/lib/systems/animation/catalogs/GonTechniqueAnimations.js.map +1 -0
- package/lib/systems/animation/catalogs/GrapplingAnimations.js.map +1 -1
- package/lib/systems/animation/catalogs/JinStanceAnimations.js.map +1 -1
- package/lib/systems/animation/catalogs/JinTechniqueAnimations.js.map +1 -1
- package/lib/systems/animation/catalogs/KickAnimations.d.ts +2 -2
- package/lib/systems/animation/catalogs/KickAnimations.js +2 -2
- package/lib/systems/animation/catalogs/KickAnimations.js.map +1 -1
- package/lib/systems/animation/catalogs/LiStanceAnimations.js +14 -1
- package/lib/systems/animation/catalogs/LiStanceAnimations.js.map +1 -1
- package/lib/systems/animation/catalogs/LiTechniqueAnimations.js.map +1 -1
- package/lib/systems/animation/catalogs/MovementAnimations.js.map +1 -1
- package/lib/systems/animation/catalogs/PunchAnimations.d.ts +1 -1
- package/lib/systems/animation/catalogs/PunchAnimations.js +1 -1
- package/lib/systems/animation/catalogs/PunchAnimations.js.map +1 -1
- package/lib/systems/animation/catalogs/RecoveryAnimations.js.map +1 -1
- package/lib/systems/animation/catalogs/SonStanceAnimations.js.map +1 -1
- package/lib/systems/animation/catalogs/SonTechniqueAnimations.js.map +1 -1
- package/lib/systems/animation/catalogs/SpecializedPunchAnimations.js.map +1 -1
- package/lib/systems/animation/catalogs/StanceAnimations.js.map +1 -1
- package/lib/systems/animation/catalogs/StanceAttackAnimations.js.map +1 -1
- package/lib/systems/animation/catalogs/StanceGuardPoses.d.ts +6 -6
- package/lib/systems/animation/catalogs/StanceGuardPoses.js +36 -36
- package/lib/systems/animation/catalogs/StanceGuardPoses.js.map +1 -1
- package/lib/systems/animation/catalogs/StanceIdleAnimations.js.map +1 -1
- package/lib/systems/animation/catalogs/StanceLocomotionAnimations.js.map +1 -1
- package/lib/systems/animation/catalogs/StepSkeletalAnimations.js.map +1 -1
- package/lib/systems/animation/catalogs/TaeJointLockAnimations.js.map +1 -1
- package/lib/systems/animation/catalogs/TaeStanceAnimations.js.map +1 -1
- package/lib/systems/animation/constants/AnatomicalLimits.js.map +1 -1
- package/lib/systems/animation/core/AnimationHitTiming.js.map +1 -1
- package/lib/systems/animation/core/AnimationOptimizations.js.map +1 -1
- package/lib/systems/animation/core/AnimationPriority.js +15 -15
- package/lib/systems/animation/core/AnimationPriority.js.map +1 -1
- package/lib/systems/animation/core/AnimationRegistry.d.ts +30 -0
- package/lib/systems/animation/core/AnimationRegistry.d.ts.map +1 -1
- package/lib/systems/animation/core/AnimationRegistry.js +74 -12
- package/lib/systems/animation/core/AnimationRegistry.js.map +1 -1
- package/lib/systems/animation/core/AnimationStateMachine.js +16 -16
- package/lib/systems/animation/core/AnimationStateMachine.js.map +1 -1
- package/lib/systems/animation/core/AnimationTransitions.d.ts.map +1 -1
- package/lib/systems/animation/core/AnimationTransitions.js +34 -0
- package/lib/systems/animation/core/AnimationTransitions.js.map +1 -1
- package/lib/systems/animation/core/LateralityTransform.js.map +1 -1
- package/lib/systems/animation/core/RecoveryPhaseEnhancer.js.map +1 -1
- package/lib/systems/animation/core/TechniqueAnimationMapper.js.map +1 -1
- package/lib/systems/animation/core/TechniqueAnimationMapping.js.map +1 -1
- package/lib/systems/animation/core/index.d.ts +1 -1
- package/lib/systems/animation/core/index.d.ts.map +1 -1
- package/lib/systems/animation/core/types.d.ts +24 -0
- package/lib/systems/animation/core/types.d.ts.map +1 -1
- package/lib/systems/animation/core/types.js +27 -11
- package/lib/systems/animation/core/types.js.map +1 -1
- package/lib/systems/animation/systems/AdvancedJointMovements.js.map +1 -1
- package/lib/systems/animation/systems/BodyFacingSystem.js.map +1 -1
- package/lib/systems/animation/systems/FacialExpressions.js.map +1 -1
- package/lib/systems/animation/systems/FallAnimations.js.map +1 -1
- package/lib/systems/animation/systems/MuscleActivation.js.map +1 -1
- package/lib/systems/bodypart/BodyPartDamageIntegration.js.map +1 -1
- package/lib/systems/bodypart/BodyPartHealthSystem.js.map +1 -1
- package/lib/systems/bodypart/BodyPartPositionMapping.js.map +1 -1
- package/lib/systems/bodypart/CombatInjuryIntegration.js.map +1 -1
- package/lib/systems/bodypart/InjuryIntegration.js.map +1 -1
- package/lib/systems/bodypart/InjuryTracker.js.map +1 -1
- package/lib/systems/bodypart/MovementPenaltySystem.js.map +1 -1
- package/lib/systems/bodypart/PlayerInjuryTrackingManager.js.map +1 -1
- package/lib/systems/bodypart/types.js.map +1 -1
- package/lib/systems/breathing/BreathingDisruptionSystem.js +19 -19
- package/lib/systems/breathing/BreathingDisruptionSystem.js.map +1 -1
- package/lib/systems/breathing/feedback.js.map +1 -1
- package/lib/systems/breathing/integration.js.map +1 -1
- package/lib/systems/combat/BalanceSystem.js +19 -19
- package/lib/systems/combat/BalanceSystem.js.map +1 -1
- package/lib/systems/combat/BreakingStatusEffects.js.map +1 -1
- package/lib/systems/combat/CombatStateSystem.js +17 -17
- package/lib/systems/combat/CombatStateSystem.js.map +1 -1
- package/lib/systems/combat/ConsciousnessSystem.js +24 -24
- package/lib/systems/combat/ConsciousnessSystem.js.map +1 -1
- package/lib/systems/combat/FallIntegration.js.map +1 -1
- package/lib/systems/combat/GrappleSystem.js.map +1 -1
- package/lib/systems/combat/LimbExposureSystem.js.map +1 -1
- package/lib/systems/combat/PainResponseSystem.js +21 -21
- package/lib/systems/combat/PainResponseSystem.js.map +1 -1
- package/lib/systems/combat/TrainingCombatSystem.js.map +1 -1
- package/lib/systems/combat/painConsciousnessUtils.js.map +1 -1
- package/lib/systems/combat/typeGuards.js.map +1 -1
- package/lib/systems/effects.js.map +1 -1
- package/lib/systems/game.js.map +1 -1
- package/lib/systems/movement/InjuryMovementModifier.js.map +1 -1
- package/lib/systems/movement/helpers/AccelerationUpdater.js.map +1 -1
- package/lib/systems/movement/helpers/accelerationUtils.js.map +1 -1
- package/lib/systems/movement/integration.js.map +1 -1
- package/lib/systems/physics/AttackMovementPhysics.js.map +1 -1
- package/lib/systems/physics/CollisionDetection.js.map +1 -1
- package/lib/systems/physics/CoordinateMapper.js.map +1 -1
- package/lib/systems/physics/KnockbackPhysics.js.map +1 -1
- package/lib/systems/physics/MovementPhysics.js.map +1 -1
- package/lib/systems/physics/PhysicalReachCalculator.js.map +1 -1
- package/lib/systems/physics/SpeedModifierSystem.js +6 -6
- package/lib/systems/physics/SpeedModifierSystem.js.map +1 -1
- package/lib/systems/trigram/KoreanCulture.js.map +1 -1
- package/lib/systems/trigram/KoreanTechniques.js.map +1 -1
- package/lib/systems/trigram/StanceManager.js.map +1 -1
- package/lib/systems/trigram/TransitionCalculator.js.map +1 -1
- package/lib/systems/trigram/TrigramCalculator.js.map +1 -1
- package/lib/systems/trigram/techniques/DarkOpsTechniques.js.map +1 -1
- package/lib/systems/trigram/techniques/GamTechniques.js.map +1 -1
- package/lib/systems/trigram/techniques/GanTechniques.js.map +1 -1
- package/lib/systems/trigram/techniques/GeonTechniques.js.map +1 -1
- package/lib/systems/trigram/techniques/GonTechniques.js.map +1 -1
- package/lib/systems/trigram/techniques/JinTechniques.js.map +1 -1
- package/lib/systems/trigram/techniques/LiTechniques.js.map +1 -1
- package/lib/systems/trigram/techniques/SonTechniques.js.map +1 -1
- package/lib/systems/trigram/techniques/TaeTechniques.js.map +1 -1
- package/lib/systems/trigram/techniques/TechniqueConfig.js.map +1 -1
- package/lib/systems/trigram/techniques/index.js.map +1 -1
- package/lib/systems/trigram/types/GonTechniqueExtensions.js.map +1 -1
- package/lib/systems/trigram/types.js.map +1 -1
- package/lib/systems/types.js.map +1 -1
- package/lib/systems/vitalpoint/DamageCalculator.js.map +1 -1
- package/lib/systems/vitalpoint/HitDetection.js.map +1 -1
- package/lib/systems/vitalpoint/KoreanAnatomy.js.map +1 -1
- package/lib/systems/vitalpoint/KoreanVitalPoints.js.map +1 -1
- package/lib/systems/vitalpoint/MeridianVitalPointMapping.js.map +1 -1
- package/lib/systems/vitalpoint/VitalPointsData.js.map +1 -1
- package/lib/types/AccessibilityTypes.js.map +1 -1
- package/lib/types/LayoutTypes.js.map +1 -1
- package/lib/types/PhysicsTypes.js.map +1 -1
- package/lib/types/common.js.map +1 -1
- package/lib/types/constants/animations.js.map +1 -1
- package/lib/types/constants/colors.js.map +1 -1
- package/lib/types/constants/designSystem.js.map +1 -1
- package/lib/types/constants/index.js.map +1 -1
- package/lib/types/constants/layout.d.ts +21 -0
- package/lib/types/constants/layout.d.ts.map +1 -1
- package/lib/types/constants/layout.js +22 -1
- package/lib/types/constants/layout.js.map +1 -1
- package/lib/types/constants/performance.js.map +1 -1
- package/lib/types/constants/typography.js.map +1 -1
- package/lib/types/constants/ui.js.map +1 -1
- package/lib/types/facial.js +19 -19
- package/lib/types/facial.js.map +1 -1
- package/lib/types/hand-animation.js.map +1 -1
- package/lib/types/injury.js.map +1 -1
- package/lib/types/muscle.js.map +1 -1
- package/lib/types/physics.js.map +1 -1
- package/lib/types/physicsConstants.js.map +1 -1
- package/lib/types/player-visual.d.ts +1 -1
- package/lib/types/player-visual.d.ts.map +1 -1
- package/lib/types/skeletal.js.map +1 -1
- package/lib/types/techniqueId.js.map +1 -1
- package/lib/utils/accessibility.js.map +1 -1
- package/lib/utils/arenaWorldDimensions.js.map +1 -1
- package/lib/utils/assetConfig.js.map +1 -1
- package/lib/utils/characterScaling.js.map +1 -1
- package/lib/utils/colorHelpers.js.map +1 -1
- package/lib/utils/colorUtils.js.map +1 -1
- package/lib/utils/combatReadiness.js.map +1 -1
- package/lib/utils/controlMapping.js.map +1 -1
- package/lib/utils/deviceDetection.js +6 -7
- package/lib/utils/deviceDetection.js.map +1 -1
- package/lib/utils/effectUtils.js.map +1 -1
- package/lib/utils/fabricTextures.js.map +1 -1
- package/lib/utils/hapticFeedback.js.map +1 -1
- package/lib/utils/haptics.js.map +1 -1
- package/lib/utils/htmlOverlayHelpers.js.map +1 -1
- package/lib/utils/inputSystem.js.map +1 -1
- package/lib/utils/koreanThemeHelpers.js.map +1 -1
- package/lib/utils/math.js.map +1 -1
- package/lib/utils/mobileLayoutHelpers.js.map +1 -1
- package/lib/utils/mobileUIUtils.js.map +1 -1
- package/lib/utils/performance/PerformanceMonitor.js.map +1 -1
- package/lib/utils/performance/PerformanceOverlay3D.js.map +1 -1
- package/lib/utils/performance/usePerformanceMonitor.js.map +1 -1
- package/lib/utils/performanceOptimization.js.map +1 -1
- package/lib/utils/player3DHelpers.js.map +1 -1
- package/lib/utils/playerUtils.js.map +1 -1
- package/lib/utils/responsiveLayout.js.map +1 -1
- package/lib/utils/responsiveLayoutHelpers.d.ts +7 -0
- package/lib/utils/responsiveLayoutHelpers.d.ts.map +1 -1
- package/lib/utils/responsiveLayoutHelpers.js +16 -2
- package/lib/utils/responsiveLayoutHelpers.js.map +1 -1
- package/lib/utils/responsiveOrientationConstants.js.map +1 -1
- package/lib/utils/safeAreaUtils.js.map +1 -1
- package/lib/utils/sharedPhysicsConfig.js.map +1 -1
- package/lib/utils/skeletonScaling.js.map +1 -1
- package/lib/utils/stanceHelpers.js.map +1 -1
- package/lib/utils/threeObjectPool.js.map +1 -1
- package/lib/utils/visualEffects.js.map +1 -1
- package/package.json +7 -7
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"FallAnimations.js","names":[],"sources":["../../../../src/systems/animation/systems/FallAnimations.ts"],"sourcesContent":["/**\n * Fall Animation System for Black Trigram\n *\n * Implements realistic fall down animations for knockdowns, leg sweeps,\n * and loss of consciousness events. Based on Korean martial arts falling\n * techniques (낙법 - Nakbeop).\n *\n * @module systems/animation/FallAnimations\n * @category Animation\n * @korean 낙법애니메이션\n */\n\nimport { TrigramStance } from \"@/types/common\";\nimport type { FallType } from \"../core/types\";\n\n/**\n * Fall animation impact frame numbers\n *\n * Defines which frame in each fall animation represents ground impact.\n * Used to trigger camera shake, audio, and particle effects.\n *\n * @korean 낙법충격프레임\n */\nexport const FALL_IMPACT_FRAMES: Record<FallType, number> = {\n forward: 18, // Frame 18 of 24 - hands hit ground\n backward: 22, // Frame 22 of 30 - back impacts\n side_left: 20, // Frame 20 of 27 - shoulder/side impacts\n side_right: 20, // Frame 20 of 27 - shoulder/side impacts\n};\n\n/**\n * Determines fall direction from attack vector and player facing\n *\n * Calculates which direction the player should fall based on:\n * - Attack vector (direction of incoming force)\n * - Player facing direction (from stance)\n * - Attack impact point (high/low)\n *\n * Korean terminology:\n * - 전방낙법 (Jeonbang Nakbeop): Forward fall from rear attacks\n * - 후방낙법 (Hubang Nakbeop): Backward fall from frontal attacks\n * - 측방낙법 (Cheukbang Nakbeop): Side fall from lateral attacks\n *\n * @param attackAngle - Angle of attack in radians (0 = from front)\n * @param playerFacing - Player facing angle in radians\n * @param attackHeight - Attack height: 'high', 'mid', or 'low'\n * @returns Fall type to use for animation\n *\n * @example\n * ```typescript\n * // Attack from behind while facing forward\n * const fallType = determineFallDirection(Math.PI, 0, 'mid');\n * // Returns: 'forward' (pushed forward)\n *\n * // Attack from front while facing forward\n * const fallType = determineFallDirection(0, 0, 'mid');\n * // Returns: 'backward' (pushed backward)\n *\n * // Attack from left side\n * const fallType = determineFallDirection(-Math.PI/2, 0, 'mid');\n * // Returns: 'side_left'\n * ```\n *\n * @korean 낙법방향결정\n */\nexport function determineFallDirection(\n attackAngle: number,\n playerFacing: number,\n attackHeight: \"high\" | \"mid\" | \"low\" = \"mid\",\n): FallType {\n // Calculate relative attack angle (attack direction relative to player facing)\n let relativeAngle = attackAngle - playerFacing;\n\n // Normalize to -π to π range\n while (relativeAngle > Math.PI) relativeAngle -= 2 * Math.PI;\n while (relativeAngle < -Math.PI) relativeAngle += 2 * Math.PI;\n\n const absAngle = Math.abs(relativeAngle);\n\n // Leg sweeps always cause side falls (more realistic for sweeps)\n if (attackHeight === \"low\") {\n // Determine which side based on angle\n // Use a small threshold to avoid edge case at exactly 0\n // Threshold represents the angle within which we consider the sweep \"frontal\"\n // 0.01 radians ≈ 0.57 degrees - small enough to only catch near-perfect frontal sweeps\n // while still allowing accurate left/right determination for most angles\n const SWEEP_ANGLE_THRESHOLD = 0.01;\n if (Math.abs(relativeAngle) < SWEEP_ANGLE_THRESHOLD) {\n // For perfectly frontal sweeps, default to right side fall\n return \"side_right\";\n }\n if (relativeAngle < 0) {\n return \"side_left\";\n } else {\n return \"side_right\";\n }\n }\n\n // High attacks to head often cause backward falls\n if (attackHeight === \"high\" && absAngle < Math.PI / 3) {\n return \"backward\";\n }\n\n // Determine fall direction based on attack angle\n // Front quadrant (±45°): Backward fall\n if (absAngle < Math.PI / 4) {\n return \"backward\";\n }\n\n // Rear quadrant (±45° from back): Forward fall\n if (absAngle > (3 * Math.PI) / 4) {\n return \"forward\";\n }\n\n // Side quadrants: Side falls\n if (relativeAngle < 0) {\n return \"side_left\";\n } else {\n return \"side_right\";\n }\n}\n\n/**\n * Determines fall direction from current trigram stance\n *\n * Some stances have inherent instability in certain directions.\n * This function returns likely fall directions when balance is lost.\n *\n * Korean stances and fall tendencies:\n * - 건 (Heaven): Forward bias - aggressive stance\n * - 태 (Lake): Backward bias - fluid retreating\n * - 리 (Fire): Forward bias - aggressive advance\n * - 진 (Thunder): Backward bias - explosive preparation\n * - 손 (Wind): Side bias - lateral movement\n * - 감 (Water): Backward bias - defensive flow\n * - 간 (Mountain): Backward bias - solid defense\n * - 곤 (Earth): Forward bias - grounding takedowns\n *\n * @param stance - Current trigram stance\n * @param defaultFall - Default fall type if stance doesn't suggest direction\n * @returns Likely fall direction for the stance\n *\n * @korean 자세낙법방향\n */\nexport function determineFallFromStance(\n stance: TrigramStance,\n defaultFall: FallType = \"backward\",\n): FallType {\n const stanceFallBias: Record<TrigramStance, FallType> = {\n [TrigramStance.GEON]: \"forward\", // Heaven - aggressive forward\n [TrigramStance.TAE]: \"backward\", // Lake - fluid retreat\n [TrigramStance.LI]: \"forward\", // Fire - aggressive strike\n [TrigramStance.JIN]: \"backward\", // Thunder - explosive back\n [TrigramStance.SON]: \"side_left\", // Wind - lateral pressure\n [TrigramStance.GAM]: \"backward\", // Water - flowing back\n [TrigramStance.GAN]: \"backward\", // Mountain - defensive back\n [TrigramStance.GON]: \"forward\", // Earth - forward throws\n };\n\n return stanceFallBias[stance] ?? defaultFall;\n}\n\n/**\n * Fall animation keyframe data structure\n *\n * Defines key poses during fall animations for skeletal rendering.\n * Each keyframe specifies body positions at critical moments.\n *\n * @korean 낙법키프레임\n */\nexport interface FallKeyframe {\n /** Frame number (0-indexed) */\n readonly frame: number;\n\n /** Torso rotation (radians) */\n readonly torsoRotation: { x: number; y: number; z: number };\n\n /** Center of mass vertical position (0-1, 1=standing, 0=ground) */\n readonly centerOfMassHeight: number;\n\n /** Description of this keyframe */\n readonly description: {\n readonly korean: string;\n readonly english: string;\n };\n}\n\n/**\n * Forward fall keyframes (전방낙법)\n *\n * 24 frames (400ms) sequence:\n * - Frames 0-8: Forward stumble, losing balance\n * - Frames 9-15: Knee collapse, forward momentum\n * - Frames 16-20: Hands extend to brace fall\n * - Frames 21-23: Impact and settle face-down\n *\n * @korean 전방낙법키프레임\n */\nexport const FALL_FORWARD_KEYFRAMES: readonly FallKeyframe[] = [\n {\n frame: 0,\n torsoRotation: { x: 0, y: 0, z: 0 },\n centerOfMassHeight: 0.9,\n description: {\n korean: \"초기 자세\",\n english: \"Initial stance\",\n },\n },\n {\n frame: 8,\n torsoRotation: { x: 0.3, y: 0, z: 0 }, // Leaning forward\n centerOfMassHeight: 0.75,\n description: {\n korean: \"전방으로 비틀거림\",\n english: \"Forward stumble\",\n },\n },\n {\n frame: 15,\n torsoRotation: { x: 0.7, y: 0, z: 0 }, // Falling forward\n centerOfMassHeight: 0.4,\n description: {\n korean: \"무릎 붕괴\",\n english: \"Knee collapse\",\n },\n },\n {\n frame: 18,\n torsoRotation: { x: 1.2, y: 0, z: 0 }, // Hands extending\n centerOfMassHeight: 0.15,\n description: {\n korean: \"손으로 지면 충격 완화\",\n english: \"Hands brace impact\",\n },\n },\n {\n frame: 23,\n torsoRotation: { x: 1.57, y: 0, z: 0 }, // Face down (90° forward)\n centerOfMassHeight: 0.05,\n description: {\n korean: \"엎드려 정지\",\n english: \"Face-down prone\",\n },\n },\n] as const;\n\n/**\n * Backward fall keyframes (후방낙법)\n *\n * 30 frames (500ms) sequence:\n * - Frames 0-10: Backward stumble, balance loss\n * - Frames 11-18: Sitting motion begins\n * - Frames 19-25: Back impact preparation\n * - Frames 26-29: Full supine position\n *\n * @korean 후방낙법키프레임\n */\nexport const FALL_BACKWARD_KEYFRAMES: readonly FallKeyframe[] = [\n {\n frame: 0,\n torsoRotation: { x: 0, y: 0, z: 0 },\n centerOfMassHeight: 0.9,\n description: {\n korean: \"초기 자세\",\n english: \"Initial stance\",\n },\n },\n {\n frame: 10,\n torsoRotation: { x: -0.2, y: 0, z: 0 }, // Leaning back\n centerOfMassHeight: 0.8,\n description: {\n korean: \"후방으로 비틀거림\",\n english: \"Backward stumble\",\n },\n },\n {\n frame: 18,\n torsoRotation: { x: -0.6, y: 0, z: 0 }, // Sitting motion\n centerOfMassHeight: 0.45,\n description: {\n korean: \"앉는 동작\",\n english: \"Sitting motion\",\n },\n },\n {\n frame: 22,\n torsoRotation: { x: -1.2, y: 0, z: 0 }, // Back impact\n centerOfMassHeight: 0.2,\n description: {\n korean: \"등 충격\",\n english: \"Back impact\",\n },\n },\n {\n frame: 29,\n torsoRotation: { x: -1.57, y: 0, z: 0 }, // Face up (90° back)\n centerOfMassHeight: 0.05,\n description: {\n korean: \"누워 정지\",\n english: \"Supine position\",\n },\n },\n] as const;\n\n/**\n * Side fall keyframes (측방낙법)\n *\n * 27 frames (450ms) sequence:\n * - Frames 0-9: Side rotation begins\n * - Frames 10-16: Shoulder roll motion\n * - Frames 17-22: Hip impact\n * - Frames 23-26: Side sprawl position\n *\n * @korean 측방낙법키프레임\n */\nexport const FALL_SIDE_KEYFRAMES: readonly FallKeyframe[] = [\n {\n frame: 0,\n torsoRotation: { x: 0, y: 0, z: 0 },\n centerOfMassHeight: 0.9,\n description: {\n korean: \"초기 자세\",\n english: \"Initial stance\",\n },\n },\n {\n frame: 9,\n torsoRotation: { x: 0, y: 0.3, z: 0.4 }, // Side rotation\n centerOfMassHeight: 0.7,\n description: {\n korean: \"측면 회전 시작\",\n english: \"Side rotation begins\",\n },\n },\n {\n frame: 16,\n torsoRotation: { x: 0.2, y: 0.8, z: 0.9 }, // Shoulder roll\n centerOfMassHeight: 0.4,\n description: {\n korean: \"어깨 구르기\",\n english: \"Shoulder roll\",\n },\n },\n {\n frame: 20,\n torsoRotation: { x: 0.3, y: 1.2, z: 1.3 }, // Hip impact\n centerOfMassHeight: 0.2,\n description: {\n korean: \"엉덩이 충격\",\n english: \"Hip impact\",\n },\n },\n {\n frame: 26,\n torsoRotation: { x: 0, y: 1.57, z: 1.57 }, // Side position (90° roll)\n centerOfMassHeight: 0.05,\n description: {\n korean: \"측면 정지\",\n english: \"Side sprawl\",\n },\n },\n] as const;\n\n/**\n * Side fall keyframes - RIGHT side (우측 낙법)\n *\n * Mirrored version of FALL_SIDE_KEYFRAMES with negated Y and Z rotations\n * so the character falls to the opposite side.\n *\n * @korean 우측낙법키프레임\n */\nexport const FALL_SIDE_RIGHT_KEYFRAMES: readonly FallKeyframe[] = [\n {\n frame: 0,\n torsoRotation: { x: 0, y: 0, z: 0 },\n centerOfMassHeight: 0.9,\n description: {\n korean: \"초기 자세\",\n english: \"Initial stance\",\n },\n },\n {\n frame: 9,\n torsoRotation: { x: 0, y: -0.3, z: -0.4 }, // Opposite side rotation\n centerOfMassHeight: 0.7,\n description: {\n korean: \"우측 회전 시작\",\n english: \"Right side rotation begins\",\n },\n },\n {\n frame: 16,\n torsoRotation: { x: 0.2, y: -0.8, z: -0.9 }, // Opposite shoulder roll\n centerOfMassHeight: 0.4,\n description: {\n korean: \"우측 어깨 구르기\",\n english: \"Right shoulder roll\",\n },\n },\n {\n frame: 20,\n torsoRotation: { x: 0.3, y: -1.2, z: -1.3 }, // Opposite hip impact\n centerOfMassHeight: 0.2,\n description: {\n korean: \"우측 엉덩이 충격\",\n english: \"Right hip impact\",\n },\n },\n {\n frame: 26,\n torsoRotation: { x: 0, y: -1.57, z: -1.57 }, // Right side position (-90° roll)\n centerOfMassHeight: 0.05,\n description: {\n korean: \"우측 정지\",\n english: \"Right side sprawl\",\n },\n },\n] as const;\n\n/**\n * Get keyframes for a specific fall type\n *\n * @param fallType - Type of fall animation\n * @returns Array of keyframes for that fall type\n *\n * @korean 낙법키프레임가져오기\n */\nexport function getFallKeyframes(fallType: FallType): readonly FallKeyframe[] {\n switch (fallType) {\n case \"forward\":\n return FALL_FORWARD_KEYFRAMES;\n case \"backward\":\n return FALL_BACKWARD_KEYFRAMES;\n case \"side_left\":\n return FALL_SIDE_KEYFRAMES;\n case \"side_right\":\n return FALL_SIDE_RIGHT_KEYFRAMES;\n default:\n return FALL_BACKWARD_KEYFRAMES;\n }\n}\n\n/**\n * Get impact frame number for fall type\n *\n * @param fallType - Type of fall animation\n * @returns Frame number when ground impact occurs\n *\n * @korean 충격프레임가져오기\n */\nexport function getImpactFrame(fallType: FallType): number {\n return FALL_IMPACT_FRAMES[fallType];\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAiEA,SAAgB,uBACd,aACA,cACA,eAAuC,OAC7B;CAEV,IAAI,gBAAgB,cAAc;CAGlC,OAAO,gBAAgB,KAAK,IAAI,iBAAiB,IAAI,KAAK;CAC1D,OAAO,gBAAgB,CAAC,KAAK,IAAI,iBAAiB,IAAI,KAAK;CAE3D,MAAM,WAAW,KAAK,IAAI,cAAc;CAGxC,IAAI,iBAAiB,OAAO;EAO1B,IAAI,KAAK,IAAI,cAAc,GAAG,KAE5B,OAAO;EAET,IAAI,gBAAgB,GAClB,OAAO;OAEP,OAAO;;CAKX,IAAI,iBAAiB,UAAU,WAAW,KAAK,KAAK,GAClD,OAAO;CAKT,IAAI,WAAW,KAAK,KAAK,GACvB,OAAO;CAIT,IAAI,WAAY,IAAI,KAAK,KAAM,GAC7B,OAAO;CAIT,IAAI,gBAAgB,GAClB,OAAO;MAEP,OAAO;;;;;;;;;;;;;;;;;;;;;;;;AA0BX,SAAgB,wBACd,QACA,cAAwB,YACd;CAYV,OAAO;GAVJ,cAAc,OAAO;GACrB,cAAc,MAAM;GACpB,cAAc,KAAK;GACnB,cAAc,MAAM;GACpB,cAAc,MAAM;GACpB,cAAc,MAAM;GACpB,cAAc,MAAM;GACpB,cAAc,MAAM;EAGhB,CAAe,WAAW"}
|
|
1
|
+
{"version":3,"file":"FallAnimations.js","names":[],"sources":["../../../../src/systems/animation/systems/FallAnimations.ts"],"sourcesContent":["/**\n * Fall Animation System for Black Trigram\n *\n * Implements realistic fall down animations for knockdowns, leg sweeps,\n * and loss of consciousness events. Based on Korean martial arts falling\n * techniques (낙법 - Nakbeop).\n *\n * @module systems/animation/FallAnimations\n * @category Animation\n * @korean 낙법애니메이션\n */\n\nimport { TrigramStance } from \"@/types/common\";\nimport type { FallType } from \"../core/types\";\n\n/**\n * Fall animation impact frame numbers\n *\n * Defines which frame in each fall animation represents ground impact.\n * Used to trigger camera shake, audio, and particle effects.\n *\n * @korean 낙법충격프레임\n */\nexport const FALL_IMPACT_FRAMES: Record<FallType, number> = {\n forward: 18, // Frame 18 of 24 - hands hit ground\n backward: 22, // Frame 22 of 30 - back impacts\n side_left: 20, // Frame 20 of 27 - shoulder/side impacts\n side_right: 20, // Frame 20 of 27 - shoulder/side impacts\n};\n\n/**\n * Determines fall direction from attack vector and player facing\n *\n * Calculates which direction the player should fall based on:\n * - Attack vector (direction of incoming force)\n * - Player facing direction (from stance)\n * - Attack impact point (high/low)\n *\n * Korean terminology:\n * - 전방낙법 (Jeonbang Nakbeop): Forward fall from rear attacks\n * - 후방낙법 (Hubang Nakbeop): Backward fall from frontal attacks\n * - 측방낙법 (Cheukbang Nakbeop): Side fall from lateral attacks\n *\n * @param attackAngle - Angle of attack in radians (0 = from front)\n * @param playerFacing - Player facing angle in radians\n * @param attackHeight - Attack height: 'high', 'mid', or 'low'\n * @returns Fall type to use for animation\n *\n * @example\n * ```typescript\n * // Attack from behind while facing forward\n * const fallType = determineFallDirection(Math.PI, 0, 'mid');\n * // Returns: 'forward' (pushed forward)\n *\n * // Attack from front while facing forward\n * const fallType = determineFallDirection(0, 0, 'mid');\n * // Returns: 'backward' (pushed backward)\n *\n * // Attack from left side\n * const fallType = determineFallDirection(-Math.PI/2, 0, 'mid');\n * // Returns: 'side_left'\n * ```\n *\n * @korean 낙법방향결정\n */\nexport function determineFallDirection(\n attackAngle: number,\n playerFacing: number,\n attackHeight: \"high\" | \"mid\" | \"low\" = \"mid\",\n): FallType {\n // Calculate relative attack angle (attack direction relative to player facing)\n let relativeAngle = attackAngle - playerFacing;\n\n // Normalize to -π to π range\n while (relativeAngle > Math.PI) relativeAngle -= 2 * Math.PI;\n while (relativeAngle < -Math.PI) relativeAngle += 2 * Math.PI;\n\n const absAngle = Math.abs(relativeAngle);\n\n // Leg sweeps always cause side falls (more realistic for sweeps)\n if (attackHeight === \"low\") {\n // Determine which side based on angle\n // Use a small threshold to avoid edge case at exactly 0\n // Threshold represents the angle within which we consider the sweep \"frontal\"\n // 0.01 radians ≈ 0.57 degrees - small enough to only catch near-perfect frontal sweeps\n // while still allowing accurate left/right determination for most angles\n const SWEEP_ANGLE_THRESHOLD = 0.01;\n if (Math.abs(relativeAngle) < SWEEP_ANGLE_THRESHOLD) {\n // For perfectly frontal sweeps, default to right side fall\n return \"side_right\";\n }\n if (relativeAngle < 0) {\n return \"side_left\";\n } else {\n return \"side_right\";\n }\n }\n\n // High attacks to head often cause backward falls\n if (attackHeight === \"high\" && absAngle < Math.PI / 3) {\n return \"backward\";\n }\n\n // Determine fall direction based on attack angle\n // Front quadrant (±45°): Backward fall\n if (absAngle < Math.PI / 4) {\n return \"backward\";\n }\n\n // Rear quadrant (±45° from back): Forward fall\n if (absAngle > (3 * Math.PI) / 4) {\n return \"forward\";\n }\n\n // Side quadrants: Side falls\n if (relativeAngle < 0) {\n return \"side_left\";\n } else {\n return \"side_right\";\n }\n}\n\n/**\n * Determines fall direction from current trigram stance\n *\n * Some stances have inherent instability in certain directions.\n * This function returns likely fall directions when balance is lost.\n *\n * Korean stances and fall tendencies:\n * - 건 (Heaven): Forward bias - aggressive stance\n * - 태 (Lake): Backward bias - fluid retreating\n * - 리 (Fire): Forward bias - aggressive advance\n * - 진 (Thunder): Backward bias - explosive preparation\n * - 손 (Wind): Side bias - lateral movement\n * - 감 (Water): Backward bias - defensive flow\n * - 간 (Mountain): Backward bias - solid defense\n * - 곤 (Earth): Forward bias - grounding takedowns\n *\n * @param stance - Current trigram stance\n * @param defaultFall - Default fall type if stance doesn't suggest direction\n * @returns Likely fall direction for the stance\n *\n * @korean 자세낙법방향\n */\nexport function determineFallFromStance(\n stance: TrigramStance,\n defaultFall: FallType = \"backward\",\n): FallType {\n const stanceFallBias: Record<TrigramStance, FallType> = {\n [TrigramStance.GEON]: \"forward\", // Heaven - aggressive forward\n [TrigramStance.TAE]: \"backward\", // Lake - fluid retreat\n [TrigramStance.LI]: \"forward\", // Fire - aggressive strike\n [TrigramStance.JIN]: \"backward\", // Thunder - explosive back\n [TrigramStance.SON]: \"side_left\", // Wind - lateral pressure\n [TrigramStance.GAM]: \"backward\", // Water - flowing back\n [TrigramStance.GAN]: \"backward\", // Mountain - defensive back\n [TrigramStance.GON]: \"forward\", // Earth - forward throws\n };\n\n return stanceFallBias[stance] ?? defaultFall;\n}\n\n/**\n * Fall animation keyframe data structure\n *\n * Defines key poses during fall animations for skeletal rendering.\n * Each keyframe specifies body positions at critical moments.\n *\n * @korean 낙법키프레임\n */\nexport interface FallKeyframe {\n /** Frame number (0-indexed) */\n readonly frame: number;\n\n /** Torso rotation (radians) */\n readonly torsoRotation: { x: number; y: number; z: number };\n\n /** Center of mass vertical position (0-1, 1=standing, 0=ground) */\n readonly centerOfMassHeight: number;\n\n /** Description of this keyframe */\n readonly description: {\n readonly korean: string;\n readonly english: string;\n };\n}\n\n/**\n * Forward fall keyframes (전방낙법)\n *\n * 24 frames (400ms) sequence:\n * - Frames 0-8: Forward stumble, losing balance\n * - Frames 9-15: Knee collapse, forward momentum\n * - Frames 16-20: Hands extend to brace fall\n * - Frames 21-23: Impact and settle face-down\n *\n * @korean 전방낙법키프레임\n */\nexport const FALL_FORWARD_KEYFRAMES: readonly FallKeyframe[] = [\n {\n frame: 0,\n torsoRotation: { x: 0, y: 0, z: 0 },\n centerOfMassHeight: 0.9,\n description: {\n korean: \"초기 자세\",\n english: \"Initial stance\",\n },\n },\n {\n frame: 8,\n torsoRotation: { x: 0.3, y: 0, z: 0 }, // Leaning forward\n centerOfMassHeight: 0.75,\n description: {\n korean: \"전방으로 비틀거림\",\n english: \"Forward stumble\",\n },\n },\n {\n frame: 15,\n torsoRotation: { x: 0.7, y: 0, z: 0 }, // Falling forward\n centerOfMassHeight: 0.4,\n description: {\n korean: \"무릎 붕괴\",\n english: \"Knee collapse\",\n },\n },\n {\n frame: 18,\n torsoRotation: { x: 1.2, y: 0, z: 0 }, // Hands extending\n centerOfMassHeight: 0.15,\n description: {\n korean: \"손으로 지면 충격 완화\",\n english: \"Hands brace impact\",\n },\n },\n {\n frame: 23,\n torsoRotation: { x: 1.57, y: 0, z: 0 }, // Face down (90° forward)\n centerOfMassHeight: 0.05,\n description: {\n korean: \"엎드려 정지\",\n english: \"Face-down prone\",\n },\n },\n] as const;\n\n/**\n * Backward fall keyframes (후방낙법)\n *\n * 30 frames (500ms) sequence:\n * - Frames 0-10: Backward stumble, balance loss\n * - Frames 11-18: Sitting motion begins\n * - Frames 19-25: Back impact preparation\n * - Frames 26-29: Full supine position\n *\n * @korean 후방낙법키프레임\n */\nexport const FALL_BACKWARD_KEYFRAMES: readonly FallKeyframe[] = [\n {\n frame: 0,\n torsoRotation: { x: 0, y: 0, z: 0 },\n centerOfMassHeight: 0.9,\n description: {\n korean: \"초기 자세\",\n english: \"Initial stance\",\n },\n },\n {\n frame: 10,\n torsoRotation: { x: -0.2, y: 0, z: 0 }, // Leaning back\n centerOfMassHeight: 0.8,\n description: {\n korean: \"후방으로 비틀거림\",\n english: \"Backward stumble\",\n },\n },\n {\n frame: 18,\n torsoRotation: { x: -0.6, y: 0, z: 0 }, // Sitting motion\n centerOfMassHeight: 0.45,\n description: {\n korean: \"앉는 동작\",\n english: \"Sitting motion\",\n },\n },\n {\n frame: 22,\n torsoRotation: { x: -1.2, y: 0, z: 0 }, // Back impact\n centerOfMassHeight: 0.2,\n description: {\n korean: \"등 충격\",\n english: \"Back impact\",\n },\n },\n {\n frame: 29,\n torsoRotation: { x: -1.57, y: 0, z: 0 }, // Face up (90° back)\n centerOfMassHeight: 0.05,\n description: {\n korean: \"누워 정지\",\n english: \"Supine position\",\n },\n },\n] as const;\n\n/**\n * Side fall keyframes (측방낙법)\n *\n * 27 frames (450ms) sequence:\n * - Frames 0-9: Side rotation begins\n * - Frames 10-16: Shoulder roll motion\n * - Frames 17-22: Hip impact\n * - Frames 23-26: Side sprawl position\n *\n * @korean 측방낙법키프레임\n */\nexport const FALL_SIDE_KEYFRAMES: readonly FallKeyframe[] = [\n {\n frame: 0,\n torsoRotation: { x: 0, y: 0, z: 0 },\n centerOfMassHeight: 0.9,\n description: {\n korean: \"초기 자세\",\n english: \"Initial stance\",\n },\n },\n {\n frame: 9,\n torsoRotation: { x: 0, y: 0.3, z: 0.4 }, // Side rotation\n centerOfMassHeight: 0.7,\n description: {\n korean: \"측면 회전 시작\",\n english: \"Side rotation begins\",\n },\n },\n {\n frame: 16,\n torsoRotation: { x: 0.2, y: 0.8, z: 0.9 }, // Shoulder roll\n centerOfMassHeight: 0.4,\n description: {\n korean: \"어깨 구르기\",\n english: \"Shoulder roll\",\n },\n },\n {\n frame: 20,\n torsoRotation: { x: 0.3, y: 1.2, z: 1.3 }, // Hip impact\n centerOfMassHeight: 0.2,\n description: {\n korean: \"엉덩이 충격\",\n english: \"Hip impact\",\n },\n },\n {\n frame: 26,\n torsoRotation: { x: 0, y: 1.57, z: 1.57 }, // Side position (90° roll)\n centerOfMassHeight: 0.05,\n description: {\n korean: \"측면 정지\",\n english: \"Side sprawl\",\n },\n },\n] as const;\n\n/**\n * Side fall keyframes - RIGHT side (우측 낙법)\n *\n * Mirrored version of FALL_SIDE_KEYFRAMES with negated Y and Z rotations\n * so the character falls to the opposite side.\n *\n * @korean 우측낙법키프레임\n */\nexport const FALL_SIDE_RIGHT_KEYFRAMES: readonly FallKeyframe[] = [\n {\n frame: 0,\n torsoRotation: { x: 0, y: 0, z: 0 },\n centerOfMassHeight: 0.9,\n description: {\n korean: \"초기 자세\",\n english: \"Initial stance\",\n },\n },\n {\n frame: 9,\n torsoRotation: { x: 0, y: -0.3, z: -0.4 }, // Opposite side rotation\n centerOfMassHeight: 0.7,\n description: {\n korean: \"우측 회전 시작\",\n english: \"Right side rotation begins\",\n },\n },\n {\n frame: 16,\n torsoRotation: { x: 0.2, y: -0.8, z: -0.9 }, // Opposite shoulder roll\n centerOfMassHeight: 0.4,\n description: {\n korean: \"우측 어깨 구르기\",\n english: \"Right shoulder roll\",\n },\n },\n {\n frame: 20,\n torsoRotation: { x: 0.3, y: -1.2, z: -1.3 }, // Opposite hip impact\n centerOfMassHeight: 0.2,\n description: {\n korean: \"우측 엉덩이 충격\",\n english: \"Right hip impact\",\n },\n },\n {\n frame: 26,\n torsoRotation: { x: 0, y: -1.57, z: -1.57 }, // Right side position (-90° roll)\n centerOfMassHeight: 0.05,\n description: {\n korean: \"우측 정지\",\n english: \"Right side sprawl\",\n },\n },\n] as const;\n\n/**\n * Get keyframes for a specific fall type\n *\n * @param fallType - Type of fall animation\n * @returns Array of keyframes for that fall type\n *\n * @korean 낙법키프레임가져오기\n */\nexport function getFallKeyframes(fallType: FallType): readonly FallKeyframe[] {\n switch (fallType) {\n case \"forward\":\n return FALL_FORWARD_KEYFRAMES;\n case \"backward\":\n return FALL_BACKWARD_KEYFRAMES;\n case \"side_left\":\n return FALL_SIDE_KEYFRAMES;\n case \"side_right\":\n return FALL_SIDE_RIGHT_KEYFRAMES;\n default:\n return FALL_BACKWARD_KEYFRAMES;\n }\n}\n\n/**\n * Get impact frame number for fall type\n *\n * @param fallType - Type of fall animation\n * @returns Frame number when ground impact occurs\n *\n * @korean 충격프레임가져오기\n */\nexport function getImpactFrame(fallType: FallType): number {\n return FALL_IMPACT_FRAMES[fallType];\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAiEA,SAAgB,uBACd,aACA,cACA,eAAuC,OAC7B;CAEV,IAAI,gBAAgB,cAAc;CAGlC,OAAO,gBAAgB,KAAK,IAAI,iBAAiB,IAAI,KAAK;CAC1D,OAAO,gBAAgB,CAAC,KAAK,IAAI,iBAAiB,IAAI,KAAK;CAE3D,MAAM,WAAW,KAAK,IAAI,aAAa;CAGvC,IAAI,iBAAiB,OAAO;EAO1B,IAAI,KAAK,IAAI,aAAa,IAAI,KAE5B,OAAO;EAET,IAAI,gBAAgB,GAClB,OAAO;OAEP,OAAO;CAEX;CAGA,IAAI,iBAAiB,UAAU,WAAW,KAAK,KAAK,GAClD,OAAO;CAKT,IAAI,WAAW,KAAK,KAAK,GACvB,OAAO;CAIT,IAAI,WAAY,IAAI,KAAK,KAAM,GAC7B,OAAO;CAIT,IAAI,gBAAgB,GAClB,OAAO;MAEP,OAAO;AAEX;;;;;;;;;;;;;;;;;;;;;;;AAwBA,SAAgB,wBACd,QACA,cAAwB,YACd;CAYV,OAAO;GAVJ,cAAc,OAAO;GACrB,cAAc,MAAM;GACpB,cAAc,KAAK;GACnB,cAAc,MAAM;GACpB,cAAc,MAAM;GACpB,cAAc,MAAM;GACpB,cAAc,MAAM;GACpB,cAAc,MAAM;CAGhB,EAAe,WAAW;AACnC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"MuscleActivation.js","names":[],"sources":["../../../../src/systems/animation/systems/MuscleActivation.ts"],"sourcesContent":["/**\n * Muscle activation system for realistic combat physiology\n *\n * Maps Korean martial arts techniques to anatomically accurate muscle activation patterns.\n * Manages dynamic muscle tension, stamina effects, and smooth relaxation transitions.\n * Includes stance-specific leg muscle tension for isometric holds and weight-bearing.\n *\n * @module systems/animation/MuscleActivation\n * @category Combat Animation\n * @korean 근육활성화시스템\n */\n\nimport type { TrigramStance } from \"@/types/common\";\nimport type {\n MuscleActivationMap,\n MuscleActivationState,\n MuscleGroupName,\n MuscleSystemConfig,\n} from \"@/types/muscle\";\nimport { KOREAN_STANCE_BIOMECHANICS } from \"../builders/MartialArtsConstants\";\n\n/**\n * Type-safe mapping of TrigramStance to biomechanics keys\n * Defined at module level to avoid recreation at 60fps\n * @korean 자세생체역학매핑\n */\nconst STANCE_TO_BIOMECH_KEY = {\n geon: \"GEON_HEAVEN\",\n tae: \"TAE_LAKE\",\n li: \"LI_FIRE\",\n jin: \"JIN_THUNDER\",\n son: \"SON_WIND\",\n gam: \"GAM_WATER\",\n gan: \"GAN_MOUNTAIN\",\n gon: \"GON_EARTH\",\n} as const satisfies Record<\n TrigramStance,\n keyof typeof KOREAN_STANCE_BIOMECHANICS\n>;\n\n/**\n * Get leg muscle tension for a specific trigram stance\n *\n * Calculates realistic leg muscle activation based on stance biomechanics:\n * - Weight distribution (체중부하) - Which leg bears more load\n * - Knee flexion angles (무릎굽힘각도) - Deep stances require more tension\n * - Isometric contraction (등척성수축) - Holding positions activates muscles\n *\n * Based on authentic Korean martial arts stance mechanics:\n * - Deep stances (Jin 90°, Gon 80°) = High quad/calf tension\n * - Front-weighted (Geon 60/40) = Front leg emphasis\n * - Back-weighted (Tae 10/90, Gam 30/70) = Back leg emphasis\n *\n * @param stance - Trigram stance (e.g., TrigramStance.GEON)\n * @returns Map of leg muscle groups to tension levels (0-1)\n *\n * @example\n * ```typescript\n * const jinMuscles = getMuscleTensionForStance(TrigramStance.JIN);\n * // Returns: Map {\n * // \"QUAD_L\" => 0.90, // Deep 90° knee bend\n * // \"QUAD_R\" => 0.90, // Equal weight distribution\n * // \"CALF_L\" => 0.85, // Isometric hold\n * // \"CALF_R\" => 0.85,\n * // \"HAMSTRING_L\" => 0.45,\n * // \"HAMSTRING_R\" => 0.45,\n * // \"GLUTE_L\" => 0.40,\n * // \"GLUTE_R\" => 0.40\n * // }\n * ```\n *\n * @korean 자세다리근육긴장도\n */\nexport const getMuscleTensionForStance = (\n stance: TrigramStance,\n): MuscleActivationMap => {\n const activations = new Map<MuscleGroupName, number>();\n\n // Get biomechanical data for stance (type-safe lookup)\n const biomechKey = STANCE_TO_BIOMECH_KEY[stance];\n const biomech = KOREAN_STANCE_BIOMECHANICS[biomechKey];\n\n if (!biomech) {\n // This branch should never execute with proper typing, but kept for safety\n // Provide conservative defaults for all muscle groups to keep map consistent\n activations.set(\"QUAD_L\", 0.2);\n activations.set(\"QUAD_R\", 0.2);\n activations.set(\"CALF_L\", 0.1);\n activations.set(\"CALF_R\", 0.1);\n activations.set(\"HAMSTRING_L\", 0.1);\n activations.set(\"HAMSTRING_R\", 0.1);\n activations.set(\"GLUTE_L\", 0.1);\n activations.set(\"GLUTE_R\", 0.1);\n return activations;\n }\n\n // Calculate quadriceps tension based on knee flexion\n // Formula (base tension): baseTension = (180° - kneeAngle) / 110°\n // Range: 90° knee → 0.82 base tension, 180° knee → 0.0 base tension\n // This base value is then combined with weight distribution for final tension\n const frontQuadTensionFromBend = Math.max(\n 0,\n Math.min(1.0, (180 - biomech.frontKneeBend) / 110),\n );\n const backQuadTensionFromBend = Math.max(\n 0,\n Math.min(1.0, (180 - biomech.backKneeBend) / 110),\n );\n\n // Apply weight distribution to muscle tension\n // Front leg quadriceps (체중부하 + 등척성수축)\n // Base tension from bend + weight factor\n // Note: Even with 0% weight, deep knee flexion produces tension\n // (e.g., raised leg in crane stance requires quad engagement to hold position)\n const frontQuadTension = Math.min(\n 1.0,\n frontQuadTensionFromBend * 0.6 + biomech.weightDistribution.front * 0.4,\n );\n\n // Back leg quadriceps\n const backQuadTension = Math.min(\n 1.0,\n backQuadTensionFromBend * 0.6 + biomech.weightDistribution.back * 0.4,\n );\n\n // Set quadriceps tension (right = front, left = back in standard stance)\n activations.set(\"QUAD_R\", frontQuadTension);\n activations.set(\"QUAD_L\", backQuadTension);\n\n // Calculate hamstring tension (antagonist muscles, lower activation)\n // Hamstrings activate more in bent positions for stabilization\n const frontHamstringTension =\n frontQuadTensionFromBend * 0.5 * biomech.weightDistribution.front;\n const backHamstringTension =\n backQuadTensionFromBend * 0.5 * biomech.weightDistribution.back;\n\n activations.set(\"HAMSTRING_R\", Math.min(0.8, frontHamstringTension));\n activations.set(\"HAMSTRING_L\", Math.min(0.8, backHamstringTension));\n\n // Calculate calf tension for deep stances (isometric hold support)\n // Very deep stances (< 100° knee angle) require significant calf engagement\n const frontCalfTension =\n biomech.frontKneeBend < 100\n ? Math.min(0.9, 0.5 + ((100 - biomech.frontKneeBend) / 50) * 0.4) *\n biomech.weightDistribution.front\n : 0.25 * biomech.weightDistribution.front;\n\n const backCalfTension =\n biomech.backKneeBend < 100\n ? Math.min(0.9, 0.5 + ((100 - biomech.backKneeBend) / 50) * 0.4) *\n biomech.weightDistribution.back\n : 0.25 * biomech.weightDistribution.back;\n\n activations.set(\"CALF_R\", frontCalfTension);\n activations.set(\"CALF_L\", backCalfTension);\n\n // Calculate glute activation (hip extension support)\n // Glutes engage more in deep stances for posture maintenance\n const gluteBaseActivation = (1.0 - biomech.hipHeight) * 0.5;\n\n activations.set(\n \"GLUTE_R\",\n Math.min(\n 0.7,\n gluteBaseActivation +\n frontQuadTensionFromBend * 0.2 * biomech.weightDistribution.front,\n ),\n );\n activations.set(\n \"GLUTE_L\",\n Math.min(\n 0.7,\n gluteBaseActivation +\n backQuadTensionFromBend * 0.2 * biomech.weightDistribution.back,\n ),\n );\n\n return activations;\n};\n\n/**\n * Get muscle activation mapping for a specific Korean martial arts technique\n *\n * Maps technique names to muscle groups with tension levels (0-1 scale).\n * Based on authentic Korean martial arts (Taekwondo, Hapkido, Taekyon).\n *\n * @param technique - Technique name (e.g., \"jab\", \"cross\", \"front_kick\", \"block\")\n * @returns Map of muscle groups to target tension levels\n *\n * @example\n * ```typescript\n * const jabMuscles = getMuscleActivationForTechnique(\"jab\");\n * // Returns: Map {\n * // \"SHOULDER_R\" => 0.7,\n * // \"BICEP_R\" => 1.0,\n * // \"TRICEP_R\" => 0.8,\n * // \"CORE\" => 0.5\n * // }\n * ```\n *\n * @korean 기법근육활성화가져오기\n */\nexport const getMuscleActivationForTechnique = (\n technique: string,\n): MuscleActivationMap => {\n const activations = new Map<MuscleGroupName, number>();\n\n switch (technique.toLowerCase()) {\n // Punching techniques - 주먹 기술\n case \"jab\":\n case \"정권\": // Front jab\n activations.set(\"SHOULDER_R\", 0.8);\n activations.set(\"TRICEP_R\", 1.0); // Primary mover for arm extension\n activations.set(\"BICEP_R\", 0.3); // Antagonist - stabilization only\n activations.set(\"CORE\", 0.5);\n activations.set(\"PECTORALS\", 0.5); // Shoulder horizontal adduction\n break;\n\n case \"cross\":\n case \"역권\": // Reverse punch\n activations.set(\"SHOULDER_L\", 0.9);\n activations.set(\"TRICEP_L\", 1.0); // Primary mover for arm extension\n activations.set(\"BICEP_L\", 0.3); // Antagonist - stabilization only\n activations.set(\"CORE\", 0.8); // More core rotation\n activations.set(\"PECTORALS\", 0.7); // Chest drive for power\n activations.set(\"OBLIQUES\", 0.8); // Torso rotation for hip transfer\n break;\n\n case \"hook\":\n case \"갈고리주먹\": // Hook punch\n activations.set(\"SHOULDER_R\", 1.0); // Primary mover for horizontal arc\n activations.set(\"BICEP_R\", 0.8); // Maintains elbow angle during arc\n activations.set(\"FOREARM_R\", 0.8);\n activations.set(\"CORE\", 0.8); // Drives rotational power\n activations.set(\"OBLIQUES\", 0.9); // Primary torso rotation driver\n break;\n\n case \"uppercut\":\n case \"올려치기\": // Uppercut\n activations.set(\"SHOULDER_R\", 0.95); // Drives upward arc\n activations.set(\"BICEP_R\", 0.9); // Maintains elbow angle for upward drive\n activations.set(\"TRICEP_R\", 0.5); // Partial extension\n activations.set(\"CORE\", 0.9);\n activations.set(\"ABS\", 0.8);\n activations.set(\"QUAD_R\", 0.7); // Lower body drive (legs push up)\n activations.set(\"GLUTE_R\", 0.6); // Hip extension for power\n break;\n\n // Kicking techniques - 발차기 기술\n case \"front_kick\":\n case \"앞차기\": // Mae Chagi\n activations.set(\"QUAD_R\", 1.0); // Kicking leg fully flexed\n activations.set(\"GLUTE_R\", 0.9);\n activations.set(\"CALF_R\", 0.7);\n activations.set(\"CORE\", 0.6); // Balance\n activations.set(\"ABS\", 0.7);\n activations.set(\"QUAD_L\", 0.4); // Support leg tensed\n activations.set(\"CALF_L\", 0.5);\n break;\n\n case \"roundhouse_kick\":\n case \"dollyeochagi\":\n case \"돌려차기\": // Dollyeo Chagi\n activations.set(\"QUAD_R\", 1.0);\n activations.set(\"HAMSTRING_R\", 0.8);\n activations.set(\"GLUTE_R\", 0.95);\n activations.set(\"CORE\", 0.8);\n activations.set(\"OBLIQUES\", 0.9); // Hip rotation\n activations.set(\"QUAD_L\", 0.5);\n break;\n\n case \"side_kick\":\n case \"옆차기\": // Yeop Chagi\n activations.set(\"QUAD_R\", 1.0);\n activations.set(\"GLUTE_R\", 1.0);\n activations.set(\"HAMSTRING_R\", 0.85);\n activations.set(\"CALF_R\", 0.8);\n activations.set(\"CORE\", 0.7);\n activations.set(\"OBLIQUES\", 0.8);\n break;\n\n case \"back_kick\":\n case \"뒤차기\": // Dwi Chagi\n activations.set(\"GLUTE_R\", 1.0);\n activations.set(\"HAMSTRING_R\", 0.95);\n activations.set(\"QUAD_R\", 0.8);\n activations.set(\"CORE\", 0.9);\n activations.set(\"ABS\", 0.8);\n break;\n\n case \"axe_kick\":\n case \"내려차기\": // Naeryeo Chagi\n activations.set(\"QUAD_R\", 0.9);\n activations.set(\"HAMSTRING_R\", 1.0); // Maximum hamstring flex\n activations.set(\"GLUTE_R\", 0.85);\n activations.set(\"CORE\", 0.8);\n activations.set(\"ABS\", 0.9);\n break;\n\n // Defensive techniques - 방어 기술\n case \"block\":\n case \"makgi\":\n case \"막기\": // Block\n activations.set(\"SHOULDER_L\", 0.9);\n activations.set(\"SHOULDER_R\", 0.9);\n activations.set(\"BICEP_L\", 0.7);\n activations.set(\"BICEP_R\", 0.7);\n activations.set(\"FOREARM_L\", 0.8);\n activations.set(\"FOREARM_R\", 0.8);\n activations.set(\"CORE\", 0.8); // Brace for impact\n activations.set(\"ABS\", 0.7);\n break;\n\n case \"parry\":\n case \"흘려막기\": // Deflecting block\n activations.set(\"SHOULDER_R\", 0.6);\n activations.set(\"FOREARM_R\", 0.7);\n activations.set(\"CORE\", 0.5);\n break;\n\n // Elbow techniques - 팔꿈치 기술\n case \"elbow_strike\":\n case \"팔꿈치치기\": // Palkumchi Chigi\n activations.set(\"SHOULDER_R\", 1.0);\n activations.set(\"TRICEP_R\", 0.95);\n activations.set(\"FOREARM_R\", 0.9);\n activations.set(\"CORE\", 0.85);\n activations.set(\"OBLIQUES\", 0.8);\n break;\n\n // Knee techniques - 무릎 기술\n case \"knee_strike\":\n case \"무릎치기\": // Mureup Chigi\n activations.set(\"QUAD_R\", 1.0);\n activations.set(\"HAMSTRING_R\", 0.9);\n activations.set(\"GLUTE_R\", 0.85);\n activations.set(\"CORE\", 0.9);\n activations.set(\"ABS\", 0.85);\n break;\n\n // Grappling techniques - 잡기 기술 (Hapkido)\n case \"grab\":\n case \"잡기\":\n activations.set(\"FOREARM_L\", 0.9);\n activations.set(\"FOREARM_R\", 0.9);\n activations.set(\"BICEP_L\", 0.7);\n activations.set(\"BICEP_R\", 0.7);\n activations.set(\"CORE\", 0.6);\n break;\n\n // Stance changes - 자세 전환\n case \"stance_change\":\n case \"자세전환\":\n activations.set(\"CORE\", 0.7);\n activations.set(\"ABS\", 0.6);\n activations.set(\"QUAD_L\", 0.5);\n activations.set(\"QUAD_R\", 0.5);\n activations.set(\"CALF_L\", 0.5);\n activations.set(\"CALF_R\", 0.5);\n break;\n\n // Default - minimal activation\n default:\n activations.set(\"CORE\", 0.3);\n break;\n }\n\n return activations;\n};\n\n/**\n * Muscle activation manager for real-time combat physiology\n *\n * Manages 60fps muscle tension updates, stamina effects, and smooth transitions.\n * Optimized for performance with ref-based updates to avoid React re-renders.\n *\n * @korean 근육활성화관리자\n */\nexport class MuscleActivationManager {\n private activations: Map<MuscleGroupName, MuscleActivationState>;\n private config: MuscleSystemConfig;\n private scratchMap: Map<string, number>; // Reusable map for state sync to avoid allocations\n\n /**\n * Create a new muscle activation manager\n *\n * @param config - Optional configuration (uses defaults if not provided)\n * @korean 생성자\n */\n constructor(config: Partial<MuscleSystemConfig> = {}) {\n this.config = {\n maxFrameTime: 3,\n muscleCount: 20,\n useInstancing: false,\n relaxationDelay: 0.3,\n exhaustionThreshold: 20,\n shakeFrequency: 20,\n shakeAmplitude: 0.02,\n activationSpeed: 5.0,\n relaxationSpeed: 3.0,\n shakingTensionThreshold: 0.3,\n ...config,\n };\n\n // Initialize all muscle groups to relaxed state\n this.activations = new Map();\n // Initialize reusable scratch map for state sync\n this.scratchMap = new Map();\n const allMuscles: MuscleGroupName[] = [\n \"SHOULDER_L\",\n \"SHOULDER_R\",\n \"BICEP_L\",\n \"BICEP_R\",\n \"TRICEP_L\",\n \"TRICEP_R\",\n \"FOREARM_L\",\n \"FOREARM_R\",\n \"PECTORALS\",\n \"CORE\",\n \"ABS\",\n \"OBLIQUES\",\n \"QUAD_L\",\n \"QUAD_R\",\n \"HAMSTRING_L\",\n \"HAMSTRING_R\",\n \"CALF_L\",\n \"CALF_R\",\n \"GLUTE_L\",\n \"GLUTE_R\",\n ];\n\n allMuscles.forEach((muscle) => {\n this.activations.set(muscle, {\n muscleGroup: muscle,\n tension: 0,\n targetTension: 0,\n isShaking: false,\n });\n });\n }\n\n /**\n * Update muscle activations for a technique at 60fps\n *\n * @param technique - Technique name\n * @param stamina - Current stamina (0-100)\n * @param delta - Time since last frame in seconds\n *\n * @korean 업데이트\n */\n update(technique: string, stamina: number, delta: number): void {\n const targetActivations = getMuscleActivationForTechnique(technique);\n const isExhausted = stamina < this.config.exhaustionThreshold;\n\n // Update each muscle group\n this.activations.forEach((state, muscleGroup) => {\n const targetTension = targetActivations.get(muscleGroup) ?? 0;\n\n // Adjust tension based on stamina (exhaustion reduces muscle effectiveness)\n const staminaFactor = Math.max(0.3, stamina / 100); // Minimum 30% even when exhausted\n const adjustedTarget = targetTension * staminaFactor;\n\n // Smooth transition to target tension using lerp\n const lerp = (start: number, end: number, t: number) =>\n start + (end - start) * t;\n const newTension = lerp(\n state.tension,\n adjustedTarget,\n this.config.activationSpeed * delta,\n );\n\n // Update shaking state for exhaustion\n const isShaking =\n isExhausted && state.tension > this.config.shakingTensionThreshold;\n\n // Update state directly for performance\n state.tension = newTension;\n state.targetTension = adjustedTarget;\n state.isShaking = isShaking;\n });\n }\n\n /**\n * Gradually relax all muscles to idle state\n *\n * Used after technique completion with configurable delay.\n *\n * @param delta - Time since last frame in seconds\n *\n * @korean 근육이완\n */\n relaxAllMuscles(delta: number): void {\n this.activations.forEach((state) => {\n // Use configured relaxation speed (slower than activation for realism)\n const lerp = (start: number, end: number, t: number) =>\n start + (end - start) * t;\n const newTension = lerp(\n state.tension,\n 0,\n this.config.relaxationSpeed * delta,\n );\n\n state.tension = newTension;\n state.targetTension = 0;\n state.isShaking = false;\n });\n }\n\n /**\n * Get current tension for a specific muscle group\n *\n * @param muscleGroup - Muscle group name\n * @returns Current tension (0-1) or 0 if not found\n *\n * @korean 긴장도가져오기\n */\n getTension(muscleGroup: MuscleGroupName): number {\n return this.activations.get(muscleGroup)?.tension ?? 0;\n }\n\n /**\n * Get shaking state for a specific muscle group\n *\n * @param muscleGroup - Muscle group name\n * @returns Whether muscle is shaking\n *\n * @korean 흔들림상태가져오기\n */\n isShaking(muscleGroup: MuscleGroupName): boolean {\n return this.activations.get(muscleGroup)?.isShaking ?? false;\n }\n\n /**\n * Get all current muscle activations\n *\n * @returns Map of muscle groups to current states\n *\n * @korean 모든활성화가져오기\n */\n getAllActivations(): ReadonlyMap<MuscleGroupName, MuscleActivationState> {\n return this.activations;\n }\n\n /**\n * Get reusable scratch map with current tension values for state sync\n *\n * Populates and returns a reusable Map to avoid allocations during state sync.\n * This map is cleared and repopulated on each call.\n *\n * @returns Reusable Map with current tension values\n *\n * @korean 상태동기화맵가져오기\n */\n getScratchMapForSync(): Map<string, number> {\n this.scratchMap.clear();\n this.activations.forEach((state, name) => {\n this.scratchMap.set(name, state.tension);\n });\n return this.scratchMap;\n }\n\n /**\n * Reset all muscles to relaxed state immediately\n *\n * @korean 즉시이완\n */\n reset(): void {\n this.activations.forEach((state) => {\n state.tension = 0;\n state.targetTension = 0;\n state.isShaking = false;\n });\n this.scratchMap.clear();\n }\n\n /**\n * Dispose of the muscle activation system\n *\n * Clears all internal state and references to prevent memory leaks.\n * Should be called when the system is no longer needed (e.g., component unmount).\n *\n * @korean 근육시스템해제\n */\n dispose(): void {\n this.activations.clear();\n this.scratchMap.clear();\n }\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"MuscleActivation.js","names":[],"sources":["../../../../src/systems/animation/systems/MuscleActivation.ts"],"sourcesContent":["/**\n * Muscle activation system for realistic combat physiology\n *\n * Maps Korean martial arts techniques to anatomically accurate muscle activation patterns.\n * Manages dynamic muscle tension, stamina effects, and smooth relaxation transitions.\n * Includes stance-specific leg muscle tension for isometric holds and weight-bearing.\n *\n * @module systems/animation/MuscleActivation\n * @category Combat Animation\n * @korean 근육활성화시스템\n */\n\nimport type { TrigramStance } from \"@/types/common\";\nimport type {\n MuscleActivationMap,\n MuscleActivationState,\n MuscleGroupName,\n MuscleSystemConfig,\n} from \"@/types/muscle\";\nimport { KOREAN_STANCE_BIOMECHANICS } from \"../builders/MartialArtsConstants\";\n\n/**\n * Type-safe mapping of TrigramStance to biomechanics keys\n * Defined at module level to avoid recreation at 60fps\n * @korean 자세생체역학매핑\n */\nconst STANCE_TO_BIOMECH_KEY = {\n geon: \"GEON_HEAVEN\",\n tae: \"TAE_LAKE\",\n li: \"LI_FIRE\",\n jin: \"JIN_THUNDER\",\n son: \"SON_WIND\",\n gam: \"GAM_WATER\",\n gan: \"GAN_MOUNTAIN\",\n gon: \"GON_EARTH\",\n} as const satisfies Record<\n TrigramStance,\n keyof typeof KOREAN_STANCE_BIOMECHANICS\n>;\n\n/**\n * Get leg muscle tension for a specific trigram stance\n *\n * Calculates realistic leg muscle activation based on stance biomechanics:\n * - Weight distribution (체중부하) - Which leg bears more load\n * - Knee flexion angles (무릎굽힘각도) - Deep stances require more tension\n * - Isometric contraction (등척성수축) - Holding positions activates muscles\n *\n * Based on authentic Korean martial arts stance mechanics:\n * - Deep stances (Jin 90°, Gon 80°) = High quad/calf tension\n * - Front-weighted (Geon 60/40) = Front leg emphasis\n * - Back-weighted (Tae 10/90, Gam 30/70) = Back leg emphasis\n *\n * @param stance - Trigram stance (e.g., TrigramStance.GEON)\n * @returns Map of leg muscle groups to tension levels (0-1)\n *\n * @example\n * ```typescript\n * const jinMuscles = getMuscleTensionForStance(TrigramStance.JIN);\n * // Returns: Map {\n * // \"QUAD_L\" => 0.90, // Deep 90° knee bend\n * // \"QUAD_R\" => 0.90, // Equal weight distribution\n * // \"CALF_L\" => 0.85, // Isometric hold\n * // \"CALF_R\" => 0.85,\n * // \"HAMSTRING_L\" => 0.45,\n * // \"HAMSTRING_R\" => 0.45,\n * // \"GLUTE_L\" => 0.40,\n * // \"GLUTE_R\" => 0.40\n * // }\n * ```\n *\n * @korean 자세다리근육긴장도\n */\nexport const getMuscleTensionForStance = (\n stance: TrigramStance,\n): MuscleActivationMap => {\n const activations = new Map<MuscleGroupName, number>();\n\n // Get biomechanical data for stance (type-safe lookup)\n const biomechKey = STANCE_TO_BIOMECH_KEY[stance];\n const biomech = KOREAN_STANCE_BIOMECHANICS[biomechKey];\n\n if (!biomech) {\n // This branch should never execute with proper typing, but kept for safety\n // Provide conservative defaults for all muscle groups to keep map consistent\n activations.set(\"QUAD_L\", 0.2);\n activations.set(\"QUAD_R\", 0.2);\n activations.set(\"CALF_L\", 0.1);\n activations.set(\"CALF_R\", 0.1);\n activations.set(\"HAMSTRING_L\", 0.1);\n activations.set(\"HAMSTRING_R\", 0.1);\n activations.set(\"GLUTE_L\", 0.1);\n activations.set(\"GLUTE_R\", 0.1);\n return activations;\n }\n\n // Calculate quadriceps tension based on knee flexion\n // Formula (base tension): baseTension = (180° - kneeAngle) / 110°\n // Range: 90° knee → 0.82 base tension, 180° knee → 0.0 base tension\n // This base value is then combined with weight distribution for final tension\n const frontQuadTensionFromBend = Math.max(\n 0,\n Math.min(1.0, (180 - biomech.frontKneeBend) / 110),\n );\n const backQuadTensionFromBend = Math.max(\n 0,\n Math.min(1.0, (180 - biomech.backKneeBend) / 110),\n );\n\n // Apply weight distribution to muscle tension\n // Front leg quadriceps (체중부하 + 등척성수축)\n // Base tension from bend + weight factor\n // Note: Even with 0% weight, deep knee flexion produces tension\n // (e.g., raised leg in crane stance requires quad engagement to hold position)\n const frontQuadTension = Math.min(\n 1.0,\n frontQuadTensionFromBend * 0.6 + biomech.weightDistribution.front * 0.4,\n );\n\n // Back leg quadriceps\n const backQuadTension = Math.min(\n 1.0,\n backQuadTensionFromBend * 0.6 + biomech.weightDistribution.back * 0.4,\n );\n\n // Set quadriceps tension (right = front, left = back in standard stance)\n activations.set(\"QUAD_R\", frontQuadTension);\n activations.set(\"QUAD_L\", backQuadTension);\n\n // Calculate hamstring tension (antagonist muscles, lower activation)\n // Hamstrings activate more in bent positions for stabilization\n const frontHamstringTension =\n frontQuadTensionFromBend * 0.5 * biomech.weightDistribution.front;\n const backHamstringTension =\n backQuadTensionFromBend * 0.5 * biomech.weightDistribution.back;\n\n activations.set(\"HAMSTRING_R\", Math.min(0.8, frontHamstringTension));\n activations.set(\"HAMSTRING_L\", Math.min(0.8, backHamstringTension));\n\n // Calculate calf tension for deep stances (isometric hold support)\n // Very deep stances (< 100° knee angle) require significant calf engagement\n const frontCalfTension =\n biomech.frontKneeBend < 100\n ? Math.min(0.9, 0.5 + ((100 - biomech.frontKneeBend) / 50) * 0.4) *\n biomech.weightDistribution.front\n : 0.25 * biomech.weightDistribution.front;\n\n const backCalfTension =\n biomech.backKneeBend < 100\n ? Math.min(0.9, 0.5 + ((100 - biomech.backKneeBend) / 50) * 0.4) *\n biomech.weightDistribution.back\n : 0.25 * biomech.weightDistribution.back;\n\n activations.set(\"CALF_R\", frontCalfTension);\n activations.set(\"CALF_L\", backCalfTension);\n\n // Calculate glute activation (hip extension support)\n // Glutes engage more in deep stances for posture maintenance\n const gluteBaseActivation = (1.0 - biomech.hipHeight) * 0.5;\n\n activations.set(\n \"GLUTE_R\",\n Math.min(\n 0.7,\n gluteBaseActivation +\n frontQuadTensionFromBend * 0.2 * biomech.weightDistribution.front,\n ),\n );\n activations.set(\n \"GLUTE_L\",\n Math.min(\n 0.7,\n gluteBaseActivation +\n backQuadTensionFromBend * 0.2 * biomech.weightDistribution.back,\n ),\n );\n\n return activations;\n};\n\n/**\n * Get muscle activation mapping for a specific Korean martial arts technique\n *\n * Maps technique names to muscle groups with tension levels (0-1 scale).\n * Based on authentic Korean martial arts (Taekwondo, Hapkido, Taekyon).\n *\n * @param technique - Technique name (e.g., \"jab\", \"cross\", \"front_kick\", \"block\")\n * @returns Map of muscle groups to target tension levels\n *\n * @example\n * ```typescript\n * const jabMuscles = getMuscleActivationForTechnique(\"jab\");\n * // Returns: Map {\n * // \"SHOULDER_R\" => 0.7,\n * // \"BICEP_R\" => 1.0,\n * // \"TRICEP_R\" => 0.8,\n * // \"CORE\" => 0.5\n * // }\n * ```\n *\n * @korean 기법근육활성화가져오기\n */\nexport const getMuscleActivationForTechnique = (\n technique: string,\n): MuscleActivationMap => {\n const activations = new Map<MuscleGroupName, number>();\n\n switch (technique.toLowerCase()) {\n // Punching techniques - 주먹 기술\n case \"jab\":\n case \"정권\": // Front jab\n activations.set(\"SHOULDER_R\", 0.8);\n activations.set(\"TRICEP_R\", 1.0); // Primary mover for arm extension\n activations.set(\"BICEP_R\", 0.3); // Antagonist - stabilization only\n activations.set(\"CORE\", 0.5);\n activations.set(\"PECTORALS\", 0.5); // Shoulder horizontal adduction\n break;\n\n case \"cross\":\n case \"역권\": // Reverse punch\n activations.set(\"SHOULDER_L\", 0.9);\n activations.set(\"TRICEP_L\", 1.0); // Primary mover for arm extension\n activations.set(\"BICEP_L\", 0.3); // Antagonist - stabilization only\n activations.set(\"CORE\", 0.8); // More core rotation\n activations.set(\"PECTORALS\", 0.7); // Chest drive for power\n activations.set(\"OBLIQUES\", 0.8); // Torso rotation for hip transfer\n break;\n\n case \"hook\":\n case \"갈고리주먹\": // Hook punch\n activations.set(\"SHOULDER_R\", 1.0); // Primary mover for horizontal arc\n activations.set(\"BICEP_R\", 0.8); // Maintains elbow angle during arc\n activations.set(\"FOREARM_R\", 0.8);\n activations.set(\"CORE\", 0.8); // Drives rotational power\n activations.set(\"OBLIQUES\", 0.9); // Primary torso rotation driver\n break;\n\n case \"uppercut\":\n case \"올려치기\": // Uppercut\n activations.set(\"SHOULDER_R\", 0.95); // Drives upward arc\n activations.set(\"BICEP_R\", 0.9); // Maintains elbow angle for upward drive\n activations.set(\"TRICEP_R\", 0.5); // Partial extension\n activations.set(\"CORE\", 0.9);\n activations.set(\"ABS\", 0.8);\n activations.set(\"QUAD_R\", 0.7); // Lower body drive (legs push up)\n activations.set(\"GLUTE_R\", 0.6); // Hip extension for power\n break;\n\n // Kicking techniques - 발차기 기술\n case \"front_kick\":\n case \"앞차기\": // Mae Chagi\n activations.set(\"QUAD_R\", 1.0); // Kicking leg fully flexed\n activations.set(\"GLUTE_R\", 0.9);\n activations.set(\"CALF_R\", 0.7);\n activations.set(\"CORE\", 0.6); // Balance\n activations.set(\"ABS\", 0.7);\n activations.set(\"QUAD_L\", 0.4); // Support leg tensed\n activations.set(\"CALF_L\", 0.5);\n break;\n\n case \"roundhouse_kick\":\n case \"dollyeochagi\":\n case \"돌려차기\": // Dollyeo Chagi\n activations.set(\"QUAD_R\", 1.0);\n activations.set(\"HAMSTRING_R\", 0.8);\n activations.set(\"GLUTE_R\", 0.95);\n activations.set(\"CORE\", 0.8);\n activations.set(\"OBLIQUES\", 0.9); // Hip rotation\n activations.set(\"QUAD_L\", 0.5);\n break;\n\n case \"side_kick\":\n case \"옆차기\": // Yeop Chagi\n activations.set(\"QUAD_R\", 1.0);\n activations.set(\"GLUTE_R\", 1.0);\n activations.set(\"HAMSTRING_R\", 0.85);\n activations.set(\"CALF_R\", 0.8);\n activations.set(\"CORE\", 0.7);\n activations.set(\"OBLIQUES\", 0.8);\n break;\n\n case \"back_kick\":\n case \"뒤차기\": // Dwi Chagi\n activations.set(\"GLUTE_R\", 1.0);\n activations.set(\"HAMSTRING_R\", 0.95);\n activations.set(\"QUAD_R\", 0.8);\n activations.set(\"CORE\", 0.9);\n activations.set(\"ABS\", 0.8);\n break;\n\n case \"axe_kick\":\n case \"내려차기\": // Naeryeo Chagi\n activations.set(\"QUAD_R\", 0.9);\n activations.set(\"HAMSTRING_R\", 1.0); // Maximum hamstring flex\n activations.set(\"GLUTE_R\", 0.85);\n activations.set(\"CORE\", 0.8);\n activations.set(\"ABS\", 0.9);\n break;\n\n // Defensive techniques - 방어 기술\n case \"block\":\n case \"makgi\":\n case \"막기\": // Block\n activations.set(\"SHOULDER_L\", 0.9);\n activations.set(\"SHOULDER_R\", 0.9);\n activations.set(\"BICEP_L\", 0.7);\n activations.set(\"BICEP_R\", 0.7);\n activations.set(\"FOREARM_L\", 0.8);\n activations.set(\"FOREARM_R\", 0.8);\n activations.set(\"CORE\", 0.8); // Brace for impact\n activations.set(\"ABS\", 0.7);\n break;\n\n case \"parry\":\n case \"흘려막기\": // Deflecting block\n activations.set(\"SHOULDER_R\", 0.6);\n activations.set(\"FOREARM_R\", 0.7);\n activations.set(\"CORE\", 0.5);\n break;\n\n // Elbow techniques - 팔꿈치 기술\n case \"elbow_strike\":\n case \"팔꿈치치기\": // Palkumchi Chigi\n activations.set(\"SHOULDER_R\", 1.0);\n activations.set(\"TRICEP_R\", 0.95);\n activations.set(\"FOREARM_R\", 0.9);\n activations.set(\"CORE\", 0.85);\n activations.set(\"OBLIQUES\", 0.8);\n break;\n\n // Knee techniques - 무릎 기술\n case \"knee_strike\":\n case \"무릎치기\": // Mureup Chigi\n activations.set(\"QUAD_R\", 1.0);\n activations.set(\"HAMSTRING_R\", 0.9);\n activations.set(\"GLUTE_R\", 0.85);\n activations.set(\"CORE\", 0.9);\n activations.set(\"ABS\", 0.85);\n break;\n\n // Grappling techniques - 잡기 기술 (Hapkido)\n case \"grab\":\n case \"잡기\":\n activations.set(\"FOREARM_L\", 0.9);\n activations.set(\"FOREARM_R\", 0.9);\n activations.set(\"BICEP_L\", 0.7);\n activations.set(\"BICEP_R\", 0.7);\n activations.set(\"CORE\", 0.6);\n break;\n\n // Stance changes - 자세 전환\n case \"stance_change\":\n case \"자세전환\":\n activations.set(\"CORE\", 0.7);\n activations.set(\"ABS\", 0.6);\n activations.set(\"QUAD_L\", 0.5);\n activations.set(\"QUAD_R\", 0.5);\n activations.set(\"CALF_L\", 0.5);\n activations.set(\"CALF_R\", 0.5);\n break;\n\n // Default - minimal activation\n default:\n activations.set(\"CORE\", 0.3);\n break;\n }\n\n return activations;\n};\n\n/**\n * Muscle activation manager for real-time combat physiology\n *\n * Manages 60fps muscle tension updates, stamina effects, and smooth transitions.\n * Optimized for performance with ref-based updates to avoid React re-renders.\n *\n * @korean 근육활성화관리자\n */\nexport class MuscleActivationManager {\n private activations: Map<MuscleGroupName, MuscleActivationState>;\n private config: MuscleSystemConfig;\n private scratchMap: Map<string, number>; // Reusable map for state sync to avoid allocations\n\n /**\n * Create a new muscle activation manager\n *\n * @param config - Optional configuration (uses defaults if not provided)\n * @korean 생성자\n */\n constructor(config: Partial<MuscleSystemConfig> = {}) {\n this.config = {\n maxFrameTime: 3,\n muscleCount: 20,\n useInstancing: false,\n relaxationDelay: 0.3,\n exhaustionThreshold: 20,\n shakeFrequency: 20,\n shakeAmplitude: 0.02,\n activationSpeed: 5.0,\n relaxationSpeed: 3.0,\n shakingTensionThreshold: 0.3,\n ...config,\n };\n\n // Initialize all muscle groups to relaxed state\n this.activations = new Map();\n // Initialize reusable scratch map for state sync\n this.scratchMap = new Map();\n const allMuscles: MuscleGroupName[] = [\n \"SHOULDER_L\",\n \"SHOULDER_R\",\n \"BICEP_L\",\n \"BICEP_R\",\n \"TRICEP_L\",\n \"TRICEP_R\",\n \"FOREARM_L\",\n \"FOREARM_R\",\n \"PECTORALS\",\n \"CORE\",\n \"ABS\",\n \"OBLIQUES\",\n \"QUAD_L\",\n \"QUAD_R\",\n \"HAMSTRING_L\",\n \"HAMSTRING_R\",\n \"CALF_L\",\n \"CALF_R\",\n \"GLUTE_L\",\n \"GLUTE_R\",\n ];\n\n allMuscles.forEach((muscle) => {\n this.activations.set(muscle, {\n muscleGroup: muscle,\n tension: 0,\n targetTension: 0,\n isShaking: false,\n });\n });\n }\n\n /**\n * Update muscle activations for a technique at 60fps\n *\n * @param technique - Technique name\n * @param stamina - Current stamina (0-100)\n * @param delta - Time since last frame in seconds\n *\n * @korean 업데이트\n */\n update(technique: string, stamina: number, delta: number): void {\n const targetActivations = getMuscleActivationForTechnique(technique);\n const isExhausted = stamina < this.config.exhaustionThreshold;\n\n // Update each muscle group\n this.activations.forEach((state, muscleGroup) => {\n const targetTension = targetActivations.get(muscleGroup) ?? 0;\n\n // Adjust tension based on stamina (exhaustion reduces muscle effectiveness)\n const staminaFactor = Math.max(0.3, stamina / 100); // Minimum 30% even when exhausted\n const adjustedTarget = targetTension * staminaFactor;\n\n // Smooth transition to target tension using lerp\n const lerp = (start: number, end: number, t: number) =>\n start + (end - start) * t;\n const newTension = lerp(\n state.tension,\n adjustedTarget,\n this.config.activationSpeed * delta,\n );\n\n // Update shaking state for exhaustion\n const isShaking =\n isExhausted && state.tension > this.config.shakingTensionThreshold;\n\n // Update state directly for performance\n state.tension = newTension;\n state.targetTension = adjustedTarget;\n state.isShaking = isShaking;\n });\n }\n\n /**\n * Gradually relax all muscles to idle state\n *\n * Used after technique completion with configurable delay.\n *\n * @param delta - Time since last frame in seconds\n *\n * @korean 근육이완\n */\n relaxAllMuscles(delta: number): void {\n this.activations.forEach((state) => {\n // Use configured relaxation speed (slower than activation for realism)\n const lerp = (start: number, end: number, t: number) =>\n start + (end - start) * t;\n const newTension = lerp(\n state.tension,\n 0,\n this.config.relaxationSpeed * delta,\n );\n\n state.tension = newTension;\n state.targetTension = 0;\n state.isShaking = false;\n });\n }\n\n /**\n * Get current tension for a specific muscle group\n *\n * @param muscleGroup - Muscle group name\n * @returns Current tension (0-1) or 0 if not found\n *\n * @korean 긴장도가져오기\n */\n getTension(muscleGroup: MuscleGroupName): number {\n return this.activations.get(muscleGroup)?.tension ?? 0;\n }\n\n /**\n * Get shaking state for a specific muscle group\n *\n * @param muscleGroup - Muscle group name\n * @returns Whether muscle is shaking\n *\n * @korean 흔들림상태가져오기\n */\n isShaking(muscleGroup: MuscleGroupName): boolean {\n return this.activations.get(muscleGroup)?.isShaking ?? false;\n }\n\n /**\n * Get all current muscle activations\n *\n * @returns Map of muscle groups to current states\n *\n * @korean 모든활성화가져오기\n */\n getAllActivations(): ReadonlyMap<MuscleGroupName, MuscleActivationState> {\n return this.activations;\n }\n\n /**\n * Get reusable scratch map with current tension values for state sync\n *\n * Populates and returns a reusable Map to avoid allocations during state sync.\n * This map is cleared and repopulated on each call.\n *\n * @returns Reusable Map with current tension values\n *\n * @korean 상태동기화맵가져오기\n */\n getScratchMapForSync(): Map<string, number> {\n this.scratchMap.clear();\n this.activations.forEach((state, name) => {\n this.scratchMap.set(name, state.tension);\n });\n return this.scratchMap;\n }\n\n /**\n * Reset all muscles to relaxed state immediately\n *\n * @korean 즉시이완\n */\n reset(): void {\n this.activations.forEach((state) => {\n state.tension = 0;\n state.targetTension = 0;\n state.isShaking = false;\n });\n this.scratchMap.clear();\n }\n\n /**\n * Dispose of the muscle activation system\n *\n * Clears all internal state and references to prevent memory leaks.\n * Should be called when the system is no longer needed (e.g., component unmount).\n *\n * @korean 근육시스템해제\n */\n dispose(): void {\n this.activations.clear();\n this.scratchMap.clear();\n }\n}\n"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"BodyPartDamageIntegration.js","names":[],"sources":["../../../src/systems/bodypart/BodyPartDamageIntegration.ts"],"sourcesContent":["/**\n * Body Part Damage Integration for Combat System\n *\n * **Korean**: 신체부위 피해 통합\n *\n * Extends the damage calculation system to apply damage to specific body parts\n * based on vital point hits and attack locations. Integrates with the existing\n * VitalPointSystem and DamageCalculator.\n *\n * @module systems/bodypart/BodyPartDamageIntegration\n * @category Body Part System\n * @korean 신체부위피해통합\n */\n\nimport { BodyRegion } from \"@/types\";\nimport type { VitalPoint } from \"../vitalpoint/types\";\nimport { bodyPartHealthSystem } from \"./BodyPartHealthSystem\";\nimport { BodyPart, BodyPartHealth, BodyPartMaxHealth } from \"./types\";\n\n/**\n * Minimal player view needed for body part damage operations.\n */\nexport interface BodyPartDamageTarget {\n readonly health: number;\n readonly bodyPartHealth?: BodyPartHealth;\n readonly bodyPartMaxHealth?: BodyPartMaxHealth;\n}\n\n/**\n * Map vital point to its corresponding body region.\n *\n * **Korean**: 급소에서 신체 영역 매핑\n *\n * Analyzes a vital point's location and category to determine which\n * BodyRegion it belongs to, enabling proper damage distribution.\n *\n * @param vitalPoint - Vital point that was struck\n * @returns Corresponding body region\n *\n */\nexport function getBodyRegionFromVitalPoint(\n vitalPoint: VitalPoint\n): BodyRegion {\n const pointId = vitalPoint.id.toLowerCase();\n\n // Head region vital points\n if (\n pointId.includes(\"head_\") ||\n pointId.includes(\"temple\") ||\n pointId.includes(\"jaw\") ||\n pointId.includes(\"crown\") ||\n pointId.includes(\"forehead\") ||\n pointId.includes(\"eye\") ||\n pointId.includes(\"nose\") ||\n pointId.includes(\"ear\") ||\n pointId.includes(\"skull\")\n ) {\n return BodyRegion.HEAD;\n }\n\n // Neck region vital points\n if (\n pointId.includes(\"neck\") ||\n pointId.includes(\"throat\") ||\n pointId.includes(\"carotid\") ||\n pointId.includes(\"cervical\")\n ) {\n return BodyRegion.NECK;\n }\n\n // Torso region vital points\n if (\n pointId.includes(\"torso_\") ||\n pointId.includes(\"chest\") ||\n pointId.includes(\"solar_plexus\") ||\n pointId.includes(\"ribs\") ||\n pointId.includes(\"sternum\") ||\n pointId.includes(\"heart\") ||\n pointId.includes(\"lung\") ||\n pointId.includes(\"abdomen\")\n ) {\n return BodyRegion.TORSO;\n }\n\n // Core region vital points\n if (\n pointId.includes(\"core\") ||\n pointId.includes(\"dantian\") ||\n pointId.includes(\"kidney\") ||\n pointId.includes(\"liver\") ||\n pointId.includes(\"spleen\") ||\n pointId.includes(\"bladder\")\n ) {\n return BodyRegion.CORE;\n }\n\n // Left arm vital points\n if (\n pointId.includes(\"arm_left\") ||\n pointId.includes(\"left_shoulder\") ||\n pointId.includes(\"left_elbow\") ||\n pointId.includes(\"left_wrist\") ||\n pointId.includes(\"left_hand\")\n ) {\n return BodyRegion.LEFT_ARM;\n }\n\n // Right arm vital points\n if (\n pointId.includes(\"arm_right\") ||\n pointId.includes(\"right_shoulder\") ||\n pointId.includes(\"right_elbow\") ||\n pointId.includes(\"right_wrist\") ||\n pointId.includes(\"right_hand\")\n ) {\n return BodyRegion.RIGHT_ARM;\n }\n\n // Left leg vital points\n if (\n pointId.includes(\"leg_left\") ||\n pointId.includes(\"left_knee\") ||\n pointId.includes(\"left_ankle\") ||\n pointId.includes(\"left_foot\") ||\n pointId.includes(\"left_thigh\")\n ) {\n return BodyRegion.LEFT_LEG;\n }\n\n // Right leg vital points\n if (\n pointId.includes(\"leg_right\") ||\n pointId.includes(\"right_knee\") ||\n pointId.includes(\"right_ankle\") ||\n pointId.includes(\"right_foot\") ||\n pointId.includes(\"right_thigh\")\n ) {\n return BodyRegion.RIGHT_LEG;\n }\n\n // Default to torso for unmatched points\n return BodyRegion.TORSO;\n}\n\n/**\n * Apply damage to player's body parts based on hit location.\n *\n * **Korean**: 타격 위치 기반 신체부위 피해 적용\n *\n * Takes total damage and distributes it across body parts according to\n * the hit location. Updates both aggregate health and body part health.\n *\n * For combat compatibility, the aggregate health is directly reduced by\n * the damage amount (traditional behavior), while body part health is\n * updated proportionally for visualization purposes.\n *\n * @param player - Player receiving damage\n * @param totalDamage - Total damage amount\n * @param bodyRegion - Body region that was hit\n * @returns Updated player state with body part damage applied\n *\n */\nexport function applyDamageToBodyParts<T extends BodyPartDamageTarget>(\n player: T,\n totalDamage: number,\n bodyRegion: BodyRegion\n): T {\n // If body part health not initialized, initialize it\n const bodyPartHealth =\n player.bodyPartHealth ?? bodyPartHealthSystem.createDefaultBodyPartHealth();\n const bodyPartMaxHealth =\n player.bodyPartMaxHealth ?? bodyPartHealthSystem.createDefaultMaxHealth();\n\n // Apply distributed damage to body parts for visualization\n const updatedBodyPartHealth = bodyPartHealthSystem.applyDistributedDamage(\n bodyPartHealth,\n bodyRegion,\n totalDamage,\n bodyPartMaxHealth\n );\n\n // Calculate new aggregate health directly: traditional damage reduction\n // This ensures combat damage calculations remain consistent\n const newAggregateHealth = Math.max(0, player.health - totalDamage);\n\n // Update player state with new body part health and aggregate health\n return {\n ...player,\n bodyPartHealth: updatedBodyPartHealth,\n bodyPartMaxHealth,\n health: newAggregateHealth,\n };\n}\n\n/**\n * Apply damage from a vital point hit to appropriate body parts.\n *\n * **Korean**: 급소 타격 신체부위 피해 적용\n *\n * Specialized damage application for vital point strikes. Maps the vital\n * point to its body region and applies damage with appropriate distribution.\n *\n * @param player - Player receiving damage\n * @param totalDamage - Total damage amount\n * @param vitalPoint - Vital point that was struck\n * @returns Updated player state with vital point damage applied\n *\n */\nexport function applyVitalPointDamageToBodyParts<\n T extends BodyPartDamageTarget\n>(player: T, totalDamage: number, vitalPoint: VitalPoint): T {\n const bodyRegion = getBodyRegionFromVitalPoint(vitalPoint);\n return applyDamageToBodyParts(player, totalDamage, bodyRegion);\n}\n\n/**\n * Get combat capability modifiers from body part health.\n *\n * **Korean**: 신체부위 전투 능력 수정치 조회\n *\n * Calculates how body part damage affects combat capabilities.\n * Returns modifiers that should be applied to:\n * - Consciousness (awareness, reaction time)\n * - Stamina regeneration\n * - Attack damage output\n * - Movement speed\n * - Balance and stability\n * - Technique accuracy\n *\n * @param player - Player state to analyze\n * @returns Combat capability effect multipliers (0.0-1.0)\n *\n * @example\n * ```typescript\n * const effects = getBodyPartCombatEffects(player);\n * const actualDamage = baseDamage * effects.attackDamageModifier;\n * const actualSpeed = baseSpeed * effects.movementSpeedModifier;\n * ```\n *\n */\nexport function getBodyPartCombatEffects(player: BodyPartDamageTarget) {\n // If no body part health, return no effects\n if (!player.bodyPartHealth) {\n return {\n consciousnessModifier: 1.0,\n staminaRegenModifier: 1.0,\n attackDamageModifier: 1.0,\n movementSpeedModifier: 1.0,\n balanceModifier: 1.0,\n techniqueAccuracyModifier: 1.0,\n };\n }\n\n return bodyPartHealthSystem.calculateBodyPartEffects(\n player.bodyPartHealth,\n player.bodyPartMaxHealth\n );\n}\n\n/**\n * Check if player is incapacitated by body part damage.\n *\n * **Korean**: 신체부위 무력화 확인\n *\n * Determines if the player can continue fighting based on body part health.\n * Player is incapacitated if:\n * - Head is at 0 HP (unconscious)\n * - Both legs are at 0 HP (cannot stand)\n * - Aggregate health is below 10%\n *\n * @param player - Player state to check\n * @returns Whether player is incapacitated\n *\n */\nexport function isPlayerIncapacitatedByBodyDamage(\n player: BodyPartDamageTarget\n): boolean {\n if (!player.bodyPartHealth) {\n return false; // No body part system = use aggregate health only\n }\n\n return bodyPartHealthSystem.isIncapacitated(player.bodyPartHealth);\n}\n\n/**\n * Initialize body part health for a player.\n *\n * **Korean**: 플레이어 신체부위 체력 초기화\n *\n * Sets up body part health and max health for a player if not already present.\n * Uses the provided max health or defaults to 100 per part.\n *\n * @param player - Player state to initialize\n * @param maxHealthPerPart - Optional custom max health per part\n * @returns Player state with body part health initialized\n *\n */\nexport function initializeBodyPartHealthForPlayer<\n T extends BodyPartDamageTarget\n>(player: T, maxHealthPerPart: number = 100): T {\n // Skip if already initialized\n if (player.bodyPartHealth && player.bodyPartMaxHealth) {\n return player;\n }\n\n const bodyPartHealth =\n bodyPartHealthSystem.createDefaultBodyPartHealth(maxHealthPerPart);\n const bodyPartMaxHealth =\n bodyPartHealthSystem.createDefaultMaxHealth(maxHealthPerPart);\n\n return {\n ...player,\n bodyPartHealth,\n bodyPartMaxHealth,\n health: bodyPartHealthSystem.calculateAggregateHealth(bodyPartHealth),\n };\n}\n\n/**\n * Heal body parts proportionally based on total healing amount.\n *\n * **Korean**: 신체부위 비례 치유\n *\n * Distributes healing across all damaged body parts proportionally to\n * their damage level. Most damaged parts receive more healing.\n *\n * @param player - Player state to heal\n * @param totalHealAmount - Total healing to distribute\n * @returns Updated player state with healing applied\n *\n */\nexport function healBodyPartsProportionally<T extends BodyPartDamageTarget>(\n player: T,\n totalHealAmount: number\n): T {\n if (!player.bodyPartHealth || !player.bodyPartMaxHealth) {\n return player; // No body part system active\n }\n\n const bodyPartHealth = player.bodyPartHealth;\n const bodyPartMaxHealth = player.bodyPartMaxHealth;\n\n // Calculate total missing health across all parts\n const missingHealth = (Object.keys(bodyPartHealth) as BodyPart[]).reduce(\n (sum, part) => {\n return sum + (bodyPartMaxHealth[part] - bodyPartHealth[part]);\n },\n 0\n );\n\n // If no damage, return unchanged\n if (missingHealth <= 0) {\n return player;\n }\n\n // Heal each part proportionally to its damage\n let updatedHealth = { ...bodyPartHealth };\n\n (Object.keys(bodyPartHealth) as BodyPart[]).forEach((part) => {\n const partMissingHealth = bodyPartMaxHealth[part] - bodyPartHealth[part];\n if (partMissingHealth > 0) {\n const proportion = partMissingHealth / missingHealth;\n const healForThisPart = totalHealAmount * proportion;\n updatedHealth = bodyPartHealthSystem.healBodyPart(\n updatedHealth,\n part,\n healForThisPart,\n bodyPartMaxHealth\n );\n }\n });\n\n // Calculate new aggregate health\n const newAggregateHealth =\n bodyPartHealthSystem.calculateAggregateHealth(updatedHealth);\n\n return {\n ...player,\n bodyPartHealth: updatedHealth,\n health: newAggregateHealth,\n };\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"BodyPartDamageIntegration.js","names":[],"sources":["../../../src/systems/bodypart/BodyPartDamageIntegration.ts"],"sourcesContent":["/**\n * Body Part Damage Integration for Combat System\n *\n * **Korean**: 신체부위 피해 통합\n *\n * Extends the damage calculation system to apply damage to specific body parts\n * based on vital point hits and attack locations. Integrates with the existing\n * VitalPointSystem and DamageCalculator.\n *\n * @module systems/bodypart/BodyPartDamageIntegration\n * @category Body Part System\n * @korean 신체부위피해통합\n */\n\nimport { BodyRegion } from \"@/types\";\nimport type { VitalPoint } from \"../vitalpoint/types\";\nimport { bodyPartHealthSystem } from \"./BodyPartHealthSystem\";\nimport { BodyPart, BodyPartHealth, BodyPartMaxHealth } from \"./types\";\n\n/**\n * Minimal player view needed for body part damage operations.\n */\nexport interface BodyPartDamageTarget {\n readonly health: number;\n readonly bodyPartHealth?: BodyPartHealth;\n readonly bodyPartMaxHealth?: BodyPartMaxHealth;\n}\n\n/**\n * Map vital point to its corresponding body region.\n *\n * **Korean**: 급소에서 신체 영역 매핑\n *\n * Analyzes a vital point's location and category to determine which\n * BodyRegion it belongs to, enabling proper damage distribution.\n *\n * @param vitalPoint - Vital point that was struck\n * @returns Corresponding body region\n *\n */\nexport function getBodyRegionFromVitalPoint(\n vitalPoint: VitalPoint\n): BodyRegion {\n const pointId = vitalPoint.id.toLowerCase();\n\n // Head region vital points\n if (\n pointId.includes(\"head_\") ||\n pointId.includes(\"temple\") ||\n pointId.includes(\"jaw\") ||\n pointId.includes(\"crown\") ||\n pointId.includes(\"forehead\") ||\n pointId.includes(\"eye\") ||\n pointId.includes(\"nose\") ||\n pointId.includes(\"ear\") ||\n pointId.includes(\"skull\")\n ) {\n return BodyRegion.HEAD;\n }\n\n // Neck region vital points\n if (\n pointId.includes(\"neck\") ||\n pointId.includes(\"throat\") ||\n pointId.includes(\"carotid\") ||\n pointId.includes(\"cervical\")\n ) {\n return BodyRegion.NECK;\n }\n\n // Torso region vital points\n if (\n pointId.includes(\"torso_\") ||\n pointId.includes(\"chest\") ||\n pointId.includes(\"solar_plexus\") ||\n pointId.includes(\"ribs\") ||\n pointId.includes(\"sternum\") ||\n pointId.includes(\"heart\") ||\n pointId.includes(\"lung\") ||\n pointId.includes(\"abdomen\")\n ) {\n return BodyRegion.TORSO;\n }\n\n // Core region vital points\n if (\n pointId.includes(\"core\") ||\n pointId.includes(\"dantian\") ||\n pointId.includes(\"kidney\") ||\n pointId.includes(\"liver\") ||\n pointId.includes(\"spleen\") ||\n pointId.includes(\"bladder\")\n ) {\n return BodyRegion.CORE;\n }\n\n // Left arm vital points\n if (\n pointId.includes(\"arm_left\") ||\n pointId.includes(\"left_shoulder\") ||\n pointId.includes(\"left_elbow\") ||\n pointId.includes(\"left_wrist\") ||\n pointId.includes(\"left_hand\")\n ) {\n return BodyRegion.LEFT_ARM;\n }\n\n // Right arm vital points\n if (\n pointId.includes(\"arm_right\") ||\n pointId.includes(\"right_shoulder\") ||\n pointId.includes(\"right_elbow\") ||\n pointId.includes(\"right_wrist\") ||\n pointId.includes(\"right_hand\")\n ) {\n return BodyRegion.RIGHT_ARM;\n }\n\n // Left leg vital points\n if (\n pointId.includes(\"leg_left\") ||\n pointId.includes(\"left_knee\") ||\n pointId.includes(\"left_ankle\") ||\n pointId.includes(\"left_foot\") ||\n pointId.includes(\"left_thigh\")\n ) {\n return BodyRegion.LEFT_LEG;\n }\n\n // Right leg vital points\n if (\n pointId.includes(\"leg_right\") ||\n pointId.includes(\"right_knee\") ||\n pointId.includes(\"right_ankle\") ||\n pointId.includes(\"right_foot\") ||\n pointId.includes(\"right_thigh\")\n ) {\n return BodyRegion.RIGHT_LEG;\n }\n\n // Default to torso for unmatched points\n return BodyRegion.TORSO;\n}\n\n/**\n * Apply damage to player's body parts based on hit location.\n *\n * **Korean**: 타격 위치 기반 신체부위 피해 적용\n *\n * Takes total damage and distributes it across body parts according to\n * the hit location. Updates both aggregate health and body part health.\n *\n * For combat compatibility, the aggregate health is directly reduced by\n * the damage amount (traditional behavior), while body part health is\n * updated proportionally for visualization purposes.\n *\n * @param player - Player receiving damage\n * @param totalDamage - Total damage amount\n * @param bodyRegion - Body region that was hit\n * @returns Updated player state with body part damage applied\n *\n */\nexport function applyDamageToBodyParts<T extends BodyPartDamageTarget>(\n player: T,\n totalDamage: number,\n bodyRegion: BodyRegion\n): T {\n // If body part health not initialized, initialize it\n const bodyPartHealth =\n player.bodyPartHealth ?? bodyPartHealthSystem.createDefaultBodyPartHealth();\n const bodyPartMaxHealth =\n player.bodyPartMaxHealth ?? bodyPartHealthSystem.createDefaultMaxHealth();\n\n // Apply distributed damage to body parts for visualization\n const updatedBodyPartHealth = bodyPartHealthSystem.applyDistributedDamage(\n bodyPartHealth,\n bodyRegion,\n totalDamage,\n bodyPartMaxHealth\n );\n\n // Calculate new aggregate health directly: traditional damage reduction\n // This ensures combat damage calculations remain consistent\n const newAggregateHealth = Math.max(0, player.health - totalDamage);\n\n // Update player state with new body part health and aggregate health\n return {\n ...player,\n bodyPartHealth: updatedBodyPartHealth,\n bodyPartMaxHealth,\n health: newAggregateHealth,\n };\n}\n\n/**\n * Apply damage from a vital point hit to appropriate body parts.\n *\n * **Korean**: 급소 타격 신체부위 피해 적용\n *\n * Specialized damage application for vital point strikes. Maps the vital\n * point to its body region and applies damage with appropriate distribution.\n *\n * @param player - Player receiving damage\n * @param totalDamage - Total damage amount\n * @param vitalPoint - Vital point that was struck\n * @returns Updated player state with vital point damage applied\n *\n */\nexport function applyVitalPointDamageToBodyParts<\n T extends BodyPartDamageTarget\n>(player: T, totalDamage: number, vitalPoint: VitalPoint): T {\n const bodyRegion = getBodyRegionFromVitalPoint(vitalPoint);\n return applyDamageToBodyParts(player, totalDamage, bodyRegion);\n}\n\n/**\n * Get combat capability modifiers from body part health.\n *\n * **Korean**: 신체부위 전투 능력 수정치 조회\n *\n * Calculates how body part damage affects combat capabilities.\n * Returns modifiers that should be applied to:\n * - Consciousness (awareness, reaction time)\n * - Stamina regeneration\n * - Attack damage output\n * - Movement speed\n * - Balance and stability\n * - Technique accuracy\n *\n * @param player - Player state to analyze\n * @returns Combat capability effect multipliers (0.0-1.0)\n *\n * @example\n * ```typescript\n * const effects = getBodyPartCombatEffects(player);\n * const actualDamage = baseDamage * effects.attackDamageModifier;\n * const actualSpeed = baseSpeed * effects.movementSpeedModifier;\n * ```\n *\n */\nexport function getBodyPartCombatEffects(player: BodyPartDamageTarget) {\n // If no body part health, return no effects\n if (!player.bodyPartHealth) {\n return {\n consciousnessModifier: 1.0,\n staminaRegenModifier: 1.0,\n attackDamageModifier: 1.0,\n movementSpeedModifier: 1.0,\n balanceModifier: 1.0,\n techniqueAccuracyModifier: 1.0,\n };\n }\n\n return bodyPartHealthSystem.calculateBodyPartEffects(\n player.bodyPartHealth,\n player.bodyPartMaxHealth\n );\n}\n\n/**\n * Check if player is incapacitated by body part damage.\n *\n * **Korean**: 신체부위 무력화 확인\n *\n * Determines if the player can continue fighting based on body part health.\n * Player is incapacitated if:\n * - Head is at 0 HP (unconscious)\n * - Both legs are at 0 HP (cannot stand)\n * - Aggregate health is below 10%\n *\n * @param player - Player state to check\n * @returns Whether player is incapacitated\n *\n */\nexport function isPlayerIncapacitatedByBodyDamage(\n player: BodyPartDamageTarget\n): boolean {\n if (!player.bodyPartHealth) {\n return false; // No body part system = use aggregate health only\n }\n\n return bodyPartHealthSystem.isIncapacitated(player.bodyPartHealth);\n}\n\n/**\n * Initialize body part health for a player.\n *\n * **Korean**: 플레이어 신체부위 체력 초기화\n *\n * Sets up body part health and max health for a player if not already present.\n * Uses the provided max health or defaults to 100 per part.\n *\n * @param player - Player state to initialize\n * @param maxHealthPerPart - Optional custom max health per part\n * @returns Player state with body part health initialized\n *\n */\nexport function initializeBodyPartHealthForPlayer<\n T extends BodyPartDamageTarget\n>(player: T, maxHealthPerPart: number = 100): T {\n // Skip if already initialized\n if (player.bodyPartHealth && player.bodyPartMaxHealth) {\n return player;\n }\n\n const bodyPartHealth =\n bodyPartHealthSystem.createDefaultBodyPartHealth(maxHealthPerPart);\n const bodyPartMaxHealth =\n bodyPartHealthSystem.createDefaultMaxHealth(maxHealthPerPart);\n\n return {\n ...player,\n bodyPartHealth,\n bodyPartMaxHealth,\n health: bodyPartHealthSystem.calculateAggregateHealth(bodyPartHealth),\n };\n}\n\n/**\n * Heal body parts proportionally based on total healing amount.\n *\n * **Korean**: 신체부위 비례 치유\n *\n * Distributes healing across all damaged body parts proportionally to\n * their damage level. Most damaged parts receive more healing.\n *\n * @param player - Player state to heal\n * @param totalHealAmount - Total healing to distribute\n * @returns Updated player state with healing applied\n *\n */\nexport function healBodyPartsProportionally<T extends BodyPartDamageTarget>(\n player: T,\n totalHealAmount: number\n): T {\n if (!player.bodyPartHealth || !player.bodyPartMaxHealth) {\n return player; // No body part system active\n }\n\n const bodyPartHealth = player.bodyPartHealth;\n const bodyPartMaxHealth = player.bodyPartMaxHealth;\n\n // Calculate total missing health across all parts\n const missingHealth = (Object.keys(bodyPartHealth) as BodyPart[]).reduce(\n (sum, part) => {\n return sum + (bodyPartMaxHealth[part] - bodyPartHealth[part]);\n },\n 0\n );\n\n // If no damage, return unchanged\n if (missingHealth <= 0) {\n return player;\n }\n\n // Heal each part proportionally to its damage\n let updatedHealth = { ...bodyPartHealth };\n\n (Object.keys(bodyPartHealth) as BodyPart[]).forEach((part) => {\n const partMissingHealth = bodyPartMaxHealth[part] - bodyPartHealth[part];\n if (partMissingHealth > 0) {\n const proportion = partMissingHealth / missingHealth;\n const healForThisPart = totalHealAmount * proportion;\n updatedHealth = bodyPartHealthSystem.healBodyPart(\n updatedHealth,\n part,\n healForThisPart,\n bodyPartMaxHealth\n );\n }\n });\n\n // Calculate new aggregate health\n const newAggregateHealth =\n bodyPartHealthSystem.calculateAggregateHealth(updatedHealth);\n\n return {\n ...player,\n bodyPartHealth: updatedHealth,\n health: newAggregateHealth,\n };\n}\n"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"BodyPartHealthSystem.js","names":[],"sources":["../../../src/systems/bodypart/BodyPartHealthSystem.ts"],"sourcesContent":["/**\n * Body Part Health System\n * \n * **Korean**: 신체부위 체력 시스템\n * \n * Manages body part-specific health tracking, damage distribution, and combat\n * capability effects. Implements realistic localized damage mechanics based on\n * Korean martial arts vital point targeting principles.\n * \n * ## System Features\n * \n * - Independent health tracking for 8 body parts\n * - Automatic combat capability penalties based on damage\n * - Damage distribution from hit locations to body parts\n * - UI-friendly status information generation\n * - Performance-optimized calculations\n * \n * @module systems/bodypart/BodyPartHealthSystem\n * @category Body Part System\n * @korean 신체부위체력시스템\n */\n\nimport { BodyRegion, KOREAN_COLORS, KoreanText } from \"@/types\";\nimport {\n BodyPart,\n BodyPartDamageDistribution,\n BodyPartEffects,\n BodyPartHealth,\n BodyPartHealthConfig,\n BodyPartMaxHealth,\n BodyPartStatus,\n BODY_PART_EFFECT_CONSTANTS,\n DEFAULT_BODY_PART_CONFIG,\n} from \"./types\";\n\n/**\n * Body Part Health System class.\n * \n * **Korean**: 신체부위 체력 시스템 클래스\n * \n * Provides methods for managing body part health, calculating effects,\n * and distributing damage based on hit locations.\n * \n * @example\n * ```typescript\n * const system = new BodyPartHealthSystem();\n * \n * // Create initial body part health\n * const health = system.createDefaultBodyPartHealth();\n * \n * // Apply damage to head\n * const damaged = system.applyDamageToBodyPart(\n * health,\n * BodyPart.HEAD,\n * 25\n * );\n * \n * // Calculate combat effects\n * const effects = system.calculateBodyPartEffects(damaged);\n * ```\n * \n * @category Body Part System\n */\nexport class BodyPartHealthSystem {\n private config: BodyPartHealthConfig;\n\n constructor(config: BodyPartHealthConfig = DEFAULT_BODY_PART_CONFIG) {\n this.config = config;\n }\n\n /**\n * Create default body part health with all parts at maximum.\n * \n * **Korean**: 기본 신체부위 체력 생성\n * \n * @param maxHealth - Optional custom max health per part (default: 100)\n * @returns Body part health structure with all parts at max HP\n * \n */\n createDefaultBodyPartHealth(maxHealth: number = 100): BodyPartHealth {\n return {\n head: maxHealth,\n neck: maxHealth,\n torsoUpper: maxHealth,\n torsoLower: maxHealth,\n armLeft: maxHealth,\n armRight: maxHealth,\n legLeft: maxHealth,\n legRight: maxHealth,\n };\n }\n\n /**\n * Create default maximum health values for body parts.\n * \n * **Korean**: 기본 최대 체력 생성\n * \n * @param baseMaxHealth - Base maximum health per part\n * @returns Maximum health structure\n * \n */\n createDefaultMaxHealth(baseMaxHealth: number = 100): BodyPartMaxHealth {\n return {\n head: baseMaxHealth,\n neck: baseMaxHealth,\n torsoUpper: baseMaxHealth,\n torsoLower: baseMaxHealth,\n armLeft: baseMaxHealth,\n armRight: baseMaxHealth,\n legLeft: baseMaxHealth,\n legRight: baseMaxHealth,\n };\n }\n\n /**\n * Apply damage to a specific body part.\n * \n * **Korean**: 신체부위 피해 적용\n * \n * Reduces health of the specified body part by the damage amount.\n * Health is clamped to 0-maxHealth range.\n * \n * @param current - Current body part health\n * @param bodyPart - Body part to damage\n * @param damage - Damage amount to apply\n * @param maxHealth - Optional maximum health limits\n * @returns Updated body part health\n * \n */\n applyDamageToBodyPart(\n current: BodyPartHealth,\n bodyPart: BodyPart,\n damage: number,\n maxHealth?: BodyPartMaxHealth\n ): BodyPartHealth {\n const newHealth = Math.max(0, current[bodyPart] - damage);\n const clampedHealth = maxHealth\n ? Math.min(newHealth, maxHealth[bodyPart])\n : newHealth;\n\n return {\n ...current,\n [bodyPart]: clampedHealth,\n };\n }\n\n /**\n * Distribute damage across body parts based on hit location.\n * \n * **Korean**: 피해 분배\n * \n * Maps a BodyRegion hit location to primary and secondary body parts,\n * distributing damage according to impact mechanics.\n * \n * @param bodyRegion - Hit location\n * @returns Damage distribution configuration\n * \n */\n getDamageDistribution(bodyRegion: BodyRegion): BodyPartDamageDistribution {\n switch (bodyRegion) {\n case BodyRegion.HEAD:\n return {\n primary: { part: BodyPart.HEAD, percentage: 0.9 },\n secondary: [{ part: BodyPart.NECK, percentage: 0.1 }],\n };\n\n case BodyRegion.NECK:\n return {\n primary: { part: BodyPart.NECK, percentage: 0.8 },\n secondary: [\n { part: BodyPart.HEAD, percentage: 0.1 },\n { part: BodyPart.TORSO_UPPER, percentage: 0.1 },\n ],\n };\n\n case BodyRegion.TORSO:\n return {\n primary: { part: BodyPart.TORSO_UPPER, percentage: 0.6 },\n secondary: [{ part: BodyPart.TORSO_LOWER, percentage: 0.4 }],\n };\n\n case BodyRegion.CORE:\n return {\n primary: { part: BodyPart.TORSO_LOWER, percentage: 0.7 },\n secondary: [{ part: BodyPart.TORSO_UPPER, percentage: 0.3 }],\n };\n\n case BodyRegion.LEFT_ARM:\n return {\n primary: { part: BodyPart.ARM_LEFT, percentage: 0.9 },\n secondary: [{ part: BodyPart.TORSO_UPPER, percentage: 0.1 }],\n };\n\n case BodyRegion.RIGHT_ARM:\n return {\n primary: { part: BodyPart.ARM_RIGHT, percentage: 0.9 },\n secondary: [{ part: BodyPart.TORSO_UPPER, percentage: 0.1 }],\n };\n\n case BodyRegion.LEFT_LEG:\n return {\n primary: { part: BodyPart.LEG_LEFT, percentage: 0.9 },\n secondary: [{ part: BodyPart.TORSO_LOWER, percentage: 0.1 }],\n };\n\n case BodyRegion.RIGHT_LEG:\n return {\n primary: { part: BodyPart.LEG_RIGHT, percentage: 0.9 },\n secondary: [{ part: BodyPart.TORSO_LOWER, percentage: 0.1 }],\n };\n\n default:\n // Default to torso upper for unknown regions\n return {\n primary: { part: BodyPart.TORSO_UPPER, percentage: 1.0 },\n secondary: [],\n };\n }\n }\n\n /**\n * Apply distributed damage to body parts from a hit.\n * \n * **Korean**: 분산 피해 적용\n * \n * Takes total damage and distributes it across primary and secondary\n * body parts based on the hit location.\n * \n * @param current - Current body part health\n * @param bodyRegion - Hit location\n * @param totalDamage - Total damage to distribute\n * @param maxHealth - Optional maximum health limits\n * @returns Updated body part health\n * \n */\n applyDistributedDamage(\n current: BodyPartHealth,\n bodyRegion: BodyRegion,\n totalDamage: number,\n maxHealth?: BodyPartMaxHealth\n ): BodyPartHealth {\n const distribution = this.getDamageDistribution(bodyRegion);\n let updated = { ...current };\n\n // Apply primary damage\n updated = this.applyDamageToBodyPart(\n updated,\n distribution.primary.part,\n totalDamage * distribution.primary.percentage,\n maxHealth\n );\n\n // Apply secondary damage\n for (const secondary of distribution.secondary) {\n updated = this.applyDamageToBodyPart(\n updated,\n secondary.part,\n totalDamage * secondary.percentage,\n maxHealth\n );\n }\n\n return updated;\n }\n\n /**\n * Calculate combat capability effects from body part damage.\n * \n * **Korean**: 전투 능력 효과 계산\n * \n * Analyzes body part health and returns multipliers for various combat\n * capabilities. Implements acceptance criteria:\n * - Head <50%: Consciousness penalties\n * - Torso <50%: Stamina regen -50%\n * - Arms <50%: Attack damage -30%\n * - Legs <50%: Movement speed -40%\n * \n * @param health - Current body part health\n * @param maxHealth - Maximum health values\n * @returns Combat capability effect multipliers\n * \n */\n calculateBodyPartEffects(\n health: BodyPartHealth,\n maxHealth?: BodyPartMaxHealth\n ): BodyPartEffects {\n const max = maxHealth ?? this.createDefaultMaxHealth();\n\n // Calculate head/neck effects on consciousness\n const headPercentage = health.head / max.head;\n const neckPercentage = health.neck / max.neck;\n const minHeadNeck = Math.min(headPercentage, neckPercentage);\n \n let consciousnessModifier = 1.0;\n if (minHeadNeck < BODY_PART_EFFECT_CONSTANTS.HEAD.CRITICAL_THRESHOLD) {\n consciousnessModifier = BODY_PART_EFFECT_CONSTANTS.HEAD.CONSCIOUSNESS_PENALTY_AT_50;\n }\n\n // Calculate torso effects on stamina regeneration\n const torsoUpperPercentage = health.torsoUpper / max.torsoUpper;\n const torsoLowerPercentage = health.torsoLower / max.torsoLower;\n const avgTorso = (torsoUpperPercentage + torsoLowerPercentage) / 2;\n \n let staminaRegenModifier = 1.0;\n if (avgTorso < BODY_PART_EFFECT_CONSTANTS.TORSO.CRITICAL_THRESHOLD) {\n staminaRegenModifier = BODY_PART_EFFECT_CONSTANTS.TORSO.STAMINA_REGEN_PENALTY_AT_50;\n }\n\n // Calculate arm effects on attack damage\n const armLeftPercentage = health.armLeft / max.armLeft;\n const armRightPercentage = health.armRight / max.armRight;\n const avgArms = (armLeftPercentage + armRightPercentage) / 2;\n \n let attackDamageModifier = 1.0;\n if (avgArms < BODY_PART_EFFECT_CONSTANTS.ARMS.CRITICAL_THRESHOLD) {\n attackDamageModifier = BODY_PART_EFFECT_CONSTANTS.ARMS.ATTACK_DAMAGE_PENALTY_AT_50;\n }\n\n // Calculate leg effects on movement speed\n const legLeftPercentage = health.legLeft / max.legLeft;\n const legRightPercentage = health.legRight / max.legRight;\n const avgLegs = (legLeftPercentage + legRightPercentage) / 2;\n \n let movementSpeedModifier = 1.0;\n if (avgLegs < BODY_PART_EFFECT_CONSTANTS.LEGS.CRITICAL_THRESHOLD) {\n movementSpeedModifier = BODY_PART_EFFECT_CONSTANTS.LEGS.MOVEMENT_SPEED_PENALTY_AT_50;\n }\n\n // Balance affected by torso and legs\n const balanceModifier = Math.min(avgTorso, avgLegs);\n\n // Technique accuracy affected by head and arms\n const techniqueAccuracyModifier = Math.min(headPercentage, avgArms);\n\n return {\n consciousnessModifier,\n staminaRegenModifier,\n attackDamageModifier,\n movementSpeedModifier,\n balanceModifier,\n techniqueAccuracyModifier,\n };\n }\n\n /**\n * Calculate aggregate health from body parts.\n * \n * **Korean**: 종합 체력 계산\n * \n * Computes overall health as average of all body part health values.\n * This maintains backwards compatibility with single health bar systems.\n * \n * @param health - Body part health\n * @returns Aggregate health value (0-100)\n * \n */\n calculateAggregateHealth(health: BodyPartHealth): number {\n const sum =\n health.head +\n health.neck +\n health.torsoUpper +\n health.torsoLower +\n health.armLeft +\n health.armRight +\n health.legLeft +\n health.legRight;\n\n return sum / 8;\n }\n\n /**\n * Get body part status for UI display.\n * \n * **Korean**: 신체부위 상태 조회\n * \n * Provides UI-friendly status information including color coding,\n * status text, and critical/disabled flags.\n * \n * @param health - Current body part health\n * @param maxHealth - Maximum health values\n * @param part - Body part to query\n * @returns Status information for display\n * \n */\n getBodyPartStatus(\n health: BodyPartHealth,\n maxHealth: BodyPartMaxHealth,\n part: BodyPart\n ): BodyPartStatus {\n const current = health[part];\n const max = maxHealth[part];\n const percentage = current / max;\n\n // Determine status and color\n let status: KoreanText;\n let color: number;\n let critical: boolean;\n let disabled: boolean;\n\n if (percentage === 0) {\n status = { korean: \"무력화\", english: \"Disabled\" };\n color = KOREAN_COLORS.NEGATIVE_RED;\n critical = true;\n disabled = true;\n } else if (percentage < this.config.criticalThreshold) {\n status = { korean: \"치명적\", english: \"Critical\" };\n color = KOREAN_COLORS.NEGATIVE_RED;\n critical = true;\n disabled = false;\n } else if (percentage < this.config.severePenaltyThreshold) {\n status = { korean: \"심각\", english: \"Severe\" };\n color = KOREAN_COLORS.WARNING_ORANGE;\n critical = false;\n disabled = false;\n } else if (percentage < this.config.majorPenaltyThreshold) {\n status = { korean: \"중상\", english: \"Major\" };\n color = KOREAN_COLORS.WARNING_YELLOW;\n critical = false;\n disabled = false;\n } else if (percentage < this.config.minorPenaltyThreshold) {\n status = { korean: \"경상\", english: \"Minor\" };\n color = KOREAN_COLORS.HEALTH_FULL;\n critical = false;\n disabled = false;\n } else {\n status = { korean: \"정상\", english: \"Normal\" };\n color = KOREAN_COLORS.HEALTH_FULL;\n critical = false;\n disabled = false;\n }\n\n return {\n part,\n health: current,\n maxHealth: max,\n percentage,\n status,\n color,\n critical,\n disabled,\n };\n }\n\n /**\n * Get all body part statuses for UI.\n * \n * **Korean**: 모든 신체부위 상태 조회\n * \n * Returns status information for all body parts in a single call.\n * \n * @param health - Current body part health\n * @param maxHealth - Maximum health values\n * @returns Array of status information for all parts\n * \n */\n getAllBodyPartStatuses(\n health: BodyPartHealth,\n maxHealth: BodyPartMaxHealth\n ): BodyPartStatus[] {\n return Object.values(BodyPart).map((part) =>\n this.getBodyPartStatus(health, maxHealth, part)\n );\n }\n\n /**\n * Heal a specific body part.\n * \n * **Korean**: 신체부위 치유\n * \n * Increases health of the specified body part by the heal amount.\n * Health is clamped to 0-maxHealth range.\n * \n * @param current - Current body part health\n * @param bodyPart - Body part to heal\n * @param healAmount - Amount of health to restore\n * @param maxHealth - Optional maximum health limits\n * @returns Updated body part health\n * \n */\n healBodyPart(\n current: BodyPartHealth,\n bodyPart: BodyPart,\n healAmount: number,\n maxHealth?: BodyPartMaxHealth\n ): BodyPartHealth {\n const max = maxHealth ?? this.createDefaultMaxHealth();\n const newHealth = Math.min(max[bodyPart], current[bodyPart] + healAmount);\n\n return {\n ...current,\n [bodyPart]: newHealth,\n };\n }\n\n /**\n * Check if player is incapacitated based on body part health.\n * \n * **Korean**: 무력화 상태 확인\n * \n * Player is incapacitated if:\n * - Head is at 0 HP (unconscious)\n * - Both legs are at 0 HP (cannot move)\n * - Aggregate health below 10%\n * \n * @param health - Current body part health\n * @returns Whether player is incapacitated\n * \n */\n isIncapacitated(health: BodyPartHealth): boolean {\n // Unconscious from head damage\n if (health.head <= 0) return true;\n\n // Cannot move from leg damage\n if (health.legLeft <= 0 && health.legRight <= 0) return true;\n\n // Aggregate health critical\n const aggregate = this.calculateAggregateHealth(health);\n if (aggregate < 10) return true;\n\n return false;\n }\n}\n\n/**\n * Singleton instance of Body Part Health System.\n * \n * **Korean**: 신체부위 체력 시스템 싱글톤\n * \n * Provides global access to the body part health system throughout the game.\n * \n */\nexport const bodyPartHealthSystem = new BodyPartHealthSystem();\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"BodyPartHealthSystem.js","names":[],"sources":["../../../src/systems/bodypart/BodyPartHealthSystem.ts"],"sourcesContent":["/**\n * Body Part Health System\n * \n * **Korean**: 신체부위 체력 시스템\n * \n * Manages body part-specific health tracking, damage distribution, and combat\n * capability effects. Implements realistic localized damage mechanics based on\n * Korean martial arts vital point targeting principles.\n * \n * ## System Features\n * \n * - Independent health tracking for 8 body parts\n * - Automatic combat capability penalties based on damage\n * - Damage distribution from hit locations to body parts\n * - UI-friendly status information generation\n * - Performance-optimized calculations\n * \n * @module systems/bodypart/BodyPartHealthSystem\n * @category Body Part System\n * @korean 신체부위체력시스템\n */\n\nimport { BodyRegion, KOREAN_COLORS, KoreanText } from \"@/types\";\nimport {\n BodyPart,\n BodyPartDamageDistribution,\n BodyPartEffects,\n BodyPartHealth,\n BodyPartHealthConfig,\n BodyPartMaxHealth,\n BodyPartStatus,\n BODY_PART_EFFECT_CONSTANTS,\n DEFAULT_BODY_PART_CONFIG,\n} from \"./types\";\n\n/**\n * Body Part Health System class.\n * \n * **Korean**: 신체부위 체력 시스템 클래스\n * \n * Provides methods for managing body part health, calculating effects,\n * and distributing damage based on hit locations.\n * \n * @example\n * ```typescript\n * const system = new BodyPartHealthSystem();\n * \n * // Create initial body part health\n * const health = system.createDefaultBodyPartHealth();\n * \n * // Apply damage to head\n * const damaged = system.applyDamageToBodyPart(\n * health,\n * BodyPart.HEAD,\n * 25\n * );\n * \n * // Calculate combat effects\n * const effects = system.calculateBodyPartEffects(damaged);\n * ```\n * \n * @category Body Part System\n */\nexport class BodyPartHealthSystem {\n private config: BodyPartHealthConfig;\n\n constructor(config: BodyPartHealthConfig = DEFAULT_BODY_PART_CONFIG) {\n this.config = config;\n }\n\n /**\n * Create default body part health with all parts at maximum.\n * \n * **Korean**: 기본 신체부위 체력 생성\n * \n * @param maxHealth - Optional custom max health per part (default: 100)\n * @returns Body part health structure with all parts at max HP\n * \n */\n createDefaultBodyPartHealth(maxHealth: number = 100): BodyPartHealth {\n return {\n head: maxHealth,\n neck: maxHealth,\n torsoUpper: maxHealth,\n torsoLower: maxHealth,\n armLeft: maxHealth,\n armRight: maxHealth,\n legLeft: maxHealth,\n legRight: maxHealth,\n };\n }\n\n /**\n * Create default maximum health values for body parts.\n * \n * **Korean**: 기본 최대 체력 생성\n * \n * @param baseMaxHealth - Base maximum health per part\n * @returns Maximum health structure\n * \n */\n createDefaultMaxHealth(baseMaxHealth: number = 100): BodyPartMaxHealth {\n return {\n head: baseMaxHealth,\n neck: baseMaxHealth,\n torsoUpper: baseMaxHealth,\n torsoLower: baseMaxHealth,\n armLeft: baseMaxHealth,\n armRight: baseMaxHealth,\n legLeft: baseMaxHealth,\n legRight: baseMaxHealth,\n };\n }\n\n /**\n * Apply damage to a specific body part.\n * \n * **Korean**: 신체부위 피해 적용\n * \n * Reduces health of the specified body part by the damage amount.\n * Health is clamped to 0-maxHealth range.\n * \n * @param current - Current body part health\n * @param bodyPart - Body part to damage\n * @param damage - Damage amount to apply\n * @param maxHealth - Optional maximum health limits\n * @returns Updated body part health\n * \n */\n applyDamageToBodyPart(\n current: BodyPartHealth,\n bodyPart: BodyPart,\n damage: number,\n maxHealth?: BodyPartMaxHealth\n ): BodyPartHealth {\n const newHealth = Math.max(0, current[bodyPart] - damage);\n const clampedHealth = maxHealth\n ? Math.min(newHealth, maxHealth[bodyPart])\n : newHealth;\n\n return {\n ...current,\n [bodyPart]: clampedHealth,\n };\n }\n\n /**\n * Distribute damage across body parts based on hit location.\n * \n * **Korean**: 피해 분배\n * \n * Maps a BodyRegion hit location to primary and secondary body parts,\n * distributing damage according to impact mechanics.\n * \n * @param bodyRegion - Hit location\n * @returns Damage distribution configuration\n * \n */\n getDamageDistribution(bodyRegion: BodyRegion): BodyPartDamageDistribution {\n switch (bodyRegion) {\n case BodyRegion.HEAD:\n return {\n primary: { part: BodyPart.HEAD, percentage: 0.9 },\n secondary: [{ part: BodyPart.NECK, percentage: 0.1 }],\n };\n\n case BodyRegion.NECK:\n return {\n primary: { part: BodyPart.NECK, percentage: 0.8 },\n secondary: [\n { part: BodyPart.HEAD, percentage: 0.1 },\n { part: BodyPart.TORSO_UPPER, percentage: 0.1 },\n ],\n };\n\n case BodyRegion.TORSO:\n return {\n primary: { part: BodyPart.TORSO_UPPER, percentage: 0.6 },\n secondary: [{ part: BodyPart.TORSO_LOWER, percentage: 0.4 }],\n };\n\n case BodyRegion.CORE:\n return {\n primary: { part: BodyPart.TORSO_LOWER, percentage: 0.7 },\n secondary: [{ part: BodyPart.TORSO_UPPER, percentage: 0.3 }],\n };\n\n case BodyRegion.LEFT_ARM:\n return {\n primary: { part: BodyPart.ARM_LEFT, percentage: 0.9 },\n secondary: [{ part: BodyPart.TORSO_UPPER, percentage: 0.1 }],\n };\n\n case BodyRegion.RIGHT_ARM:\n return {\n primary: { part: BodyPart.ARM_RIGHT, percentage: 0.9 },\n secondary: [{ part: BodyPart.TORSO_UPPER, percentage: 0.1 }],\n };\n\n case BodyRegion.LEFT_LEG:\n return {\n primary: { part: BodyPart.LEG_LEFT, percentage: 0.9 },\n secondary: [{ part: BodyPart.TORSO_LOWER, percentage: 0.1 }],\n };\n\n case BodyRegion.RIGHT_LEG:\n return {\n primary: { part: BodyPart.LEG_RIGHT, percentage: 0.9 },\n secondary: [{ part: BodyPart.TORSO_LOWER, percentage: 0.1 }],\n };\n\n default:\n // Default to torso upper for unknown regions\n return {\n primary: { part: BodyPart.TORSO_UPPER, percentage: 1.0 },\n secondary: [],\n };\n }\n }\n\n /**\n * Apply distributed damage to body parts from a hit.\n * \n * **Korean**: 분산 피해 적용\n * \n * Takes total damage and distributes it across primary and secondary\n * body parts based on the hit location.\n * \n * @param current - Current body part health\n * @param bodyRegion - Hit location\n * @param totalDamage - Total damage to distribute\n * @param maxHealth - Optional maximum health limits\n * @returns Updated body part health\n * \n */\n applyDistributedDamage(\n current: BodyPartHealth,\n bodyRegion: BodyRegion,\n totalDamage: number,\n maxHealth?: BodyPartMaxHealth\n ): BodyPartHealth {\n const distribution = this.getDamageDistribution(bodyRegion);\n let updated = { ...current };\n\n // Apply primary damage\n updated = this.applyDamageToBodyPart(\n updated,\n distribution.primary.part,\n totalDamage * distribution.primary.percentage,\n maxHealth\n );\n\n // Apply secondary damage\n for (const secondary of distribution.secondary) {\n updated = this.applyDamageToBodyPart(\n updated,\n secondary.part,\n totalDamage * secondary.percentage,\n maxHealth\n );\n }\n\n return updated;\n }\n\n /**\n * Calculate combat capability effects from body part damage.\n * \n * **Korean**: 전투 능력 효과 계산\n * \n * Analyzes body part health and returns multipliers for various combat\n * capabilities. Implements acceptance criteria:\n * - Head <50%: Consciousness penalties\n * - Torso <50%: Stamina regen -50%\n * - Arms <50%: Attack damage -30%\n * - Legs <50%: Movement speed -40%\n * \n * @param health - Current body part health\n * @param maxHealth - Maximum health values\n * @returns Combat capability effect multipliers\n * \n */\n calculateBodyPartEffects(\n health: BodyPartHealth,\n maxHealth?: BodyPartMaxHealth\n ): BodyPartEffects {\n const max = maxHealth ?? this.createDefaultMaxHealth();\n\n // Calculate head/neck effects on consciousness\n const headPercentage = health.head / max.head;\n const neckPercentage = health.neck / max.neck;\n const minHeadNeck = Math.min(headPercentage, neckPercentage);\n \n let consciousnessModifier = 1.0;\n if (minHeadNeck < BODY_PART_EFFECT_CONSTANTS.HEAD.CRITICAL_THRESHOLD) {\n consciousnessModifier = BODY_PART_EFFECT_CONSTANTS.HEAD.CONSCIOUSNESS_PENALTY_AT_50;\n }\n\n // Calculate torso effects on stamina regeneration\n const torsoUpperPercentage = health.torsoUpper / max.torsoUpper;\n const torsoLowerPercentage = health.torsoLower / max.torsoLower;\n const avgTorso = (torsoUpperPercentage + torsoLowerPercentage) / 2;\n \n let staminaRegenModifier = 1.0;\n if (avgTorso < BODY_PART_EFFECT_CONSTANTS.TORSO.CRITICAL_THRESHOLD) {\n staminaRegenModifier = BODY_PART_EFFECT_CONSTANTS.TORSO.STAMINA_REGEN_PENALTY_AT_50;\n }\n\n // Calculate arm effects on attack damage\n const armLeftPercentage = health.armLeft / max.armLeft;\n const armRightPercentage = health.armRight / max.armRight;\n const avgArms = (armLeftPercentage + armRightPercentage) / 2;\n \n let attackDamageModifier = 1.0;\n if (avgArms < BODY_PART_EFFECT_CONSTANTS.ARMS.CRITICAL_THRESHOLD) {\n attackDamageModifier = BODY_PART_EFFECT_CONSTANTS.ARMS.ATTACK_DAMAGE_PENALTY_AT_50;\n }\n\n // Calculate leg effects on movement speed\n const legLeftPercentage = health.legLeft / max.legLeft;\n const legRightPercentage = health.legRight / max.legRight;\n const avgLegs = (legLeftPercentage + legRightPercentage) / 2;\n \n let movementSpeedModifier = 1.0;\n if (avgLegs < BODY_PART_EFFECT_CONSTANTS.LEGS.CRITICAL_THRESHOLD) {\n movementSpeedModifier = BODY_PART_EFFECT_CONSTANTS.LEGS.MOVEMENT_SPEED_PENALTY_AT_50;\n }\n\n // Balance affected by torso and legs\n const balanceModifier = Math.min(avgTorso, avgLegs);\n\n // Technique accuracy affected by head and arms\n const techniqueAccuracyModifier = Math.min(headPercentage, avgArms);\n\n return {\n consciousnessModifier,\n staminaRegenModifier,\n attackDamageModifier,\n movementSpeedModifier,\n balanceModifier,\n techniqueAccuracyModifier,\n };\n }\n\n /**\n * Calculate aggregate health from body parts.\n * \n * **Korean**: 종합 체력 계산\n * \n * Computes overall health as average of all body part health values.\n * This maintains backwards compatibility with single health bar systems.\n * \n * @param health - Body part health\n * @returns Aggregate health value (0-100)\n * \n */\n calculateAggregateHealth(health: BodyPartHealth): number {\n const sum =\n health.head +\n health.neck +\n health.torsoUpper +\n health.torsoLower +\n health.armLeft +\n health.armRight +\n health.legLeft +\n health.legRight;\n\n return sum / 8;\n }\n\n /**\n * Get body part status for UI display.\n * \n * **Korean**: 신체부위 상태 조회\n * \n * Provides UI-friendly status information including color coding,\n * status text, and critical/disabled flags.\n * \n * @param health - Current body part health\n * @param maxHealth - Maximum health values\n * @param part - Body part to query\n * @returns Status information for display\n * \n */\n getBodyPartStatus(\n health: BodyPartHealth,\n maxHealth: BodyPartMaxHealth,\n part: BodyPart\n ): BodyPartStatus {\n const current = health[part];\n const max = maxHealth[part];\n const percentage = current / max;\n\n // Determine status and color\n let status: KoreanText;\n let color: number;\n let critical: boolean;\n let disabled: boolean;\n\n if (percentage === 0) {\n status = { korean: \"무력화\", english: \"Disabled\" };\n color = KOREAN_COLORS.NEGATIVE_RED;\n critical = true;\n disabled = true;\n } else if (percentage < this.config.criticalThreshold) {\n status = { korean: \"치명적\", english: \"Critical\" };\n color = KOREAN_COLORS.NEGATIVE_RED;\n critical = true;\n disabled = false;\n } else if (percentage < this.config.severePenaltyThreshold) {\n status = { korean: \"심각\", english: \"Severe\" };\n color = KOREAN_COLORS.WARNING_ORANGE;\n critical = false;\n disabled = false;\n } else if (percentage < this.config.majorPenaltyThreshold) {\n status = { korean: \"중상\", english: \"Major\" };\n color = KOREAN_COLORS.WARNING_YELLOW;\n critical = false;\n disabled = false;\n } else if (percentage < this.config.minorPenaltyThreshold) {\n status = { korean: \"경상\", english: \"Minor\" };\n color = KOREAN_COLORS.HEALTH_FULL;\n critical = false;\n disabled = false;\n } else {\n status = { korean: \"정상\", english: \"Normal\" };\n color = KOREAN_COLORS.HEALTH_FULL;\n critical = false;\n disabled = false;\n }\n\n return {\n part,\n health: current,\n maxHealth: max,\n percentage,\n status,\n color,\n critical,\n disabled,\n };\n }\n\n /**\n * Get all body part statuses for UI.\n * \n * **Korean**: 모든 신체부위 상태 조회\n * \n * Returns status information for all body parts in a single call.\n * \n * @param health - Current body part health\n * @param maxHealth - Maximum health values\n * @returns Array of status information for all parts\n * \n */\n getAllBodyPartStatuses(\n health: BodyPartHealth,\n maxHealth: BodyPartMaxHealth\n ): BodyPartStatus[] {\n return Object.values(BodyPart).map((part) =>\n this.getBodyPartStatus(health, maxHealth, part)\n );\n }\n\n /**\n * Heal a specific body part.\n * \n * **Korean**: 신체부위 치유\n * \n * Increases health of the specified body part by the heal amount.\n * Health is clamped to 0-maxHealth range.\n * \n * @param current - Current body part health\n * @param bodyPart - Body part to heal\n * @param healAmount - Amount of health to restore\n * @param maxHealth - Optional maximum health limits\n * @returns Updated body part health\n * \n */\n healBodyPart(\n current: BodyPartHealth,\n bodyPart: BodyPart,\n healAmount: number,\n maxHealth?: BodyPartMaxHealth\n ): BodyPartHealth {\n const max = maxHealth ?? this.createDefaultMaxHealth();\n const newHealth = Math.min(max[bodyPart], current[bodyPart] + healAmount);\n\n return {\n ...current,\n [bodyPart]: newHealth,\n };\n }\n\n /**\n * Check if player is incapacitated based on body part health.\n * \n * **Korean**: 무력화 상태 확인\n * \n * Player is incapacitated if:\n * - Head is at 0 HP (unconscious)\n * - Both legs are at 0 HP (cannot move)\n * - Aggregate health below 10%\n * \n * @param health - Current body part health\n * @returns Whether player is incapacitated\n * \n */\n isIncapacitated(health: BodyPartHealth): boolean {\n // Unconscious from head damage\n if (health.head <= 0) return true;\n\n // Cannot move from leg damage\n if (health.legLeft <= 0 && health.legRight <= 0) return true;\n\n // Aggregate health critical\n const aggregate = this.calculateAggregateHealth(health);\n if (aggregate < 10) return true;\n\n return false;\n }\n}\n\n/**\n * Singleton instance of Body Part Health System.\n * \n * **Korean**: 신체부위 체력 시스템 싱글톤\n * \n * Provides global access to the body part health system throughout the game.\n * \n */\nexport const bodyPartHealthSystem = new BodyPartHealthSystem();\n"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"BodyPartPositionMapping.js","names":[],"sources":["../../../src/systems/bodypart/BodyPartPositionMapping.ts"],"sourcesContent":["/**\n * Body Part Position Mapping Utilities\n * \n * **Korean**: 신체 부위 위치 매핑 유틸리티\n * \n * Maps body regions and body parts to 3D positions on character models\n * for accurate trauma visualization placement.\n * \n * @module systems/bodypart/BodyPartPositionMapping\n * @category Body Part System\n * @korean 신체부위위치매핑\n */\n\nimport * as THREE from \"three\";\nimport { BodyPart } from \"./types\";\nimport { BodyRegion } from \"../../types/common\";\n\n/**\n * Standard character model dimensions.\n * \n * Based on average human proportions scaled for game character.\n * Character center is at [0, 0, 0], standing upright.\n */\nexport const CHARACTER_DIMENSIONS = {\n /** Total height from feet to head */\n HEIGHT: 2.0,\n /** Width at shoulders */\n SHOULDER_WIDTH: 0.6,\n /** Torso height */\n TORSO_HEIGHT: 0.8,\n /** Leg length */\n LEG_LENGTH: 1.0,\n /** Arm length */\n ARM_LENGTH: 0.7,\n /** Head height */\n HEAD_HEIGHT: 0.3,\n} as const;\n\n/**\n * Get 3D position for a body part on character model.\n * \n * **Korean**: 신체 부위 3D 위치 가져오기\n * \n * Returns center position of the body part relative to character origin.\n * Character is centered at [0, 0, 0] standing upright.\n * Positions are calculated from CHARACTER_DIMENSIONS for consistency.\n * \n * @param bodyPart - Body part to get position for\n * @returns 3D position vector\n * \n */\nexport function getBodyPartPosition(bodyPart: BodyPart): THREE.Vector3 {\n // Calculate positions from CHARACTER_DIMENSIONS\n const headY = CHARACTER_DIMENSIONS.HEIGHT - CHARACTER_DIMENSIONS.HEAD_HEIGHT / 2;\n const neckY = CHARACTER_DIMENSIONS.HEIGHT - CHARACTER_DIMENSIONS.HEAD_HEIGHT;\n const torsoTop = neckY - CHARACTER_DIMENSIONS.TORSO_HEIGHT / 3;\n const torsoBottom = neckY - (2 * CHARACTER_DIMENSIONS.TORSO_HEIGHT) / 3;\n const armY = neckY - CHARACTER_DIMENSIONS.TORSO_HEIGHT / 3;\n const legY = CHARACTER_DIMENSIONS.LEG_LENGTH / 2;\n \n switch (bodyPart) {\n case BodyPart.HEAD:\n return new THREE.Vector3(0, headY, 0);\n\n case BodyPart.NECK:\n return new THREE.Vector3(0, neckY, 0);\n\n case BodyPart.TORSO_UPPER:\n return new THREE.Vector3(0, torsoTop, 0);\n\n case BodyPart.TORSO_LOWER:\n return new THREE.Vector3(0, torsoBottom, 0);\n\n case BodyPart.ARM_LEFT:\n return new THREE.Vector3(-CHARACTER_DIMENSIONS.SHOULDER_WIDTH / 2, armY, 0);\n\n case BodyPart.ARM_RIGHT:\n return new THREE.Vector3(CHARACTER_DIMENSIONS.SHOULDER_WIDTH / 2, armY, 0);\n\n case BodyPart.LEG_LEFT:\n return new THREE.Vector3(-CHARACTER_DIMENSIONS.SHOULDER_WIDTH / 4, legY, 0);\n\n case BodyPart.LEG_RIGHT:\n return new THREE.Vector3(CHARACTER_DIMENSIONS.SHOULDER_WIDTH / 4, legY, 0);\n\n default:\n // Default to torso center\n return new THREE.Vector3(0, CHARACTER_DIMENSIONS.HEIGHT / 2, 0);\n }\n}\n\n/**\n * Get 3D position for a body region on character model.\n * \n * **Korean**: 신체 영역 3D 위치 가져오기\n * \n * @param bodyRegion - Body region to get position for\n * @returns 3D position vector\n * \n */\nexport function getBodyRegionPosition(bodyRegion: BodyRegion): THREE.Vector3 {\n // Calculate positions based on CHARACTER_DIMENSIONS for single source of truth\n const { HEIGHT, LEG_LENGTH, SHOULDER_WIDTH } = CHARACTER_DIMENSIONS;\n \n const headY = HEIGHT * 0.9; // 1.8 for HEIGHT 2.0\n const neckY = HEIGHT * 0.8; // 1.6 for HEIGHT 2.0\n const torsoY = HEIGHT * 0.6; // 1.2 for HEIGHT 2.0\n const coreY = HEIGHT * 0.45; // 0.9 for HEIGHT 2.0\n const armY = torsoY; // Arms at torso level\n const legY = LEG_LENGTH * 0.4; // 0.4 for LEG_LENGTH 1.0\n\n switch (bodyRegion) {\n case BodyRegion.HEAD:\n return new THREE.Vector3(0, headY, 0);\n\n case BodyRegion.NECK:\n return new THREE.Vector3(0, neckY, 0);\n\n case BodyRegion.TORSO:\n return new THREE.Vector3(0, torsoY, 0);\n\n case BodyRegion.CORE:\n return new THREE.Vector3(0, coreY, 0);\n\n case BodyRegion.LEFT_ARM:\n return new THREE.Vector3(-SHOULDER_WIDTH / 2, armY, 0);\n\n case BodyRegion.RIGHT_ARM:\n return new THREE.Vector3(SHOULDER_WIDTH / 2, armY, 0);\n\n case BodyRegion.LEFT_LEG:\n return new THREE.Vector3(-SHOULDER_WIDTH * 0.25, legY, 0);\n\n case BodyRegion.RIGHT_LEG:\n return new THREE.Vector3(SHOULDER_WIDTH * 0.25, legY, 0);\n\n default:\n // Default to torso center\n return new THREE.Vector3(0, torsoY, 0);\n }\n}\n\n/**\n * Map BodyRegion to corresponding BodyPart.\n * \n * **Korean**: 신체 영역을 신체 부위로 매핑\n * \n * @param bodyRegion - Body region to map\n * @returns Corresponding body part\n * \n */\nexport function mapBodyRegionToBodyPart(bodyRegion: BodyRegion): BodyPart {\n switch (bodyRegion) {\n case BodyRegion.HEAD:\n return BodyPart.HEAD;\n\n case BodyRegion.NECK:\n return BodyPart.NECK;\n\n case BodyRegion.TORSO:\n return BodyPart.TORSO_UPPER;\n\n case BodyRegion.CORE:\n return BodyPart.TORSO_LOWER;\n\n case BodyRegion.LEFT_ARM:\n return BodyPart.ARM_LEFT;\n\n case BodyRegion.RIGHT_ARM:\n return BodyPart.ARM_RIGHT;\n\n case BodyRegion.LEFT_LEG:\n return BodyPart.LEG_LEFT;\n\n case BodyRegion.RIGHT_LEG:\n return BodyPart.LEG_RIGHT;\n\n default:\n // Default to upper torso\n return BodyPart.TORSO_UPPER;\n }\n}\n\n/**\n * Add random offset to position for varied injury placement.\n * \n * **Korean**: 부상 위치에 무작위 오프셋 추가\n * \n * Adds slight randomness to injury positions to avoid exact overlap\n * and create more realistic trauma distribution.\n * \n * @param basePosition - Base position to offset from\n * @param maxOffset - Maximum offset in any direction (default: 0.1)\n * @returns New position with random offset\n * \n */\nexport function addRandomOffset(\n basePosition: THREE.Vector3,\n maxOffset: number = 0.1\n): THREE.Vector3 {\n const offset = new THREE.Vector3(\n (Math.random() - 0.5) * maxOffset * 2,\n (Math.random() - 0.5) * maxOffset * 2,\n (Math.random() - 0.5) * maxOffset * 2\n );\n\n return basePosition.clone().add(offset);\n}\n\n/**\n * Get injury position with slight randomization.\n * \n * **Korean**: 무작위 오프셋이 포함된 부상 위치 가져오기\n * \n * Convenience function that combines region-to-position mapping\n * with random offset for natural injury placement.\n * \n * @param bodyRegion - Body region that was hit\n * @param maxOffset - Maximum offset for randomization\n * @returns Position with random offset\n * \n */\nexport function getInjuryPositionWithOffset(\n bodyRegion: BodyRegion,\n maxOffset: number = 0.1\n): THREE.Vector3 {\n const basePosition = getBodyRegionPosition(bodyRegion);\n return addRandomOffset(basePosition, maxOffset);\n}\n\n/**\n * Check if position is within body part bounds.\n * \n * **Korean**: 위치가 신체 부위 경계 내에 있는지 확인\n * \n * @param position - Position to check\n * @param bodyPart - Body part to check against\n * @param tolerance - Distance tolerance (default: 0.3)\n * @returns Whether position is within body part bounds\n * \n */\nexport function isPositionInBodyPart(\n position: THREE.Vector3,\n bodyPart: BodyPart,\n tolerance: number = 0.3\n): boolean {\n const partPosition = getBodyPartPosition(bodyPart);\n const distance = position.distanceTo(partPosition);\n return distance <= tolerance;\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;AAuBA,IAAa,uBAAuB;;CAElC,QAAQ;;CAER,gBAAgB;;CAEhB,cAAc;;CAEd,YAAY;;CAEZ,YAAY;;CAEZ,aAAa;
|
|
1
|
+
{"version":3,"file":"BodyPartPositionMapping.js","names":[],"sources":["../../../src/systems/bodypart/BodyPartPositionMapping.ts"],"sourcesContent":["/**\n * Body Part Position Mapping Utilities\n * \n * **Korean**: 신체 부위 위치 매핑 유틸리티\n * \n * Maps body regions and body parts to 3D positions on character models\n * for accurate trauma visualization placement.\n * \n * @module systems/bodypart/BodyPartPositionMapping\n * @category Body Part System\n * @korean 신체부위위치매핑\n */\n\nimport * as THREE from \"three\";\nimport { BodyPart } from \"./types\";\nimport { BodyRegion } from \"../../types/common\";\n\n/**\n * Standard character model dimensions.\n * \n * Based on average human proportions scaled for game character.\n * Character center is at [0, 0, 0], standing upright.\n */\nexport const CHARACTER_DIMENSIONS = {\n /** Total height from feet to head */\n HEIGHT: 2.0,\n /** Width at shoulders */\n SHOULDER_WIDTH: 0.6,\n /** Torso height */\n TORSO_HEIGHT: 0.8,\n /** Leg length */\n LEG_LENGTH: 1.0,\n /** Arm length */\n ARM_LENGTH: 0.7,\n /** Head height */\n HEAD_HEIGHT: 0.3,\n} as const;\n\n/**\n * Get 3D position for a body part on character model.\n * \n * **Korean**: 신체 부위 3D 위치 가져오기\n * \n * Returns center position of the body part relative to character origin.\n * Character is centered at [0, 0, 0] standing upright.\n * Positions are calculated from CHARACTER_DIMENSIONS for consistency.\n * \n * @param bodyPart - Body part to get position for\n * @returns 3D position vector\n * \n */\nexport function getBodyPartPosition(bodyPart: BodyPart): THREE.Vector3 {\n // Calculate positions from CHARACTER_DIMENSIONS\n const headY = CHARACTER_DIMENSIONS.HEIGHT - CHARACTER_DIMENSIONS.HEAD_HEIGHT / 2;\n const neckY = CHARACTER_DIMENSIONS.HEIGHT - CHARACTER_DIMENSIONS.HEAD_HEIGHT;\n const torsoTop = neckY - CHARACTER_DIMENSIONS.TORSO_HEIGHT / 3;\n const torsoBottom = neckY - (2 * CHARACTER_DIMENSIONS.TORSO_HEIGHT) / 3;\n const armY = neckY - CHARACTER_DIMENSIONS.TORSO_HEIGHT / 3;\n const legY = CHARACTER_DIMENSIONS.LEG_LENGTH / 2;\n \n switch (bodyPart) {\n case BodyPart.HEAD:\n return new THREE.Vector3(0, headY, 0);\n\n case BodyPart.NECK:\n return new THREE.Vector3(0, neckY, 0);\n\n case BodyPart.TORSO_UPPER:\n return new THREE.Vector3(0, torsoTop, 0);\n\n case BodyPart.TORSO_LOWER:\n return new THREE.Vector3(0, torsoBottom, 0);\n\n case BodyPart.ARM_LEFT:\n return new THREE.Vector3(-CHARACTER_DIMENSIONS.SHOULDER_WIDTH / 2, armY, 0);\n\n case BodyPart.ARM_RIGHT:\n return new THREE.Vector3(CHARACTER_DIMENSIONS.SHOULDER_WIDTH / 2, armY, 0);\n\n case BodyPart.LEG_LEFT:\n return new THREE.Vector3(-CHARACTER_DIMENSIONS.SHOULDER_WIDTH / 4, legY, 0);\n\n case BodyPart.LEG_RIGHT:\n return new THREE.Vector3(CHARACTER_DIMENSIONS.SHOULDER_WIDTH / 4, legY, 0);\n\n default:\n // Default to torso center\n return new THREE.Vector3(0, CHARACTER_DIMENSIONS.HEIGHT / 2, 0);\n }\n}\n\n/**\n * Get 3D position for a body region on character model.\n * \n * **Korean**: 신체 영역 3D 위치 가져오기\n * \n * @param bodyRegion - Body region to get position for\n * @returns 3D position vector\n * \n */\nexport function getBodyRegionPosition(bodyRegion: BodyRegion): THREE.Vector3 {\n // Calculate positions based on CHARACTER_DIMENSIONS for single source of truth\n const { HEIGHT, LEG_LENGTH, SHOULDER_WIDTH } = CHARACTER_DIMENSIONS;\n \n const headY = HEIGHT * 0.9; // 1.8 for HEIGHT 2.0\n const neckY = HEIGHT * 0.8; // 1.6 for HEIGHT 2.0\n const torsoY = HEIGHT * 0.6; // 1.2 for HEIGHT 2.0\n const coreY = HEIGHT * 0.45; // 0.9 for HEIGHT 2.0\n const armY = torsoY; // Arms at torso level\n const legY = LEG_LENGTH * 0.4; // 0.4 for LEG_LENGTH 1.0\n\n switch (bodyRegion) {\n case BodyRegion.HEAD:\n return new THREE.Vector3(0, headY, 0);\n\n case BodyRegion.NECK:\n return new THREE.Vector3(0, neckY, 0);\n\n case BodyRegion.TORSO:\n return new THREE.Vector3(0, torsoY, 0);\n\n case BodyRegion.CORE:\n return new THREE.Vector3(0, coreY, 0);\n\n case BodyRegion.LEFT_ARM:\n return new THREE.Vector3(-SHOULDER_WIDTH / 2, armY, 0);\n\n case BodyRegion.RIGHT_ARM:\n return new THREE.Vector3(SHOULDER_WIDTH / 2, armY, 0);\n\n case BodyRegion.LEFT_LEG:\n return new THREE.Vector3(-SHOULDER_WIDTH * 0.25, legY, 0);\n\n case BodyRegion.RIGHT_LEG:\n return new THREE.Vector3(SHOULDER_WIDTH * 0.25, legY, 0);\n\n default:\n // Default to torso center\n return new THREE.Vector3(0, torsoY, 0);\n }\n}\n\n/**\n * Map BodyRegion to corresponding BodyPart.\n * \n * **Korean**: 신체 영역을 신체 부위로 매핑\n * \n * @param bodyRegion - Body region to map\n * @returns Corresponding body part\n * \n */\nexport function mapBodyRegionToBodyPart(bodyRegion: BodyRegion): BodyPart {\n switch (bodyRegion) {\n case BodyRegion.HEAD:\n return BodyPart.HEAD;\n\n case BodyRegion.NECK:\n return BodyPart.NECK;\n\n case BodyRegion.TORSO:\n return BodyPart.TORSO_UPPER;\n\n case BodyRegion.CORE:\n return BodyPart.TORSO_LOWER;\n\n case BodyRegion.LEFT_ARM:\n return BodyPart.ARM_LEFT;\n\n case BodyRegion.RIGHT_ARM:\n return BodyPart.ARM_RIGHT;\n\n case BodyRegion.LEFT_LEG:\n return BodyPart.LEG_LEFT;\n\n case BodyRegion.RIGHT_LEG:\n return BodyPart.LEG_RIGHT;\n\n default:\n // Default to upper torso\n return BodyPart.TORSO_UPPER;\n }\n}\n\n/**\n * Add random offset to position for varied injury placement.\n * \n * **Korean**: 부상 위치에 무작위 오프셋 추가\n * \n * Adds slight randomness to injury positions to avoid exact overlap\n * and create more realistic trauma distribution.\n * \n * @param basePosition - Base position to offset from\n * @param maxOffset - Maximum offset in any direction (default: 0.1)\n * @returns New position with random offset\n * \n */\nexport function addRandomOffset(\n basePosition: THREE.Vector3,\n maxOffset: number = 0.1\n): THREE.Vector3 {\n const offset = new THREE.Vector3(\n (Math.random() - 0.5) * maxOffset * 2,\n (Math.random() - 0.5) * maxOffset * 2,\n (Math.random() - 0.5) * maxOffset * 2\n );\n\n return basePosition.clone().add(offset);\n}\n\n/**\n * Get injury position with slight randomization.\n * \n * **Korean**: 무작위 오프셋이 포함된 부상 위치 가져오기\n * \n * Convenience function that combines region-to-position mapping\n * with random offset for natural injury placement.\n * \n * @param bodyRegion - Body region that was hit\n * @param maxOffset - Maximum offset for randomization\n * @returns Position with random offset\n * \n */\nexport function getInjuryPositionWithOffset(\n bodyRegion: BodyRegion,\n maxOffset: number = 0.1\n): THREE.Vector3 {\n const basePosition = getBodyRegionPosition(bodyRegion);\n return addRandomOffset(basePosition, maxOffset);\n}\n\n/**\n * Check if position is within body part bounds.\n * \n * **Korean**: 위치가 신체 부위 경계 내에 있는지 확인\n * \n * @param position - Position to check\n * @param bodyPart - Body part to check against\n * @param tolerance - Distance tolerance (default: 0.3)\n * @returns Whether position is within body part bounds\n * \n */\nexport function isPositionInBodyPart(\n position: THREE.Vector3,\n bodyPart: BodyPart,\n tolerance: number = 0.3\n): boolean {\n const partPosition = getBodyPartPosition(bodyPart);\n const distance = position.distanceTo(partPosition);\n return distance <= tolerance;\n}\n"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"CombatInjuryIntegration.js","names":[],"sources":["../../../src/systems/bodypart/CombatInjuryIntegration.ts"],"sourcesContent":["/**\n * Combat Injury Integration\n * \n * **Korean**: 전투 부상 통합\n * \n * Integrates InjuryTracker with combat damage events to automatically\n * record injuries and trigger visual trauma effects during combat.\n * \n * @module systems/bodypart/CombatInjuryIntegration\n * @category Body Part System\n * @korean 전투부상통합\n */\n\nimport * as THREE from \"three\";\nimport { BodyRegion, DamageType } from \"../../types/common\";\nimport { InjuryTracker } from \"./InjuryTracker\";\nimport {\n getInjuryPositionWithOffset,\n mapBodyRegionToBodyPart,\n} from \"./BodyPartPositionMapping\";\nimport { InjuryType } from \"../../types/injury\";\n\n/**\n * Combat damage event data.\n * \n * **Korean**: 전투 피해 이벤트 데이터\n * \n */\nexport interface CombatDamageEvent {\n /** Damage amount (0-100) */\n readonly damage: number;\n /** Body region hit */\n readonly bodyRegion: BodyRegion;\n /** Damage type */\n readonly damageType?: DamageType;\n /** Optional specific position override */\n readonly position?: THREE.Vector3;\n /** Whether this is a critical hit */\n readonly isCritical?: boolean;\n}\n\n/**\n * Configuration for combat injury integration.\n * \n */\nexport interface CombatInjuryConfig {\n /** Enable automatic injury tracking */\n readonly enabled: boolean;\n /** Minimum damage to create injury */\n readonly minDamage: number;\n /** Damage threshold for blood effects */\n readonly bloodThreshold: number;\n /** \n * InjuryTracker instance to use for this integration.\n * Must be explicitly provided per character; singleton usage is no longer supported.\n */\n readonly tracker: InjuryTracker;\n}\n\n/**\n * Default combat injury configuration.\n * \n * Note: A valid `tracker` must still be supplied by the caller.\n * This provides defaults for other configuration values only.\n * \n */\nexport const DEFAULT_COMBAT_INJURY_CONFIG: Omit<CombatInjuryConfig, 'tracker'> = {\n enabled: true,\n minDamage: 5,\n bloodThreshold: 30,\n} as const;\n\n/**\n * Combat Injury Integration Handler.\n * \n * **Korean**: 전투 부상 통합 핸들러\n * \n * Processes combat damage events and records injuries for visualization.\n * Automatically maps damage types to injury types and applies blood effects.\n * \n * @example\n * ```typescript\n * // Recommended: Use PlayerInjuryTrackingManager for per-player tracking\n * import { playerInjuryManager } from '@/systems/bodypart';\n * const integration = playerInjuryManager.getIntegrationForPlayer('player-1');\n * \n * // Or create with explicit tracker for testing/custom scenarios\n * const handler = new CombatInjuryIntegration({\n * ...DEFAULT_COMBAT_INJURY_CONFIG,\n * tracker: new InjuryTracker(),\n * });\n * \n * // Record combat damage\n * handler.recordCombatDamage({\n * damage: 35,\n * bodyRegion: BodyRegion.TORSO,\n * damageType: DamageType.BLUNT,\n * });\n * \n * // Get injuries for visualization\n * const injuries = handler.getInjuries();\n * ```\n * \n */\nexport class CombatInjuryIntegration {\n private tracker: InjuryTracker;\n private config: CombatInjuryConfig;\n\n constructor(config: CombatInjuryConfig) {\n this.config = config;\n // Tracker is now required in CombatInjuryConfig interface\n this.tracker = config.tracker;\n }\n\n /**\n * Record injury from combat damage event.\n * \n * **Korean**: 전투 피해로부터 부상 기록\n * \n * Automatically determines injury type from damage type and applies\n * appropriate visual effects.\n * \n * @param event - Combat damage event\n * @returns Whether injury was recorded (false if damage too low)\n * \n */\n recordCombatDamage(event: CombatDamageEvent): boolean {\n if (!this.config.enabled) {\n return false;\n }\n\n if (event.damage < this.config.minDamage) {\n return false;\n }\n\n // Determine injury type from damage type\n const injuryType = this.getInjuryTypeFromDamage(event.damageType);\n\n // Get position for injury\n const position =\n event.position ?? getInjuryPositionWithOffset(event.bodyRegion, 0.15);\n\n // Map body region to body part\n const bodyPart = mapBodyRegionToBodyPart(event.bodyRegion);\n\n // Record injury\n const recordedInjury = this.tracker.recordInjury(\n bodyPart,\n event.bodyRegion,\n position,\n event.damage,\n injuryType\n );\n\n // Return true only if the injury was actually recorded\n return recordedInjury !== null;\n }\n\n /**\n * Check if damage should trigger blood effects.\n * \n * **Korean**: 피해가 출혈 효과를 발생시켜야 하는지 확인\n * \n * @param damage - Damage amount\n * @returns Whether to show blood effects\n * \n */\n shouldShowBloodEffect(damage: number): boolean {\n return damage > this.config.bloodThreshold;\n }\n\n /**\n * Get injury type from damage type.\n * \n * **Korean**: 피해 타입으로부터 부상 타입 가져오기\n * \n * @param damageType - Type of damage dealt\n * @returns Corresponding injury type for visualization\n * \n * @private\n */\n private getInjuryTypeFromDamage(damageType?: DamageType): InjuryType {\n if (!damageType) {\n return InjuryType.BRUISE; // Default to bruise\n }\n\n switch (damageType) {\n case DamageType.BLUNT:\n case DamageType.IMPACT:\n case DamageType.CRUSHING:\n return InjuryType.BRUISE;\n\n case DamageType.PIERCING:\n case DamageType.SHARP:\n return InjuryType.CUT;\n\n case DamageType.SLASHING:\n return InjuryType.LACERATION;\n\n case DamageType.JOINT:\n return InjuryType.FRACTURE;\n\n default:\n return InjuryType.BRUISE;\n }\n }\n\n /**\n * Get all tracked injuries.\n * \n * **Korean**: 모든 추적된 부상 가져오기\n * \n * @returns Array of injuries\n * \n */\n getInjuries() {\n return this.tracker.getInjuries();\n }\n\n /**\n * Get tracker instance.\n * \n * @returns InjuryTracker instance\n * \n */\n getTracker(): InjuryTracker {\n return this.tracker;\n }\n\n /**\n * Clear all injuries (for new round/match).\n * \n * **Korean**: 모든 부상 초기화\n * \n */\n clearInjuries(): void {\n this.tracker.clearInjuries();\n }\n\n /**\n * Remove expired injuries.\n * \n * **Korean**: 만료된 부상 제거\n * \n */\n removeExpiredInjuries(): void {\n this.tracker.removeExpiredInjuries();\n }\n}\n"],"mappings":";;;;;;;;;;;AAkEA,IAAa,+BAAoE;CAC/E,SAAS;CACT,WAAW;CACX,gBAAgB;
|
|
1
|
+
{"version":3,"file":"CombatInjuryIntegration.js","names":[],"sources":["../../../src/systems/bodypart/CombatInjuryIntegration.ts"],"sourcesContent":["/**\n * Combat Injury Integration\n * \n * **Korean**: 전투 부상 통합\n * \n * Integrates InjuryTracker with combat damage events to automatically\n * record injuries and trigger visual trauma effects during combat.\n * \n * @module systems/bodypart/CombatInjuryIntegration\n * @category Body Part System\n * @korean 전투부상통합\n */\n\nimport * as THREE from \"three\";\nimport { BodyRegion, DamageType } from \"../../types/common\";\nimport { InjuryTracker } from \"./InjuryTracker\";\nimport {\n getInjuryPositionWithOffset,\n mapBodyRegionToBodyPart,\n} from \"./BodyPartPositionMapping\";\nimport { InjuryType } from \"../../types/injury\";\n\n/**\n * Combat damage event data.\n * \n * **Korean**: 전투 피해 이벤트 데이터\n * \n */\nexport interface CombatDamageEvent {\n /** Damage amount (0-100) */\n readonly damage: number;\n /** Body region hit */\n readonly bodyRegion: BodyRegion;\n /** Damage type */\n readonly damageType?: DamageType;\n /** Optional specific position override */\n readonly position?: THREE.Vector3;\n /** Whether this is a critical hit */\n readonly isCritical?: boolean;\n}\n\n/**\n * Configuration for combat injury integration.\n * \n */\nexport interface CombatInjuryConfig {\n /** Enable automatic injury tracking */\n readonly enabled: boolean;\n /** Minimum damage to create injury */\n readonly minDamage: number;\n /** Damage threshold for blood effects */\n readonly bloodThreshold: number;\n /** \n * InjuryTracker instance to use for this integration.\n * Must be explicitly provided per character; singleton usage is no longer supported.\n */\n readonly tracker: InjuryTracker;\n}\n\n/**\n * Default combat injury configuration.\n * \n * Note: A valid `tracker` must still be supplied by the caller.\n * This provides defaults for other configuration values only.\n * \n */\nexport const DEFAULT_COMBAT_INJURY_CONFIG: Omit<CombatInjuryConfig, 'tracker'> = {\n enabled: true,\n minDamage: 5,\n bloodThreshold: 30,\n} as const;\n\n/**\n * Combat Injury Integration Handler.\n * \n * **Korean**: 전투 부상 통합 핸들러\n * \n * Processes combat damage events and records injuries for visualization.\n * Automatically maps damage types to injury types and applies blood effects.\n * \n * @example\n * ```typescript\n * // Recommended: Use PlayerInjuryTrackingManager for per-player tracking\n * import { playerInjuryManager } from '@/systems/bodypart';\n * const integration = playerInjuryManager.getIntegrationForPlayer('player-1');\n * \n * // Or create with explicit tracker for testing/custom scenarios\n * const handler = new CombatInjuryIntegration({\n * ...DEFAULT_COMBAT_INJURY_CONFIG,\n * tracker: new InjuryTracker(),\n * });\n * \n * // Record combat damage\n * handler.recordCombatDamage({\n * damage: 35,\n * bodyRegion: BodyRegion.TORSO,\n * damageType: DamageType.BLUNT,\n * });\n * \n * // Get injuries for visualization\n * const injuries = handler.getInjuries();\n * ```\n * \n */\nexport class CombatInjuryIntegration {\n private tracker: InjuryTracker;\n private config: CombatInjuryConfig;\n\n constructor(config: CombatInjuryConfig) {\n this.config = config;\n // Tracker is now required in CombatInjuryConfig interface\n this.tracker = config.tracker;\n }\n\n /**\n * Record injury from combat damage event.\n * \n * **Korean**: 전투 피해로부터 부상 기록\n * \n * Automatically determines injury type from damage type and applies\n * appropriate visual effects.\n * \n * @param event - Combat damage event\n * @returns Whether injury was recorded (false if damage too low)\n * \n */\n recordCombatDamage(event: CombatDamageEvent): boolean {\n if (!this.config.enabled) {\n return false;\n }\n\n if (event.damage < this.config.minDamage) {\n return false;\n }\n\n // Determine injury type from damage type\n const injuryType = this.getInjuryTypeFromDamage(event.damageType);\n\n // Get position for injury\n const position =\n event.position ?? getInjuryPositionWithOffset(event.bodyRegion, 0.15);\n\n // Map body region to body part\n const bodyPart = mapBodyRegionToBodyPart(event.bodyRegion);\n\n // Record injury\n const recordedInjury = this.tracker.recordInjury(\n bodyPart,\n event.bodyRegion,\n position,\n event.damage,\n injuryType\n );\n\n // Return true only if the injury was actually recorded\n return recordedInjury !== null;\n }\n\n /**\n * Check if damage should trigger blood effects.\n * \n * **Korean**: 피해가 출혈 효과를 발생시켜야 하는지 확인\n * \n * @param damage - Damage amount\n * @returns Whether to show blood effects\n * \n */\n shouldShowBloodEffect(damage: number): boolean {\n return damage > this.config.bloodThreshold;\n }\n\n /**\n * Get injury type from damage type.\n * \n * **Korean**: 피해 타입으로부터 부상 타입 가져오기\n * \n * @param damageType - Type of damage dealt\n * @returns Corresponding injury type for visualization\n * \n * @private\n */\n private getInjuryTypeFromDamage(damageType?: DamageType): InjuryType {\n if (!damageType) {\n return InjuryType.BRUISE; // Default to bruise\n }\n\n switch (damageType) {\n case DamageType.BLUNT:\n case DamageType.IMPACT:\n case DamageType.CRUSHING:\n return InjuryType.BRUISE;\n\n case DamageType.PIERCING:\n case DamageType.SHARP:\n return InjuryType.CUT;\n\n case DamageType.SLASHING:\n return InjuryType.LACERATION;\n\n case DamageType.JOINT:\n return InjuryType.FRACTURE;\n\n default:\n return InjuryType.BRUISE;\n }\n }\n\n /**\n * Get all tracked injuries.\n * \n * **Korean**: 모든 추적된 부상 가져오기\n * \n * @returns Array of injuries\n * \n */\n getInjuries() {\n return this.tracker.getInjuries();\n }\n\n /**\n * Get tracker instance.\n * \n * @returns InjuryTracker instance\n * \n */\n getTracker(): InjuryTracker {\n return this.tracker;\n }\n\n /**\n * Clear all injuries (for new round/match).\n * \n * **Korean**: 모든 부상 초기화\n * \n */\n clearInjuries(): void {\n this.tracker.clearInjuries();\n }\n\n /**\n * Remove expired injuries.\n * \n * **Korean**: 만료된 부상 제거\n * \n */\n removeExpiredInjuries(): void {\n this.tracker.removeExpiredInjuries();\n }\n}\n"],"mappings":";;;;;;;;;;;AAkEA,IAAa,+BAAoE;CAC/E,SAAS;CACT,WAAW;CACX,gBAAgB;AAClB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAkCA,IAAa,0BAAb,MAAqC;CACnC;CACA;CAEA,YAAY,QAA4B;EACtC,KAAK,SAAS;EAEd,KAAK,UAAU,OAAO;CACxB;;;;;;;;;;;;;CAcA,mBAAmB,OAAmC;EACpD,IAAI,CAAC,KAAK,OAAO,SACf,OAAO;EAGT,IAAI,MAAM,SAAS,KAAK,OAAO,WAC7B,OAAO;EAIT,MAAM,aAAa,KAAK,wBAAwB,MAAM,UAAU;EAGhE,MAAM,WACJ,MAAM,YAAY,4BAA4B,MAAM,YAAY,GAAI;EAGtE,MAAM,WAAW,wBAAwB,MAAM,UAAU;EAYzD,OATuB,KAAK,QAAQ,aAClC,UACA,MAAM,YACN,UACA,MAAM,QACN,UAIK,MAAmB;CAC5B;;;;;;;;;;CAWA,sBAAsB,QAAyB;EAC7C,OAAO,SAAS,KAAK,OAAO;CAC9B;;;;;;;;;;;CAYA,wBAAgC,YAAqC;EACnE,IAAI,CAAC,YACH,OAAO,WAAW;EAGpB,QAAQ,YAAR;GACE,KAAK,WAAW;GAChB,KAAK,WAAW;GAChB,KAAK,WAAW,UACd,OAAO,WAAW;GAEpB,KAAK,WAAW;GAChB,KAAK,WAAW,OACd,OAAO,WAAW;GAEpB,KAAK,WAAW,UACd,OAAO,WAAW;GAEpB,KAAK,WAAW,OACd,OAAO,WAAW;GAEpB,SACE,OAAO,WAAW;EACtB;CACF;;;;;;;;;CAUA,cAAc;EACZ,OAAO,KAAK,QAAQ,YAAY;CAClC;;;;;;;CAQA,aAA4B;EAC1B,OAAO,KAAK;CACd;;;;;;;CAQA,gBAAsB;EACpB,KAAK,QAAQ,cAAc;CAC7B;;;;;;;CAQA,wBAA8B;EAC5B,KAAK,QAAQ,sBAAsB;CACrC;AACF"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"InjuryIntegration.js","names":[],"sources":["../../../src/systems/bodypart/InjuryIntegration.ts"],"sourcesContent":["/**\n * Injury Integration Utilities\n * \n * **Korean**: 부상 통합 유틸리티\n * \n * Utilities to convert between InjuryTracker format and TraumaOverlay3D format,\n * enabling seamless integration of injury tracking with visual trauma display.\n * \n * @module systems/bodypart/InjuryIntegration\n * @category Body Part System\n * @korean 부상통합\n */\n\nimport { InjuryLocation } from \"./InjuryTracker\";\nimport { Injury } from \"../../types/injury\";\n\n/**\n * Convert InjuryLocation to TraumaOverlay3D Injury format.\n * \n * **Korean**: InjuryLocation을 TraumaOverlay3D Injury 형식으로 변환\n * \n * @param injuryLocation - Injury from InjuryTracker\n * @param playerId - Optional player ID for multi-player scenarios\n * @returns Injury in TraumaOverlay3D format\n * \n */\nexport function convertInjuryForVisualization(\n injuryLocation: InjuryLocation,\n playerId?: string | number\n): Injury {\n return {\n id: injuryLocation.id,\n region: injuryLocation.bodyRegion,\n type: injuryLocation.type,\n position: [\n injuryLocation.position.x,\n injuryLocation.position.y,\n injuryLocation.position.z,\n ],\n severity: injuryLocation.severity / 100, // Convert 0-100 to 0-1\n hitCount: injuryLocation.hitCount,\n timestamp: injuryLocation.timestamp,\n playerId,\n };\n}\n\n/**\n * Convert array of InjuryLocations to TraumaOverlay3D Injuries.\n * \n * **Korean**: InjuryLocation 배열을 TraumaOverlay3D Injuries로 변환\n * \n * @param injuryLocations - Array of injuries from InjuryTracker\n * @param playerId - Optional player ID\n * @returns Array of injuries in TraumaOverlay3D format\n * \n */\nexport function convertInjuriesForVisualization(\n injuryLocations: InjuryLocation[],\n playerId?: string | number\n): Injury[] {\n return injuryLocations.map((loc) =>\n convertInjuryForVisualization(loc, playerId)\n );\n}\n"],"mappings":";;;;;;;;;;;AA0BA,SAAgB,8BACd,gBACA,UACQ;CACR,OAAO;EACL,IAAI,eAAe;EACnB,QAAQ,eAAe;EACvB,MAAM,eAAe;EACrB,UAAU;GACR,eAAe,SAAS;GACxB,eAAe,SAAS;GACxB,eAAe,SAAS;
|
|
1
|
+
{"version":3,"file":"InjuryIntegration.js","names":[],"sources":["../../../src/systems/bodypart/InjuryIntegration.ts"],"sourcesContent":["/**\n * Injury Integration Utilities\n * \n * **Korean**: 부상 통합 유틸리티\n * \n * Utilities to convert between InjuryTracker format and TraumaOverlay3D format,\n * enabling seamless integration of injury tracking with visual trauma display.\n * \n * @module systems/bodypart/InjuryIntegration\n * @category Body Part System\n * @korean 부상통합\n */\n\nimport { InjuryLocation } from \"./InjuryTracker\";\nimport { Injury } from \"../../types/injury\";\n\n/**\n * Convert InjuryLocation to TraumaOverlay3D Injury format.\n * \n * **Korean**: InjuryLocation을 TraumaOverlay3D Injury 형식으로 변환\n * \n * @param injuryLocation - Injury from InjuryTracker\n * @param playerId - Optional player ID for multi-player scenarios\n * @returns Injury in TraumaOverlay3D format\n * \n */\nexport function convertInjuryForVisualization(\n injuryLocation: InjuryLocation,\n playerId?: string | number\n): Injury {\n return {\n id: injuryLocation.id,\n region: injuryLocation.bodyRegion,\n type: injuryLocation.type,\n position: [\n injuryLocation.position.x,\n injuryLocation.position.y,\n injuryLocation.position.z,\n ],\n severity: injuryLocation.severity / 100, // Convert 0-100 to 0-1\n hitCount: injuryLocation.hitCount,\n timestamp: injuryLocation.timestamp,\n playerId,\n };\n}\n\n/**\n * Convert array of InjuryLocations to TraumaOverlay3D Injuries.\n * \n * **Korean**: InjuryLocation 배열을 TraumaOverlay3D Injuries로 변환\n * \n * @param injuryLocations - Array of injuries from InjuryTracker\n * @param playerId - Optional player ID\n * @returns Array of injuries in TraumaOverlay3D format\n * \n */\nexport function convertInjuriesForVisualization(\n injuryLocations: InjuryLocation[],\n playerId?: string | number\n): Injury[] {\n return injuryLocations.map((loc) =>\n convertInjuryForVisualization(loc, playerId)\n );\n}\n"],"mappings":";;;;;;;;;;;AA0BA,SAAgB,8BACd,gBACA,UACQ;CACR,OAAO;EACL,IAAI,eAAe;EACnB,QAAQ,eAAe;EACvB,MAAM,eAAe;EACrB,UAAU;GACR,eAAe,SAAS;GACxB,eAAe,SAAS;GACxB,eAAe,SAAS;EAC1B;EACA,UAAU,eAAe,WAAW;EACpC,UAAU,eAAe;EACzB,WAAW,eAAe;EAC1B;CACF;AACF;;;;;;;;;;;AAYA,SAAgB,gCACd,iBACA,UACU;CACV,OAAO,gBAAgB,KAAK,QAC1B,8BAA8B,KAAK,QAAQ,CAC7C;AACF"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"InjuryTracker.js","names":[],"sources":["../../../src/systems/bodypart/InjuryTracker.ts"],"sourcesContent":["/**\n * Injury Tracking System\n * \n * **Korean**: 부상 추적 시스템\n * \n * Tracks individual injuries on character models for realistic trauma visualization.\n * Records injury location, type, severity, and timestamp for progressive bruising,\n * cuts, and bleeding effects during combat.\n * \n * ## Features\n * \n * - Track injuries by body part and 3D position\n * - Progressive bruising: Multiple hits to same location darken existing bruises\n * - Color-coded severity (getBruiseColor): Yellow (fresh), Purple (moderate), Dark red (severe)\n * - Note: TraumaOverlay3D uses different progression: Dark red → Indigo → Black\n * - Blood effects triggered when damage > 30 in single hit\n * - Injury persistence across combat rounds\n * - Nearby injury lookup using linear scan over tracked injuries (O(n))\n * \n * @module systems/bodypart/InjuryTracker\n * @category Body Part System\n * @korean 부상추적시스템\n */\n\nimport * as THREE from \"three\";\nimport { BodyPart } from \"./types\";\nimport { InjuryType } from \"../../types/injury\";\nimport { BodyRegion } from \"../../types/common\";\n\n/**\n * Individual injury location data.\n * \n * **Korean**: 부상 위치 데이터\n * \n * Records a single injury with its location, severity, and cumulative hit count.\n * Used for progressive bruising visualization.\n * \n * @category Injury Tracking\n * @korean 부상위치\n */\nexport interface InjuryLocation {\n /** Unique identifier */\n readonly id: string;\n /** Body part affected */\n readonly bodyPart: BodyPart;\n /** Body region for hit detection */\n readonly bodyRegion: BodyRegion;\n /** 3D position relative to character center */\n readonly position: THREE.Vector3;\n /** Damage severity (0-100) */\n readonly severity: number;\n /** Number of hits to this location (for progressive bruising) */\n readonly hitCount: number;\n /** Timestamp when injury occurred */\n readonly timestamp: number;\n /** Injury type */\n readonly type: InjuryType;\n}\n\n/**\n * Configuration for injury tracking behavior.\n * \n * @category Injury Tracking\n */\nexport interface InjuryTrackerConfig {\n /** Maximum number of injuries to track per character */\n readonly maxInjuries: number;\n /** Distance threshold for considering injuries at same location (in units) */\n readonly sameLocationThreshold: number;\n /** Minimum damage required to create an injury */\n readonly minDamageForInjury: number;\n /** Damage threshold for blood effects */\n readonly bloodEffectThreshold: number;\n /** Time before injuries are removed/expired (milliseconds) */\n readonly injuryExpirationTimeMs: number;\n}\n\n/**\n * Default injury tracker configuration.\n * \n */\nexport const DEFAULT_INJURY_TRACKER_CONFIG: InjuryTrackerConfig = {\n maxInjuries: 50, // Reasonable limit for performance\n sameLocationThreshold: 0.6, // 0.6 units distance (accommodates ±0.15 randomization)\n minDamageForInjury: 5, // Minimum 5 damage to show injury\n bloodEffectThreshold: 30, // Blood effects when damage > 30\n injuryExpirationTimeMs: 30000, // Injuries are removed after 30 seconds\n} as const;\n\n/**\n * Injury Tracker System.\n * \n * **Korean**: 부상 추적 시스템\n * \n * Manages injury recording and retrieval for trauma visualization.\n * Implements progressive bruising by tracking hit counts at similar locations.\n * \n * @example\n * ```typescript\n * const tracker = new InjuryTracker();\n * \n * // Record injury from hit\n * const injury = tracker.recordInjury(\n * BodyPart.TORSO_UPPER,\n * BodyRegion.TORSO,\n * new THREE.Vector3(0, 1.5, 0),\n * 25,\n * InjuryType.BRUISE\n * );\n * \n * // Get all injuries for visualization\n * const injuries = tracker.getInjuries();\n * ```\n * \n * @category Body Part System\n */\nexport class InjuryTracker {\n private injuries: Map<string, InjuryLocation>;\n private config: InjuryTrackerConfig;\n private nextId: number;\n\n constructor(config: InjuryTrackerConfig = DEFAULT_INJURY_TRACKER_CONFIG) {\n this.injuries = new Map();\n this.config = config;\n this.nextId = 0;\n }\n\n /**\n * Record a new injury or update existing one at similar location.\n * \n * **Korean**: 부상 기록\n * \n * If an injury exists near the hit position, it updates the existing injury\n * with increased severity and hit count (progressive bruising). Otherwise,\n * creates a new injury.\n * \n * @param bodyPart - Body part affected\n * @param bodyRegion - Body region for damage distribution\n * @param position - 3D position relative to character center\n * @param damage - Damage amount (0-100)\n * @param type - Type of injury\n * @returns The created or updated injury, or null if damage is below threshold\n * \n */\n recordInjury(\n bodyPart: BodyPart,\n bodyRegion: BodyRegion,\n position: THREE.Vector3,\n damage: number,\n type: InjuryType\n ): InjuryLocation | null {\n // Check if damage is significant enough to track\n if (damage < this.config.minDamageForInjury) {\n return null;\n }\n\n // Find nearby injury at similar location (same body part)\n const existing = this.findNearbyInjury(bodyPart, position, this.config.sameLocationThreshold);\n\n // Only merge injuries that also match type and body region to avoid\n // incorrectly combining different injury types at the same spot\n const shouldMerge =\n !!existing &&\n existing.type === type &&\n existing.bodyRegion === bodyRegion;\n\n if (shouldMerge && existing) {\n // Progressive bruising - update existing injury\n const newSeverity = Math.min(100, existing.severity + damage / 2);\n const newHitCount = existing.hitCount + 1;\n\n const updated: InjuryLocation = {\n ...existing,\n severity: newSeverity,\n hitCount: newHitCount,\n timestamp: Date.now(),\n };\n\n this.injuries.set(existing.id, updated);\n return updated;\n } else {\n // Create new injury\n const id = `injury-${this.nextId++}`;\n const injury: InjuryLocation = {\n id,\n bodyPart,\n bodyRegion,\n position: position.clone(),\n severity: damage,\n hitCount: 1,\n timestamp: Date.now(),\n type,\n };\n\n this.injuries.set(id, injury);\n\n // Enforce max injuries limit\n if (this.injuries.size > this.config.maxInjuries) {\n this.removeOldestInjury();\n }\n\n return injury;\n }\n }\n\n /**\n * Find injury near a given position on the same body part.\n * \n * **Korean**: 인근 부상 찾기\n * \n * @param bodyPart - Body part to search\n * @param position - Position to search near\n * @param threshold - Distance threshold\n * @returns Nearby injury or null\n * \n */\n findNearbyInjury(\n bodyPart: BodyPart,\n position: THREE.Vector3,\n threshold: number\n ): InjuryLocation | null {\n let closestInjury: InjuryLocation | null = null;\n let closestDistance = threshold;\n\n for (const injury of this.injuries.values()) {\n if (injury.bodyPart === bodyPart) {\n const distance = injury.position.distanceTo(position);\n if (distance < closestDistance) {\n closestDistance = distance;\n closestInjury = injury;\n }\n }\n }\n\n return closestInjury;\n }\n\n /**\n * Get all tracked injuries.\n * \n * **Korean**: 모든 부상 조회\n * \n * @returns Array of all injuries\n * \n */\n getInjuries(): InjuryLocation[] {\n return Array.from(this.injuries.values());\n }\n\n /**\n * Get injuries for a specific body part.\n * \n * **Korean**: 신체 부위 부상 조회\n * \n * @param bodyPart - Body part to query\n * @returns Array of injuries on that body part\n * \n */\n getInjuriesByBodyPart(bodyPart: BodyPart): InjuryLocation[] {\n return this.getInjuries().filter((injury) => injury.bodyPart === bodyPart);\n }\n\n /**\n * Get bruise color based on severity and hit count.\n * \n * **Korean**: 타박상 색상 가져오기\n * \n * Progressive color scheme (for reference - TraumaOverlay3D uses its own colors):\n * - Light bruising (severity < 20): Yellow (#ffeb3b)\n * - Moderate bruising (severity < 50): Purple (#9c27b0)\n * - Severe bruising (severity >= 50): Dark red (#b71c1c)\n * \n * Note: TraumaOverlay3D uses Dark red → Indigo → Black progression.\n * Consider using TraumaOverlay3D's getBruiseColor for consistent visualization.\n * \n * @param severity - Injury severity (0-100)\n * @param hitCount - Number of hits to same location\n * @returns Hex color string\n * \n */\n getBruiseColor(severity: number, hitCount: number): string {\n // Progressive darkening with hit count (subtract 1 since first hit shouldn't darken)\n const effectiveSeverity = severity + Math.max(0, hitCount - 1) * 10;\n\n if (effectiveSeverity < 20) {\n return \"#ffeb3b\"; // Yellow - light bruising\n } else if (effectiveSeverity < 50) {\n return \"#9c27b0\"; // Purple - moderate bruising\n } else {\n return \"#b71c1c\"; // Dark red - severe bruising\n }\n }\n\n /**\n * Check if damage should trigger blood effects.\n * \n * **Korean**: 출혈 효과 필요 여부 확인\n * \n * @param damage - Damage amount\n * @returns Whether to show blood effects\n * \n */\n shouldShowBloodEffect(damage: number): boolean {\n return damage > this.config.bloodEffectThreshold;\n }\n\n /**\n * Remove oldest injury to maintain performance.\n * \n * @private\n */\n private removeOldestInjury(): void {\n let oldestId: string | null = null;\n let oldestTimestamp = Infinity;\n\n for (const [id, injury] of this.injuries.entries()) {\n if (injury.timestamp < oldestTimestamp) {\n oldestTimestamp = injury.timestamp;\n oldestId = id;\n }\n }\n\n if (oldestId) {\n this.injuries.delete(oldestId);\n }\n }\n\n /**\n * Clear all injuries (for new match/round).\n * \n * **Korean**: 모든 부상 초기화\n * \n */\n clearInjuries(): void {\n this.injuries.clear();\n this.nextId = 0;\n }\n\n /**\n * Remove injuries older than expiration time.\n * \n * **Korean**: 만료된 부상 제거\n * \n */\n removeExpiredInjuries(): void {\n const now = Date.now();\n const expiredIds: string[] = [];\n\n for (const [id, injury] of this.injuries.entries()) {\n if (now - injury.timestamp > this.config.injuryExpirationTimeMs) {\n expiredIds.push(id);\n }\n }\n\n for (const id of expiredIds) {\n this.injuries.delete(id);\n }\n }\n\n /**\n * Get injury count for performance monitoring.\n * \n * @returns Current number of tracked injuries\n * \n */\n getInjuryCount(): number {\n return this.injuries.size;\n }\n}\n\n/**\n * Legacy module-level injury tracker singleton.\n *\n * **Korean**: 레거시 모듈 전역 부상 추적기\n *\n * **Warning**: This singleton does **not** track any playerId/character identifier\n * on injuries. All recorded injuries are stored together in a single collection.\n * For any scenario with more than one character (including 1v1 combat), you\n * **must** create a separate {@link InjuryTracker} instance per character to\n * avoid mixing injuries between characters:\n *\n * ```typescript\n * const player1Tracker = new InjuryTracker();\n * const player2Tracker = new InjuryTracker();\n * ```\n *\n * @deprecated Use an explicitly scoped {@link InjuryTracker} instance per\n * player/character instead of relying on this shared module singleton. This\n * export is retained temporarily for backward compatibility with existing\n * consumers importing `injuryTracker` from `systems/bodypart` and will be\n * removed in a future major release.\n */\nexport const injuryTracker = new InjuryTracker();\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"InjuryTracker.js","names":[],"sources":["../../../src/systems/bodypart/InjuryTracker.ts"],"sourcesContent":["/**\n * Injury Tracking System\n * \n * **Korean**: 부상 추적 시스템\n * \n * Tracks individual injuries on character models for realistic trauma visualization.\n * Records injury location, type, severity, and timestamp for progressive bruising,\n * cuts, and bleeding effects during combat.\n * \n * ## Features\n * \n * - Track injuries by body part and 3D position\n * - Progressive bruising: Multiple hits to same location darken existing bruises\n * - Color-coded severity (getBruiseColor): Yellow (fresh), Purple (moderate), Dark red (severe)\n * - Note: TraumaOverlay3D uses different progression: Dark red → Indigo → Black\n * - Blood effects triggered when damage > 30 in single hit\n * - Injury persistence across combat rounds\n * - Nearby injury lookup using linear scan over tracked injuries (O(n))\n * \n * @module systems/bodypart/InjuryTracker\n * @category Body Part System\n * @korean 부상추적시스템\n */\n\nimport * as THREE from \"three\";\nimport { BodyPart } from \"./types\";\nimport { InjuryType } from \"../../types/injury\";\nimport { BodyRegion } from \"../../types/common\";\n\n/**\n * Individual injury location data.\n * \n * **Korean**: 부상 위치 데이터\n * \n * Records a single injury with its location, severity, and cumulative hit count.\n * Used for progressive bruising visualization.\n * \n * @category Injury Tracking\n * @korean 부상위치\n */\nexport interface InjuryLocation {\n /** Unique identifier */\n readonly id: string;\n /** Body part affected */\n readonly bodyPart: BodyPart;\n /** Body region for hit detection */\n readonly bodyRegion: BodyRegion;\n /** 3D position relative to character center */\n readonly position: THREE.Vector3;\n /** Damage severity (0-100) */\n readonly severity: number;\n /** Number of hits to this location (for progressive bruising) */\n readonly hitCount: number;\n /** Timestamp when injury occurred */\n readonly timestamp: number;\n /** Injury type */\n readonly type: InjuryType;\n}\n\n/**\n * Configuration for injury tracking behavior.\n * \n * @category Injury Tracking\n */\nexport interface InjuryTrackerConfig {\n /** Maximum number of injuries to track per character */\n readonly maxInjuries: number;\n /** Distance threshold for considering injuries at same location (in units) */\n readonly sameLocationThreshold: number;\n /** Minimum damage required to create an injury */\n readonly minDamageForInjury: number;\n /** Damage threshold for blood effects */\n readonly bloodEffectThreshold: number;\n /** Time before injuries are removed/expired (milliseconds) */\n readonly injuryExpirationTimeMs: number;\n}\n\n/**\n * Default injury tracker configuration.\n * \n */\nexport const DEFAULT_INJURY_TRACKER_CONFIG: InjuryTrackerConfig = {\n maxInjuries: 50, // Reasonable limit for performance\n sameLocationThreshold: 0.6, // 0.6 units distance (accommodates ±0.15 randomization)\n minDamageForInjury: 5, // Minimum 5 damage to show injury\n bloodEffectThreshold: 30, // Blood effects when damage > 30\n injuryExpirationTimeMs: 30000, // Injuries are removed after 30 seconds\n} as const;\n\n/**\n * Injury Tracker System.\n * \n * **Korean**: 부상 추적 시스템\n * \n * Manages injury recording and retrieval for trauma visualization.\n * Implements progressive bruising by tracking hit counts at similar locations.\n * \n * @example\n * ```typescript\n * const tracker = new InjuryTracker();\n * \n * // Record injury from hit\n * const injury = tracker.recordInjury(\n * BodyPart.TORSO_UPPER,\n * BodyRegion.TORSO,\n * new THREE.Vector3(0, 1.5, 0),\n * 25,\n * InjuryType.BRUISE\n * );\n * \n * // Get all injuries for visualization\n * const injuries = tracker.getInjuries();\n * ```\n * \n * @category Body Part System\n */\nexport class InjuryTracker {\n private injuries: Map<string, InjuryLocation>;\n private config: InjuryTrackerConfig;\n private nextId: number;\n\n constructor(config: InjuryTrackerConfig = DEFAULT_INJURY_TRACKER_CONFIG) {\n this.injuries = new Map();\n this.config = config;\n this.nextId = 0;\n }\n\n /**\n * Record a new injury or update existing one at similar location.\n * \n * **Korean**: 부상 기록\n * \n * If an injury exists near the hit position, it updates the existing injury\n * with increased severity and hit count (progressive bruising). Otherwise,\n * creates a new injury.\n * \n * @param bodyPart - Body part affected\n * @param bodyRegion - Body region for damage distribution\n * @param position - 3D position relative to character center\n * @param damage - Damage amount (0-100)\n * @param type - Type of injury\n * @returns The created or updated injury, or null if damage is below threshold\n * \n */\n recordInjury(\n bodyPart: BodyPart,\n bodyRegion: BodyRegion,\n position: THREE.Vector3,\n damage: number,\n type: InjuryType\n ): InjuryLocation | null {\n // Check if damage is significant enough to track\n if (damage < this.config.minDamageForInjury) {\n return null;\n }\n\n // Find nearby injury at similar location (same body part)\n const existing = this.findNearbyInjury(bodyPart, position, this.config.sameLocationThreshold);\n\n // Only merge injuries that also match type and body region to avoid\n // incorrectly combining different injury types at the same spot\n const shouldMerge =\n !!existing &&\n existing.type === type &&\n existing.bodyRegion === bodyRegion;\n\n if (shouldMerge && existing) {\n // Progressive bruising - update existing injury\n const newSeverity = Math.min(100, existing.severity + damage / 2);\n const newHitCount = existing.hitCount + 1;\n\n const updated: InjuryLocation = {\n ...existing,\n severity: newSeverity,\n hitCount: newHitCount,\n timestamp: Date.now(),\n };\n\n this.injuries.set(existing.id, updated);\n return updated;\n } else {\n // Create new injury\n const id = `injury-${this.nextId++}`;\n const injury: InjuryLocation = {\n id,\n bodyPart,\n bodyRegion,\n position: position.clone(),\n severity: damage,\n hitCount: 1,\n timestamp: Date.now(),\n type,\n };\n\n this.injuries.set(id, injury);\n\n // Enforce max injuries limit\n if (this.injuries.size > this.config.maxInjuries) {\n this.removeOldestInjury();\n }\n\n return injury;\n }\n }\n\n /**\n * Find injury near a given position on the same body part.\n * \n * **Korean**: 인근 부상 찾기\n * \n * @param bodyPart - Body part to search\n * @param position - Position to search near\n * @param threshold - Distance threshold\n * @returns Nearby injury or null\n * \n */\n findNearbyInjury(\n bodyPart: BodyPart,\n position: THREE.Vector3,\n threshold: number\n ): InjuryLocation | null {\n let closestInjury: InjuryLocation | null = null;\n let closestDistance = threshold;\n\n for (const injury of this.injuries.values()) {\n if (injury.bodyPart === bodyPart) {\n const distance = injury.position.distanceTo(position);\n if (distance < closestDistance) {\n closestDistance = distance;\n closestInjury = injury;\n }\n }\n }\n\n return closestInjury;\n }\n\n /**\n * Get all tracked injuries.\n * \n * **Korean**: 모든 부상 조회\n * \n * @returns Array of all injuries\n * \n */\n getInjuries(): InjuryLocation[] {\n return Array.from(this.injuries.values());\n }\n\n /**\n * Get injuries for a specific body part.\n * \n * **Korean**: 신체 부위 부상 조회\n * \n * @param bodyPart - Body part to query\n * @returns Array of injuries on that body part\n * \n */\n getInjuriesByBodyPart(bodyPart: BodyPart): InjuryLocation[] {\n return this.getInjuries().filter((injury) => injury.bodyPart === bodyPart);\n }\n\n /**\n * Get bruise color based on severity and hit count.\n * \n * **Korean**: 타박상 색상 가져오기\n * \n * Progressive color scheme (for reference - TraumaOverlay3D uses its own colors):\n * - Light bruising (severity < 20): Yellow (#ffeb3b)\n * - Moderate bruising (severity < 50): Purple (#9c27b0)\n * - Severe bruising (severity >= 50): Dark red (#b71c1c)\n * \n * Note: TraumaOverlay3D uses Dark red → Indigo → Black progression.\n * Consider using TraumaOverlay3D's getBruiseColor for consistent visualization.\n * \n * @param severity - Injury severity (0-100)\n * @param hitCount - Number of hits to same location\n * @returns Hex color string\n * \n */\n getBruiseColor(severity: number, hitCount: number): string {\n // Progressive darkening with hit count (subtract 1 since first hit shouldn't darken)\n const effectiveSeverity = severity + Math.max(0, hitCount - 1) * 10;\n\n if (effectiveSeverity < 20) {\n return \"#ffeb3b\"; // Yellow - light bruising\n } else if (effectiveSeverity < 50) {\n return \"#9c27b0\"; // Purple - moderate bruising\n } else {\n return \"#b71c1c\"; // Dark red - severe bruising\n }\n }\n\n /**\n * Check if damage should trigger blood effects.\n * \n * **Korean**: 출혈 효과 필요 여부 확인\n * \n * @param damage - Damage amount\n * @returns Whether to show blood effects\n * \n */\n shouldShowBloodEffect(damage: number): boolean {\n return damage > this.config.bloodEffectThreshold;\n }\n\n /**\n * Remove oldest injury to maintain performance.\n * \n * @private\n */\n private removeOldestInjury(): void {\n let oldestId: string | null = null;\n let oldestTimestamp = Infinity;\n\n for (const [id, injury] of this.injuries.entries()) {\n if (injury.timestamp < oldestTimestamp) {\n oldestTimestamp = injury.timestamp;\n oldestId = id;\n }\n }\n\n if (oldestId) {\n this.injuries.delete(oldestId);\n }\n }\n\n /**\n * Clear all injuries (for new match/round).\n * \n * **Korean**: 모든 부상 초기화\n * \n */\n clearInjuries(): void {\n this.injuries.clear();\n this.nextId = 0;\n }\n\n /**\n * Remove injuries older than expiration time.\n * \n * **Korean**: 만료된 부상 제거\n * \n */\n removeExpiredInjuries(): void {\n const now = Date.now();\n const expiredIds: string[] = [];\n\n for (const [id, injury] of this.injuries.entries()) {\n if (now - injury.timestamp > this.config.injuryExpirationTimeMs) {\n expiredIds.push(id);\n }\n }\n\n for (const id of expiredIds) {\n this.injuries.delete(id);\n }\n }\n\n /**\n * Get injury count for performance monitoring.\n * \n * @returns Current number of tracked injuries\n * \n */\n getInjuryCount(): number {\n return this.injuries.size;\n }\n}\n\n/**\n * Legacy module-level injury tracker singleton.\n *\n * **Korean**: 레거시 모듈 전역 부상 추적기\n *\n * **Warning**: This singleton does **not** track any playerId/character identifier\n * on injuries. All recorded injuries are stored together in a single collection.\n * For any scenario with more than one character (including 1v1 combat), you\n * **must** create a separate {@link InjuryTracker} instance per character to\n * avoid mixing injuries between characters:\n *\n * ```typescript\n * const player1Tracker = new InjuryTracker();\n * const player2Tracker = new InjuryTracker();\n * ```\n *\n * @deprecated Use an explicitly scoped {@link InjuryTracker} instance per\n * player/character instead of relying on this shared module singleton. This\n * export is retained temporarily for backward compatibility with existing\n * consumers importing `injuryTracker` from `systems/bodypart` and will be\n * removed in a future major release.\n */\nexport const injuryTracker = new InjuryTracker();\n"],"mappings":"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"}
|