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":"AtmosphericParticles3D.js","names":[],"sources":["../../../../../src/components/shared/three/scene/AtmosphericParticles3D.tsx"],"sourcesContent":["/**\n * AtmosphericParticles3D - Three.js 3D atmospheric particle effects\n *\n * Renders rain/mist particles for environmental depth in the combat arena\n * Creates an immersive cyberpunk urban night atmosphere\n */\n\nimport { useFrame } from \"@react-three/fiber\";\nimport React, { useEffect, useRef, useState } from \"react\";\nimport * as THREE from \"three\";\n\n/**\n * Props for the AtmosphericParticles3D component.\n */\nexport interface AtmosphericParticles3DProps {\n /** Number of particles to render. Defaults to 500 */\n readonly count?: number;\n /** Scale factor for particle spread (1.0 = desktop, <1.0 = mobile). Defaults to 1.0 */\n readonly scale?: number;\n /** Particle fall speed. Defaults to 2 */\n readonly speed?: number;\n}\n\n/**\n * Generate particle positions using a deterministic pattern\n * This avoids Math.random() in render functions for React purity\n */\nfunction generateParticlePositions(\n count: number,\n spreadX: number,\n spreadY: number,\n spreadZ: number\n): Float32Array {\n const pos = new Float32Array(count * 3);\n\n for (let i = 0; i < count; i++) {\n const t = i / count; // Normalized index [0, 1]\n\n const offsetX = Math.sin(t * 123.456) * Math.cos(t * 789.012);\n const offsetY = Math.sin(t * 234.567) * Math.cos(t * 890.123);\n const offsetZ = Math.sin(t * 345.678) * Math.cos(t * 901.234);\n\n pos[i * 3] = offsetX * spreadX * 0.5;\n pos[i * 3 + 1] = offsetY * spreadY * 0.5 + spreadY * 0.5; // Bias upward\n pos[i * 3 + 2] = offsetZ * spreadZ * 0.5;\n }\n\n return pos;\n}\n\n/**\n * AtmosphericParticles3D Component\n * Creates rain/mist particle effects for atmospheric depth\n *\n * Performance optimized with:\n * - BufferGeometry for efficient rendering\n * - Additive blending for transparent particles\n * - Configurable particle count for mobile optimization\n * - Deterministic position generation (no Math.random in render)\n */\nexport const AtmosphericParticles3D: React.FC<\n AtmosphericParticles3DProps\n> = ({ count = 500, scale = 1.0, speed = 2 }) => {\n const particlesRef = useRef<THREE.Points>(null);\n const [geometry] = useState(() => new THREE.BufferGeometry());\n\n const spreadX = 40 * scale;\n const spreadY = 20;\n const spreadZ = 40 * scale;\n\n useEffect(() => {\n const positions = generateParticlePositions(\n count,\n spreadX,\n spreadY,\n spreadZ\n );\n \n geometry.setAttribute(\"position\", new THREE.BufferAttribute(positions, 3));\n\n return () => {\n geometry.dispose();\n };\n }, [count, spreadX, spreadY, spreadZ, geometry]);\n\n useFrame((_state, delta) => {\n if (!particlesRef.current) return;\n\n const positions =\n particlesRef.current.geometry.attributes.position.array as Float32Array;\n for (let i = 0; i < count; i++) {\n positions[i * 3 + 1] -= delta * speed; // Fall down\n if (positions[i * 3 + 1] < 0) {\n positions[i * 3 + 1] = spreadY; // Reset to top\n }\n }\n particlesRef.current.geometry.attributes.position.needsUpdate = true;\n });\n\n return (\n <points ref={particlesRef} geometry={geometry}>\n <pointsMaterial\n size={0.05}\n color={0xffffff}\n transparent\n opacity={0.3}\n sizeAttenuation\n blending={THREE.AdditiveBlending}\n depthWrite={false}\n />\n </points>\n );\n};\n\nexport default AtmosphericParticles3D;\n"],"mappings":";;;;;;;;;;;;;;;AA2BA,SAAS,0BACP,OACA,SACA,SACA,SACc;CACd,MAAM,MAAM,IAAI,aAAa,QAAQ,
|
|
1
|
+
{"version":3,"file":"AtmosphericParticles3D.js","names":[],"sources":["../../../../../src/components/shared/three/scene/AtmosphericParticles3D.tsx"],"sourcesContent":["/**\n * AtmosphericParticles3D - Three.js 3D atmospheric particle effects\n *\n * Renders rain/mist particles for environmental depth in the combat arena\n * Creates an immersive cyberpunk urban night atmosphere\n */\n\nimport { useFrame } from \"@react-three/fiber\";\nimport React, { useEffect, useRef, useState } from \"react\";\nimport * as THREE from \"three\";\n\n/**\n * Props for the AtmosphericParticles3D component.\n */\nexport interface AtmosphericParticles3DProps {\n /** Number of particles to render. Defaults to 500 */\n readonly count?: number;\n /** Scale factor for particle spread (1.0 = desktop, <1.0 = mobile). Defaults to 1.0 */\n readonly scale?: number;\n /** Particle fall speed. Defaults to 2 */\n readonly speed?: number;\n}\n\n/**\n * Generate particle positions using a deterministic pattern\n * This avoids Math.random() in render functions for React purity\n */\nfunction generateParticlePositions(\n count: number,\n spreadX: number,\n spreadY: number,\n spreadZ: number\n): Float32Array {\n const pos = new Float32Array(count * 3);\n\n for (let i = 0; i < count; i++) {\n const t = i / count; // Normalized index [0, 1]\n\n const offsetX = Math.sin(t * 123.456) * Math.cos(t * 789.012);\n const offsetY = Math.sin(t * 234.567) * Math.cos(t * 890.123);\n const offsetZ = Math.sin(t * 345.678) * Math.cos(t * 901.234);\n\n pos[i * 3] = offsetX * spreadX * 0.5;\n pos[i * 3 + 1] = offsetY * spreadY * 0.5 + spreadY * 0.5; // Bias upward\n pos[i * 3 + 2] = offsetZ * spreadZ * 0.5;\n }\n\n return pos;\n}\n\n/**\n * AtmosphericParticles3D Component\n * Creates rain/mist particle effects for atmospheric depth\n *\n * Performance optimized with:\n * - BufferGeometry for efficient rendering\n * - Additive blending for transparent particles\n * - Configurable particle count for mobile optimization\n * - Deterministic position generation (no Math.random in render)\n */\nexport const AtmosphericParticles3D: React.FC<\n AtmosphericParticles3DProps\n> = ({ count = 500, scale = 1.0, speed = 2 }) => {\n const particlesRef = useRef<THREE.Points>(null);\n const [geometry] = useState(() => new THREE.BufferGeometry());\n\n const spreadX = 40 * scale;\n const spreadY = 20;\n const spreadZ = 40 * scale;\n\n useEffect(() => {\n const positions = generateParticlePositions(\n count,\n spreadX,\n spreadY,\n spreadZ\n );\n \n geometry.setAttribute(\"position\", new THREE.BufferAttribute(positions, 3));\n\n return () => {\n geometry.dispose();\n };\n }, [count, spreadX, spreadY, spreadZ, geometry]);\n\n useFrame((_state, delta) => {\n if (!particlesRef.current) return;\n\n const positions =\n particlesRef.current.geometry.attributes.position.array as Float32Array;\n for (let i = 0; i < count; i++) {\n positions[i * 3 + 1] -= delta * speed; // Fall down\n if (positions[i * 3 + 1] < 0) {\n positions[i * 3 + 1] = spreadY; // Reset to top\n }\n }\n particlesRef.current.geometry.attributes.position.needsUpdate = true;\n });\n\n return (\n <points ref={particlesRef} geometry={geometry}>\n <pointsMaterial\n size={0.05}\n color={0xffffff}\n transparent\n opacity={0.3}\n sizeAttenuation\n blending={THREE.AdditiveBlending}\n depthWrite={false}\n />\n </points>\n );\n};\n\nexport default AtmosphericParticles3D;\n"],"mappings":";;;;;;;;;;;;;;;AA2BA,SAAS,0BACP,OACA,SACA,SACA,SACc;CACd,MAAM,MAAM,IAAI,aAAa,QAAQ,CAAC;CAEtC,KAAK,IAAI,IAAI,GAAG,IAAI,OAAO,KAAK;EAC9B,MAAM,IAAI,IAAI;EAEd,MAAM,UAAU,KAAK,IAAI,IAAI,OAAO,IAAI,KAAK,IAAI,IAAI,OAAO;EAC5D,MAAM,UAAU,KAAK,IAAI,IAAI,OAAO,IAAI,KAAK,IAAI,IAAI,OAAO;EAC5D,MAAM,UAAU,KAAK,IAAI,IAAI,OAAO,IAAI,KAAK,IAAI,IAAI,OAAO;EAE5D,IAAI,IAAI,KAAK,UAAU,UAAU;EACjC,IAAI,IAAI,IAAI,KAAK,UAAU,UAAU,KAAM,UAAU;EACrD,IAAI,IAAI,IAAI,KAAK,UAAU,UAAU;CACvC;CAEA,OAAO;AACT;;;;;;;;;;;AAYA,IAAa,0BAER,EAAE,QAAQ,KAAK,QAAQ,GAAK,QAAQ,QAAQ;CAC/C,MAAM,eAAe,OAAqB,IAAI;CAC9C,MAAM,CAAC,YAAY,eAAe,IAAI,MAAM,eAAe,CAAC;CAE5D,MAAM,UAAU,KAAK;CACrB,MAAM,UAAU;CAChB,MAAM,UAAU,KAAK;CAErB,gBAAgB;EACd,MAAM,YAAY,0BAChB,OACA,SACA,SACA,OACF;EAEA,SAAS,aAAa,YAAY,IAAI,MAAM,gBAAgB,WAAW,CAAC,CAAC;EAEzE,aAAa;GACX,SAAS,QAAQ;EACnB;CACF,GAAG;EAAC;EAAO;EAAS;EAAS;EAAS;CAAQ,CAAC;CAE/C,UAAU,QAAQ,UAAU;EAC1B,IAAI,CAAC,aAAa,SAAS;EAE3B,MAAM,YACJ,aAAa,QAAQ,SAAS,WAAW,SAAS;EACpD,KAAK,IAAI,IAAI,GAAG,IAAI,OAAO,KAAK;GAC9B,UAAU,IAAI,IAAI,MAAM,QAAQ;GAChC,IAAI,UAAU,IAAI,IAAI,KAAK,GACzB,UAAU,IAAI,IAAI,KAAK;EAE3B;EACA,aAAa,QAAQ,SAAS,WAAW,SAAS,cAAc;CAClE,CAAC;CAED,OACE,oBAAC,UAAD;EAAQ,KAAK;EAAwB;YACnC,oBAAC,kBAAD;GACE,MAAM;GACN,OAAO;GACP,aAAA;GACA,SAAS;GACT,iBAAA;GACA,UAAU,MAAM;GAChB,YAAY;EACb,CAAA;CACK,CAAA;AAEZ"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"BackgroundScene3D.js","names":[],"sources":["../../../../../src/components/shared/three/scene/BackgroundScene3D.tsx"],"sourcesContent":["/**\n * BackgroundScene3D - Shared cyberpunk Korean-themed 3D background scene\n *\n * @korean 배경씬3D - 공유 사이버펑크 한국 테마 3D 배경 씬\n *\n * Eliminates duplication across IntroScreen, ControlsScreen, and PhilosophyScreen\n */\n\nimport { useFrame } from \"@react-three/fiber\";\nimport React, { useRef } from \"react\";\nimport * as THREE from \"three\";\nimport { KOREAN_COLORS } from \"../../../../types/constants\";\n\n/**\n * Color theme variants for background scenes\n */\nexport type BackgroundTheme = \"intro\" | \"controls\" | \"philosophy\" | \"default\";\n\nexport interface BackgroundScene3DProps {\n /**\n * Color theme for the background\n * @default \"default\"\n */\n readonly theme?: BackgroundTheme;\n\n /**\n * Grid rotation speed (radians per frame)\n * @default 0.0005\n */\n readonly gridRotationSpeed?: number;\n\n /**\n * Grid position Y offset\n * @default -8\n */\n readonly gridPositionY?: number;\n\n /**\n * Grid size\n * @default 80\n */\n readonly gridSize?: number;\n\n /**\n * Grid divisions\n * @default 40\n */\n readonly gridDivisions?: number;\n\n /**\n * Fog near distance\n * @default 5\n */\n readonly fogNear?: number;\n\n /**\n * Fog far distance\n * @default 40\n */\n readonly fogFar?: number;\n\n /**\n * Ambient light intensity\n * @default 0.4\n */\n readonly ambientIntensity?: number;\n\n /**\n * Directional light intensity\n * @default 1\n */\n readonly directionalIntensity?: number;\n\n /**\n * Point light intensity\n * @default 0.5\n */\n readonly pointIntensity?: number;\n}\n\n/**\n * Theme color configurations\n */\nconst THEME_COLORS: Record<\n BackgroundTheme,\n {\n ambient: number;\n grid: number;\n point: number;\n ambientIntensity: number;\n directionalIntensity: number;\n pointIntensity: number;\n }\n> = {\n intro: {\n ambient: KOREAN_COLORS.PRIMARY_CYAN,\n grid: KOREAN_COLORS.PRIMARY_CYAN,\n point: KOREAN_COLORS.ACCENT_BLUE,\n ambientIntensity: 0.4,\n directionalIntensity: 1,\n pointIntensity: 0.5,\n },\n controls: {\n ambient: KOREAN_COLORS.PRIMARY_CYAN,\n grid: KOREAN_COLORS.PRIMARY_CYAN,\n point: KOREAN_COLORS.ACCENT_BLUE,\n ambientIntensity: 0.3,\n directionalIntensity: 0.8,\n pointIntensity: 0.4,\n },\n philosophy: {\n ambient: KOREAN_COLORS.ACCENT_GOLD,\n grid: KOREAN_COLORS.ACCENT_GOLD,\n point: KOREAN_COLORS.PRIMARY_CYAN,\n ambientIntensity: 0.3,\n directionalIntensity: 0.8,\n pointIntensity: 0.4,\n },\n default: {\n ambient: KOREAN_COLORS.PRIMARY_CYAN,\n grid: KOREAN_COLORS.PRIMARY_CYAN,\n point: KOREAN_COLORS.ACCENT_BLUE,\n ambientIntensity: 0.4,\n directionalIntensity: 1,\n pointIntensity: 0.5,\n },\n};\n\n/**\n * Shared 3D Background Scene Component\n *\n * Renders cyberpunk Korean-themed 3D background with rotating grid,\n * atmospheric lighting, and fog effects.\n *\n * @korean 사이버펑크 한국 테마의 3D 배경을 렌더링합니다.\n * 회전하는 그리드, 분위기 있는 조명, 안개 효과를 포함합니다.\n */\nexport const BackgroundScene3D: React.FC<BackgroundScene3DProps> = ({\n theme = \"default\",\n gridRotationSpeed = 0.0005,\n gridPositionY = -8,\n gridSize = 80,\n gridDivisions = 40,\n fogNear = 5,\n fogFar = 40,\n ambientIntensity,\n directionalIntensity,\n pointIntensity,\n}) => {\n const gridRef = useRef<THREE.GridHelper>(null);\n\n const themeColors = THEME_COLORS[theme];\n\n const finalAmbientIntensity =\n ambientIntensity ?? themeColors.ambientIntensity;\n const finalDirectionalIntensity =\n directionalIntensity ?? themeColors.directionalIntensity;\n const finalPointIntensity = pointIntensity ?? themeColors.pointIntensity;\n\n useFrame(() => {\n if (gridRef.current) {\n gridRef.current.rotation.y += gridRotationSpeed;\n }\n });\n\n return (\n <>\n {/* Ambient lighting */}\n <ambientLight\n intensity={finalAmbientIntensity}\n color={themeColors.ambient}\n />\n\n {/* Directional lights for Korean aesthetic */}\n <directionalLight\n position={[10, 10, 5]}\n intensity={finalDirectionalIntensity}\n color={KOREAN_COLORS.ACCENT_GOLD}\n />\n <pointLight\n position={[-10, 5, -5]}\n intensity={finalPointIntensity}\n color={themeColors.point}\n />\n\n {/* Cyberpunk grid plane - positioned lower with fog to hide edges */}\n <gridHelper\n ref={gridRef}\n args={[\n gridSize,\n gridDivisions,\n themeColors.grid,\n KOREAN_COLORS.UI_BACKGROUND_MEDIUM,\n ]}\n position={[0, gridPositionY, 0]}\n rotation={[0, 0, 0]}\n />\n\n {/* Fog for depth - starts closer to hide grid edges */}\n <fog\n attach=\"fog\"\n args={[KOREAN_COLORS.UI_BACKGROUND_DARK, fogNear, fogFar]}\n />\n </>\n );\n};\n\nexport default BackgroundScene3D;\n"],"mappings":";;;;;;;;;;;;;;;AAmFA,IAAM,eAUF;CACF,OAAO;EACL,SAAS,cAAc;EACvB,MAAM,cAAc;EACpB,OAAO,cAAc;EACrB,kBAAkB;EAClB,sBAAsB;EACtB,gBAAgB;
|
|
1
|
+
{"version":3,"file":"BackgroundScene3D.js","names":[],"sources":["../../../../../src/components/shared/three/scene/BackgroundScene3D.tsx"],"sourcesContent":["/**\n * BackgroundScene3D - Shared cyberpunk Korean-themed 3D background scene\n *\n * @korean 배경씬3D - 공유 사이버펑크 한국 테마 3D 배경 씬\n *\n * Eliminates duplication across IntroScreen, ControlsScreen, and PhilosophyScreen\n */\n\nimport { useFrame } from \"@react-three/fiber\";\nimport React, { useRef } from \"react\";\nimport * as THREE from \"three\";\nimport { KOREAN_COLORS } from \"../../../../types/constants\";\n\n/**\n * Color theme variants for background scenes\n */\nexport type BackgroundTheme = \"intro\" | \"controls\" | \"philosophy\" | \"default\";\n\nexport interface BackgroundScene3DProps {\n /**\n * Color theme for the background\n * @default \"default\"\n */\n readonly theme?: BackgroundTheme;\n\n /**\n * Grid rotation speed (radians per frame)\n * @default 0.0005\n */\n readonly gridRotationSpeed?: number;\n\n /**\n * Grid position Y offset\n * @default -8\n */\n readonly gridPositionY?: number;\n\n /**\n * Grid size\n * @default 80\n */\n readonly gridSize?: number;\n\n /**\n * Grid divisions\n * @default 40\n */\n readonly gridDivisions?: number;\n\n /**\n * Fog near distance\n * @default 5\n */\n readonly fogNear?: number;\n\n /**\n * Fog far distance\n * @default 40\n */\n readonly fogFar?: number;\n\n /**\n * Ambient light intensity\n * @default 0.4\n */\n readonly ambientIntensity?: number;\n\n /**\n * Directional light intensity\n * @default 1\n */\n readonly directionalIntensity?: number;\n\n /**\n * Point light intensity\n * @default 0.5\n */\n readonly pointIntensity?: number;\n}\n\n/**\n * Theme color configurations\n */\nconst THEME_COLORS: Record<\n BackgroundTheme,\n {\n ambient: number;\n grid: number;\n point: number;\n ambientIntensity: number;\n directionalIntensity: number;\n pointIntensity: number;\n }\n> = {\n intro: {\n ambient: KOREAN_COLORS.PRIMARY_CYAN,\n grid: KOREAN_COLORS.PRIMARY_CYAN,\n point: KOREAN_COLORS.ACCENT_BLUE,\n ambientIntensity: 0.4,\n directionalIntensity: 1,\n pointIntensity: 0.5,\n },\n controls: {\n ambient: KOREAN_COLORS.PRIMARY_CYAN,\n grid: KOREAN_COLORS.PRIMARY_CYAN,\n point: KOREAN_COLORS.ACCENT_BLUE,\n ambientIntensity: 0.3,\n directionalIntensity: 0.8,\n pointIntensity: 0.4,\n },\n philosophy: {\n ambient: KOREAN_COLORS.ACCENT_GOLD,\n grid: KOREAN_COLORS.ACCENT_GOLD,\n point: KOREAN_COLORS.PRIMARY_CYAN,\n ambientIntensity: 0.3,\n directionalIntensity: 0.8,\n pointIntensity: 0.4,\n },\n default: {\n ambient: KOREAN_COLORS.PRIMARY_CYAN,\n grid: KOREAN_COLORS.PRIMARY_CYAN,\n point: KOREAN_COLORS.ACCENT_BLUE,\n ambientIntensity: 0.4,\n directionalIntensity: 1,\n pointIntensity: 0.5,\n },\n};\n\n/**\n * Shared 3D Background Scene Component\n *\n * Renders cyberpunk Korean-themed 3D background with rotating grid,\n * atmospheric lighting, and fog effects.\n *\n * @korean 사이버펑크 한국 테마의 3D 배경을 렌더링합니다.\n * 회전하는 그리드, 분위기 있는 조명, 안개 효과를 포함합니다.\n */\nexport const BackgroundScene3D: React.FC<BackgroundScene3DProps> = ({\n theme = \"default\",\n gridRotationSpeed = 0.0005,\n gridPositionY = -8,\n gridSize = 80,\n gridDivisions = 40,\n fogNear = 5,\n fogFar = 40,\n ambientIntensity,\n directionalIntensity,\n pointIntensity,\n}) => {\n const gridRef = useRef<THREE.GridHelper>(null);\n\n const themeColors = THEME_COLORS[theme];\n\n const finalAmbientIntensity =\n ambientIntensity ?? themeColors.ambientIntensity;\n const finalDirectionalIntensity =\n directionalIntensity ?? themeColors.directionalIntensity;\n const finalPointIntensity = pointIntensity ?? themeColors.pointIntensity;\n\n useFrame(() => {\n if (gridRef.current) {\n gridRef.current.rotation.y += gridRotationSpeed;\n }\n });\n\n return (\n <>\n {/* Ambient lighting */}\n <ambientLight\n intensity={finalAmbientIntensity}\n color={themeColors.ambient}\n />\n\n {/* Directional lights for Korean aesthetic */}\n <directionalLight\n position={[10, 10, 5]}\n intensity={finalDirectionalIntensity}\n color={KOREAN_COLORS.ACCENT_GOLD}\n />\n <pointLight\n position={[-10, 5, -5]}\n intensity={finalPointIntensity}\n color={themeColors.point}\n />\n\n {/* Cyberpunk grid plane - positioned lower with fog to hide edges */}\n <gridHelper\n ref={gridRef}\n args={[\n gridSize,\n gridDivisions,\n themeColors.grid,\n KOREAN_COLORS.UI_BACKGROUND_MEDIUM,\n ]}\n position={[0, gridPositionY, 0]}\n rotation={[0, 0, 0]}\n />\n\n {/* Fog for depth - starts closer to hide grid edges */}\n <fog\n attach=\"fog\"\n args={[KOREAN_COLORS.UI_BACKGROUND_DARK, fogNear, fogFar]}\n />\n </>\n );\n};\n\nexport default BackgroundScene3D;\n"],"mappings":";;;;;;;;;;;;;;;AAmFA,IAAM,eAUF;CACF,OAAO;EACL,SAAS,cAAc;EACvB,MAAM,cAAc;EACpB,OAAO,cAAc;EACrB,kBAAkB;EAClB,sBAAsB;EACtB,gBAAgB;CAClB;CACA,UAAU;EACR,SAAS,cAAc;EACvB,MAAM,cAAc;EACpB,OAAO,cAAc;EACrB,kBAAkB;EAClB,sBAAsB;EACtB,gBAAgB;CAClB;CACA,YAAY;EACV,SAAS,cAAc;EACvB,MAAM,cAAc;EACpB,OAAO,cAAc;EACrB,kBAAkB;EAClB,sBAAsB;EACtB,gBAAgB;CAClB;CACA,SAAS;EACP,SAAS,cAAc;EACvB,MAAM,cAAc;EACpB,OAAO,cAAc;EACrB,kBAAkB;EAClB,sBAAsB;EACtB,gBAAgB;CAClB;AACF;;;;;;;;;;AAWA,IAAa,qBAAuD,EAClE,QAAQ,WACR,oBAAoB,MACpB,gBAAgB,IAChB,WAAW,IACX,gBAAgB,IAChB,UAAU,GACV,SAAS,IACT,kBACA,sBACA,qBACI;CACJ,MAAM,UAAU,OAAyB,IAAI;CAE7C,MAAM,cAAc,aAAa;CAEjC,MAAM,wBACJ,oBAAoB,YAAY;CAClC,MAAM,4BACJ,wBAAwB,YAAY;CACtC,MAAM,sBAAsB,kBAAkB,YAAY;CAE1D,eAAe;EACb,IAAI,QAAQ,SACV,QAAQ,QAAQ,SAAS,KAAK;CAElC,CAAC;CAED,OACE,qBAAA,UAAA,EAAA,UAAA;EAEE,oBAAC,gBAAD;GACE,WAAW;GACX,OAAO,YAAY;EACpB,CAAA;EAGD,oBAAC,oBAAD;GACE,UAAU;IAAC;IAAI;IAAI;GAAC;GACpB,WAAW;GACX,OAAO,cAAc;EACtB,CAAA;EACD,oBAAC,cAAD;GACE,UAAU;IAAC;IAAK;IAAG;GAAE;GACrB,WAAW;GACX,OAAO,YAAY;EACpB,CAAA;EAGD,oBAAC,cAAD;GACE,KAAK;GACL,MAAM;IACJ;IACA;IACA,YAAY;IACZ,cAAc;GAChB;GACA,UAAU;IAAC;IAAG;IAAe;GAAC;GAC9B,UAAU;IAAC;IAAG;IAAG;GAAC;EACnB,CAAA;EAGD,oBAAC,OAAD;GACE,QAAO;GACP,MAAM;IAAC,cAAc;IAAoB;IAAS;GAAM;EACzD,CAAA;CACD,EAAA,CAAA;AAEN"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"CombatArena3D.js","names":[],"sources":["../../../../../src/components/shared/three/scene/CombatArena3D.tsx"],"sourcesContent":["/**\n * CombatArena3D - Three.js 3D arena environment\n *\n * Renders the 3D combat arena with Korean dojang aesthetic\n * Includes floor, lighting, and atmospheric effects\n *\n * Enhanced features:\n * - Korean cyberpunk lighting with neon point lights (cyan, gold, blue)\n * - Reflective wet concrete floor with clearcoat and emissive glow\n * - Upgraded shadow quality (2048x2048 shadow maps)\n * - Atmospheric fog with Korean color gradient\n * - Korean signage with emissive neon glow (전투, 흑괘, 급소격)\n * - Optional atmospheric particles (rain/mist)\n */\n\nimport { useFrame } from \"@react-three/fiber\";\nimport React, { useEffect, useMemo, useRef } from \"react\";\nimport * as THREE from \"three\";\nimport { KOREAN_COLORS } from \"../../../../types/constants\";\nimport { ThreeObjectPools } from \"../../../../utils/threeObjectPool\";\nimport AtmosphericParticles3D from \"./AtmosphericParticles3D\";\nimport KoreanSignage3D from \"./KoreanSignage3D\";\n\n/**\n * Props for the CombatArena3D component.\n * Configures the lighting and atmosphere of the 3D arena.\n */\nexport interface CombatArena3DProps {\n /** Lighting theme affecting ambiance and colors. Defaults to \"cyberpunk\" */\n readonly lighting?: \"cyberpunk\" | \"traditional\" | \"neutral\";\n /** Scale factor for arena size (1.0 = desktop, <1.0 = mobile). Defaults to 1.0 */\n readonly scale?: number;\n /** Physical arena width in meters. Defaults to 10m (desktop standard) */\n readonly worldWidthMeters?: number;\n /** Physical arena depth in meters. Defaults to worldWidthMeters for square arena */\n readonly worldDepthMeters?: number;\n /** Enable atmospheric particles (rain/mist). Defaults to true on desktop, false on mobile */\n readonly enableParticles?: boolean;\n}\n\nconst FLOOR_SCALE_FACTOR = 1.5;\n\nconst SHADOW_MAP_SIZE_MOBILE: [number, number] = [1024, 1024]; // Upgraded from 512x512\nconst SHADOW_MAP_SIZE_DESKTOP: [number, number] = [2048, 2048]; // Upgraded from 1024x1024\n\n/**\n * CombatArena3D Component\n * Creates a Korean-themed 3D arena environment with cyberpunk aesthetic\n */\nexport const CombatArena3D: React.FC<CombatArena3DProps> = ({\n lighting = \"cyberpunk\",\n scale = 1.0,\n worldWidthMeters = 10, // Default desktop standard\n worldDepthMeters,\n enableParticles = scale >= 1.0, // Enable particles on desktop by default\n}) => {\n const gridRef = useRef<THREE.GridHelper>(null);\n\n useFrame(() => {\n if (gridRef.current) {\n gridRef.current.rotation.y += 0.0002;\n }\n });\n\n const effectiveDepth = worldDepthMeters ?? worldWidthMeters;\n const floorWidth = worldWidthMeters * FLOOR_SCALE_FACTOR;\n const floorDepth = effectiveDepth * FLOOR_SCALE_FACTOR;\n const gridSize = worldWidthMeters * FLOOR_SCALE_FACTOR;\n const markerDistance = worldWidthMeters * 0.8;\n const markerDepth = effectiveDepth * 0.4;\n\n const floorMaterial = useMemo(\n () => {\n const pooledBaseColor = ThreeObjectPools.color.acquire();\n const pooledEmissiveColor = ThreeObjectPools.color.acquire();\n \n try {\n pooledBaseColor.set(0x2a2a2a); // Dark concrete\n pooledEmissiveColor.set(KOREAN_COLORS.PRIMARY_CYAN);\n \n const material = new THREE.MeshPhysicalMaterial({\n color: pooledBaseColor.clone(), // Material takes ownership of cloned color\n roughness: 0.3, // Wet/reflective surface\n metalness: 0.1,\n clearcoat: 0.3, // Wet sheen\n clearcoatRoughness: 0.4,\n envMapIntensity: 1.5, // Enhanced reflections from Environment preset\n emissive: pooledEmissiveColor.clone(), // Material takes ownership of cloned color\n emissiveIntensity: 0.05,\n });\n \n return material;\n } finally {\n ThreeObjectPools.color.release(pooledBaseColor);\n ThreeObjectPools.color.release(pooledEmissiveColor);\n }\n },\n [],\n );\n\n useEffect(() => {\n return () => {\n floorMaterial.dispose();\n };\n }, [floorMaterial]);\n\n return (\n <group>\n {/* Lighting based on theme */}\n {lighting === \"cyberpunk\" && (\n <>\n {/* Base ambient light with Korean cyan tint */}\n <ambientLight intensity={0.5} color={KOREAN_COLORS.PRIMARY_CYAN} />\n\n {/* Primary directional light (moonlight) with upgraded shadows */}\n <directionalLight\n position={[15, 20, 10]}\n intensity={1.5}\n color={0xffffff}\n castShadow\n shadow-mapSize={\n scale < 1.0 ? SHADOW_MAP_SIZE_MOBILE : SHADOW_MAP_SIZE_DESKTOP\n }\n shadow-camera-far={50}\n shadow-camera-left={-20}\n shadow-camera-right={20}\n shadow-camera-top={20}\n shadow-camera-bottom={-20}\n shadow-bias={-0.0001}\n />\n\n {/* Korean neon accent lights */}\n {/* Cyan neon light (left side) */}\n <pointLight\n position={[-10, 3, 0]}\n intensity={4}\n distance={25}\n decay={2}\n color={KOREAN_COLORS.PRIMARY_CYAN}\n />\n\n {/* Gold neon light (right side) */}\n <pointLight\n position={[10, 3, 0]}\n intensity={4}\n distance={25}\n decay={2}\n color={KOREAN_COLORS.ACCENT_GOLD}\n />\n\n {/* Blue accent light (behind) */}\n <pointLight\n position={[0, 5, -15]}\n intensity={3}\n distance={30}\n decay={2}\n color={KOREAN_COLORS.ACCENT_BLUE}\n />\n </>\n )}\n\n {lighting === \"traditional\" && (\n <>\n <ambientLight intensity={0.6} color={0xffffee} />\n <directionalLight\n position={[5, 10, 5]}\n intensity={0.8}\n castShadow\n shadow-mapSize={[2048, 2048]}\n />\n </>\n )}\n\n {lighting === \"neutral\" && (\n <>\n <ambientLight intensity={0.5} />\n <directionalLight position={[10, 10, 5]} intensity={1} castShadow />\n </>\n )}\n\n {/* Arena floor - dojang mat with reflective wet concrete aesthetic */}\n {/* Floor dimensions use pre-calculated floorWidth/floorDepth which already include FLOOR_SCALE_FACTOR */}\n <mesh rotation={[-Math.PI / 2, 0, 0]} position={[0, 0, 0]} receiveShadow>\n <planeGeometry args={[floorWidth, floorDepth]} />\n {lighting === \"cyberpunk\" ? (\n <primitive object={floorMaterial} attach=\"material\" />\n ) : (\n <meshPhysicalMaterial\n color={KOREAN_COLORS.UI_BACKGROUND_MEDIUM}\n roughness={0.7}\n metalness={0.1}\n clearcoat={0}\n clearcoatRoughness={0.2}\n />\n )}\n </mesh>\n\n {/* Cyberpunk grid overlay (scale-aware) */}\n <gridHelper\n ref={gridRef}\n args={[\n gridSize,\n 20,\n KOREAN_COLORS.PRIMARY_CYAN,\n KOREAN_COLORS.UI_BACKGROUND_DARK,\n ]}\n position={[0, 0.01, 0]}\n />\n\n {/* Korean traditional boundary markers (scale-aware) */}\n {[\n [-markerDistance, 0, -markerDepth],\n [-markerDistance, 0, markerDepth],\n [markerDistance, 0, -markerDepth],\n [markerDistance, 0, markerDepth],\n ].map((pos, i) => (\n <mesh key={i} position={pos as [number, number, number]} castShadow>\n <cylinderGeometry args={[0.1 * scale, 0.15 * scale, 0.8, 8]} />\n <meshPhysicalMaterial\n color={KOREAN_COLORS.ACCENT_GOLD}\n emissive={KOREAN_COLORS.ACCENT_GOLD}\n emissiveIntensity={0.5}\n metalness={0.8}\n roughness={0.2}\n clearcoat={1.0}\n />\n </mesh>\n ))}\n\n {/* Center marker - Yin Yang inspired (scale-aware) */}\n <mesh rotation={[-Math.PI / 2, 0, 0]} position={[0, 0.02, 0]}>\n <ringGeometry args={[0.8 * scale, 1.0 * scale, 32]} />\n <meshBasicMaterial\n color={KOREAN_COLORS.ACCENT_GOLD}\n transparent\n opacity={0.6}\n side={THREE.DoubleSide}\n />\n </mesh>\n\n {/* Korean Signage with neon glow */}\n {lighting === \"cyberpunk\" && <KoreanSignage3D scale={scale} />}\n\n {/* Atmospheric Particles (rain/mist) */}\n {lighting === \"cyberpunk\" && enableParticles && (\n <AtmosphericParticles3D\n count={scale >= 1.0 ? 500 : 250}\n scale={scale}\n speed={2}\n />\n )}\n </group>\n );\n};\n\nexport default CombatArena3D;\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"CombatArena3D.js","names":[],"sources":["../../../../../src/components/shared/three/scene/CombatArena3D.tsx"],"sourcesContent":["/**\n * CombatArena3D - Three.js 3D arena environment\n *\n * Renders the 3D combat arena with Korean dojang aesthetic\n * Includes floor, lighting, and atmospheric effects\n *\n * Enhanced features:\n * - Korean cyberpunk lighting with neon point lights (cyan, gold, blue)\n * - Reflective wet concrete floor with clearcoat and emissive glow\n * - Upgraded shadow quality (2048x2048 shadow maps)\n * - Atmospheric fog with Korean color gradient\n * - Korean signage with emissive neon glow (전투, 흑괘, 급소격)\n * - Optional atmospheric particles (rain/mist)\n */\n\nimport { useFrame } from \"@react-three/fiber\";\nimport React, { useEffect, useMemo, useRef } from \"react\";\nimport * as THREE from \"three\";\nimport { KOREAN_COLORS } from \"../../../../types/constants\";\nimport { ThreeObjectPools } from \"../../../../utils/threeObjectPool\";\nimport AtmosphericParticles3D from \"./AtmosphericParticles3D\";\nimport KoreanSignage3D from \"./KoreanSignage3D\";\n\n/**\n * Props for the CombatArena3D component.\n * Configures the lighting and atmosphere of the 3D arena.\n */\nexport interface CombatArena3DProps {\n /** Lighting theme affecting ambiance and colors. Defaults to \"cyberpunk\" */\n readonly lighting?: \"cyberpunk\" | \"traditional\" | \"neutral\";\n /** Scale factor for arena size (1.0 = desktop, <1.0 = mobile). Defaults to 1.0 */\n readonly scale?: number;\n /** Physical arena width in meters. Defaults to 10m (desktop standard) */\n readonly worldWidthMeters?: number;\n /** Physical arena depth in meters. Defaults to worldWidthMeters for square arena */\n readonly worldDepthMeters?: number;\n /** Enable atmospheric particles (rain/mist). Defaults to true on desktop, false on mobile */\n readonly enableParticles?: boolean;\n}\n\nconst FLOOR_SCALE_FACTOR = 1.5;\n\nconst SHADOW_MAP_SIZE_MOBILE: [number, number] = [1024, 1024]; // Upgraded from 512x512\nconst SHADOW_MAP_SIZE_DESKTOP: [number, number] = [2048, 2048]; // Upgraded from 1024x1024\n\n/**\n * CombatArena3D Component\n * Creates a Korean-themed 3D arena environment with cyberpunk aesthetic\n */\nexport const CombatArena3D: React.FC<CombatArena3DProps> = ({\n lighting = \"cyberpunk\",\n scale = 1.0,\n worldWidthMeters = 10, // Default desktop standard\n worldDepthMeters,\n enableParticles = scale >= 1.0, // Enable particles on desktop by default\n}) => {\n const gridRef = useRef<THREE.GridHelper>(null);\n\n useFrame(() => {\n if (gridRef.current) {\n gridRef.current.rotation.y += 0.0002;\n }\n });\n\n const effectiveDepth = worldDepthMeters ?? worldWidthMeters;\n const floorWidth = worldWidthMeters * FLOOR_SCALE_FACTOR;\n const floorDepth = effectiveDepth * FLOOR_SCALE_FACTOR;\n const gridSize = worldWidthMeters * FLOOR_SCALE_FACTOR;\n const markerDistance = worldWidthMeters * 0.8;\n const markerDepth = effectiveDepth * 0.4;\n\n const floorMaterial = useMemo(\n () => {\n const pooledBaseColor = ThreeObjectPools.color.acquire();\n const pooledEmissiveColor = ThreeObjectPools.color.acquire();\n \n try {\n pooledBaseColor.set(0x2a2a2a); // Dark concrete\n pooledEmissiveColor.set(KOREAN_COLORS.PRIMARY_CYAN);\n \n const material = new THREE.MeshPhysicalMaterial({\n color: pooledBaseColor.clone(), // Material takes ownership of cloned color\n roughness: 0.3, // Wet/reflective surface\n metalness: 0.1,\n clearcoat: 0.3, // Wet sheen\n clearcoatRoughness: 0.4,\n envMapIntensity: 1.5, // Enhanced reflections from Environment preset\n emissive: pooledEmissiveColor.clone(), // Material takes ownership of cloned color\n emissiveIntensity: 0.05,\n });\n \n return material;\n } finally {\n ThreeObjectPools.color.release(pooledBaseColor);\n ThreeObjectPools.color.release(pooledEmissiveColor);\n }\n },\n [],\n );\n\n useEffect(() => {\n return () => {\n floorMaterial.dispose();\n };\n }, [floorMaterial]);\n\n return (\n <group>\n {/* Lighting based on theme */}\n {lighting === \"cyberpunk\" && (\n <>\n {/* Base ambient light with Korean cyan tint */}\n <ambientLight intensity={0.5} color={KOREAN_COLORS.PRIMARY_CYAN} />\n\n {/* Primary directional light (moonlight) with upgraded shadows */}\n <directionalLight\n position={[15, 20, 10]}\n intensity={1.5}\n color={0xffffff}\n castShadow\n shadow-mapSize={\n scale < 1.0 ? SHADOW_MAP_SIZE_MOBILE : SHADOW_MAP_SIZE_DESKTOP\n }\n shadow-camera-far={50}\n shadow-camera-left={-20}\n shadow-camera-right={20}\n shadow-camera-top={20}\n shadow-camera-bottom={-20}\n shadow-bias={-0.0001}\n />\n\n {/* Korean neon accent lights */}\n {/* Cyan neon light (left side) */}\n <pointLight\n position={[-10, 3, 0]}\n intensity={4}\n distance={25}\n decay={2}\n color={KOREAN_COLORS.PRIMARY_CYAN}\n />\n\n {/* Gold neon light (right side) */}\n <pointLight\n position={[10, 3, 0]}\n intensity={4}\n distance={25}\n decay={2}\n color={KOREAN_COLORS.ACCENT_GOLD}\n />\n\n {/* Blue accent light (behind) */}\n <pointLight\n position={[0, 5, -15]}\n intensity={3}\n distance={30}\n decay={2}\n color={KOREAN_COLORS.ACCENT_BLUE}\n />\n </>\n )}\n\n {lighting === \"traditional\" && (\n <>\n <ambientLight intensity={0.6} color={0xffffee} />\n <directionalLight\n position={[5, 10, 5]}\n intensity={0.8}\n castShadow\n shadow-mapSize={[2048, 2048]}\n />\n </>\n )}\n\n {lighting === \"neutral\" && (\n <>\n <ambientLight intensity={0.5} />\n <directionalLight position={[10, 10, 5]} intensity={1} castShadow />\n </>\n )}\n\n {/* Arena floor - dojang mat with reflective wet concrete aesthetic */}\n {/* Floor dimensions use pre-calculated floorWidth/floorDepth which already include FLOOR_SCALE_FACTOR */}\n <mesh rotation={[-Math.PI / 2, 0, 0]} position={[0, 0, 0]} receiveShadow>\n <planeGeometry args={[floorWidth, floorDepth]} />\n {lighting === \"cyberpunk\" ? (\n <primitive object={floorMaterial} attach=\"material\" />\n ) : (\n <meshPhysicalMaterial\n color={KOREAN_COLORS.UI_BACKGROUND_MEDIUM}\n roughness={0.7}\n metalness={0.1}\n clearcoat={0}\n clearcoatRoughness={0.2}\n />\n )}\n </mesh>\n\n {/* Cyberpunk grid overlay (scale-aware) */}\n <gridHelper\n ref={gridRef}\n args={[\n gridSize,\n 20,\n KOREAN_COLORS.PRIMARY_CYAN,\n KOREAN_COLORS.UI_BACKGROUND_DARK,\n ]}\n position={[0, 0.01, 0]}\n />\n\n {/* Korean traditional boundary markers (scale-aware) */}\n {[\n [-markerDistance, 0, -markerDepth],\n [-markerDistance, 0, markerDepth],\n [markerDistance, 0, -markerDepth],\n [markerDistance, 0, markerDepth],\n ].map((pos, i) => (\n <mesh key={i} position={pos as [number, number, number]} castShadow>\n <cylinderGeometry args={[0.1 * scale, 0.15 * scale, 0.8, 8]} />\n <meshPhysicalMaterial\n color={KOREAN_COLORS.ACCENT_GOLD}\n emissive={KOREAN_COLORS.ACCENT_GOLD}\n emissiveIntensity={0.5}\n metalness={0.8}\n roughness={0.2}\n clearcoat={1.0}\n />\n </mesh>\n ))}\n\n {/* Center marker - Yin Yang inspired (scale-aware) */}\n <mesh rotation={[-Math.PI / 2, 0, 0]} position={[0, 0.02, 0]}>\n <ringGeometry args={[0.8 * scale, 1.0 * scale, 32]} />\n <meshBasicMaterial\n color={KOREAN_COLORS.ACCENT_GOLD}\n transparent\n opacity={0.6}\n side={THREE.DoubleSide}\n />\n </mesh>\n\n {/* Korean Signage with neon glow */}\n {lighting === \"cyberpunk\" && <KoreanSignage3D scale={scale} />}\n\n {/* Atmospheric Particles (rain/mist) */}\n {lighting === \"cyberpunk\" && enableParticles && (\n <AtmosphericParticles3D\n count={scale >= 1.0 ? 500 : 250}\n scale={scale}\n speed={2}\n />\n )}\n </group>\n );\n};\n\nexport default CombatArena3D;\n"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"KoreanSignage3D.js","names":[],"sources":["../../../../../src/components/shared/three/scene/KoreanSignage3D.tsx"],"sourcesContent":["/**\n * KoreanSignage3D - Three.js 3D Korean text signage with emissive glow\n *\n * Renders Korean text signs with neon emissive effects positioned around the arena\n * Creates an immersive cyberpunk Korean martial arts atmosphere\n *\n * Note: Uses drei's Text component with default Inter font which supports Korean\n * The `font` prop requires a font FILE URL (.ttf/.woff), not a CSS font-family string\n */\n\nimport { Text } from \"@react-three/drei\";\nimport React, { Suspense, useEffect, useMemo } from \"react\";\nimport * as THREE from \"three\";\nimport { KOREAN_COLORS } from \"../../../../types/constants\";\n\n/**\n * Props for the KoreanSignage3D component.\n */\nexport interface KoreanSignage3DProps {\n /** Scale factor for signage size (1.0 = desktop, <1.0 = mobile). Defaults to 1.0 */\n readonly scale?: number;\n}\n\n/**\n * KoreanSignage3D Component\n * Creates Korean text signs with neon emissive glow around the combat arena\n *\n * Signs:\n * - \"전투\" (Combat) - Left wall, gold accent\n * - \"흑괘\" (Black Trigram) - Right wall, cyan accent\n * - \"급소격\" (Vital Point Strike) - Back wall, red accent\n */\nexport const KoreanSignage3D: React.FC<KoreanSignage3DProps> = ({\n scale = 1.0,\n}) => {\n const goldNeonMaterial = useMemo(\n () =>\n new THREE.MeshBasicMaterial({\n color: KOREAN_COLORS.ACCENT_GOLD,\n toneMapped: false, // Prevent tone mapping for bloom effect\n }),\n [],\n );\n\n const cyanNeonMaterial = useMemo(\n () =>\n new THREE.MeshBasicMaterial({\n color: KOREAN_COLORS.PRIMARY_CYAN,\n toneMapped: false,\n }),\n [],\n );\n\n const redNeonMaterial = useMemo(\n () =>\n new THREE.MeshBasicMaterial({\n color: KOREAN_COLORS.KOREAN_RED,\n toneMapped: false,\n }),\n [],\n );\n\n const leftWallX = -12 * scale;\n const rightWallX = 12 * scale;\n const backWallZ = -14 * scale;\n const signHeight = 5 * scale;\n const fontSize = 1.5 * scale;\n const outlineWidth = 0.05 * scale;\n\n useEffect(() => {\n return () => {\n goldNeonMaterial.dispose();\n cyanNeonMaterial.dispose();\n redNeonMaterial.dispose();\n };\n }, [goldNeonMaterial, cyanNeonMaterial, redNeonMaterial]);\n\n return (\n <Suspense fallback={null}>\n <group>\n {/* \"전투\" (Combat) sign - left wall */}\n {/* Note: material prop takes precedence over color prop in Text component */}\n {/* Note: Omitting font prop uses default Inter font which supports Korean */}\n <Text\n position={[leftWallX, signHeight, 0]}\n rotation={[0, Math.PI / 2, 0]}\n fontSize={fontSize}\n outlineColor={KOREAN_COLORS.PRIMARY_CYAN}\n outlineWidth={outlineWidth}\n material={goldNeonMaterial}\n anchorX=\"center\"\n anchorY=\"middle\"\n >\n 전투\n </Text>\n\n {/* \"흑괘\" (Black Trigram) sign - right wall */}\n <Text\n position={[rightWallX, signHeight, 0]}\n rotation={[0, -Math.PI / 2, 0]}\n fontSize={fontSize}\n outlineColor={KOREAN_COLORS.ACCENT_GOLD}\n outlineWidth={outlineWidth}\n material={cyanNeonMaterial}\n anchorX=\"center\"\n anchorY=\"middle\"\n >\n 흑괘\n </Text>\n\n {/* \"급소격\" (Vital Point Strike) sign - back wall */}\n <Text\n position={[0, signHeight, backWallZ]}\n rotation={[0, 0, 0]}\n fontSize={fontSize * 0.8} // Slightly smaller for back wall\n outlineColor={KOREAN_COLORS.ACCENT_GOLD}\n outlineWidth={outlineWidth}\n material={redNeonMaterial}\n anchorX=\"center\"\n anchorY=\"middle\"\n >\n 급소격\n </Text>\n </group>\n </Suspense>\n );\n};\n\nexport default KoreanSignage3D;\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;AAgCA,IAAa,mBAAmD,EAC9D,QAAQ,QACJ;CACJ,MAAM,mBAAmB,cAErB,IAAI,MAAM,kBAAkB;EAC1B,OAAO,cAAc;EACrB,YAAY;
|
|
1
|
+
{"version":3,"file":"KoreanSignage3D.js","names":[],"sources":["../../../../../src/components/shared/three/scene/KoreanSignage3D.tsx"],"sourcesContent":["/**\n * KoreanSignage3D - Three.js 3D Korean text signage with emissive glow\n *\n * Renders Korean text signs with neon emissive effects positioned around the arena\n * Creates an immersive cyberpunk Korean martial arts atmosphere\n *\n * Note: Uses drei's Text component with default Inter font which supports Korean\n * The `font` prop requires a font FILE URL (.ttf/.woff), not a CSS font-family string\n */\n\nimport { Text } from \"@react-three/drei\";\nimport React, { Suspense, useEffect, useMemo } from \"react\";\nimport * as THREE from \"three\";\nimport { KOREAN_COLORS } from \"../../../../types/constants\";\n\n/**\n * Props for the KoreanSignage3D component.\n */\nexport interface KoreanSignage3DProps {\n /** Scale factor for signage size (1.0 = desktop, <1.0 = mobile). Defaults to 1.0 */\n readonly scale?: number;\n}\n\n/**\n * KoreanSignage3D Component\n * Creates Korean text signs with neon emissive glow around the combat arena\n *\n * Signs:\n * - \"전투\" (Combat) - Left wall, gold accent\n * - \"흑괘\" (Black Trigram) - Right wall, cyan accent\n * - \"급소격\" (Vital Point Strike) - Back wall, red accent\n */\nexport const KoreanSignage3D: React.FC<KoreanSignage3DProps> = ({\n scale = 1.0,\n}) => {\n const goldNeonMaterial = useMemo(\n () =>\n new THREE.MeshBasicMaterial({\n color: KOREAN_COLORS.ACCENT_GOLD,\n toneMapped: false, // Prevent tone mapping for bloom effect\n }),\n [],\n );\n\n const cyanNeonMaterial = useMemo(\n () =>\n new THREE.MeshBasicMaterial({\n color: KOREAN_COLORS.PRIMARY_CYAN,\n toneMapped: false,\n }),\n [],\n );\n\n const redNeonMaterial = useMemo(\n () =>\n new THREE.MeshBasicMaterial({\n color: KOREAN_COLORS.KOREAN_RED,\n toneMapped: false,\n }),\n [],\n );\n\n const leftWallX = -12 * scale;\n const rightWallX = 12 * scale;\n const backWallZ = -14 * scale;\n const signHeight = 5 * scale;\n const fontSize = 1.5 * scale;\n const outlineWidth = 0.05 * scale;\n\n useEffect(() => {\n return () => {\n goldNeonMaterial.dispose();\n cyanNeonMaterial.dispose();\n redNeonMaterial.dispose();\n };\n }, [goldNeonMaterial, cyanNeonMaterial, redNeonMaterial]);\n\n return (\n <Suspense fallback={null}>\n <group>\n {/* \"전투\" (Combat) sign - left wall */}\n {/* Note: material prop takes precedence over color prop in Text component */}\n {/* Note: Omitting font prop uses default Inter font which supports Korean */}\n <Text\n position={[leftWallX, signHeight, 0]}\n rotation={[0, Math.PI / 2, 0]}\n fontSize={fontSize}\n outlineColor={KOREAN_COLORS.PRIMARY_CYAN}\n outlineWidth={outlineWidth}\n material={goldNeonMaterial}\n anchorX=\"center\"\n anchorY=\"middle\"\n >\n 전투\n </Text>\n\n {/* \"흑괘\" (Black Trigram) sign - right wall */}\n <Text\n position={[rightWallX, signHeight, 0]}\n rotation={[0, -Math.PI / 2, 0]}\n fontSize={fontSize}\n outlineColor={KOREAN_COLORS.ACCENT_GOLD}\n outlineWidth={outlineWidth}\n material={cyanNeonMaterial}\n anchorX=\"center\"\n anchorY=\"middle\"\n >\n 흑괘\n </Text>\n\n {/* \"급소격\" (Vital Point Strike) sign - back wall */}\n <Text\n position={[0, signHeight, backWallZ]}\n rotation={[0, 0, 0]}\n fontSize={fontSize * 0.8} // Slightly smaller for back wall\n outlineColor={KOREAN_COLORS.ACCENT_GOLD}\n outlineWidth={outlineWidth}\n material={redNeonMaterial}\n anchorX=\"center\"\n anchorY=\"middle\"\n >\n 급소격\n </Text>\n </group>\n </Suspense>\n );\n};\n\nexport default KoreanSignage3D;\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;AAgCA,IAAa,mBAAmD,EAC9D,QAAQ,QACJ;CACJ,MAAM,mBAAmB,cAErB,IAAI,MAAM,kBAAkB;EAC1B,OAAO,cAAc;EACrB,YAAY;CACd,CAAC,GACH,CAAC,CACH;CAEA,MAAM,mBAAmB,cAErB,IAAI,MAAM,kBAAkB;EAC1B,OAAO,cAAc;EACrB,YAAY;CACd,CAAC,GACH,CAAC,CACH;CAEA,MAAM,kBAAkB,cAEpB,IAAI,MAAM,kBAAkB;EAC1B,OAAO,cAAc;EACrB,YAAY;CACd,CAAC,GACH,CAAC,CACH;CAEA,MAAM,YAAY,MAAM;CACxB,MAAM,aAAa,KAAK;CACxB,MAAM,YAAY,MAAM;CACxB,MAAM,aAAa,IAAI;CACvB,MAAM,WAAW,MAAM;CACvB,MAAM,eAAe,MAAO;CAE5B,gBAAgB;EACd,aAAa;GACX,iBAAiB,QAAQ;GACzB,iBAAiB,QAAQ;GACzB,gBAAgB,QAAQ;EAC1B;CACF,GAAG;EAAC;EAAkB;EAAkB;CAAe,CAAC;CAExD,OACE,oBAAC,UAAD;EAAU,UAAU;YAClB,qBAAC,SAAD,EAAA,UAAA;GAIE,oBAAC,MAAD;IACE,UAAU;KAAC;KAAW;KAAY;IAAC;IACnC,UAAU;KAAC;KAAG,KAAK,KAAK;KAAG;IAAC;IAClB;IACV,cAAc,cAAc;IACd;IACd,UAAU;IACV,SAAQ;IACR,SAAQ;cACT;GAEK,CAAA;GAGN,oBAAC,MAAD;IACE,UAAU;KAAC;KAAY;KAAY;IAAC;IACpC,UAAU;KAAC;KAAG,CAAC,KAAK,KAAK;KAAG;IAAC;IACnB;IACV,cAAc,cAAc;IACd;IACd,UAAU;IACV,SAAQ;IACR,SAAQ;cACT;GAEK,CAAA;GAGN,oBAAC,MAAD;IACE,UAAU;KAAC;KAAG;KAAY;IAAS;IACnC,UAAU;KAAC;KAAG;KAAG;IAAC;IAClB,UAAU,WAAW;IACrB,cAAc,cAAc;IACd;IACd,UAAU;IACV,SAAQ;IACR,SAAQ;cACT;GAEK,CAAA;EACD,EAAA,CAAA;CACC,CAAA;AAEd"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ArchetypeCard.js","names":[],"sources":["../../../../../src/components/shared/three/ui/ArchetypeCard.tsx"],"sourcesContent":["/**\n * ArchetypeCard - Three.js-compatible character archetype card\n * \n * Displays player archetype information with Korean theming\n * \n * @module components/three\n */\n\nimport { Html } from \"@react-three/drei\";\nimport React, { useCallback, useMemo, useState } from \"react\";\nimport { PlayerArchetype } from \"../../../../types/common\";\nimport { KOREAN_COLORS, FONT_FAMILY } from \"../../../../types/constants\";\nimport { hexToRgbaString } from \"../../../../utils/colorUtils\";\nimport { PLAYER_ARCHETYPES_DATA } from \"../../../../systems/types\";\n\n/**\n * Props for ArchetypeCard component\n */\nexport interface ArchetypeCardProps {\n readonly archetype: PlayerArchetype;\n readonly onSelect?: (archetype: PlayerArchetype) => void;\n readonly isSelected?: boolean;\n readonly position?: [number, number, number];\n readonly width?: number;\n readonly showStats?: boolean;\n readonly testId?: string;\n}\n\n/**\n * ArchetypeCard Component\n * \n * A card component for displaying player archetypes with Korean martial arts styling.\n * Includes archetype name, description, and optional stats.\n * \n * @example\n * ```tsx\n * <ArchetypeCard\n * archetype={PlayerArchetype.MUSA}\n * onSelect={(archetype) => console.log(archetype)}\n * showStats\n * />\n * ```\n */\nexport const ArchetypeCard: React.FC<ArchetypeCardProps> = ({\n archetype,\n onSelect,\n isSelected = false,\n position = [0, 0, 0],\n width = 320,\n showStats = true,\n testId,\n}) => {\n const [isHovered, setIsHovered] = useState(false);\n\n const archetypeData = useMemo(\n () => PLAYER_ARCHETYPES_DATA[archetype],\n [archetype]\n );\n\n const handleClick = useCallback(() => {\n if (onSelect) {\n onSelect(archetype);\n }\n }, [onSelect, archetype]);\n\n const handleMouseEnter = useCallback(() => {\n setIsHovered(true);\n }, []);\n\n const handleMouseLeave = useCallback(() => {\n setIsHovered(false);\n }, []);\n\n const cardStyle = useMemo<React.CSSProperties>(() => {\n let background = hexToRgbaString(KOREAN_COLORS.UI_BACKGROUND_DARK, 0.95);\n let borderColor = hexToRgbaString(KOREAN_COLORS.ACCENT_GOLD, 0.4);\n let boxShadow = `0 2px 8px ${hexToRgbaString(KOREAN_COLORS.BLACK_SOLID, 0.3)}`;\n\n if (isSelected) {\n background = hexToRgbaString(KOREAN_COLORS.PRIMARY_CYAN, 0.15);\n borderColor = hexToRgbaString(KOREAN_COLORS.PRIMARY_CYAN, 0.9);\n boxShadow = `\n 0 4px 12px ${hexToRgbaString(KOREAN_COLORS.BLACK_SOLID, 0.5)},\n 0 0 20px ${hexToRgbaString(KOREAN_COLORS.PRIMARY_CYAN, 0.4)}\n `;\n } else if (isHovered) {\n background = hexToRgbaString(KOREAN_COLORS.ACCENT_GOLD, 0.1);\n borderColor = hexToRgbaString(KOREAN_COLORS.ACCENT_GOLD, 0.7);\n boxShadow = `\n 0 4px 12px ${hexToRgbaString(KOREAN_COLORS.BLACK_SOLID, 0.4)},\n 0 0 15px ${hexToRgbaString(KOREAN_COLORS.ACCENT_GOLD, 0.3)}\n `;\n }\n\n return {\n width: `${width}px`,\n background,\n border: `2px solid ${borderColor}`,\n borderRadius: \"8px\",\n padding: \"16px\",\n cursor: onSelect ? \"pointer\" : \"default\",\n transition: \"all 0.3s ease\",\n boxShadow,\n transform: isHovered && onSelect ? \"translateY(-4px)\" : \"translateY(0)\",\n userSelect: \"none\",\n WebkitUserSelect: \"none\",\n };\n }, [width, isSelected, isHovered, onSelect]);\n\n const headerStyle = useMemo<React.CSSProperties>(\n () => ({\n marginBottom: \"12px\",\n paddingBottom: \"8px\",\n borderBottom: `1px solid ${hexToRgbaString(KOREAN_COLORS.ACCENT_GOLD, 0.3)}`,\n }),\n []\n );\n\n const titleStyle = useMemo<React.CSSProperties>(\n () => ({\n fontFamily: FONT_FAMILY.KOREAN,\n fontSize: \"20px\",\n fontWeight: \"bold\",\n color: hexToRgbaString(KOREAN_COLORS.ACCENT_GOLD),\n textShadow: `0 2px 4px ${hexToRgbaString(KOREAN_COLORS.BLACK_SOLID, 0.5)}`,\n marginBottom: \"4px\",\n }),\n []\n );\n\n const subtitleStyle = useMemo<React.CSSProperties>(\n () => ({\n fontFamily: FONT_FAMILY.KOREAN,\n fontSize: \"14px\",\n fontStyle: \"italic\",\n color: hexToRgbaString(KOREAN_COLORS.TEXT_SECONDARY),\n opacity: 0.8,\n }),\n []\n );\n\n const descriptionStyle = useMemo<React.CSSProperties>(\n () => ({\n fontFamily: FONT_FAMILY.KOREAN,\n fontSize: \"14px\",\n lineHeight: \"1.6\",\n color: hexToRgbaString(KOREAN_COLORS.TEXT_PRIMARY),\n marginBottom: showStats ? \"12px\" : \"0\",\n }),\n [showStats]\n );\n\n const statsContainerStyle = useMemo<React.CSSProperties>(\n () => ({\n display: \"grid\",\n gridTemplateColumns: \"repeat(2, 1fr)\",\n gap: \"8px\",\n marginTop: \"12px\",\n }),\n []\n );\n\n const statItemStyle = useMemo<React.CSSProperties>(\n () => ({\n display: \"flex\",\n flexDirection: \"column\",\n gap: \"4px\",\n }),\n []\n );\n\n const statLabelStyle = useMemo<React.CSSProperties>(\n () => ({\n fontFamily: FONT_FAMILY.KOREAN,\n fontSize: \"12px\",\n color: hexToRgbaString(KOREAN_COLORS.TEXT_SECONDARY),\n fontWeight: \"bold\",\n }),\n []\n );\n\n const statValueStyle = useMemo<React.CSSProperties>(\n () => ({\n fontFamily: FONT_FAMILY.KOREAN,\n fontSize: \"16px\",\n color: hexToRgbaString(KOREAN_COLORS.PRIMARY_CYAN),\n fontWeight: \"bold\",\n }),\n []\n );\n\n return (\n <Html position={position} center>\n <div\n onClick={handleClick}\n onMouseEnter={handleMouseEnter}\n onMouseLeave={handleMouseLeave}\n style={cardStyle}\n data-testid={testId ?? `archetype-card-${archetype}`}\n >\n {/* Header */}\n <div style={headerStyle}>\n <div style={titleStyle}>{archetypeData.name.korean}</div>\n <div style={subtitleStyle}>{archetypeData.name.english}</div>\n </div>\n\n {/* Description */}\n <div style={descriptionStyle}>\n {archetypeData.description.korean}\n </div>\n\n {/* Stats */}\n {showStats && (\n <div style={statsContainerStyle}>\n <div style={statItemStyle}>\n <div style={statLabelStyle}>공격 | Attack</div>\n <div style={statValueStyle}>{archetypeData.stats.attackPower}</div>\n </div>\n <div style={statItemStyle}>\n <div style={statLabelStyle}>방어 | Defense</div>\n <div style={statValueStyle}>{archetypeData.stats.defense}</div>\n </div>\n <div style={statItemStyle}>\n <div style={statLabelStyle}>속도 | Speed</div>\n <div style={statValueStyle}>{archetypeData.stats.speed}</div>\n </div>\n <div style={statItemStyle}>\n <div style={statLabelStyle}>기력 | Ki</div>\n <div style={statValueStyle}>{archetypeData.baseKi}</div>\n </div>\n </div>\n )}\n </div>\n </Html>\n );\n};\n\nArchetypeCard.displayName = \"ArchetypeCard\";\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"ArchetypeCard.js","names":[],"sources":["../../../../../src/components/shared/three/ui/ArchetypeCard.tsx"],"sourcesContent":["/**\n * ArchetypeCard - Three.js-compatible character archetype card\n * \n * Displays player archetype information with Korean theming\n * \n * @module components/three\n */\n\nimport { Html } from \"@react-three/drei\";\nimport React, { useCallback, useMemo, useState } from \"react\";\nimport { PlayerArchetype } from \"../../../../types/common\";\nimport { KOREAN_COLORS, FONT_FAMILY } from \"../../../../types/constants\";\nimport { hexToRgbaString } from \"../../../../utils/colorUtils\";\nimport { PLAYER_ARCHETYPES_DATA } from \"../../../../systems/types\";\n\n/**\n * Props for ArchetypeCard component\n */\nexport interface ArchetypeCardProps {\n readonly archetype: PlayerArchetype;\n readonly onSelect?: (archetype: PlayerArchetype) => void;\n readonly isSelected?: boolean;\n readonly position?: [number, number, number];\n readonly width?: number;\n readonly showStats?: boolean;\n readonly testId?: string;\n}\n\n/**\n * ArchetypeCard Component\n * \n * A card component for displaying player archetypes with Korean martial arts styling.\n * Includes archetype name, description, and optional stats.\n * \n * @example\n * ```tsx\n * <ArchetypeCard\n * archetype={PlayerArchetype.MUSA}\n * onSelect={(archetype) => console.log(archetype)}\n * showStats\n * />\n * ```\n */\nexport const ArchetypeCard: React.FC<ArchetypeCardProps> = ({\n archetype,\n onSelect,\n isSelected = false,\n position = [0, 0, 0],\n width = 320,\n showStats = true,\n testId,\n}) => {\n const [isHovered, setIsHovered] = useState(false);\n\n const archetypeData = useMemo(\n () => PLAYER_ARCHETYPES_DATA[archetype],\n [archetype]\n );\n\n const handleClick = useCallback(() => {\n if (onSelect) {\n onSelect(archetype);\n }\n }, [onSelect, archetype]);\n\n const handleMouseEnter = useCallback(() => {\n setIsHovered(true);\n }, []);\n\n const handleMouseLeave = useCallback(() => {\n setIsHovered(false);\n }, []);\n\n const cardStyle = useMemo<React.CSSProperties>(() => {\n let background = hexToRgbaString(KOREAN_COLORS.UI_BACKGROUND_DARK, 0.95);\n let borderColor = hexToRgbaString(KOREAN_COLORS.ACCENT_GOLD, 0.4);\n let boxShadow = `0 2px 8px ${hexToRgbaString(KOREAN_COLORS.BLACK_SOLID, 0.3)}`;\n\n if (isSelected) {\n background = hexToRgbaString(KOREAN_COLORS.PRIMARY_CYAN, 0.15);\n borderColor = hexToRgbaString(KOREAN_COLORS.PRIMARY_CYAN, 0.9);\n boxShadow = `\n 0 4px 12px ${hexToRgbaString(KOREAN_COLORS.BLACK_SOLID, 0.5)},\n 0 0 20px ${hexToRgbaString(KOREAN_COLORS.PRIMARY_CYAN, 0.4)}\n `;\n } else if (isHovered) {\n background = hexToRgbaString(KOREAN_COLORS.ACCENT_GOLD, 0.1);\n borderColor = hexToRgbaString(KOREAN_COLORS.ACCENT_GOLD, 0.7);\n boxShadow = `\n 0 4px 12px ${hexToRgbaString(KOREAN_COLORS.BLACK_SOLID, 0.4)},\n 0 0 15px ${hexToRgbaString(KOREAN_COLORS.ACCENT_GOLD, 0.3)}\n `;\n }\n\n return {\n width: `${width}px`,\n background,\n border: `2px solid ${borderColor}`,\n borderRadius: \"8px\",\n padding: \"16px\",\n cursor: onSelect ? \"pointer\" : \"default\",\n transition: \"all 0.3s ease\",\n boxShadow,\n transform: isHovered && onSelect ? \"translateY(-4px)\" : \"translateY(0)\",\n userSelect: \"none\",\n WebkitUserSelect: \"none\",\n };\n }, [width, isSelected, isHovered, onSelect]);\n\n const headerStyle = useMemo<React.CSSProperties>(\n () => ({\n marginBottom: \"12px\",\n paddingBottom: \"8px\",\n borderBottom: `1px solid ${hexToRgbaString(KOREAN_COLORS.ACCENT_GOLD, 0.3)}`,\n }),\n []\n );\n\n const titleStyle = useMemo<React.CSSProperties>(\n () => ({\n fontFamily: FONT_FAMILY.KOREAN,\n fontSize: \"20px\",\n fontWeight: \"bold\",\n color: hexToRgbaString(KOREAN_COLORS.ACCENT_GOLD),\n textShadow: `0 2px 4px ${hexToRgbaString(KOREAN_COLORS.BLACK_SOLID, 0.5)}`,\n marginBottom: \"4px\",\n }),\n []\n );\n\n const subtitleStyle = useMemo<React.CSSProperties>(\n () => ({\n fontFamily: FONT_FAMILY.KOREAN,\n fontSize: \"14px\",\n fontStyle: \"italic\",\n color: hexToRgbaString(KOREAN_COLORS.TEXT_SECONDARY),\n opacity: 0.8,\n }),\n []\n );\n\n const descriptionStyle = useMemo<React.CSSProperties>(\n () => ({\n fontFamily: FONT_FAMILY.KOREAN,\n fontSize: \"14px\",\n lineHeight: \"1.6\",\n color: hexToRgbaString(KOREAN_COLORS.TEXT_PRIMARY),\n marginBottom: showStats ? \"12px\" : \"0\",\n }),\n [showStats]\n );\n\n const statsContainerStyle = useMemo<React.CSSProperties>(\n () => ({\n display: \"grid\",\n gridTemplateColumns: \"repeat(2, 1fr)\",\n gap: \"8px\",\n marginTop: \"12px\",\n }),\n []\n );\n\n const statItemStyle = useMemo<React.CSSProperties>(\n () => ({\n display: \"flex\",\n flexDirection: \"column\",\n gap: \"4px\",\n }),\n []\n );\n\n const statLabelStyle = useMemo<React.CSSProperties>(\n () => ({\n fontFamily: FONT_FAMILY.KOREAN,\n fontSize: \"12px\",\n color: hexToRgbaString(KOREAN_COLORS.TEXT_SECONDARY),\n fontWeight: \"bold\",\n }),\n []\n );\n\n const statValueStyle = useMemo<React.CSSProperties>(\n () => ({\n fontFamily: FONT_FAMILY.KOREAN,\n fontSize: \"16px\",\n color: hexToRgbaString(KOREAN_COLORS.PRIMARY_CYAN),\n fontWeight: \"bold\",\n }),\n []\n );\n\n return (\n <Html position={position} center>\n <div\n onClick={handleClick}\n onMouseEnter={handleMouseEnter}\n onMouseLeave={handleMouseLeave}\n style={cardStyle}\n data-testid={testId ?? `archetype-card-${archetype}`}\n >\n {/* Header */}\n <div style={headerStyle}>\n <div style={titleStyle}>{archetypeData.name.korean}</div>\n <div style={subtitleStyle}>{archetypeData.name.english}</div>\n </div>\n\n {/* Description */}\n <div style={descriptionStyle}>\n {archetypeData.description.korean}\n </div>\n\n {/* Stats */}\n {showStats && (\n <div style={statsContainerStyle}>\n <div style={statItemStyle}>\n <div style={statLabelStyle}>공격 | Attack</div>\n <div style={statValueStyle}>{archetypeData.stats.attackPower}</div>\n </div>\n <div style={statItemStyle}>\n <div style={statLabelStyle}>방어 | Defense</div>\n <div style={statValueStyle}>{archetypeData.stats.defense}</div>\n </div>\n <div style={statItemStyle}>\n <div style={statLabelStyle}>속도 | Speed</div>\n <div style={statValueStyle}>{archetypeData.stats.speed}</div>\n </div>\n <div style={statItemStyle}>\n <div style={statLabelStyle}>기력 | Ki</div>\n <div style={statValueStyle}>{archetypeData.baseKi}</div>\n </div>\n </div>\n )}\n </div>\n </Html>\n );\n};\n\nArchetypeCard.displayName = \"ArchetypeCard\";\n"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"BodyPartHealthDisplay.js","names":[],"sources":["../../../../../src/components/shared/three/ui/BodyPartHealthDisplay.tsx"],"sourcesContent":["/**\n * BodyPartHealthDisplay Component - Individual body part health visualization\n *\n * Displays health bars for all 8 body parts:\n * - Head (두부)\n * - Neck (경부)\n * - Torso Upper (상부 몸통)\n * - Torso Lower (하부 몸통)\n * - Left Arm (좌팔)\n * - Right Arm (우팔)\n * - Left Leg (좌다리)\n * - Right Leg (우다리)\n *\n * @module components/shared/three/ui/BodyPartHealthDisplay\n * @category Shared UI\n * @korean 신체부위체력표시\n */\n\nimport React, { useMemo } from \"react\";\nimport { BodyPart, BodyPartHealth } from \"../../../../systems/bodypart/types\";\nimport { FONT_FAMILY, KOREAN_COLORS } from \"../../../../types/constants\";\nimport { hexToRgbaString } from \"../../../../utils/colorUtils\";\n\nexport interface BodyPartHealthDisplayProps {\n /** Body part health data */\n readonly bodyPartHealth: BodyPartHealth;\n /** Player identifier for test IDs */\n readonly playerId: string;\n /** Position: 'left' for player 1, 'right' for player 2 */\n readonly position: \"left\" | \"right\";\n /** Whether to use mobile-optimized sizing */\n readonly isMobile: boolean;\n}\n\n/**\n * Body part names in Korean and English\n */\nconst BODY_PART_NAMES: Record<BodyPart, { korean: string; english: string }> = {\n [BodyPart.HEAD]: { korean: \"두부\", english: \"Head\" },\n [BodyPart.NECK]: { korean: \"경부\", english: \"Neck\" },\n [BodyPart.TORSO_UPPER]: { korean: \"상부\", english: \"Upper\" },\n [BodyPart.TORSO_LOWER]: { korean: \"하부\", english: \"Lower\" },\n [BodyPart.ARM_LEFT]: { korean: \"좌팔\", english: \"L.Arm\" },\n [BodyPart.ARM_RIGHT]: { korean: \"우팔\", english: \"R.Arm\" },\n [BodyPart.LEG_LEFT]: { korean: \"좌다리\", english: \"L.Leg\" },\n [BodyPart.LEG_RIGHT]: { korean: \"우다리\", english: \"R.Leg\" },\n};\n\n/**\n * Get health bar color based on health percentage\n */\nconst getHealthColor = (health: number): number => {\n if (health >= 80) return KOREAN_COLORS.HEALTH_FULL;\n if (health >= 60) return KOREAN_COLORS.ACCENT_GOLD;\n if (health >= 40) return KOREAN_COLORS.WARNING_ORANGE;\n if (health >= 20) return KOREAN_COLORS.PAIN_INDICATOR;\n return KOREAN_COLORS.NEGATIVE_RED_DARK;\n};\n\n/**\n * BodyPartHealthDisplay - Shows health bars for all 8 body parts\n *\n * @example\n * ```tsx\n * <BodyPartHealthDisplay\n * bodyPartHealth={player.bodyPartHealth}\n * playerId=\"player-1\"\n * position=\"left\"\n * isMobile={false}\n * />\n * ```\n */\nexport const BodyPartHealthDisplay: React.FC<BodyPartHealthDisplayProps> = ({\n bodyPartHealth,\n playerId,\n position,\n isMobile,\n}) => {\n const isLeft = position === \"left\";\n\n const barWidth = isMobile ? 100 : 140;\n const barHeight = isMobile ? 8 : 10;\n const fontSize = isMobile ? 9 : 10;\n const gap = isMobile ? \"4px\" : \"5px\";\n\n const bodyPartGroups = useMemo(\n () => [\n {\n label: \"상체 | Upper\",\n parts: [\n BodyPart.HEAD,\n BodyPart.NECK,\n BodyPart.TORSO_UPPER,\n BodyPart.TORSO_LOWER,\n ],\n },\n { label: \"팔 | Arms\", parts: [BodyPart.ARM_LEFT, BodyPart.ARM_RIGHT] },\n { label: \"다리 | Legs\", parts: [BodyPart.LEG_LEFT, BodyPart.LEG_RIGHT] },\n ],\n [],\n );\n\n return (\n <div\n data-testid={`body-part-health-${playerId}`}\n style={{\n position: \"relative\",\n display: \"flex\",\n flexDirection: \"column\",\n gap,\n backgroundColor: hexToRgbaString(KOREAN_COLORS.UI_BACKGROUND_DARK, 0.9),\n borderRadius: \"8px\",\n padding: isMobile ? \"6px\" : \"8px\",\n border: `2px solid ${hexToRgbaString(KOREAN_COLORS.PRIMARY_CYAN, 0.7)}`,\n boxShadow: `0 0 12px ${hexToRgbaString(\n KOREAN_COLORS.PRIMARY_CYAN,\n 0.3,\n )}`,\n pointerEvents: \"none\",\n width: \"100%\",\n boxSizing: \"border-box\" as const,\n fontSize: isMobile ? \"9px\" : \"10px\",\n }}\n >\n {/* Title */}\n <div\n style={{\n fontSize: isMobile ? \"10px\" : \"11px\",\n fontWeight: \"bold\",\n fontFamily: FONT_FAMILY.KOREAN,\n color: hexToRgbaString(KOREAN_COLORS.PRIMARY_CYAN, 1),\n textAlign: isLeft ? \"left\" : \"right\",\n marginBottom: \"2px\",\n }}\n >\n 신체 | Body Parts\n </div>\n\n {/* Body part groups */}\n {bodyPartGroups.map((group) => (\n <div\n key={group.label}\n style={{\n display: \"flex\",\n flexDirection: \"column\",\n gap: \"3px\",\n }}\n >\n {/* Group label - optional, can be hidden for compact display */}\n <div\n style={{\n fontSize: `${fontSize - 1}px`,\n fontFamily: FONT_FAMILY.KOREAN,\n color: hexToRgbaString(KOREAN_COLORS.TEXT_SECONDARY, 0.8),\n textAlign: isLeft ? \"left\" : \"right\",\n marginTop: group.label === bodyPartGroups[0].label ? \"0\" : \"4px\",\n }}\n >\n {group.label}\n </div>\n\n {/* Body parts in group */}\n {group.parts.map((part) => {\n const health = bodyPartHealth[part];\n const names = BODY_PART_NAMES[part];\n const healthColor = getHealthColor(health);\n const shouldPulse = health < 20;\n\n return (\n <div\n key={part}\n data-testid={`body-part-${playerId}-${part}`}\n style={{\n display: \"flex\",\n flexDirection: \"column\",\n gap: \"2px\",\n }}\n >\n {/* Body part name and value */}\n <div\n style={{\n display: \"flex\",\n justifyContent: \"space-between\",\n alignItems: \"center\",\n fontSize: `${fontSize}px`,\n fontFamily: FONT_FAMILY.KOREAN,\n color: hexToRgbaString(healthColor, 1),\n }}\n >\n <span style={{ fontWeight: health < 40 ? \"bold\" : \"normal\" }}>\n {isMobile\n ? names.korean\n : `${names.korean} | ${names.english}`}\n </span>\n <span style={{ fontSize: `${fontSize - 1}px` }}>\n {Math.round(health)}%\n </span>\n </div>\n\n {/* Health bar */}\n <div\n style={{\n width: `${barWidth}px`,\n height: `${barHeight}px`,\n backgroundColor: hexToRgbaString(\n KOREAN_COLORS.UI_BACKGROUND_MEDIUM,\n 1,\n ),\n borderRadius: \"2px\",\n overflow: \"hidden\",\n animation: shouldPulse\n ? \"healthPulse 0.8s infinite\"\n : \"none\",\n }}\n >\n <div\n style={{\n width: `${health}%`,\n height: \"100%\",\n backgroundColor: hexToRgbaString(healthColor, 1),\n transition:\n \"width 0.3s ease-in-out, background-color 0.3s ease-in-out\",\n boxShadow: `0 0 8px ${hexToRgbaString(healthColor, 0.4)}`,\n }}\n />\n </div>\n </div>\n );\n })}\n </div>\n ))}\n </div>\n );\n};\n\nexport default BodyPartHealthDisplay;\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"BodyPartHealthDisplay.js","names":[],"sources":["../../../../../src/components/shared/three/ui/BodyPartHealthDisplay.tsx"],"sourcesContent":["/**\n * BodyPartHealthDisplay Component - Individual body part health visualization\n *\n * Displays health bars for all 8 body parts:\n * - Head (두부)\n * - Neck (경부)\n * - Torso Upper (상부 몸통)\n * - Torso Lower (하부 몸통)\n * - Left Arm (좌팔)\n * - Right Arm (우팔)\n * - Left Leg (좌다리)\n * - Right Leg (우다리)\n *\n * @module components/shared/three/ui/BodyPartHealthDisplay\n * @category Shared UI\n * @korean 신체부위체력표시\n */\n\nimport React, { useMemo } from \"react\";\nimport { BodyPart, BodyPartHealth } from \"../../../../systems/bodypart/types\";\nimport { FONT_FAMILY, KOREAN_COLORS } from \"../../../../types/constants\";\nimport { hexToRgbaString } from \"../../../../utils/colorUtils\";\n\nexport interface BodyPartHealthDisplayProps {\n /** Body part health data */\n readonly bodyPartHealth: BodyPartHealth;\n /** Player identifier for test IDs */\n readonly playerId: string;\n /** Position: 'left' for player 1, 'right' for player 2 */\n readonly position: \"left\" | \"right\";\n /** Whether to use mobile-optimized sizing */\n readonly isMobile: boolean;\n}\n\n/**\n * Body part names in Korean and English\n */\nconst BODY_PART_NAMES: Record<BodyPart, { korean: string; english: string }> = {\n [BodyPart.HEAD]: { korean: \"두부\", english: \"Head\" },\n [BodyPart.NECK]: { korean: \"경부\", english: \"Neck\" },\n [BodyPart.TORSO_UPPER]: { korean: \"상부\", english: \"Upper\" },\n [BodyPart.TORSO_LOWER]: { korean: \"하부\", english: \"Lower\" },\n [BodyPart.ARM_LEFT]: { korean: \"좌팔\", english: \"L.Arm\" },\n [BodyPart.ARM_RIGHT]: { korean: \"우팔\", english: \"R.Arm\" },\n [BodyPart.LEG_LEFT]: { korean: \"좌다리\", english: \"L.Leg\" },\n [BodyPart.LEG_RIGHT]: { korean: \"우다리\", english: \"R.Leg\" },\n};\n\n/**\n * Get health bar color based on health percentage\n */\nconst getHealthColor = (health: number): number => {\n if (health >= 80) return KOREAN_COLORS.HEALTH_FULL;\n if (health >= 60) return KOREAN_COLORS.ACCENT_GOLD;\n if (health >= 40) return KOREAN_COLORS.WARNING_ORANGE;\n if (health >= 20) return KOREAN_COLORS.PAIN_INDICATOR;\n return KOREAN_COLORS.NEGATIVE_RED_DARK;\n};\n\n/**\n * BodyPartHealthDisplay - Shows health bars for all 8 body parts\n *\n * @example\n * ```tsx\n * <BodyPartHealthDisplay\n * bodyPartHealth={player.bodyPartHealth}\n * playerId=\"player-1\"\n * position=\"left\"\n * isMobile={false}\n * />\n * ```\n */\nexport const BodyPartHealthDisplay: React.FC<BodyPartHealthDisplayProps> = ({\n bodyPartHealth,\n playerId,\n position,\n isMobile,\n}) => {\n const isLeft = position === \"left\";\n\n const barWidth = isMobile ? 100 : 140;\n const barHeight = isMobile ? 8 : 10;\n const fontSize = isMobile ? 9 : 10;\n const gap = isMobile ? \"4px\" : \"5px\";\n\n const bodyPartGroups = useMemo(\n () => [\n {\n label: \"상체 | Upper\",\n parts: [\n BodyPart.HEAD,\n BodyPart.NECK,\n BodyPart.TORSO_UPPER,\n BodyPart.TORSO_LOWER,\n ],\n },\n { label: \"팔 | Arms\", parts: [BodyPart.ARM_LEFT, BodyPart.ARM_RIGHT] },\n { label: \"다리 | Legs\", parts: [BodyPart.LEG_LEFT, BodyPart.LEG_RIGHT] },\n ],\n [],\n );\n\n return (\n <div\n data-testid={`body-part-health-${playerId}`}\n style={{\n position: \"relative\",\n display: \"flex\",\n flexDirection: \"column\",\n gap,\n backgroundColor: hexToRgbaString(KOREAN_COLORS.UI_BACKGROUND_DARK, 0.9),\n borderRadius: \"8px\",\n padding: isMobile ? \"6px\" : \"8px\",\n border: `2px solid ${hexToRgbaString(KOREAN_COLORS.PRIMARY_CYAN, 0.7)}`,\n boxShadow: `0 0 12px ${hexToRgbaString(\n KOREAN_COLORS.PRIMARY_CYAN,\n 0.3,\n )}`,\n pointerEvents: \"none\",\n width: \"100%\",\n boxSizing: \"border-box\" as const,\n fontSize: isMobile ? \"9px\" : \"10px\",\n }}\n >\n {/* Title */}\n <div\n style={{\n fontSize: isMobile ? \"10px\" : \"11px\",\n fontWeight: \"bold\",\n fontFamily: FONT_FAMILY.KOREAN,\n color: hexToRgbaString(KOREAN_COLORS.PRIMARY_CYAN, 1),\n textAlign: isLeft ? \"left\" : \"right\",\n marginBottom: \"2px\",\n }}\n >\n 신체 | Body Parts\n </div>\n\n {/* Body part groups */}\n {bodyPartGroups.map((group) => (\n <div\n key={group.label}\n style={{\n display: \"flex\",\n flexDirection: \"column\",\n gap: \"3px\",\n }}\n >\n {/* Group label - optional, can be hidden for compact display */}\n <div\n style={{\n fontSize: `${fontSize - 1}px`,\n fontFamily: FONT_FAMILY.KOREAN,\n color: hexToRgbaString(KOREAN_COLORS.TEXT_SECONDARY, 0.8),\n textAlign: isLeft ? \"left\" : \"right\",\n marginTop: group.label === bodyPartGroups[0].label ? \"0\" : \"4px\",\n }}\n >\n {group.label}\n </div>\n\n {/* Body parts in group */}\n {group.parts.map((part) => {\n const health = bodyPartHealth[part];\n const names = BODY_PART_NAMES[part];\n const healthColor = getHealthColor(health);\n const shouldPulse = health < 20;\n\n return (\n <div\n key={part}\n data-testid={`body-part-${playerId}-${part}`}\n style={{\n display: \"flex\",\n flexDirection: \"column\",\n gap: \"2px\",\n }}\n >\n {/* Body part name and value */}\n <div\n style={{\n display: \"flex\",\n justifyContent: \"space-between\",\n alignItems: \"center\",\n fontSize: `${fontSize}px`,\n fontFamily: FONT_FAMILY.KOREAN,\n color: hexToRgbaString(healthColor, 1),\n }}\n >\n <span style={{ fontWeight: health < 40 ? \"bold\" : \"normal\" }}>\n {isMobile\n ? names.korean\n : `${names.korean} | ${names.english}`}\n </span>\n <span style={{ fontSize: `${fontSize - 1}px` }}>\n {Math.round(health)}%\n </span>\n </div>\n\n {/* Health bar */}\n <div\n style={{\n width: `${barWidth}px`,\n height: `${barHeight}px`,\n backgroundColor: hexToRgbaString(\n KOREAN_COLORS.UI_BACKGROUND_MEDIUM,\n 1,\n ),\n borderRadius: \"2px\",\n overflow: \"hidden\",\n animation: shouldPulse\n ? \"healthPulse 0.8s infinite\"\n : \"none\",\n }}\n >\n <div\n style={{\n width: `${health}%`,\n height: \"100%\",\n backgroundColor: hexToRgbaString(healthColor, 1),\n transition:\n \"width 0.3s ease-in-out, background-color 0.3s ease-in-out\",\n boxShadow: `0 0 8px ${hexToRgbaString(healthColor, 0.4)}`,\n }}\n />\n </div>\n </div>\n );\n })}\n </div>\n ))}\n </div>\n );\n};\n\nexport default BodyPartHealthDisplay;\n"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"BreathingIndicator2.js","names":[],"sources":["../../../../../src/components/shared/three/ui/BreathingIndicator.tsx"],"sourcesContent":["/**\n * BreathingIndicator Component - Visual feedback for breathing disruption status\n * \n * **Korean**: 호흡곤란 표시기\n * \n * Displays breathing difficulty with:\n * - Color-coded lungs icon (🫁)\n * - Bilingual label (Korean | English)\n * - Time remaining until recovery\n * - Pulsing animation based on severity\n */\n\nimport React, { useEffect, useMemo, useState } from \"react\";\nimport {\n BreathingDisruptionSystem,\n createBreathingIndicator,\n} from \"../../../../systems/breathing\";\nimport { PlayerState } from \"../../../../systems/player\";\nimport { FONT_FAMILY, KOREAN_COLORS } from \"../../../../types/constants\";\nimport { hexToRgbaString } from \"../../../../utils/colorUtils\";\nimport \"./BreathingIndicator.css\";\n\nexport interface BreathingIndicatorProps {\n /** Player state to check for breathing disruption */\n readonly player: PlayerState;\n /** Whether to use mobile-optimized sizing */\n readonly isMobile?: boolean;\n}\n\n/**\n * BreathingIndicator - Shows breathing disruption status with Korean-English labels\n */\nexport const BreathingIndicator: React.FC<BreathingIndicatorProps> = ({\n player,\n isMobile = false,\n}) => {\n const [currentTime, setCurrentTime] = useState(() => Date.now());\n\n useEffect(() => {\n const interval = setInterval(() => {\n setCurrentTime(Date.now());\n }, 100);\n\n return () => clearInterval(interval);\n }, []);\n\n const breathingState = useMemo(() => {\n const level = BreathingDisruptionSystem.getCurrentLevel(player);\n const activeEffect = BreathingDisruptionSystem.getActiveEffect(player);\n const timeRemaining = activeEffect\n ? Math.max(0, activeEffect.endTime - currentTime)\n : 0;\n const isRecovering = BreathingDisruptionSystem.canRecover(player);\n\n return createBreathingIndicator(level, timeRemaining, isRecovering);\n }, [player, currentTime]);\n\n if (!breathingState.visible) {\n return null;\n }\n\n const iconSize = isMobile ? 24 : 32;\n const fontSize = isMobile ? 10 : 12;\n const padding = isMobile ? \"4px 8px\" : \"6px 12px\";\n\n const secondsRemaining = Math.ceil(breathingState.timeRemaining / 1000);\n\n return (\n <div\n data-testid=\"breathing-indicator\"\n style={{\n display: \"flex\",\n alignItems: \"center\",\n gap: isMobile ? \"6px\" : \"8px\",\n padding,\n backgroundColor: hexToRgbaString(KOREAN_COLORS.BLACK, 0.7),\n borderRadius: \"8px\",\n border: `2px solid ${hexToRgbaString(breathingState.color, breathingState.opacity)}`,\n boxShadow: `0 0 10px ${hexToRgbaString(breathingState.color, 0.5)}`,\n animation: \"breathing-pulse 1s ease-in-out infinite\",\n pointerEvents: \"none\",\n }}\n >\n {/* Lungs icon */}\n <div\n data-testid=\"breathing-icon\"\n style={{\n fontSize: `${iconSize}px`,\n lineHeight: 1,\n transform: `scale(${breathingState.scale})`,\n filter: `drop-shadow(0 0 6px ${hexToRgbaString(breathingState.color, 0.6)})`,\n }}\n >\n {breathingState.icon}\n </div>\n\n {/* Label and time */}\n <div\n style={{\n display: \"flex\",\n flexDirection: \"column\",\n gap: \"2px\",\n }}\n >\n {/* Bilingual label */}\n <div\n data-testid=\"breathing-label\"\n style={{\n fontSize: `${fontSize}px`,\n fontFamily: FONT_FAMILY.KOREAN,\n fontWeight: \"bold\",\n color: hexToRgbaString(breathingState.color, 1),\n textShadow: `0 0 4px ${hexToRgbaString(breathingState.color, 0.8)}`,\n whiteSpace: \"nowrap\",\n }}\n >\n {breathingState.label.korean} | {breathingState.label.english}\n </div>\n\n {/* Time remaining */}\n <div\n data-testid=\"breathing-timer\"\n style={{\n fontSize: `${fontSize - 2}px`,\n fontFamily: FONT_FAMILY.KOREAN,\n color: breathingState.isRecovering\n ? hexToRgbaString(KOREAN_COLORS.POSITIVE_GREEN, 0.8)\n : hexToRgbaString(KOREAN_COLORS.TEXT_PRIMARY, 0.6),\n whiteSpace: \"nowrap\",\n }}\n >\n {breathingState.isRecovering ? \"회복중 | Recovering\" : `${secondsRemaining}s`}\n </div>\n </div>\n </div>\n );\n};\n\nBreathingIndicator.displayName = \"BreathingIndicator\";\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;AAgCA,IAAa,sBAAyD,EACpE,QACA,WAAW,YACP;CACJ,MAAM,CAAC,aAAa,kBAAkB,eAAe,KAAK,
|
|
1
|
+
{"version":3,"file":"BreathingIndicator2.js","names":[],"sources":["../../../../../src/components/shared/three/ui/BreathingIndicator.tsx"],"sourcesContent":["/**\n * BreathingIndicator Component - Visual feedback for breathing disruption status\n * \n * **Korean**: 호흡곤란 표시기\n * \n * Displays breathing difficulty with:\n * - Color-coded lungs icon (🫁)\n * - Bilingual label (Korean | English)\n * - Time remaining until recovery\n * - Pulsing animation based on severity\n */\n\nimport React, { useEffect, useMemo, useState } from \"react\";\nimport {\n BreathingDisruptionSystem,\n createBreathingIndicator,\n} from \"../../../../systems/breathing\";\nimport { PlayerState } from \"../../../../systems/player\";\nimport { FONT_FAMILY, KOREAN_COLORS } from \"../../../../types/constants\";\nimport { hexToRgbaString } from \"../../../../utils/colorUtils\";\nimport \"./BreathingIndicator.css\";\n\nexport interface BreathingIndicatorProps {\n /** Player state to check for breathing disruption */\n readonly player: PlayerState;\n /** Whether to use mobile-optimized sizing */\n readonly isMobile?: boolean;\n}\n\n/**\n * BreathingIndicator - Shows breathing disruption status with Korean-English labels\n */\nexport const BreathingIndicator: React.FC<BreathingIndicatorProps> = ({\n player,\n isMobile = false,\n}) => {\n const [currentTime, setCurrentTime] = useState(() => Date.now());\n\n useEffect(() => {\n const interval = setInterval(() => {\n setCurrentTime(Date.now());\n }, 100);\n\n return () => clearInterval(interval);\n }, []);\n\n const breathingState = useMemo(() => {\n const level = BreathingDisruptionSystem.getCurrentLevel(player);\n const activeEffect = BreathingDisruptionSystem.getActiveEffect(player);\n const timeRemaining = activeEffect\n ? Math.max(0, activeEffect.endTime - currentTime)\n : 0;\n const isRecovering = BreathingDisruptionSystem.canRecover(player);\n\n return createBreathingIndicator(level, timeRemaining, isRecovering);\n }, [player, currentTime]);\n\n if (!breathingState.visible) {\n return null;\n }\n\n const iconSize = isMobile ? 24 : 32;\n const fontSize = isMobile ? 10 : 12;\n const padding = isMobile ? \"4px 8px\" : \"6px 12px\";\n\n const secondsRemaining = Math.ceil(breathingState.timeRemaining / 1000);\n\n return (\n <div\n data-testid=\"breathing-indicator\"\n style={{\n display: \"flex\",\n alignItems: \"center\",\n gap: isMobile ? \"6px\" : \"8px\",\n padding,\n backgroundColor: hexToRgbaString(KOREAN_COLORS.BLACK, 0.7),\n borderRadius: \"8px\",\n border: `2px solid ${hexToRgbaString(breathingState.color, breathingState.opacity)}`,\n boxShadow: `0 0 10px ${hexToRgbaString(breathingState.color, 0.5)}`,\n animation: \"breathing-pulse 1s ease-in-out infinite\",\n pointerEvents: \"none\",\n }}\n >\n {/* Lungs icon */}\n <div\n data-testid=\"breathing-icon\"\n style={{\n fontSize: `${iconSize}px`,\n lineHeight: 1,\n transform: `scale(${breathingState.scale})`,\n filter: `drop-shadow(0 0 6px ${hexToRgbaString(breathingState.color, 0.6)})`,\n }}\n >\n {breathingState.icon}\n </div>\n\n {/* Label and time */}\n <div\n style={{\n display: \"flex\",\n flexDirection: \"column\",\n gap: \"2px\",\n }}\n >\n {/* Bilingual label */}\n <div\n data-testid=\"breathing-label\"\n style={{\n fontSize: `${fontSize}px`,\n fontFamily: FONT_FAMILY.KOREAN,\n fontWeight: \"bold\",\n color: hexToRgbaString(breathingState.color, 1),\n textShadow: `0 0 4px ${hexToRgbaString(breathingState.color, 0.8)}`,\n whiteSpace: \"nowrap\",\n }}\n >\n {breathingState.label.korean} | {breathingState.label.english}\n </div>\n\n {/* Time remaining */}\n <div\n data-testid=\"breathing-timer\"\n style={{\n fontSize: `${fontSize - 2}px`,\n fontFamily: FONT_FAMILY.KOREAN,\n color: breathingState.isRecovering\n ? hexToRgbaString(KOREAN_COLORS.POSITIVE_GREEN, 0.8)\n : hexToRgbaString(KOREAN_COLORS.TEXT_PRIMARY, 0.6),\n whiteSpace: \"nowrap\",\n }}\n >\n {breathingState.isRecovering ? \"회복중 | Recovering\" : `${secondsRemaining}s`}\n </div>\n </div>\n </div>\n );\n};\n\nBreathingIndicator.displayName = \"BreathingIndicator\";\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;AAgCA,IAAa,sBAAyD,EACpE,QACA,WAAW,YACP;CACJ,MAAM,CAAC,aAAa,kBAAkB,eAAe,KAAK,IAAI,CAAC;CAE/D,gBAAgB;EACd,MAAM,WAAW,kBAAkB;GACjC,eAAe,KAAK,IAAI,CAAC;EAC3B,GAAG,GAAG;EAEN,aAAa,cAAc,QAAQ;CACrC,GAAG,CAAC,CAAC;CAEL,MAAM,iBAAiB,cAAc;EACnC,MAAM,QAAQ,0BAA0B,gBAAgB,MAAM;EAC9D,MAAM,eAAe,0BAA0B,gBAAgB,MAAM;EAMrE,OAAO,yBAAyB,OALV,eAClB,KAAK,IAAI,GAAG,aAAa,UAAU,WAAW,IAC9C,GACiB,0BAA0B,WAAW,MAEJ,CAAY;CACpE,GAAG,CAAC,QAAQ,WAAW,CAAC;CAExB,IAAI,CAAC,eAAe,SAClB,OAAO;CAGT,MAAM,WAAW,WAAW,KAAK;CACjC,MAAM,WAAW,WAAW,KAAK;CACjC,MAAM,UAAU,WAAW,YAAY;CAEvC,MAAM,mBAAmB,KAAK,KAAK,eAAe,gBAAgB,GAAI;CAEtE,OACE,qBAAC,OAAD;EACE,eAAY;EACZ,OAAO;GACL,SAAS;GACT,YAAY;GACZ,KAAK,WAAW,QAAQ;GACxB;GACA,iBAAiB,gBAAgB,cAAc,OAAO,EAAG;GACzD,cAAc;GACd,QAAQ,aAAa,gBAAgB,eAAe,OAAO,eAAe,OAAO;GACjF,WAAW,YAAY,gBAAgB,eAAe,OAAO,EAAG;GAChE,WAAW;GACX,eAAe;EACjB;YAbF,CAgBI,oBAAC,OAAD;GACE,eAAY;GACZ,OAAO;IACL,UAAU,GAAG,SAAS;IACtB,YAAY;IACZ,WAAW,SAAS,eAAe,MAAM;IACzC,QAAQ,uBAAuB,gBAAgB,eAAe,OAAO,EAAG,EAAE;GAC5E;aAEC,eAAe;EACb,CAAA,GAGL,qBAAC,OAAD;GACE,OAAO;IACL,SAAS;IACT,eAAe;IACf,KAAK;GACP;aALF,CAQE,qBAAC,OAAD;IACE,eAAY;IACZ,OAAO;KACL,UAAU,GAAG,SAAS;KACtB,YAAY,YAAY;KACxB,YAAY;KACZ,OAAO,gBAAgB,eAAe,OAAO,CAAC;KAC9C,YAAY,WAAW,gBAAgB,eAAe,OAAO,EAAG;KAChE,YAAY;IACd;cATF;KAWG,eAAe,MAAM;KAAO;KAAI,eAAe,MAAM;IACnD;OAGL,oBAAC,OAAD;IACE,eAAY;IACZ,OAAO;KACL,UAAU,GAAG,WAAW,EAAE;KAC1B,YAAY,YAAY;KACxB,OAAO,eAAe,eAClB,gBAAgB,cAAc,gBAAgB,EAAG,IACjD,gBAAgB,cAAc,cAAc,EAAG;KACnD,YAAY;IACd;cAEC,eAAe,eAAe,qBAAqB,GAAG,iBAAiB;GACrE,CAAA,CACF;IACF;;AAEX;AAEA,mBAAmB,cAAc"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"CombatReadinessBar.js","names":[],"sources":["../../../../../src/components/shared/three/ui/CombatReadinessBar.tsx"],"sourcesContent":["/**\n * CombatReadinessBar Component - 10-bar segmented combat readiness display\n * \n * Displays comprehensive combat readiness with:\n * - 10 segmented bars representing 10% each\n * - Color transitions: Green (>80%), Yellow (60-79%), Orange (40-59%), Red (20-39%), Dark Red (<20%)\n * - Smooth 0.3s transition animations\n * - Korean/English bilingual labels\n * - Numeric percentage display\n * - Responsive sizing for mobile/tablet/desktop\n * - Real-time calculation from body health, pain, consciousness, and balance\n */\n\nimport React, { useMemo } from \"react\";\nimport { KOREAN_COLORS, FONT_FAMILY } from \"../../../../types/constants\";\nimport { hexToRgbaString } from \"../../../../utils/colorUtils\";\nimport { \n calculateCombatReadiness, \n getCombatReadinessColor, \n getCombatReadinessLabel,\n getCombatReadinessBars\n} from \"../../../../utils/combatReadiness\";\nimport type { PlayerState } from \"../../../../systems/player\";\n\nexport interface CombatReadinessBarProps {\n /** Player state containing all combat factors */\n readonly player: PlayerState;\n /** Player identifier for test ID */\n readonly playerId: string;\n /** Whether to use mobile-optimized sizing */\n readonly isMobile: boolean;\n}\n\n/**\n * CombatReadinessBar - 10-bar segmented combat readiness display with Korean theming\n * \n * Calculates and displays overall combat readiness from multiple factors:\n * - Body part health (40% weight)\n * - Pain level (20% weight)\n * - Consciousness (20% weight)\n * - Balance state (20% weight)\n * \n * @example\n * ```tsx\n * <CombatReadinessBar \n * player={playerState} \n * playerId=\"player-1\"\n * isMobile={false}\n * />\n * ```\n */\nexport const CombatReadinessBar: React.FC<CombatReadinessBarProps> = ({\n player,\n playerId,\n isMobile,\n}) => {\n const readiness = useMemo(\n () => calculateCombatReadiness(player),\n [player]\n );\n\n const segments = 10;\n const filledSegments = getCombatReadinessBars(readiness, segments);\n const readinessColor = getCombatReadinessColor(readiness);\n const readinessLabel = getCombatReadinessLabel(readiness);\n const shouldPulse = readiness < 20;\n\n const barWidth = isMobile ? 180 : 250;\n const barHeight = isMobile ? 16 : 20;\n const fontSize = isMobile ? 11 : 13;\n const padding = isMobile ? \"8px 12px\" : \"12px 16px\";\n\n const statusText = `${readiness}% ${readinessLabel.korean}`;\n\n return (\n <div\n data-testid={`combat-readiness-bar-${playerId}`}\n role=\"progressbar\"\n aria-label=\"전투 준비도 | Combat Readiness\"\n aria-valuenow={readiness}\n aria-valuemin={0}\n aria-valuemax={100}\n aria-valuetext={`${readiness}% Combat Readiness - ${readinessLabel.english}`}\n style={{\n width: `${barWidth}px`,\n padding,\n backgroundColor: hexToRgbaString(KOREAN_COLORS.UI_BACKGROUND_DARK, 1),\n borderRadius: \"8px\",\n border: `2px solid ${hexToRgbaString(KOREAN_COLORS.PRIMARY_CYAN, 1)}`,\n boxShadow: `0 0 10px ${hexToRgbaString(KOREAN_COLORS.PRIMARY_CYAN, 0.2)}`,\n }}\n >\n {/* Label and numeric/status display */}\n <div\n style={{\n fontSize: `${fontSize}px`,\n color: hexToRgbaString(KOREAN_COLORS.PRIMARY_CYAN, 1),\n fontFamily: FONT_FAMILY.KOREAN,\n marginBottom: \"4px\",\n display: \"flex\",\n justifyContent: \"space-between\",\n fontWeight: \"bold\",\n }}\n >\n <span>전투 준비도 | Combat Readiness</span>\n <span data-testid={`combat-readiness-value-${playerId}`}>\n {statusText}\n </span>\n </div>\n\n {/* 10-segment combat readiness bar */}\n <div\n style={{\n display: \"flex\",\n gap: \"3px\",\n height: `${barHeight}px`,\n animation: shouldPulse ? \"healthPulse 0.8s infinite\" : \"none\",\n }}\n >\n {Array.from({ length: segments }).map((_, index) => (\n <div\n key={index}\n data-testid={`combat-readiness-segment-${playerId}-${index}`}\n style={{\n flex: 1,\n backgroundColor:\n index < filledSegments\n ? hexToRgbaString(readinessColor, 1)\n : hexToRgbaString(KOREAN_COLORS.UI_BACKGROUND_MEDIUM, 1),\n borderRadius: \"2px\",\n transition: \"background-color 0.3s ease-in-out\",\n boxShadow:\n index < filledSegments\n ? `0 0 8px ${hexToRgbaString(readinessColor, 0.4)}`\n : \"none\",\n }}\n />\n ))}\n </div>\n\n {/* Breakdown tooltip (hover) - optional enhancement */}\n <div\n data-testid={`combat-readiness-breakdown-${playerId}`}\n style={{\n display: \"none\", // Hidden by default, can be shown on hover\n fontSize: `${fontSize - 2}px`,\n color: hexToRgbaString(KOREAN_COLORS.TEXT_SECONDARY, 1),\n marginTop: \"4px\",\n fontFamily: FONT_FAMILY.KOREAN,\n }}\n >\n {/* Detailed breakdown for debugging/advanced players */}\n <div>Body: {player.bodyPartHealth ? \"tracked\" : \"aggregate\"}</div>\n <div>Pain: {Math.round(player.pain)}%</div>\n <div>Consciousness: {Math.round(player.consciousness)}%</div>\n <div>Balance: {Math.round(player.balance)}%</div>\n </div>\n </div>\n );\n};\n\nexport default CombatReadinessBar;\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAmDA,IAAa,sBAAyD,EACpE,QACA,UACA,eACI;CACJ,MAAM,YAAY,cACV,yBAAyB,
|
|
1
|
+
{"version":3,"file":"CombatReadinessBar.js","names":[],"sources":["../../../../../src/components/shared/three/ui/CombatReadinessBar.tsx"],"sourcesContent":["/**\n * CombatReadinessBar Component - 10-bar segmented combat readiness display\n * \n * Displays comprehensive combat readiness with:\n * - 10 segmented bars representing 10% each\n * - Color transitions: Green (>80%), Yellow (60-79%), Orange (40-59%), Red (20-39%), Dark Red (<20%)\n * - Smooth 0.3s transition animations\n * - Korean/English bilingual labels\n * - Numeric percentage display\n * - Responsive sizing for mobile/tablet/desktop\n * - Real-time calculation from body health, pain, consciousness, and balance\n */\n\nimport React, { useMemo } from \"react\";\nimport { KOREAN_COLORS, FONT_FAMILY } from \"../../../../types/constants\";\nimport { hexToRgbaString } from \"../../../../utils/colorUtils\";\nimport { \n calculateCombatReadiness, \n getCombatReadinessColor, \n getCombatReadinessLabel,\n getCombatReadinessBars\n} from \"../../../../utils/combatReadiness\";\nimport type { PlayerState } from \"../../../../systems/player\";\n\nexport interface CombatReadinessBarProps {\n /** Player state containing all combat factors */\n readonly player: PlayerState;\n /** Player identifier for test ID */\n readonly playerId: string;\n /** Whether to use mobile-optimized sizing */\n readonly isMobile: boolean;\n}\n\n/**\n * CombatReadinessBar - 10-bar segmented combat readiness display with Korean theming\n * \n * Calculates and displays overall combat readiness from multiple factors:\n * - Body part health (40% weight)\n * - Pain level (20% weight)\n * - Consciousness (20% weight)\n * - Balance state (20% weight)\n * \n * @example\n * ```tsx\n * <CombatReadinessBar \n * player={playerState} \n * playerId=\"player-1\"\n * isMobile={false}\n * />\n * ```\n */\nexport const CombatReadinessBar: React.FC<CombatReadinessBarProps> = ({\n player,\n playerId,\n isMobile,\n}) => {\n const readiness = useMemo(\n () => calculateCombatReadiness(player),\n [player]\n );\n\n const segments = 10;\n const filledSegments = getCombatReadinessBars(readiness, segments);\n const readinessColor = getCombatReadinessColor(readiness);\n const readinessLabel = getCombatReadinessLabel(readiness);\n const shouldPulse = readiness < 20;\n\n const barWidth = isMobile ? 180 : 250;\n const barHeight = isMobile ? 16 : 20;\n const fontSize = isMobile ? 11 : 13;\n const padding = isMobile ? \"8px 12px\" : \"12px 16px\";\n\n const statusText = `${readiness}% ${readinessLabel.korean}`;\n\n return (\n <div\n data-testid={`combat-readiness-bar-${playerId}`}\n role=\"progressbar\"\n aria-label=\"전투 준비도 | Combat Readiness\"\n aria-valuenow={readiness}\n aria-valuemin={0}\n aria-valuemax={100}\n aria-valuetext={`${readiness}% Combat Readiness - ${readinessLabel.english}`}\n style={{\n width: `${barWidth}px`,\n padding,\n backgroundColor: hexToRgbaString(KOREAN_COLORS.UI_BACKGROUND_DARK, 1),\n borderRadius: \"8px\",\n border: `2px solid ${hexToRgbaString(KOREAN_COLORS.PRIMARY_CYAN, 1)}`,\n boxShadow: `0 0 10px ${hexToRgbaString(KOREAN_COLORS.PRIMARY_CYAN, 0.2)}`,\n }}\n >\n {/* Label and numeric/status display */}\n <div\n style={{\n fontSize: `${fontSize}px`,\n color: hexToRgbaString(KOREAN_COLORS.PRIMARY_CYAN, 1),\n fontFamily: FONT_FAMILY.KOREAN,\n marginBottom: \"4px\",\n display: \"flex\",\n justifyContent: \"space-between\",\n fontWeight: \"bold\",\n }}\n >\n <span>전투 준비도 | Combat Readiness</span>\n <span data-testid={`combat-readiness-value-${playerId}`}>\n {statusText}\n </span>\n </div>\n\n {/* 10-segment combat readiness bar */}\n <div\n style={{\n display: \"flex\",\n gap: \"3px\",\n height: `${barHeight}px`,\n animation: shouldPulse ? \"healthPulse 0.8s infinite\" : \"none\",\n }}\n >\n {Array.from({ length: segments }).map((_, index) => (\n <div\n key={index}\n data-testid={`combat-readiness-segment-${playerId}-${index}`}\n style={{\n flex: 1,\n backgroundColor:\n index < filledSegments\n ? hexToRgbaString(readinessColor, 1)\n : hexToRgbaString(KOREAN_COLORS.UI_BACKGROUND_MEDIUM, 1),\n borderRadius: \"2px\",\n transition: \"background-color 0.3s ease-in-out\",\n boxShadow:\n index < filledSegments\n ? `0 0 8px ${hexToRgbaString(readinessColor, 0.4)}`\n : \"none\",\n }}\n />\n ))}\n </div>\n\n {/* Breakdown tooltip (hover) - optional enhancement */}\n <div\n data-testid={`combat-readiness-breakdown-${playerId}`}\n style={{\n display: \"none\", // Hidden by default, can be shown on hover\n fontSize: `${fontSize - 2}px`,\n color: hexToRgbaString(KOREAN_COLORS.TEXT_SECONDARY, 1),\n marginTop: \"4px\",\n fontFamily: FONT_FAMILY.KOREAN,\n }}\n >\n {/* Detailed breakdown for debugging/advanced players */}\n <div>Body: {player.bodyPartHealth ? \"tracked\" : \"aggregate\"}</div>\n <div>Pain: {Math.round(player.pain)}%</div>\n <div>Consciousness: {Math.round(player.consciousness)}%</div>\n <div>Balance: {Math.round(player.balance)}%</div>\n </div>\n </div>\n );\n};\n\nexport default CombatReadinessBar;\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAmDA,IAAa,sBAAyD,EACpE,QACA,UACA,eACI;CACJ,MAAM,YAAY,cACV,yBAAyB,MAAM,GACrC,CAAC,MAAM,CACT;CAEA,MAAM,WAAW;CACjB,MAAM,iBAAiB,uBAAuB,WAAW,QAAQ;CACjE,MAAM,iBAAiB,wBAAwB,SAAS;CACxD,MAAM,iBAAiB,wBAAwB,SAAS;CACxD,MAAM,cAAc,YAAY;CAEhC,MAAM,WAAW,WAAW,MAAM;CAClC,MAAM,YAAY,WAAW,KAAK;CAClC,MAAM,WAAW,WAAW,KAAK;CACjC,MAAM,UAAU,WAAW,aAAa;CAExC,MAAM,aAAa,GAAG,UAAU,IAAI,eAAe;CAEnD,OACE,qBAAC,OAAD;EACE,eAAa,wBAAwB;EACrC,MAAK;EACL,cAAW;EACX,iBAAe;EACf,iBAAe;EACf,iBAAe;EACf,kBAAgB,GAAG,UAAU,uBAAuB,eAAe;EACnE,OAAO;GACL,OAAO,GAAG,SAAS;GACnB;GACA,iBAAiB,gBAAgB,cAAc,oBAAoB,CAAC;GACpE,cAAc;GACd,QAAQ,aAAa,gBAAgB,cAAc,cAAc,CAAC;GAClE,WAAW,YAAY,gBAAgB,cAAc,cAAc,EAAG;EACxE;YAfF;GAkBE,qBAAC,OAAD;IACE,OAAO;KACL,UAAU,GAAG,SAAS;KACtB,OAAO,gBAAgB,cAAc,cAAc,CAAC;KACpD,YAAY,YAAY;KACxB,cAAc;KACd,SAAS;KACT,gBAAgB;KAChB,YAAY;IACd;cATF,CAWE,oBAAC,QAAD,EAAA,UAAM,4BAA+B,CAAA,GACrC,oBAAC,QAAD;KAAM,eAAa,0BAA0B;eAC1C;IACG,CAAA,CACH;;GAGL,oBAAC,OAAD;IACE,OAAO;KACL,SAAS;KACT,KAAK;KACL,QAAQ,GAAG,UAAU;KACrB,WAAW,cAAc,8BAA8B;IACzD;cAEC,MAAM,KAAK,EAAE,QAAQ,SAAS,CAAC,EAAE,KAAK,GAAG,UACxC,oBAAC,OAAD;KAEE,eAAa,4BAA4B,SAAS,GAAG;KACrD,OAAO;MACL,MAAM;MACN,iBACE,QAAQ,iBACJ,gBAAgB,gBAAgB,CAAC,IACjC,gBAAgB,cAAc,sBAAsB,CAAC;MAC3D,cAAc;MACd,YAAY;MACZ,WACE,QAAQ,iBACJ,WAAW,gBAAgB,gBAAgB,EAAG,MAC9C;KACR;IACD,GAfM,KAeN,CACF;GACE,CAAA;GAGL,qBAAC,OAAD;IACE,eAAa,8BAA8B;IAC3C,OAAO;KACL,SAAS;KACT,UAAU,GAAG,WAAW,EAAE;KAC1B,OAAO,gBAAgB,cAAc,gBAAgB,CAAC;KACtD,WAAW;KACX,YAAY,YAAY;IAC1B;cARF;KAWE,qBAAC,OAAD,EAAA,UAAA,CAAK,UAAO,OAAO,iBAAiB,YAAY,WAAiB,EAAA,CAAA;KACjE,qBAAC,OAAD,EAAA,UAAA;MAAK;MAAO,KAAK,MAAM,OAAO,IAAI;MAAE;KAAM,EAAA,CAAA;KAC1C,qBAAC,OAAD,EAAA,UAAA;MAAK;MAAgB,KAAK,MAAM,OAAO,aAAa;MAAE;KAAM,EAAA,CAAA;KAC5D,qBAAC,OAAD,EAAA,UAAA;MAAK;MAAU,KAAK,MAAM,OAAO,OAAO;MAAE;KAAM,EAAA,CAAA;IAC7C;;EACF;;AAET"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ComboCounter.js","names":[],"sources":["../../../../../src/components/shared/three/ui/ComboCounter.tsx"],"sourcesContent":["/**\n * ComboCounter - Combo counter display component\n *\n * Displays the current combo count with Korean-English bilingual text.\n * Animates on combo increment and shows milestone indicators.\n *\n * Uses Html overlay from @react-three/drei for rendering within 3D scenes.\n *\n * @module components/shared/three/ui/ComboCounter\n * @category Shared UI\n * @korean 콤보카운터\n */\n\nimport { Html } from \"@react-three/drei\";\nimport React, { useEffect, useMemo, useRef, useState } from \"react\";\nimport { FONT_FAMILY, KOREAN_COLORS } from \"../../../../types/constants\";\nimport { hexColorToCSS, hexToRgbaString } from \"../../../../utils/colorUtils\";\nimport \"./HUDAnimations.css\";\n\n/**\n * Props for the ComboCounter component\n */\nexport interface ComboCounterProps {\n /** Current combo count */\n readonly combo: number;\n /** Whether to use mobile-optimized sizing */\n readonly isMobile?: boolean;\n /** Minimum combo to display (default: 2) */\n readonly minDisplayCombo?: number;\n}\n\n/**\n * Get combo tier color based on combo count\n */\nfunction getComboColor(combo: number): string {\n if (combo >= 10) {\n return hexColorToCSS(KOREAN_COLORS.SECONDARY_MAGENTA);\n }\n if (combo >= 7) {\n return hexColorToCSS(KOREAN_COLORS.ACCENT_RED);\n }\n if (combo >= 5) {\n return hexColorToCSS(KOREAN_COLORS.ACCENT_GOLD);\n }\n if (combo >= 3) {\n return hexColorToCSS(KOREAN_COLORS.PRIMARY_CYAN);\n }\n return hexColorToCSS(KOREAN_COLORS.TEXT_PRIMARY);\n}\n\n/**\n * Get glow color based on combo tier\n */\nfunction getGlowColor(combo: number): string {\n if (combo >= 10) {\n return hexToRgbaString(KOREAN_COLORS.SECONDARY_MAGENTA, 0.8);\n }\n if (combo >= 7) {\n return hexToRgbaString(KOREAN_COLORS.ACCENT_RED, 0.8);\n }\n if (combo >= 5) {\n return hexToRgbaString(KOREAN_COLORS.ACCENT_GOLD, 0.8);\n }\n if (combo >= 3) {\n return hexToRgbaString(KOREAN_COLORS.PRIMARY_CYAN, 0.6);\n }\n return hexToRgbaString(KOREAN_COLORS.TEXT_PRIMARY, 0.4);\n}\n\n/**\n * Get combo milestone text\n */\nfunction getComboMilestone(\n combo: number\n): { korean: string; english: string } | null {\n if (combo === 5) {\n return { korean: \"훌륭합니다!\", english: \"Great!\" };\n }\n if (combo === 10) {\n return { korean: \"놀라운 연속 공격!\", english: \"Amazing!\" };\n }\n if (combo === 15) {\n return { korean: \"전설적인 공격!\", english: \"Legendary!\" };\n }\n if (combo === 20) {\n return { korean: \"신의 일격!\", english: \"GODLIKE!\" };\n }\n return null;\n}\n\n/**\n * ComboCounter Component\n *\n * Displays the current combo count with animations and milestone indicators.\n * Only visible when combo >= minDisplayCombo (default: 2).\n *\n * @example\n * ```tsx\n * <ComboCounter\n * combo={5}\n * isMobile={isMobile}\n * />\n * ```\n */\nexport const ComboCounter: React.FC<ComboCounterProps> = ({\n combo,\n isMobile = false,\n minDisplayCombo = 2,\n}) => {\n const [scale, setScale] = useState(1);\n const [showMilestone, setShowMilestone] = useState(false);\n const [prevCombo, setPrevCombo] = useState(combo);\n const milestoneTimeoutRef = useRef<ReturnType<typeof setTimeout> | null>(\n null\n );\n const scaleTimeoutRef = useRef<ReturnType<typeof setTimeout> | null>(null);\n\n const position3D: [number, number, number] = useMemo(() => {\n return [0, 4.5, 0];\n }, []);\n\n useEffect(() => {\n if (combo > prevCombo) {\n setTimeout(() => setScale(1.3), 0);\n\n if (scaleTimeoutRef.current) {\n clearTimeout(scaleTimeoutRef.current);\n }\n scaleTimeoutRef.current = setTimeout(() => {\n setScale(1);\n }, 150);\n\n const milestone = getComboMilestone(combo);\n if (milestone) {\n setTimeout(() => setShowMilestone(true), 0);\n if (milestoneTimeoutRef.current) {\n clearTimeout(milestoneTimeoutRef.current);\n }\n milestoneTimeoutRef.current = setTimeout(() => {\n setShowMilestone(false);\n }, 1500);\n }\n }\n setTimeout(() => setPrevCombo(combo), 0);\n }, [combo, prevCombo]);\n\n useEffect(() => {\n return () => {\n if (milestoneTimeoutRef.current) {\n clearTimeout(milestoneTimeoutRef.current);\n }\n if (scaleTimeoutRef.current) {\n clearTimeout(scaleTimeoutRef.current);\n }\n };\n }, []);\n\n if (combo < minDisplayCombo) {\n return null;\n }\n\n const milestone = getComboMilestone(combo);\n const comboColor = getComboColor(combo);\n const glowColor = getGlowColor(combo);\n const getMilestoneBackground = (comboVal: number): string => {\n if (comboVal >= 10) {\n return hexToRgbaString(KOREAN_COLORS.SECONDARY_MAGENTA, 0.3);\n }\n if (comboVal >= 7) {\n return hexToRgbaString(KOREAN_COLORS.ACCENT_RED, 0.3);\n }\n if (comboVal >= 5) {\n return hexToRgbaString(KOREAN_COLORS.ACCENT_GOLD, 0.3);\n }\n if (comboVal >= 3) {\n return hexToRgbaString(KOREAN_COLORS.PRIMARY_CYAN, 0.3);\n }\n return hexToRgbaString(KOREAN_COLORS.TEXT_PRIMARY, 0.3);\n };\n\n const mainFontSize = isMobile ? 32 : 48;\n const subFontSize = isMobile ? 16 : 20;\n const milestoneFontSize = isMobile ? 20 : 28;\n\n return (\n <Html\n position={position3D}\n center\n distanceFactor={10}\n style={{ pointerEvents: \"none\" }}\n >\n <div\n data-testid=\"combo-counter\"\n className=\"hud-animated\"\n style={{\n display: \"flex\",\n flexDirection: \"column\",\n alignItems: \"center\",\n transform: `scale(${scale})`,\n transition: \"transform 0.15s ease-out\",\n animation: combo >= 5 ? \"comboFlash 0.8s ease-in-out infinite\" : \"none\",\n }}\n >\n {/* Main combo number */}\n <div\n style={{\n fontSize: `${mainFontSize}px`,\n fontWeight: \"bold\",\n fontFamily: FONT_FAMILY.KOREAN,\n color: comboColor,\n textShadow: `\n 0 0 15px ${glowColor},\n 0 0 30px ${glowColor},\n 3px 3px 6px rgba(0, 0, 0, 0.9)\n `,\n lineHeight: 1,\n }}\n >\n {combo}\n </div>\n\n {/* Korean text */}\n <div\n style={{\n fontSize: `${subFontSize}px`,\n fontWeight: \"bold\",\n fontFamily: FONT_FAMILY.KOREAN,\n color: comboColor,\n textShadow: `0 0 10px ${glowColor}, 2px 2px 4px rgba(0, 0, 0, 0.8)`,\n marginTop: \"4px\",\n }}\n >\n 연속 타격!\n </div>\n\n {/* English text */}\n <div\n style={{\n fontSize: `${subFontSize - 4}px`,\n fontWeight: \"bold\",\n fontFamily: FONT_FAMILY.KOREAN,\n color: hexColorToCSS(KOREAN_COLORS.TEXT_SECONDARY),\n textShadow: \"2px 2px 4px rgba(0, 0, 0, 0.8)\",\n }}\n >\n {combo} HIT COMBO!\n </div>\n\n {/* Milestone indicator */}\n {showMilestone && milestone && (\n <div\n className=\"hud-animated\"\n style={{\n marginTop: \"8px\",\n padding: \"4px 16px\",\n background: getMilestoneBackground(combo),\n borderRadius: \"4px\",\n border: `2px solid ${comboColor}`,\n animation: \"comboFlash 1s ease-in-out infinite\",\n }}\n >\n <div\n style={{\n fontSize: `${milestoneFontSize}px`,\n fontWeight: \"bold\",\n fontFamily: FONT_FAMILY.KOREAN,\n color: comboColor,\n textShadow: `0 0 10px ${glowColor}`,\n }}\n >\n {milestone.korean}\n </div>\n <div\n style={{\n fontSize: `${milestoneFontSize - 6}px`,\n fontWeight: \"bold\",\n fontFamily: FONT_FAMILY.KOREAN,\n color: hexColorToCSS(KOREAN_COLORS.TEXT_SECONDARY),\n }}\n >\n {milestone.english}\n </div>\n </div>\n )}\n </div>\n\n {/* CSS Animation - kept for backwards compatibility */}\n <style>{`\n @keyframes pulse {\n 0%, 100% { transform: scale(1); }\n 50% { transform: scale(1.05); }\n }\n `}</style>\n </Html>\n );\n};\n\nexport default ComboCounter;\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"ComboCounter.js","names":[],"sources":["../../../../../src/components/shared/three/ui/ComboCounter.tsx"],"sourcesContent":["/**\n * ComboCounter - Combo counter display component\n *\n * Displays the current combo count with Korean-English bilingual text.\n * Animates on combo increment and shows milestone indicators.\n *\n * Uses Html overlay from @react-three/drei for rendering within 3D scenes.\n *\n * @module components/shared/three/ui/ComboCounter\n * @category Shared UI\n * @korean 콤보카운터\n */\n\nimport { Html } from \"@react-three/drei\";\nimport React, { useEffect, useMemo, useRef, useState } from \"react\";\nimport { FONT_FAMILY, KOREAN_COLORS } from \"../../../../types/constants\";\nimport { hexColorToCSS, hexToRgbaString } from \"../../../../utils/colorUtils\";\nimport \"./HUDAnimations.css\";\n\n/**\n * Props for the ComboCounter component\n */\nexport interface ComboCounterProps {\n /** Current combo count */\n readonly combo: number;\n /** Whether to use mobile-optimized sizing */\n readonly isMobile?: boolean;\n /** Minimum combo to display (default: 2) */\n readonly minDisplayCombo?: number;\n}\n\n/**\n * Get combo tier color based on combo count\n */\nfunction getComboColor(combo: number): string {\n if (combo >= 10) {\n return hexColorToCSS(KOREAN_COLORS.SECONDARY_MAGENTA);\n }\n if (combo >= 7) {\n return hexColorToCSS(KOREAN_COLORS.ACCENT_RED);\n }\n if (combo >= 5) {\n return hexColorToCSS(KOREAN_COLORS.ACCENT_GOLD);\n }\n if (combo >= 3) {\n return hexColorToCSS(KOREAN_COLORS.PRIMARY_CYAN);\n }\n return hexColorToCSS(KOREAN_COLORS.TEXT_PRIMARY);\n}\n\n/**\n * Get glow color based on combo tier\n */\nfunction getGlowColor(combo: number): string {\n if (combo >= 10) {\n return hexToRgbaString(KOREAN_COLORS.SECONDARY_MAGENTA, 0.8);\n }\n if (combo >= 7) {\n return hexToRgbaString(KOREAN_COLORS.ACCENT_RED, 0.8);\n }\n if (combo >= 5) {\n return hexToRgbaString(KOREAN_COLORS.ACCENT_GOLD, 0.8);\n }\n if (combo >= 3) {\n return hexToRgbaString(KOREAN_COLORS.PRIMARY_CYAN, 0.6);\n }\n return hexToRgbaString(KOREAN_COLORS.TEXT_PRIMARY, 0.4);\n}\n\n/**\n * Get combo milestone text\n */\nfunction getComboMilestone(\n combo: number\n): { korean: string; english: string } | null {\n if (combo === 5) {\n return { korean: \"훌륭합니다!\", english: \"Great!\" };\n }\n if (combo === 10) {\n return { korean: \"놀라운 연속 공격!\", english: \"Amazing!\" };\n }\n if (combo === 15) {\n return { korean: \"전설적인 공격!\", english: \"Legendary!\" };\n }\n if (combo === 20) {\n return { korean: \"신의 일격!\", english: \"GODLIKE!\" };\n }\n return null;\n}\n\n/**\n * ComboCounter Component\n *\n * Displays the current combo count with animations and milestone indicators.\n * Only visible when combo >= minDisplayCombo (default: 2).\n *\n * @example\n * ```tsx\n * <ComboCounter\n * combo={5}\n * isMobile={isMobile}\n * />\n * ```\n */\nexport const ComboCounter: React.FC<ComboCounterProps> = ({\n combo,\n isMobile = false,\n minDisplayCombo = 2,\n}) => {\n const [scale, setScale] = useState(1);\n const [showMilestone, setShowMilestone] = useState(false);\n const [prevCombo, setPrevCombo] = useState(combo);\n const milestoneTimeoutRef = useRef<ReturnType<typeof setTimeout> | null>(\n null\n );\n const scaleTimeoutRef = useRef<ReturnType<typeof setTimeout> | null>(null);\n\n const position3D: [number, number, number] = useMemo(() => {\n return [0, 4.5, 0];\n }, []);\n\n useEffect(() => {\n if (combo > prevCombo) {\n setTimeout(() => setScale(1.3), 0);\n\n if (scaleTimeoutRef.current) {\n clearTimeout(scaleTimeoutRef.current);\n }\n scaleTimeoutRef.current = setTimeout(() => {\n setScale(1);\n }, 150);\n\n const milestone = getComboMilestone(combo);\n if (milestone) {\n setTimeout(() => setShowMilestone(true), 0);\n if (milestoneTimeoutRef.current) {\n clearTimeout(milestoneTimeoutRef.current);\n }\n milestoneTimeoutRef.current = setTimeout(() => {\n setShowMilestone(false);\n }, 1500);\n }\n }\n setTimeout(() => setPrevCombo(combo), 0);\n }, [combo, prevCombo]);\n\n useEffect(() => {\n return () => {\n if (milestoneTimeoutRef.current) {\n clearTimeout(milestoneTimeoutRef.current);\n }\n if (scaleTimeoutRef.current) {\n clearTimeout(scaleTimeoutRef.current);\n }\n };\n }, []);\n\n if (combo < minDisplayCombo) {\n return null;\n }\n\n const milestone = getComboMilestone(combo);\n const comboColor = getComboColor(combo);\n const glowColor = getGlowColor(combo);\n const getMilestoneBackground = (comboVal: number): string => {\n if (comboVal >= 10) {\n return hexToRgbaString(KOREAN_COLORS.SECONDARY_MAGENTA, 0.3);\n }\n if (comboVal >= 7) {\n return hexToRgbaString(KOREAN_COLORS.ACCENT_RED, 0.3);\n }\n if (comboVal >= 5) {\n return hexToRgbaString(KOREAN_COLORS.ACCENT_GOLD, 0.3);\n }\n if (comboVal >= 3) {\n return hexToRgbaString(KOREAN_COLORS.PRIMARY_CYAN, 0.3);\n }\n return hexToRgbaString(KOREAN_COLORS.TEXT_PRIMARY, 0.3);\n };\n\n const mainFontSize = isMobile ? 32 : 48;\n const subFontSize = isMobile ? 16 : 20;\n const milestoneFontSize = isMobile ? 20 : 28;\n\n return (\n <Html\n position={position3D}\n center\n distanceFactor={10}\n style={{ pointerEvents: \"none\" }}\n >\n <div\n data-testid=\"combo-counter\"\n className=\"hud-animated\"\n style={{\n display: \"flex\",\n flexDirection: \"column\",\n alignItems: \"center\",\n transform: `scale(${scale})`,\n transition: \"transform 0.15s ease-out\",\n animation: combo >= 5 ? \"comboFlash 0.8s ease-in-out infinite\" : \"none\",\n }}\n >\n {/* Main combo number */}\n <div\n style={{\n fontSize: `${mainFontSize}px`,\n fontWeight: \"bold\",\n fontFamily: FONT_FAMILY.KOREAN,\n color: comboColor,\n textShadow: `\n 0 0 15px ${glowColor},\n 0 0 30px ${glowColor},\n 3px 3px 6px rgba(0, 0, 0, 0.9)\n `,\n lineHeight: 1,\n }}\n >\n {combo}\n </div>\n\n {/* Korean text */}\n <div\n style={{\n fontSize: `${subFontSize}px`,\n fontWeight: \"bold\",\n fontFamily: FONT_FAMILY.KOREAN,\n color: comboColor,\n textShadow: `0 0 10px ${glowColor}, 2px 2px 4px rgba(0, 0, 0, 0.8)`,\n marginTop: \"4px\",\n }}\n >\n 연속 타격!\n </div>\n\n {/* English text */}\n <div\n style={{\n fontSize: `${subFontSize - 4}px`,\n fontWeight: \"bold\",\n fontFamily: FONT_FAMILY.KOREAN,\n color: hexColorToCSS(KOREAN_COLORS.TEXT_SECONDARY),\n textShadow: \"2px 2px 4px rgba(0, 0, 0, 0.8)\",\n }}\n >\n {combo} HIT COMBO!\n </div>\n\n {/* Milestone indicator */}\n {showMilestone && milestone && (\n <div\n className=\"hud-animated\"\n style={{\n marginTop: \"8px\",\n padding: \"4px 16px\",\n background: getMilestoneBackground(combo),\n borderRadius: \"4px\",\n border: `2px solid ${comboColor}`,\n animation: \"comboFlash 1s ease-in-out infinite\",\n }}\n >\n <div\n style={{\n fontSize: `${milestoneFontSize}px`,\n fontWeight: \"bold\",\n fontFamily: FONT_FAMILY.KOREAN,\n color: comboColor,\n textShadow: `0 0 10px ${glowColor}`,\n }}\n >\n {milestone.korean}\n </div>\n <div\n style={{\n fontSize: `${milestoneFontSize - 6}px`,\n fontWeight: \"bold\",\n fontFamily: FONT_FAMILY.KOREAN,\n color: hexColorToCSS(KOREAN_COLORS.TEXT_SECONDARY),\n }}\n >\n {milestone.english}\n </div>\n </div>\n )}\n </div>\n\n {/* CSS Animation - kept for backwards compatibility */}\n <style>{`\n @keyframes pulse {\n 0%, 100% { transform: scale(1); }\n 50% { transform: scale(1.05); }\n }\n `}</style>\n </Html>\n );\n};\n\nexport default ComboCounter;\n"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"HealthBar.js","names":[],"sources":["../../../../../src/components/shared/three/ui/HealthBar.tsx"],"sourcesContent":["/**\n * HealthBar Component - Segmented health display with Korean theming\n * \n * Displays player health with:\n * - 10 segmented bars\n * - Color transitions: Green (>50%), Yellow (25-50%), Red (<25%)\n * - Pulse animation when health <20%\n * - Korean/English bilingual labels\n * - Numeric value display (e.g., \"85/100\")\n * - Responsive sizing for mobile/tablet/desktop\n * - Smooth transitions and glow effects\n * \n * Performance: Uses React.memo with shallow comparison for 60fps optimization.\n * Note: React.memo uses shallow comparison by default, which works correctly\n * for this component since all props are primitives (number, string, boolean).\n * If object or function props are added in the future, consider adding a\n * custom comparison function or using useCallback/useMemo for prop stability.\n */\n\nimport React, { useMemo } from \"react\";\nimport { KOREAN_COLORS, FONT_FAMILY } from \"../../../../types/constants\";\nimport { hexToRgbaString } from \"../../../../utils/colorUtils\";\nimport \"./HUDAnimations.css\";\n\nexport interface HealthBarProps {\n /** Current health value */\n readonly current: number;\n /** Maximum health capacity */\n readonly max: number;\n /** Player identifier for test ID */\n readonly playerId: string;\n /** Whether to use mobile-optimized sizing */\n readonly isMobile: boolean;\n}\n\n/**\n * Get health bar color based on health percentage\n */\nconst getHealthColor = (percentage: number): number => {\n if (percentage > 50) return KOREAN_COLORS.HEALTH_FULL; // Green\n if (percentage > 25) return KOREAN_COLORS.HEALTH_MEDIUM; // Yellow\n return KOREAN_COLORS.HEALTH_CRITICAL; // Red\n};\n\n/**\n * HealthBar - Segmented health display with Korean theming\n * Performance optimized with React.memo\n */\nexport const HealthBar: React.FC<HealthBarProps> = React.memo(({\n current,\n max,\n playerId,\n isMobile,\n}) => {\n const healthPercent = useMemo(\n () => Math.max(0, Math.min(100, (current / max) * 100)),\n [current, max]\n );\n\n const segments = 10;\n const filledSegments = Math.ceil((healthPercent / 100) * segments);\n const healthColor = getHealthColor(healthPercent);\n const shouldPulse = healthPercent < 20;\n\n const layout = useMemo(() => ({\n barWidth: isMobile ? 180 : 250,\n barHeight: isMobile ? 16 : 20,\n fontSize: isMobile ? 11 : 13,\n padding: isMobile ? \"8px 12px\" : \"12px 16px\",\n }), [isMobile]);\n\n return (\n <div\n data-testid={`health-bar-${playerId}`}\n role=\"progressbar\"\n aria-label=\"체력 | Health\"\n aria-valuenow={Math.ceil(current)}\n aria-valuemin={0}\n aria-valuemax={max}\n aria-valuetext={`${Math.ceil(current)} out of ${max}`}\n className=\"hud-animated\"\n style={{\n width: `${layout.barWidth}px`,\n padding: layout.padding,\n backgroundColor: hexToRgbaString(KOREAN_COLORS.UI_BACKGROUND_DARK, 1),\n borderRadius: \"8px\",\n border: `2px solid ${hexToRgbaString(KOREAN_COLORS.PRIMARY_CYAN, 1)}`,\n boxShadow: `0 0 10px ${hexToRgbaString(KOREAN_COLORS.PRIMARY_CYAN, 0.2)}`,\n transition: \"box-shadow 0.3s ease-in-out, border-color 0.3s ease-in-out\",\n }}\n >\n {/* Label and numeric display */}\n <div\n style={{\n fontSize: `${layout.fontSize}px`,\n color: hexToRgbaString(KOREAN_COLORS.PRIMARY_CYAN, 1),\n fontFamily: FONT_FAMILY.KOREAN,\n marginBottom: \"4px\",\n display: \"flex\",\n justifyContent: \"space-between\",\n fontWeight: \"bold\",\n transition: \"color 0.2s ease-in-out\",\n }}\n >\n <span>체력 | Health</span>\n <span data-testid={`health-value-${playerId}`}>\n {Math.ceil(current)}/{max}\n </span>\n </div>\n\n {/* Segmented health bar */}\n <div\n style={{\n display: \"flex\",\n gap: \"3px\",\n height: `${layout.barHeight}px`,\n animation: shouldPulse ? \"healthPulse 0.8s ease-in-out infinite\" : \"none\",\n }}\n >\n {Array.from({ length: segments }).map((_, index) => (\n <div\n key={index}\n data-testid={`health-segment-${playerId}-${index}`}\n style={{\n flex: 1,\n backgroundColor:\n index < filledSegments\n ? hexToRgbaString(healthColor, 1)\n : hexToRgbaString(KOREAN_COLORS.UI_BACKGROUND_MEDIUM, 1),\n borderRadius: \"2px\",\n transition: \"background-color 0.2s ease-in-out\",\n boxShadow:\n index < filledSegments\n ? `0 0 8px ${hexToRgbaString(healthColor, 0.4)}`\n : \"none\",\n }}\n />\n ))}\n </div>\n </div>\n );\n});\n\nHealthBar.displayName = \"HealthBar\";\n\nexport default HealthBar;\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;AAsCA,IAAM,kBAAkB,eAA+B;CACrD,IAAI,aAAa,IAAI,OAAO,cAAc;CAC1C,IAAI,aAAa,IAAI,OAAO,cAAc;CAC1C,OAAO,cAAc
|
|
1
|
+
{"version":3,"file":"HealthBar.js","names":[],"sources":["../../../../../src/components/shared/three/ui/HealthBar.tsx"],"sourcesContent":["/**\n * HealthBar Component - Segmented health display with Korean theming\n * \n * Displays player health with:\n * - 10 segmented bars\n * - Color transitions: Green (>50%), Yellow (25-50%), Red (<25%)\n * - Pulse animation when health <20%\n * - Korean/English bilingual labels\n * - Numeric value display (e.g., \"85/100\")\n * - Responsive sizing for mobile/tablet/desktop\n * - Smooth transitions and glow effects\n * \n * Performance: Uses React.memo with shallow comparison for 60fps optimization.\n * Note: React.memo uses shallow comparison by default, which works correctly\n * for this component since all props are primitives (number, string, boolean).\n * If object or function props are added in the future, consider adding a\n * custom comparison function or using useCallback/useMemo for prop stability.\n */\n\nimport React, { useMemo } from \"react\";\nimport { KOREAN_COLORS, FONT_FAMILY } from \"../../../../types/constants\";\nimport { hexToRgbaString } from \"../../../../utils/colorUtils\";\nimport \"./HUDAnimations.css\";\n\nexport interface HealthBarProps {\n /** Current health value */\n readonly current: number;\n /** Maximum health capacity */\n readonly max: number;\n /** Player identifier for test ID */\n readonly playerId: string;\n /** Whether to use mobile-optimized sizing */\n readonly isMobile: boolean;\n}\n\n/**\n * Get health bar color based on health percentage\n */\nconst getHealthColor = (percentage: number): number => {\n if (percentage > 50) return KOREAN_COLORS.HEALTH_FULL; // Green\n if (percentage > 25) return KOREAN_COLORS.HEALTH_MEDIUM; // Yellow\n return KOREAN_COLORS.HEALTH_CRITICAL; // Red\n};\n\n/**\n * HealthBar - Segmented health display with Korean theming\n * Performance optimized with React.memo\n */\nexport const HealthBar: React.FC<HealthBarProps> = React.memo(({\n current,\n max,\n playerId,\n isMobile,\n}) => {\n const healthPercent = useMemo(\n () => Math.max(0, Math.min(100, (current / max) * 100)),\n [current, max]\n );\n\n const segments = 10;\n const filledSegments = Math.ceil((healthPercent / 100) * segments);\n const healthColor = getHealthColor(healthPercent);\n const shouldPulse = healthPercent < 20;\n\n const layout = useMemo(() => ({\n barWidth: isMobile ? 180 : 250,\n barHeight: isMobile ? 16 : 20,\n fontSize: isMobile ? 11 : 13,\n padding: isMobile ? \"8px 12px\" : \"12px 16px\",\n }), [isMobile]);\n\n return (\n <div\n data-testid={`health-bar-${playerId}`}\n role=\"progressbar\"\n aria-label=\"체력 | Health\"\n aria-valuenow={Math.ceil(current)}\n aria-valuemin={0}\n aria-valuemax={max}\n aria-valuetext={`${Math.ceil(current)} out of ${max}`}\n className=\"hud-animated\"\n style={{\n width: `${layout.barWidth}px`,\n padding: layout.padding,\n backgroundColor: hexToRgbaString(KOREAN_COLORS.UI_BACKGROUND_DARK, 1),\n borderRadius: \"8px\",\n border: `2px solid ${hexToRgbaString(KOREAN_COLORS.PRIMARY_CYAN, 1)}`,\n boxShadow: `0 0 10px ${hexToRgbaString(KOREAN_COLORS.PRIMARY_CYAN, 0.2)}`,\n transition: \"box-shadow 0.3s ease-in-out, border-color 0.3s ease-in-out\",\n }}\n >\n {/* Label and numeric display */}\n <div\n style={{\n fontSize: `${layout.fontSize}px`,\n color: hexToRgbaString(KOREAN_COLORS.PRIMARY_CYAN, 1),\n fontFamily: FONT_FAMILY.KOREAN,\n marginBottom: \"4px\",\n display: \"flex\",\n justifyContent: \"space-between\",\n fontWeight: \"bold\",\n transition: \"color 0.2s ease-in-out\",\n }}\n >\n <span>체력 | Health</span>\n <span data-testid={`health-value-${playerId}`}>\n {Math.ceil(current)}/{max}\n </span>\n </div>\n\n {/* Segmented health bar */}\n <div\n style={{\n display: \"flex\",\n gap: \"3px\",\n height: `${layout.barHeight}px`,\n animation: shouldPulse ? \"healthPulse 0.8s ease-in-out infinite\" : \"none\",\n }}\n >\n {Array.from({ length: segments }).map((_, index) => (\n <div\n key={index}\n data-testid={`health-segment-${playerId}-${index}`}\n style={{\n flex: 1,\n backgroundColor:\n index < filledSegments\n ? hexToRgbaString(healthColor, 1)\n : hexToRgbaString(KOREAN_COLORS.UI_BACKGROUND_MEDIUM, 1),\n borderRadius: \"2px\",\n transition: \"background-color 0.2s ease-in-out\",\n boxShadow:\n index < filledSegments\n ? `0 0 8px ${hexToRgbaString(healthColor, 0.4)}`\n : \"none\",\n }}\n />\n ))}\n </div>\n </div>\n );\n});\n\nHealthBar.displayName = \"HealthBar\";\n\nexport default HealthBar;\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;AAsCA,IAAM,kBAAkB,eAA+B;CACrD,IAAI,aAAa,IAAI,OAAO,cAAc;CAC1C,IAAI,aAAa,IAAI,OAAO,cAAc;CAC1C,OAAO,cAAc;AACvB;;;;;AAMA,IAAa,YAAsC,MAAM,MAAM,EAC7D,SACA,KACA,UACA,eACI;CACJ,MAAM,gBAAgB,cACd,KAAK,IAAI,GAAG,KAAK,IAAI,KAAM,UAAU,MAAO,GAAG,CAAC,GACtD,CAAC,SAAS,GAAG,CACf;CAEA,MAAM,WAAW;CACjB,MAAM,iBAAiB,KAAK,KAAM,gBAAgB,MAAO,QAAQ;CACjE,MAAM,cAAc,eAAe,aAAa;CAChD,MAAM,cAAc,gBAAgB;CAEpC,MAAM,SAAS,eAAe;EAC5B,UAAU,WAAW,MAAM;EAC3B,WAAW,WAAW,KAAK;EAC3B,UAAU,WAAW,KAAK;EAC1B,SAAS,WAAW,aAAa;CACnC,IAAI,CAAC,QAAQ,CAAC;CAEd,OACE,qBAAC,OAAD;EACE,eAAa,cAAc;EAC3B,MAAK;EACL,cAAW;EACX,iBAAe,KAAK,KAAK,OAAO;EAChC,iBAAe;EACf,iBAAe;EACf,kBAAgB,GAAG,KAAK,KAAK,OAAO,EAAE,UAAU;EAChD,WAAU;EACV,OAAO;GACL,OAAO,GAAG,OAAO,SAAS;GAC1B,SAAS,OAAO;GAChB,iBAAiB,gBAAgB,cAAc,oBAAoB,CAAC;GACpE,cAAc;GACd,QAAQ,aAAa,gBAAgB,cAAc,cAAc,CAAC;GAClE,WAAW,YAAY,gBAAgB,cAAc,cAAc,EAAG;GACtE,YAAY;EACd;YAjBF,CAoBE,qBAAC,OAAD;GACE,OAAO;IACL,UAAU,GAAG,OAAO,SAAS;IAC7B,OAAO,gBAAgB,cAAc,cAAc,CAAC;IACpD,YAAY,YAAY;IACxB,cAAc;IACd,SAAS;IACT,gBAAgB;IAChB,YAAY;IACZ,YAAY;GACd;aAVF,CAYE,oBAAC,QAAD,EAAA,UAAM,cAAiB,CAAA,GACvB,qBAAC,QAAD;IAAM,eAAa,gBAAgB;cAAnC;KACG,KAAK,KAAK,OAAO;KAAE;KAAE;IAClB;KACH;MAGL,oBAAC,OAAD;GACE,OAAO;IACL,SAAS;IACT,KAAK;IACL,QAAQ,GAAG,OAAO,UAAU;IAC5B,WAAW,cAAc,0CAA0C;GACrE;aAEC,MAAM,KAAK,EAAE,QAAQ,SAAS,CAAC,EAAE,KAAK,GAAG,UACxC,oBAAC,OAAD;IAEE,eAAa,kBAAkB,SAAS,GAAG;IAC3C,OAAO;KACL,MAAM;KACN,iBACE,QAAQ,iBACJ,gBAAgB,aAAa,CAAC,IAC9B,gBAAgB,cAAc,sBAAsB,CAAC;KAC3D,cAAc;KACd,YAAY;KACZ,WACE,QAAQ,iBACJ,WAAW,gBAAgB,aAAa,EAAG,MAC3C;IACR;GACD,GAfM,KAeN,CACF;EACE,CAAA,CACF;;AAET,CAAC;AAED,UAAU,cAAc"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"KoreanButton.js","names":[],"sources":["../../../../../src/components/shared/three/ui/KoreanButton.tsx"],"sourcesContent":["/**\n * KoreanButton - Three.js-compatible button component with Korean theming\n * \n * Provides bilingual button with cyberpunk Korean aesthetic\n * Supports both HTML overlay and 3D mesh rendering\n * \n * Now refactored to use BaseButton for consistent styling\n * \n * @module components/three\n */\n\nimport React from \"react\";\nimport { BaseButton, type BaseButtonProps } from \"../../base\";\n\n/**\n * Props for KoreanButton component\n * Extends BaseButtonProps for consistency\n */\nexport interface KoreanButtonProps extends Omit<BaseButtonProps, \"isMobile\"> {\n}\n\n/**\n * KoreanButton Component\n * \n * A bilingual button component with Korean cyberpunk theming.\n * Now uses BaseButton internally for consistent styling and reduced duplication.\n * \n * @example\n * ```tsx\n * <KoreanButton\n * korean=\"공격\"\n * english=\"Attack\"\n * onClick={() => console.log(\"Attack clicked\")}\n * variant=\"primary\"\n * size=\"md\"\n * />\n * ```\n */\nexport const KoreanButton: React.FC<KoreanButtonProps> = ({ testId, ...rest }) => {\n return <BaseButton testId={testId ?? \"korean-button\"} {...rest} />;\n};\n\nKoreanButton.displayName = \"KoreanButton\";\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;AAsCA,IAAa,gBAA6C,EAAE,QAAQ,GAAG,WAAW;CAChF,OAAO,oBAAC,YAAD;EAAY,QAAQ,UAAU;EAAiB,GAAI;
|
|
1
|
+
{"version":3,"file":"KoreanButton.js","names":[],"sources":["../../../../../src/components/shared/three/ui/KoreanButton.tsx"],"sourcesContent":["/**\n * KoreanButton - Three.js-compatible button component with Korean theming\n * \n * Provides bilingual button with cyberpunk Korean aesthetic\n * Supports both HTML overlay and 3D mesh rendering\n * \n * Now refactored to use BaseButton for consistent styling\n * \n * @module components/three\n */\n\nimport React from \"react\";\nimport { BaseButton, type BaseButtonProps } from \"../../base\";\n\n/**\n * Props for KoreanButton component\n * Extends BaseButtonProps for consistency\n */\nexport interface KoreanButtonProps extends Omit<BaseButtonProps, \"isMobile\"> {\n}\n\n/**\n * KoreanButton Component\n * \n * A bilingual button component with Korean cyberpunk theming.\n * Now uses BaseButton internally for consistent styling and reduced duplication.\n * \n * @example\n * ```tsx\n * <KoreanButton\n * korean=\"공격\"\n * english=\"Attack\"\n * onClick={() => console.log(\"Attack clicked\")}\n * variant=\"primary\"\n * size=\"md\"\n * />\n * ```\n */\nexport const KoreanButton: React.FC<KoreanButtonProps> = ({ testId, ...rest }) => {\n return <BaseButton testId={testId ?? \"korean-button\"} {...rest} />;\n};\n\nKoreanButton.displayName = \"KoreanButton\";\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;AAsCA,IAAa,gBAA6C,EAAE,QAAQ,GAAG,WAAW;CAChF,OAAO,oBAAC,YAAD;EAAY,QAAQ,UAAU;EAAiB,GAAI;CAAO,CAAA;AACnE;AAEA,aAAa,cAAc"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"KoreanPanel.js","names":[],"sources":["../../../../../src/components/shared/three/ui/KoreanPanel.tsx"],"sourcesContent":["/**\n * KoreanPanel - Three.js-compatible panel container component\n * \n * Provides a styled container with Korean cyberpunk theming\n * \n * Now refactored to use BasePanel for consistent styling\n * \n * @module components/three\n */\n\nimport React from \"react\";\nimport { BasePanel, type BasePanelProps } from \"../../base\";\n\n/**\n * Props for KoreanPanel component\n * Extends BasePanelProps for consistency\n */\nexport interface KoreanPanelProps extends Omit<BasePanelProps, \"isMobile\"> {\n}\n\n/**\n * KoreanPanel Component\n * \n * A container component with Korean cyberpunk styling.\n * Now uses BasePanel internally for consistent styling and reduced duplication.\n * \n * @example\n * ```tsx\n * <KoreanPanel variant=\"bordered\" padding={20}>\n * <h1>Panel Content</h1>\n * </KoreanPanel>\n * ```\n */\nexport const KoreanPanel: React.FC<KoreanPanelProps> = ({ testId, ...rest }) => {\n return <BasePanel testId={testId ?? \"korean-panel\"} {...rest} />;\n};\n\nKoreanPanel.displayName = \"KoreanPanel\";\n"],"mappings":";;;;;;;;;;;;;;;;;AAiCA,IAAa,eAA2C,EAAE,QAAQ,GAAG,WAAW;CAC9E,OAAO,oBAAC,WAAD;EAAW,QAAQ,UAAU;EAAgB,GAAI;
|
|
1
|
+
{"version":3,"file":"KoreanPanel.js","names":[],"sources":["../../../../../src/components/shared/three/ui/KoreanPanel.tsx"],"sourcesContent":["/**\n * KoreanPanel - Three.js-compatible panel container component\n * \n * Provides a styled container with Korean cyberpunk theming\n * \n * Now refactored to use BasePanel for consistent styling\n * \n * @module components/three\n */\n\nimport React from \"react\";\nimport { BasePanel, type BasePanelProps } from \"../../base\";\n\n/**\n * Props for KoreanPanel component\n * Extends BasePanelProps for consistency\n */\nexport interface KoreanPanelProps extends Omit<BasePanelProps, \"isMobile\"> {\n}\n\n/**\n * KoreanPanel Component\n * \n * A container component with Korean cyberpunk styling.\n * Now uses BasePanel internally for consistent styling and reduced duplication.\n * \n * @example\n * ```tsx\n * <KoreanPanel variant=\"bordered\" padding={20}>\n * <h1>Panel Content</h1>\n * </KoreanPanel>\n * ```\n */\nexport const KoreanPanel: React.FC<KoreanPanelProps> = ({ testId, ...rest }) => {\n return <BasePanel testId={testId ?? \"korean-panel\"} {...rest} />;\n};\n\nKoreanPanel.displayName = \"KoreanPanel\";\n"],"mappings":";;;;;;;;;;;;;;;;;AAiCA,IAAa,eAA2C,EAAE,QAAQ,GAAG,WAAW;CAC9E,OAAO,oBAAC,WAAD;EAAW,QAAQ,UAAU;EAAgB,GAAI;CAAO,CAAA;AACjE;AAEA,YAAY,cAAc"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"KoreanText.js","names":[],"sources":["../../../../../src/components/shared/three/ui/KoreanText.tsx"],"sourcesContent":["/**\n * KoreanText - Three.js-compatible text component with bilingual support\n * \n * Displays Korean and English text with cyberpunk styling\n * \n * Now refactored to use BaseText for consistent styling\n * \n * @module components/three\n */\n\nimport React from \"react\";\nimport { BaseText, type BaseTextProps } from \"../../base\";\n\n/**\n * Props for KoreanText component\n * Extends BaseTextProps for consistency\n */\nexport interface KoreanTextProps extends Omit<BaseTextProps, \"isMobile\"> {\n}\n\n/**\n * KoreanText Component\n * \n * A bilingual text component with Korean cyberpunk styling.\n * Now uses BaseText internally for consistent styling and reduced duplication.\n * \n * @example\n * ```tsx\n * <KoreanText\n * korean=\"공격\"\n * english=\"Attack\"\n * size=\"large\"\n * layout=\"vertical\"\n * />\n * ```\n */\nexport const KoreanText: React.FC<KoreanTextProps> = ({ testId, ...rest }) => {\n return <BaseText testId={testId ?? \"korean-text\"} {...rest} />;\n};\n\nKoreanText.displayName = \"KoreanText\";\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAoCA,IAAa,cAAyC,EAAE,QAAQ,GAAG,WAAW;CAC5E,OAAO,oBAAC,UAAD;EAAU,QAAQ,UAAU;EAAe,GAAI;
|
|
1
|
+
{"version":3,"file":"KoreanText.js","names":[],"sources":["../../../../../src/components/shared/three/ui/KoreanText.tsx"],"sourcesContent":["/**\n * KoreanText - Three.js-compatible text component with bilingual support\n * \n * Displays Korean and English text with cyberpunk styling\n * \n * Now refactored to use BaseText for consistent styling\n * \n * @module components/three\n */\n\nimport React from \"react\";\nimport { BaseText, type BaseTextProps } from \"../../base\";\n\n/**\n * Props for KoreanText component\n * Extends BaseTextProps for consistency\n */\nexport interface KoreanTextProps extends Omit<BaseTextProps, \"isMobile\"> {\n}\n\n/**\n * KoreanText Component\n * \n * A bilingual text component with Korean cyberpunk styling.\n * Now uses BaseText internally for consistent styling and reduced duplication.\n * \n * @example\n * ```tsx\n * <KoreanText\n * korean=\"공격\"\n * english=\"Attack\"\n * size=\"large\"\n * layout=\"vertical\"\n * />\n * ```\n */\nexport const KoreanText: React.FC<KoreanTextProps> = ({ testId, ...rest }) => {\n return <BaseText testId={testId ?? \"korean-text\"} {...rest} />;\n};\n\nKoreanText.displayName = \"KoreanText\";\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAoCA,IAAa,cAAyC,EAAE,QAAQ,GAAG,WAAW;CAC5E,OAAO,oBAAC,UAAD;EAAU,QAAQ,UAAU;EAAe,GAAI;CAAO,CAAA;AAC/D;AAEA,WAAW,cAAc"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"MenuList.js","names":[],"sources":["../../../../../src/components/shared/three/ui/MenuList.tsx"],"sourcesContent":["/**\n * MenuList - Three.js-compatible menu list component\n * \n * Navigation menu with Korean theming and keyboard support\n * \n * @module components/three\n */\n\nimport { Html } from \"@react-three/drei\";\nimport React, { useCallback, useMemo, useState } from \"react\";\nimport { KOREAN_COLORS, FONT_FAMILY } from \"../../../../types/constants\";\nimport { hexToRgbaString } from \"../../../../utils/colorUtils\";\n\n/**\n * Menu item interface\n */\nexport interface MenuItem {\n readonly id: string;\n readonly korean: string;\n readonly english: string;\n readonly disabled?: boolean;\n}\n\n/**\n * Props for MenuList component\n */\nexport interface MenuListProps {\n readonly items: readonly MenuItem[];\n readonly onSelect: (id: string) => void;\n readonly selectedId?: string;\n readonly position?: [number, number, number];\n readonly width?: number;\n readonly testId?: string;\n}\n\n/**\n * MenuList Component\n * \n * A navigational menu component with Korean cyberpunk styling.\n * Supports keyboard navigation and hover states.\n * \n * @example\n * ```tsx\n * <MenuList\n * items={[\n * { id: \"combat\", korean: \"대전\", english: \"Combat\" },\n * { id: \"training\", korean: \"훈련\", english: \"Training\" }\n * ]}\n * onSelect={(id) => console.log(id)}\n * />\n * ```\n */\nexport const MenuList: React.FC<MenuListProps> = ({\n items,\n onSelect,\n selectedId,\n position = [0, 0, 0],\n width = 300,\n testId,\n}) => {\n const [hoveredId, setHoveredId] = useState<string | null>(null);\n\n const handleItemClick = useCallback(\n (id: string, disabled?: boolean) => {\n if (!disabled) {\n onSelect(id);\n }\n },\n [onSelect]\n );\n\n const handleMouseEnter = useCallback((id: string) => {\n setHoveredId(id);\n }, []);\n\n const handleMouseLeave = useCallback(() => {\n setHoveredId(null);\n }, []);\n\n const containerStyle = useMemo<React.CSSProperties>(\n () => ({\n width: `${width}px`,\n display: \"flex\",\n flexDirection: \"column\",\n gap: \"8px\",\n padding: \"12px\",\n background: hexToRgbaString(KOREAN_COLORS.UI_BACKGROUND_DARK, 0.9),\n border: `2px solid ${hexToRgbaString(KOREAN_COLORS.PRIMARY_CYAN, 0.6)}`,\n borderRadius: \"8px\",\n boxShadow: `0 0 15px ${hexToRgbaString(KOREAN_COLORS.PRIMARY_CYAN, 0.3)}`,\n }),\n [width]\n );\n\n const getItemStyle = useCallback(\n (id: string, disabled?: boolean): React.CSSProperties => {\n const isSelected = selectedId === id;\n const isHovered = hoveredId === id;\n\n let background = hexToRgbaString(KOREAN_COLORS.UI_BACKGROUND_MEDIUM, 0.5);\n let borderColor = hexToRgbaString(KOREAN_COLORS.ACCENT_GOLD, 0.3);\n\n if (isSelected) {\n background = hexToRgbaString(KOREAN_COLORS.PRIMARY_CYAN, 0.2);\n borderColor = hexToRgbaString(KOREAN_COLORS.PRIMARY_CYAN, 0.8);\n } else if (isHovered && !disabled) {\n background = hexToRgbaString(KOREAN_COLORS.ACCENT_GOLD, 0.15);\n borderColor = hexToRgbaString(KOREAN_COLORS.ACCENT_GOLD, 0.6);\n }\n\n return {\n padding: \"12px 16px\",\n background,\n border: `2px solid ${borderColor}`,\n borderRadius: \"4px\",\n cursor: disabled ? \"not-allowed\" : \"pointer\",\n transition: \"all 0.2s ease\",\n opacity: disabled ? 0.4 : 1,\n userSelect: \"none\",\n WebkitUserSelect: \"none\",\n transform: isHovered && !disabled ? \"translateX(4px)\" : \"translateX(0)\",\n };\n },\n [selectedId, hoveredId]\n );\n\n const getTextStyle = useCallback(\n (isKorean: boolean, isSelected: boolean): React.CSSProperties => ({\n display: \"block\",\n fontFamily: FONT_FAMILY.KOREAN,\n fontSize: isKorean ? \"16px\" : \"14px\",\n fontWeight: isKorean ? \"bold\" : \"normal\",\n fontStyle: isKorean ? \"normal\" : \"italic\",\n color: hexToRgbaString(\n isSelected ? KOREAN_COLORS.ACCENT_GOLD : KOREAN_COLORS.TEXT_PRIMARY\n ),\n textShadow: `0 2px 4px ${hexToRgbaString(KOREAN_COLORS.BLACK_SOLID, 0.5)}`,\n }),\n []\n );\n\n return (\n <Html position={position} center>\n <div style={containerStyle} data-testid={testId ?? \"menu-list\"}>\n {items.map((item) => {\n const isSelected = selectedId === item.id;\n return (\n <div\n key={item.id}\n onClick={() => handleItemClick(item.id, item.disabled)}\n onMouseEnter={() => handleMouseEnter(item.id)}\n onMouseLeave={handleMouseLeave}\n style={getItemStyle(item.id, item.disabled)}\n data-testid={`menu-item-${item.id}`}\n >\n <span style={getTextStyle(true, isSelected)}>\n {item.korean}\n </span>\n <span style={getTextStyle(false, isSelected)}>\n {item.english}\n </span>\n </div>\n );\n })}\n </div>\n </Html>\n );\n};\n\nMenuList.displayName = \"MenuList\";\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAoDA,IAAa,YAAqC,EAChD,OACA,UACA,YACA,WAAW;CAAC;CAAG;CAAG;
|
|
1
|
+
{"version":3,"file":"MenuList.js","names":[],"sources":["../../../../../src/components/shared/three/ui/MenuList.tsx"],"sourcesContent":["/**\n * MenuList - Three.js-compatible menu list component\n * \n * Navigation menu with Korean theming and keyboard support\n * \n * @module components/three\n */\n\nimport { Html } from \"@react-three/drei\";\nimport React, { useCallback, useMemo, useState } from \"react\";\nimport { KOREAN_COLORS, FONT_FAMILY } from \"../../../../types/constants\";\nimport { hexToRgbaString } from \"../../../../utils/colorUtils\";\n\n/**\n * Menu item interface\n */\nexport interface MenuItem {\n readonly id: string;\n readonly korean: string;\n readonly english: string;\n readonly disabled?: boolean;\n}\n\n/**\n * Props for MenuList component\n */\nexport interface MenuListProps {\n readonly items: readonly MenuItem[];\n readonly onSelect: (id: string) => void;\n readonly selectedId?: string;\n readonly position?: [number, number, number];\n readonly width?: number;\n readonly testId?: string;\n}\n\n/**\n * MenuList Component\n * \n * A navigational menu component with Korean cyberpunk styling.\n * Supports keyboard navigation and hover states.\n * \n * @example\n * ```tsx\n * <MenuList\n * items={[\n * { id: \"combat\", korean: \"대전\", english: \"Combat\" },\n * { id: \"training\", korean: \"훈련\", english: \"Training\" }\n * ]}\n * onSelect={(id) => console.log(id)}\n * />\n * ```\n */\nexport const MenuList: React.FC<MenuListProps> = ({\n items,\n onSelect,\n selectedId,\n position = [0, 0, 0],\n width = 300,\n testId,\n}) => {\n const [hoveredId, setHoveredId] = useState<string | null>(null);\n\n const handleItemClick = useCallback(\n (id: string, disabled?: boolean) => {\n if (!disabled) {\n onSelect(id);\n }\n },\n [onSelect]\n );\n\n const handleMouseEnter = useCallback((id: string) => {\n setHoveredId(id);\n }, []);\n\n const handleMouseLeave = useCallback(() => {\n setHoveredId(null);\n }, []);\n\n const containerStyle = useMemo<React.CSSProperties>(\n () => ({\n width: `${width}px`,\n display: \"flex\",\n flexDirection: \"column\",\n gap: \"8px\",\n padding: \"12px\",\n background: hexToRgbaString(KOREAN_COLORS.UI_BACKGROUND_DARK, 0.9),\n border: `2px solid ${hexToRgbaString(KOREAN_COLORS.PRIMARY_CYAN, 0.6)}`,\n borderRadius: \"8px\",\n boxShadow: `0 0 15px ${hexToRgbaString(KOREAN_COLORS.PRIMARY_CYAN, 0.3)}`,\n }),\n [width]\n );\n\n const getItemStyle = useCallback(\n (id: string, disabled?: boolean): React.CSSProperties => {\n const isSelected = selectedId === id;\n const isHovered = hoveredId === id;\n\n let background = hexToRgbaString(KOREAN_COLORS.UI_BACKGROUND_MEDIUM, 0.5);\n let borderColor = hexToRgbaString(KOREAN_COLORS.ACCENT_GOLD, 0.3);\n\n if (isSelected) {\n background = hexToRgbaString(KOREAN_COLORS.PRIMARY_CYAN, 0.2);\n borderColor = hexToRgbaString(KOREAN_COLORS.PRIMARY_CYAN, 0.8);\n } else if (isHovered && !disabled) {\n background = hexToRgbaString(KOREAN_COLORS.ACCENT_GOLD, 0.15);\n borderColor = hexToRgbaString(KOREAN_COLORS.ACCENT_GOLD, 0.6);\n }\n\n return {\n padding: \"12px 16px\",\n background,\n border: `2px solid ${borderColor}`,\n borderRadius: \"4px\",\n cursor: disabled ? \"not-allowed\" : \"pointer\",\n transition: \"all 0.2s ease\",\n opacity: disabled ? 0.4 : 1,\n userSelect: \"none\",\n WebkitUserSelect: \"none\",\n transform: isHovered && !disabled ? \"translateX(4px)\" : \"translateX(0)\",\n };\n },\n [selectedId, hoveredId]\n );\n\n const getTextStyle = useCallback(\n (isKorean: boolean, isSelected: boolean): React.CSSProperties => ({\n display: \"block\",\n fontFamily: FONT_FAMILY.KOREAN,\n fontSize: isKorean ? \"16px\" : \"14px\",\n fontWeight: isKorean ? \"bold\" : \"normal\",\n fontStyle: isKorean ? \"normal\" : \"italic\",\n color: hexToRgbaString(\n isSelected ? KOREAN_COLORS.ACCENT_GOLD : KOREAN_COLORS.TEXT_PRIMARY\n ),\n textShadow: `0 2px 4px ${hexToRgbaString(KOREAN_COLORS.BLACK_SOLID, 0.5)}`,\n }),\n []\n );\n\n return (\n <Html position={position} center>\n <div style={containerStyle} data-testid={testId ?? \"menu-list\"}>\n {items.map((item) => {\n const isSelected = selectedId === item.id;\n return (\n <div\n key={item.id}\n onClick={() => handleItemClick(item.id, item.disabled)}\n onMouseEnter={() => handleMouseEnter(item.id)}\n onMouseLeave={handleMouseLeave}\n style={getItemStyle(item.id, item.disabled)}\n data-testid={`menu-item-${item.id}`}\n >\n <span style={getTextStyle(true, isSelected)}>\n {item.korean}\n </span>\n <span style={getTextStyle(false, isSelected)}>\n {item.english}\n </span>\n </div>\n );\n })}\n </div>\n </Html>\n );\n};\n\nMenuList.displayName = \"MenuList\";\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAoDA,IAAa,YAAqC,EAChD,OACA,UACA,YACA,WAAW;CAAC;CAAG;CAAG;AAAC,GACnB,QAAQ,KACR,aACI;CACJ,MAAM,CAAC,WAAW,gBAAgB,SAAwB,IAAI;CAE9D,MAAM,kBAAkB,aACrB,IAAY,aAAuB;EAClC,IAAI,CAAC,UACH,SAAS,EAAE;CAEf,GACA,CAAC,QAAQ,CACX;CAEA,MAAM,mBAAmB,aAAa,OAAe;EACnD,aAAa,EAAE;CACjB,GAAG,CAAC,CAAC;CAEL,MAAM,mBAAmB,kBAAkB;EACzC,aAAa,IAAI;CACnB,GAAG,CAAC,CAAC;CAEL,MAAM,iBAAiB,eACd;EACL,OAAO,GAAG,MAAM;EAChB,SAAS;EACT,eAAe;EACf,KAAK;EACL,SAAS;EACT,YAAY,gBAAgB,cAAc,oBAAoB,EAAG;EACjE,QAAQ,aAAa,gBAAgB,cAAc,cAAc,EAAG;EACpE,cAAc;EACd,WAAW,YAAY,gBAAgB,cAAc,cAAc,EAAG;CACxE,IACA,CAAC,KAAK,CACR;CAEA,MAAM,eAAe,aAClB,IAAY,aAA4C;EACvD,MAAM,aAAa,eAAe;EAClC,MAAM,YAAY,cAAc;EAEhC,IAAI,aAAa,gBAAgB,cAAc,sBAAsB,EAAG;EACxE,IAAI,cAAc,gBAAgB,cAAc,aAAa,EAAG;EAEhE,IAAI,YAAY;GACd,aAAa,gBAAgB,cAAc,cAAc,EAAG;GAC5D,cAAc,gBAAgB,cAAc,cAAc,EAAG;EAC/D,OAAO,IAAI,aAAa,CAAC,UAAU;GACjC,aAAa,gBAAgB,cAAc,aAAa,GAAI;GAC5D,cAAc,gBAAgB,cAAc,aAAa,EAAG;EAC9D;EAEA,OAAO;GACL,SAAS;GACT;GACA,QAAQ,aAAa;GACrB,cAAc;GACd,QAAQ,WAAW,gBAAgB;GACnC,YAAY;GACZ,SAAS,WAAW,KAAM;GAC1B,YAAY;GACZ,kBAAkB;GAClB,WAAW,aAAa,CAAC,WAAW,oBAAoB;EAC1D;CACF,GACA,CAAC,YAAY,SAAS,CACxB;CAEA,MAAM,eAAe,aAClB,UAAmB,gBAA8C;EAChE,SAAS;EACT,YAAY,YAAY;EACxB,UAAU,WAAW,SAAS;EAC9B,YAAY,WAAW,SAAS;EAChC,WAAW,WAAW,WAAW;EACjC,OAAO,gBACL,aAAa,cAAc,cAAc,cAAc,YACzD;EACA,YAAY,aAAa,gBAAgB,cAAc,aAAa,EAAG;CACzE,IACA,CAAC,CACH;CAEA,OACE,oBAAC,MAAD;EAAgB;EAAU,QAAA;YACxB,oBAAC,OAAD;GAAK,OAAO;GAAgB,eAAa,UAAU;aAChD,MAAM,KAAK,SAAS;IACnB,MAAM,aAAa,eAAe,KAAK;IACvC,OACE,qBAAC,OAAD;KAEE,eAAe,gBAAgB,KAAK,IAAI,KAAK,QAAQ;KACrD,oBAAoB,iBAAiB,KAAK,EAAE;KAC5C,cAAc;KACd,OAAO,aAAa,KAAK,IAAI,KAAK,QAAQ;KAC1C,eAAa,aAAa,KAAK;eANjC,CAQE,oBAAC,QAAD;MAAM,OAAO,aAAa,MAAM,UAAU;gBACvC,KAAK;KACF,CAAA,GACN,oBAAC,QAAD;MAAM,OAAO,aAAa,OAAO,UAAU;gBACxC,KAAK;KACF,CAAA,CACH;OAbE,KAAK,EAaP;GAET,CAAC;EACE,CAAA;CACD,CAAA;AAEV;AAEA,SAAS,cAAc"}
|