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
package/lib/components/screens/combat/components/feedback/RoundStartAnnouncementOverlayHtml.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"RoundStartAnnouncementOverlayHtml.js","names":[],"sources":["../../../../../../src/components/screens/combat/components/feedback/RoundStartAnnouncementOverlayHtml.tsx"],"sourcesContent":["/**\n * RoundStartAnnouncement Component - Displays \"Round X Begin!\" announcement\n *\n * Korean: 라운드 시작 발표 (Round Start Announcement)\n *\n * Shows \"Round X Begin!\" for subsequent rounds (not the first round).\n * Implements Korean cyberpunk aesthetic with bilingual text support.\n *\n * Refactored to use useKoreanTheme hook for consistent styling.\n *\n * @module components/combat/RoundStartAnnouncement\n * @category Combat UI\n */\n\nimport React, {\n useCallback,\n useEffect,\n useMemo,\n useRef,\n useState,\n} from \"react\";\nimport { useAudio } from \"../../../../../audio/AudioProvider\";\nimport { useKoreanTheme } from \"../../../../shared/base/useKoreanTheme\";\nimport { hexColorToCSS } from \"../../../../../utils/colorUtils\";\n\n/**\n * Props for the RoundStartAnnouncement component\n */\nexport interface RoundStartAnnouncementProps {\n /** Round number (1-based) */\n readonly roundNumber: number;\n /** Duration to display announcement in seconds */\n readonly duration?: number;\n /** Callback when announcement completes */\n readonly onComplete: () => void;\n /** Whether layout should adapt for mobile screens */\n readonly isMobile: boolean;\n}\n\n/**\n * RoundStartAnnouncement Component\n *\n * Displays \"Round X Begin!\" announcement with:\n * - Bilingual round number and \"Begin!\" text\n * - Flash/pulse animation for impact\n * - Auto-dismiss after configured duration\n * - Audio cue for round start\n * - Responsive sizing for mobile/tablet/desktop\n * - Uses useKoreanTheme for consistent styling\n *\n * Korean: 라운드 시작 발표 컴포넌트\n */\nexport const RoundStartAnnouncement: React.FC<RoundStartAnnouncementProps> = ({\n roundNumber,\n duration = 2,\n onComplete,\n isMobile,\n}) => {\n const audio = useAudio();\n const theme = useKoreanTheme({ variant: \"primary\", size: \"xlarge\", isMobile });\n const [isVisible, setIsVisible] = useState(false);\n\n const onCompleteRef = useRef(onComplete);\n useEffect(() => {\n onCompleteRef.current = onComplete;\n }, [onComplete]);\n\n const handleComplete = useCallback(() => {\n onCompleteRef.current();\n }, []);\n\n useEffect(() => {\n const timer = setTimeout(() => setIsVisible(true), 50);\n return () => clearTimeout(timer);\n }, []);\n\n useEffect(() => {\n if (audio.isAudioReady) {\n audio.playSFX(\"attack_medium\"); // Using placeholder - will be round_start\n }\n }, [audio]);\n\n useEffect(() => {\n let isMounted = true;\n let innerTimer: ReturnType<typeof setTimeout> | null = null;\n\n const outerTimer = setTimeout(() => {\n setIsVisible(false);\n innerTimer = setTimeout(() => {\n if (isMounted) {\n handleComplete();\n }\n }, 300); // Wait for fade out\n }, duration * 1000);\n\n return () => {\n isMounted = false;\n clearTimeout(outerTimer);\n if (innerTimer) {\n clearTimeout(innerTimer);\n }\n };\n }, [duration, handleComplete]);\n\n const goldColor = useMemo(() => hexColorToCSS(theme.colors.ACCENT_GOLD), [theme.colors.ACCENT_GOLD]);\n const darkBg = useMemo(\n () => hexColorToCSS(theme.colors.UI_BACKGROUND_DARK),\n [theme.colors.UI_BACKGROUND_DARK]\n );\n\n return (\n <>\n <div\n data-testid=\"round-start-announcement\"\n role=\"alert\"\n aria-live=\"assertive\"\n aria-label={`Round ${roundNumber} starting`}\n style={{\n position: \"fixed\",\n top: 0,\n left: 0,\n width: \"100%\",\n height: \"100%\",\n display: \"flex\",\n alignItems: \"center\",\n justifyContent: \"center\",\n backgroundColor: `${darkBg}88`,\n zIndex: 900,\n opacity: isVisible ? 1 : 0,\n transition: \"opacity 0.3s ease-in-out\",\n pointerEvents: \"none\",\n }}\n >\n <div\n style={{\n fontSize: isMobile ? \"56px\" : \"96px\",\n fontWeight: \"bold\",\n color: goldColor,\n fontFamily: theme.fontFamily.KOREAN,\n textShadow: `0 0 40px ${goldColor}`,\n animation: \"roundStartFlash 0.5s ease-out\",\n textAlign: \"center\",\n userSelect: \"none\",\n }}\n data-testid=\"round-start-text\"\n >\n 라운드 {roundNumber} 시작!\n <br />\n <span\n style={{\n fontSize: isMobile ? \"40px\" : \"64px\",\n }}\n >\n Round {roundNumber} Begin!\n </span>\n </div>\n </div>\n\n {/* CSS Animation */}\n <style>\n {`\n @keyframes roundStartFlash {\n 0% {\n opacity: 0;\n transform: scale(1.5);\n }\n 30% {\n opacity: 1;\n transform: scale(1.2);\n }\n 100% {\n opacity: 1;\n transform: scale(1);\n }\n }\n `}\n </style>\n </>\n );\n};\n\nexport default RoundStartAnnouncement;\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAoDA,IAAa,0BAAiE,EAC5E,aACA,WAAW,GACX,YACA,eACI;CACJ,MAAM,QAAQ,
|
|
1
|
+
{"version":3,"file":"RoundStartAnnouncementOverlayHtml.js","names":[],"sources":["../../../../../../src/components/screens/combat/components/feedback/RoundStartAnnouncementOverlayHtml.tsx"],"sourcesContent":["/**\n * RoundStartAnnouncement Component - Displays \"Round X Begin!\" announcement\n *\n * Korean: 라운드 시작 발표 (Round Start Announcement)\n *\n * Shows \"Round X Begin!\" for subsequent rounds (not the first round).\n * Implements Korean cyberpunk aesthetic with bilingual text support.\n *\n * Refactored to use useKoreanTheme hook for consistent styling.\n *\n * @module components/combat/RoundStartAnnouncement\n * @category Combat UI\n */\n\nimport React, {\n useCallback,\n useEffect,\n useMemo,\n useRef,\n useState,\n} from \"react\";\nimport { useAudio } from \"../../../../../audio/AudioProvider\";\nimport { useKoreanTheme } from \"../../../../shared/base/useKoreanTheme\";\nimport { hexColorToCSS } from \"../../../../../utils/colorUtils\";\n\n/**\n * Props for the RoundStartAnnouncement component\n */\nexport interface RoundStartAnnouncementProps {\n /** Round number (1-based) */\n readonly roundNumber: number;\n /** Duration to display announcement in seconds */\n readonly duration?: number;\n /** Callback when announcement completes */\n readonly onComplete: () => void;\n /** Whether layout should adapt for mobile screens */\n readonly isMobile: boolean;\n}\n\n/**\n * RoundStartAnnouncement Component\n *\n * Displays \"Round X Begin!\" announcement with:\n * - Bilingual round number and \"Begin!\" text\n * - Flash/pulse animation for impact\n * - Auto-dismiss after configured duration\n * - Audio cue for round start\n * - Responsive sizing for mobile/tablet/desktop\n * - Uses useKoreanTheme for consistent styling\n *\n * Korean: 라운드 시작 발표 컴포넌트\n */\nexport const RoundStartAnnouncement: React.FC<RoundStartAnnouncementProps> = ({\n roundNumber,\n duration = 2,\n onComplete,\n isMobile,\n}) => {\n const audio = useAudio();\n const theme = useKoreanTheme({ variant: \"primary\", size: \"xlarge\", isMobile });\n const [isVisible, setIsVisible] = useState(false);\n\n const onCompleteRef = useRef(onComplete);\n useEffect(() => {\n onCompleteRef.current = onComplete;\n }, [onComplete]);\n\n const handleComplete = useCallback(() => {\n onCompleteRef.current();\n }, []);\n\n useEffect(() => {\n const timer = setTimeout(() => setIsVisible(true), 50);\n return () => clearTimeout(timer);\n }, []);\n\n useEffect(() => {\n if (audio.isAudioReady) {\n audio.playSFX(\"attack_medium\"); // Using placeholder - will be round_start\n }\n }, [audio]);\n\n useEffect(() => {\n let isMounted = true;\n let innerTimer: ReturnType<typeof setTimeout> | null = null;\n\n const outerTimer = setTimeout(() => {\n setIsVisible(false);\n innerTimer = setTimeout(() => {\n if (isMounted) {\n handleComplete();\n }\n }, 300); // Wait for fade out\n }, duration * 1000);\n\n return () => {\n isMounted = false;\n clearTimeout(outerTimer);\n if (innerTimer) {\n clearTimeout(innerTimer);\n }\n };\n }, [duration, handleComplete]);\n\n const goldColor = useMemo(() => hexColorToCSS(theme.colors.ACCENT_GOLD), [theme.colors.ACCENT_GOLD]);\n const darkBg = useMemo(\n () => hexColorToCSS(theme.colors.UI_BACKGROUND_DARK),\n [theme.colors.UI_BACKGROUND_DARK]\n );\n\n return (\n <>\n <div\n data-testid=\"round-start-announcement\"\n role=\"alert\"\n aria-live=\"assertive\"\n aria-label={`Round ${roundNumber} starting`}\n style={{\n position: \"fixed\",\n top: 0,\n left: 0,\n width: \"100%\",\n height: \"100%\",\n display: \"flex\",\n alignItems: \"center\",\n justifyContent: \"center\",\n backgroundColor: `${darkBg}88`,\n zIndex: 900,\n opacity: isVisible ? 1 : 0,\n transition: \"opacity 0.3s ease-in-out\",\n pointerEvents: \"none\",\n }}\n >\n <div\n style={{\n fontSize: isMobile ? \"56px\" : \"96px\",\n fontWeight: \"bold\",\n color: goldColor,\n fontFamily: theme.fontFamily.KOREAN,\n textShadow: `0 0 40px ${goldColor}`,\n animation: \"roundStartFlash 0.5s ease-out\",\n textAlign: \"center\",\n userSelect: \"none\",\n }}\n data-testid=\"round-start-text\"\n >\n 라운드 {roundNumber} 시작!\n <br />\n <span\n style={{\n fontSize: isMobile ? \"40px\" : \"64px\",\n }}\n >\n Round {roundNumber} Begin!\n </span>\n </div>\n </div>\n\n {/* CSS Animation */}\n <style>\n {`\n @keyframes roundStartFlash {\n 0% {\n opacity: 0;\n transform: scale(1.5);\n }\n 30% {\n opacity: 1;\n transform: scale(1.2);\n }\n 100% {\n opacity: 1;\n transform: scale(1);\n }\n }\n `}\n </style>\n </>\n );\n};\n\nexport default RoundStartAnnouncement;\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAoDA,IAAa,0BAAiE,EAC5E,aACA,WAAW,GACX,YACA,eACI;CACJ,MAAM,QAAQ,SAAS;CACvB,MAAM,QAAQ,eAAe;EAAE,SAAS;EAAW,MAAM;EAAU;CAAS,CAAC;CAC7E,MAAM,CAAC,WAAW,gBAAgB,SAAS,KAAK;CAEhD,MAAM,gBAAgB,OAAO,UAAU;CACvC,gBAAgB;EACd,cAAc,UAAU;CAC1B,GAAG,CAAC,UAAU,CAAC;CAEf,MAAM,iBAAiB,kBAAkB;EACvC,cAAc,QAAQ;CACxB,GAAG,CAAC,CAAC;CAEL,gBAAgB;EACd,MAAM,QAAQ,iBAAiB,aAAa,IAAI,GAAG,EAAE;EACrD,aAAa,aAAa,KAAK;CACjC,GAAG,CAAC,CAAC;CAEL,gBAAgB;EACd,IAAI,MAAM,cACR,MAAM,QAAQ,eAAe;CAEjC,GAAG,CAAC,KAAK,CAAC;CAEV,gBAAgB;EACd,IAAI,YAAY;EAChB,IAAI,aAAmD;EAEvD,MAAM,aAAa,iBAAiB;GAClC,aAAa,KAAK;GAClB,aAAa,iBAAiB;IAC5B,IAAI,WACF,eAAe;GAEnB,GAAG,GAAG;EACR,GAAG,WAAW,GAAI;EAElB,aAAa;GACX,YAAY;GACZ,aAAa,UAAU;GACvB,IAAI,YACF,aAAa,UAAU;EAE3B;CACF,GAAG,CAAC,UAAU,cAAc,CAAC;CAE7B,MAAM,YAAY,cAAc,cAAc,MAAM,OAAO,WAAW,GAAG,CAAC,MAAM,OAAO,WAAW,CAAC;CACnG,MAAM,SAAS,cACP,cAAc,MAAM,OAAO,kBAAkB,GACnD,CAAC,MAAM,OAAO,kBAAkB,CAClC;CAEA,OACE,qBAAA,UAAA,EAAA,UAAA,CACE,oBAAC,OAAD;EACE,eAAY;EACZ,MAAK;EACL,aAAU;EACV,cAAY,SAAS,YAAY;EACjC,OAAO;GACL,UAAU;GACV,KAAK;GACL,MAAM;GACN,OAAO;GACP,QAAQ;GACR,SAAS;GACT,YAAY;GACZ,gBAAgB;GAChB,iBAAiB,GAAG,OAAO;GAC3B,QAAQ;GACR,SAAS,YAAY,IAAI;GACzB,YAAY;GACZ,eAAe;EACjB;YAEA,qBAAC,OAAD;GACE,OAAO;IACL,UAAU,WAAW,SAAS;IAC9B,YAAY;IACZ,OAAO;IACP,YAAY,MAAM,WAAW;IAC7B,YAAY,YAAY;IACxB,WAAW;IACX,WAAW;IACX,YAAY;GACd;GACA,eAAY;aAXd;IAYC;IACM;IAAY;IACjB,oBAAC,MAAD,CAAK,CAAA;IACL,qBAAC,QAAD;KACE,OAAO,EACL,UAAU,WAAW,SAAS,OAChC;eAHF;MAIC;MACQ;MAAY;KACf;;GACH;;CACF,CAAA,GAGL,oBAAC,SAAD,EAAA,UACG;;;;;;;;;;;;;;;UAgBI,CAAA,CACP,EAAA,CAAA;AAEN"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"CombatBottomHUD.d.ts","sourceRoot":"","sources":["../../../../../../src/components/screens/combat/components/hud/CombatBottomHUD.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAEH,OAAO,KAAK,MAAM,OAAO,CAAC;AAC1B,OAAO,EAAE,WAAW,EAAE,MAAM,wBAAwB,CAAC;AACrD,OAAO,EAAE,SAAS,EAAE,MAAM,sBAAsB,CAAC;
|
|
1
|
+
{"version":3,"file":"CombatBottomHUD.d.ts","sourceRoot":"","sources":["../../../../../../src/components/screens/combat/components/hud/CombatBottomHUD.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAEH,OAAO,KAAK,MAAM,OAAO,CAAC;AAC1B,OAAO,EAAE,WAAW,EAAE,MAAM,wBAAwB,CAAC;AACrD,OAAO,EAAE,SAAS,EAAE,MAAM,sBAAsB,CAAC;AAwBjD,MAAM,WAAW,oBAAoB;IACnC,2CAA2C;IAC3C,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,4CAA4C;IAC5C,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,+DAA+D;IAC/D,QAAQ,CAAC,QAAQ,CAAC,EAAE,OAAO,CAAC;IAC5B,mDAAmD;IACnD,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAC;IAC/B,8CAA8C;IAC9C,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAC;IAC1B,iDAAiD;IACjD,QAAQ,CAAC,UAAU,EAAE,SAAS,SAAS,EAAE,CAAC;IAC1C,qDAAqD;IACrD,QAAQ,CAAC,MAAM,EAAE,WAAW,CAAC;IAC7B,yCAAyC;IACzC,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAC;IAC/B,iCAAiC;IACjC,QAAQ,CAAC,SAAS,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACxC,sCAAsC;IACtC,QAAQ,CAAC,iBAAiB,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IACpD,iCAAiC;IACjC,QAAQ,CAAC,cAAc,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;CAC7C;AAED;;;;;GAKG;AACH,eAAO,MAAM,eAAe,EAAE,KAAK,CAAC,EAAE,CAAC,oBAAoB,CA8L1D,CAAC;AAEF,eAAe,eAAe,CAAC"}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { BORDERS, BORDER_RADIUS, COMBAT_UI_DIMENSIONS, COMBAT_UI_DIMENSIONS_NUMERIC, FONT_SIZE_MULTIPLIERS, GRADIENTS, HIERARCHY, HUD_STYLE, OPACITY, SPACING, SPACING_ADJUSTMENTS, TEXT_EFFECTS, TYPOGRAPHY, TYPOGRAPHY_NUMERIC } from "../../../../../types/constants/designSystem.js";
|
|
2
2
|
import { Z_INDEX } from "../../../../../types/LayoutTypes.js";
|
|
3
|
-
import { HUD_SIDE_CONTROL_RESERVES } from "../../../../../types/constants/layout.js";
|
|
3
|
+
import { COMBAT_BOTTOM_HUD_HEIGHT_PERCENT, HUD_SIDE_CONTROL_RESERVES } from "../../../../../types/constants/layout.js";
|
|
4
4
|
import { BREAKPOINTS, getHUDHeight, getResponsiveFontSize, getResponsivePadding, parsePercentageToRatio, shouldShowMobileControls } from "../../../../../utils/responsiveLayout.js";
|
|
5
5
|
import TechniqueBar from "../../../../shared/three/ui/TechniqueBar.js";
|
|
6
6
|
import { VolumeControl } from "../../../../shared/ui/VolumeControl.js";
|
|
@@ -30,7 +30,7 @@ import { jsx, jsxs } from "react/jsx-runtime";
|
|
|
30
30
|
var CombatBottomHUD = ({ width, height, isMobile = false, positionScale, visible, techniques, player, selectedIndex, cooldowns, onTechniqueSelect, combatMessages = [] }) => {
|
|
31
31
|
const showMobileControls = shouldShowMobileControls(width, isMobile);
|
|
32
32
|
const layout = React.useMemo(() => {
|
|
33
|
-
const hudHeight = getHUDHeight(height,
|
|
33
|
+
const hudHeight = getHUDHeight(height, COMBAT_BOTTOM_HUD_HEIGHT_PERCENT) * positionScale;
|
|
34
34
|
const padding = getResponsivePadding(width) * positionScale;
|
|
35
35
|
const baseFontSize = getResponsiveFontSize(width);
|
|
36
36
|
const titleFontSize = Math.max(TYPOGRAPHY_NUMERIC.nano, baseFontSize * FONT_SIZE_MULTIPLIERS.titleSmall);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"CombatBottomHUD.js","names":[],"sources":["../../../../../../src/components/screens/combat/components/hud/CombatBottomHUD.tsx"],"sourcesContent":["/**\n * CombatBottomHUD - Bottom bar for combat screen\n *\n * Contains:\n * - Technique Bar (centered)\n * - Volume Control (bottom-right, compact)\n * - Combat Messages (above technique bar)\n *\n * Gaming Layout Best Practice:\n * - Width: 100% of screen\n * - Height: Resolution-based ~10% of screen height (40-120px range)\n *\n * @korean 전투화면 하단 바 - 기술 바, 음량, 전투 메시지\n */\n\nimport React from \"react\";\nimport { PlayerState } from \"../../../../../systems\";\nimport { Technique } from \"../../../../../types\";\nimport { HUD_SIDE_CONTROL_RESERVES } from \"../../../../../types/constants/layout\";\nimport { Z_INDEX } from \"../../../../../types/LayoutTypes\";\nimport { SPACING, SPACING_ADJUSTMENTS, BORDER_RADIUS, TYPOGRAPHY, TYPOGRAPHY_NUMERIC, HIERARCHY, BORDERS, GRADIENTS, HUD_STYLE ,\n OPACITY,\n COMBAT_UI_DIMENSIONS,\n COMBAT_UI_DIMENSIONS_NUMERIC,\n TEXT_EFFECTS,\n FONT_SIZE_MULTIPLIERS,\n} from \"../../../../../types/constants/designSystem\";\nimport {\n BREAKPOINTS,\n getHUDHeight,\n getResponsiveFontSize,\n getResponsivePadding,\n parsePercentageToRatio,\n shouldShowMobileControls,\n} from \"../../../../../utils/responsiveLayout\";\nimport { TechniqueBar } from \"../../../../shared/three/ui/TechniqueBar\";\nimport { VolumeControl } from \"../../../../shared/ui/VolumeControl\";\n\nexport interface CombatBottomHUDProps {\n /** Screen width for layout calculations */\n readonly width: number;\n /** Screen height for layout calculations */\n readonly height: number;\n /** Whether mobile controls should be shown (NOT for sizing) */\n readonly isMobile?: boolean;\n /** Position scale multiplier for large displays */\n readonly positionScale: number;\n /** Whether technique bar should be visible */\n readonly visible: boolean;\n /** Available techniques for the technique bar */\n readonly techniques: readonly Technique[];\n /** Player state for technique availability checks */\n readonly player: PlayerState;\n /** Currently selected technique index */\n readonly selectedIndex: number;\n /** Active technique cooldowns */\n readonly cooldowns: Map<string, number>;\n /** Handler for technique selection */\n readonly onTechniqueSelect: (index: number) => void;\n /** Combat messages to display */\n readonly combatMessages?: readonly string[];\n}\n\n/**\n * CombatBottomHUD Component\n *\n * Compact bottom bar with centered technique bar, volume control,\n * and combat messages. Uses resolution-based sizing for all elements.\n */\nexport const CombatBottomHUD: React.FC<CombatBottomHUDProps> = ({\n width,\n height,\n isMobile = false,\n positionScale,\n visible,\n techniques,\n player,\n selectedIndex,\n cooldowns,\n onTechniqueSelect,\n combatMessages = [],\n}) => {\n const showMobileControls = shouldShowMobileControls(width, isMobile);\n\n const layout = React.useMemo(() => {\n const hudHeight = getHUDHeight(height, 0.1) * positionScale;\n \n const padding = getResponsivePadding(width) * positionScale;\n \n const baseFontSize = getResponsiveFontSize(width);\n const titleFontSize = Math.max(TYPOGRAPHY_NUMERIC.nano, baseFontSize * FONT_SIZE_MULTIPLIERS.titleSmall);\n const messageFontSize = Math.max(TYPOGRAPHY_NUMERIC.caption, baseFontSize * FONT_SIZE_MULTIPLIERS.messageSmall);\n \n const minMessageWidth = width < BREAKPOINTS.mobile \n ? COMBAT_UI_DIMENSIONS_NUMERIC.combatLogMinMobile\n : COMBAT_UI_DIMENSIONS_NUMERIC.combatLogMinDesktop;\n const maxMessageWidth = width < BREAKPOINTS.mobile \n ? width * COMBAT_UI_DIMENSIONS.combatLogMaxWidthPercentMobile\n : COMBAT_UI_DIMENSIONS_NUMERIC.combatLogMaxDesktop;\n const maxTechniqueBarWidth = width < BREAKPOINTS.mobile \n ? COMBAT_UI_DIMENSIONS.techniqueBarWidthMobile \n : COMBAT_UI_DIMENSIONS.techniqueBarWidthDesktop;\n \n const messagePadding = width < BREAKPOINTS.mobile \n ? `${SPACING_ADJUSTMENTS.compact} ${SPACING.sm}` \n : `${SPACING.xs} ${SPACING.md}`;\n\n const usableWidth = width - padding * 2;\n const maxBarWidthPx = usableWidth * parsePercentageToRatio(maxTechniqueBarWidth);\n const volumeReserve = showMobileControls\n ? HUD_SIDE_CONTROL_RESERVES.TECHNIQUE_BAR_MOBILE\n : HUD_SIDE_CONTROL_RESERVES.VOLUME_CONTROL;\n\n const techniqueBarContainerWidth = Math.max(\n 0,\n Math.min(\n maxBarWidthPx,\n usableWidth - volumeReserve,\n ),\n );\n\n return {\n hudHeight,\n padding,\n titleFontSize,\n messageFontSize,\n minMessageWidth,\n maxMessageWidth,\n maxTechniqueBarWidth,\n messagePadding,\n volumeReserve,\n techniqueBarContainerWidth,\n };\n }, [width, height, positionScale, showMobileControls]);\n\n const recentMessages = combatMessages.slice(-3);\n\n return (\n <div\n style={{\n position: \"absolute\",\n bottom: 0,\n left: 0,\n width: \"100%\",\n height: `${layout.hudHeight}px`,\n display: \"flex\",\n flexDirection: \"column\",\n justifyContent: \"flex-end\",\n alignItems: \"center\",\n pointerEvents: \"none\",\n padding: `${layout.padding}px`,\n boxSizing: \"border-box\",\n borderTop: BORDERS.default,\n background: GRADIENTS.verticalReverse(0.9),\n backdropFilter: HUD_STYLE.backdropFilter,\n }}\n data-testid=\"combat-bottom-hud\"\n >\n {/* Combat Messages - styled box above technique bar */}\n {recentMessages.length > 0 && (\n <div\n style={{\n position: \"absolute\",\n top: `${layout.padding}px`,\n left: \"50%\",\n transform: \"translateX(-50%)\",\n display: \"flex\",\n flexDirection: \"column\",\n alignItems: \"center\",\n gap: SPACING_ADJUSTMENTS.micro,\n zIndex: Z_INDEX.HUD,\n padding: layout.messagePadding,\n background: HUD_STYLE.background,\n border: BORDERS.muted,\n borderRadius: BORDER_RADIUS.md,\n boxShadow: HUD_STYLE.shadow,\n minWidth: `${layout.minMessageWidth}px`,\n maxWidth: typeof layout.maxMessageWidth === 'number' \n ? `${layout.maxMessageWidth}px` \n : layout.maxMessageWidth,\n }}\n data-testid=\"combat-bottom-hud-messages\"\n >\n <div\n style={{\n fontSize: `${layout.titleFontSize}px`,\n fontFamily: TYPOGRAPHY.caption.fontFamily,\n color: HIERARCHY.accent70.color,\n textTransform: \"uppercase\",\n letterSpacing: \"1px\",\n marginBottom: SPACING_ADJUSTMENTS.tiny,\n }}\n >\n 전투 기록 | Combat Log\n </div>\n {recentMessages.map((message, index) => (\n <div\n key={index}\n style={{\n fontSize: `${layout.messageFontSize}px`,\n fontFamily: TYPOGRAPHY.bodySmall.fontFamily,\n color: HIERARCHY.primary.color,\n textShadow: TEXT_EFFECTS.darkShadow,\n opacity: OPACITY.base + index * OPACITY.increment,\n textAlign: \"center\",\n }}\n >\n {message}\n </div>\n ))}\n </div>\n )}\n\n {/* Technique Bar - centered, embedded mode for proper containment.\n Reserve space on the right for the absolute Volume Control so cards\n never visually overlap the volume button. */}\n {visible && (\n <div\n style={{\n pointerEvents: \"all\",\n display: \"flex\",\n justifyContent: \"center\",\n alignItems: \"center\",\n width: \"100%\",\n maxWidth: layout.maxTechniqueBarWidth,\n marginRight: layout.volumeReserve,\n }}\n data-testid=\"combat-bottom-hud-technique-section\"\n >\n <TechniqueBar\n techniques={techniques as Technique[]}\n player={player}\n selectedIndex={selectedIndex}\n cooldowns={cooldowns}\n onTechniqueSelect={onTechniqueSelect}\n onTechniqueHover={(_tech) => {}}\n isMobile={showMobileControls}\n screenWidth={width}\n screenHeight={height}\n embedded={true}\n containerWidth={layout.techniqueBarContainerWidth}\n />\n </div>\n )}\n\n {/* Volume Control - bottom right corner */}\n <div\n style={{\n position: \"absolute\",\n right: `${layout.padding * 1.5}px`,\n bottom: `${layout.padding}px`,\n pointerEvents: \"all\",\n }}\n data-testid=\"combat-bottom-hud-volume-section\"\n >\n <VolumeControl position=\"custom\" compact={true} />\n </div>\n </div>\n );\n};\n\nexport default CombatBottomHUD;\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"CombatBottomHUD.js","names":[],"sources":["../../../../../../src/components/screens/combat/components/hud/CombatBottomHUD.tsx"],"sourcesContent":["/**\n * CombatBottomHUD - Bottom bar for combat screen\n *\n * Contains:\n * - Technique Bar (centered)\n * - Volume Control (bottom-right, compact)\n * - Combat Messages (above technique bar)\n *\n * Gaming Layout Best Practice:\n * - Width: 100% of screen\n * - Height: Resolution-based ~10% of screen height (40-120px range)\n *\n * @korean 전투화면 하단 바 - 기술 바, 음량, 전투 메시지\n */\n\nimport React from \"react\";\nimport { PlayerState } from \"../../../../../systems\";\nimport { Technique } from \"../../../../../types\";\nimport {\n COMBAT_BOTTOM_HUD_HEIGHT_PERCENT,\n HUD_SIDE_CONTROL_RESERVES,\n} from \"../../../../../types/constants/layout\";\nimport { Z_INDEX } from \"../../../../../types/LayoutTypes\";\nimport { SPACING, SPACING_ADJUSTMENTS, BORDER_RADIUS, TYPOGRAPHY, TYPOGRAPHY_NUMERIC, HIERARCHY, BORDERS, GRADIENTS, HUD_STYLE ,\n OPACITY,\n COMBAT_UI_DIMENSIONS,\n COMBAT_UI_DIMENSIONS_NUMERIC,\n TEXT_EFFECTS,\n FONT_SIZE_MULTIPLIERS,\n} from \"../../../../../types/constants/designSystem\";\nimport {\n BREAKPOINTS,\n getHUDHeight,\n getResponsiveFontSize,\n getResponsivePadding,\n parsePercentageToRatio,\n shouldShowMobileControls,\n} from \"../../../../../utils/responsiveLayout\";\nimport { TechniqueBar } from \"../../../../shared/three/ui/TechniqueBar\";\nimport { VolumeControl } from \"../../../../shared/ui/VolumeControl\";\n\nexport interface CombatBottomHUDProps {\n /** Screen width for layout calculations */\n readonly width: number;\n /** Screen height for layout calculations */\n readonly height: number;\n /** Whether mobile controls should be shown (NOT for sizing) */\n readonly isMobile?: boolean;\n /** Position scale multiplier for large displays */\n readonly positionScale: number;\n /** Whether technique bar should be visible */\n readonly visible: boolean;\n /** Available techniques for the technique bar */\n readonly techniques: readonly Technique[];\n /** Player state for technique availability checks */\n readonly player: PlayerState;\n /** Currently selected technique index */\n readonly selectedIndex: number;\n /** Active technique cooldowns */\n readonly cooldowns: Map<string, number>;\n /** Handler for technique selection */\n readonly onTechniqueSelect: (index: number) => void;\n /** Combat messages to display */\n readonly combatMessages?: readonly string[];\n}\n\n/**\n * CombatBottomHUD Component\n *\n * Compact bottom bar with centered technique bar, volume control,\n * and combat messages. Uses resolution-based sizing for all elements.\n */\nexport const CombatBottomHUD: React.FC<CombatBottomHUDProps> = ({\n width,\n height,\n isMobile = false,\n positionScale,\n visible,\n techniques,\n player,\n selectedIndex,\n cooldowns,\n onTechniqueSelect,\n combatMessages = [],\n}) => {\n const showMobileControls = shouldShowMobileControls(width, isMobile);\n\n const layout = React.useMemo(() => {\n const hudHeight = getHUDHeight(height, COMBAT_BOTTOM_HUD_HEIGHT_PERCENT) * positionScale;\n \n const padding = getResponsivePadding(width) * positionScale;\n \n const baseFontSize = getResponsiveFontSize(width);\n const titleFontSize = Math.max(TYPOGRAPHY_NUMERIC.nano, baseFontSize * FONT_SIZE_MULTIPLIERS.titleSmall);\n const messageFontSize = Math.max(TYPOGRAPHY_NUMERIC.caption, baseFontSize * FONT_SIZE_MULTIPLIERS.messageSmall);\n \n const minMessageWidth = width < BREAKPOINTS.mobile \n ? COMBAT_UI_DIMENSIONS_NUMERIC.combatLogMinMobile\n : COMBAT_UI_DIMENSIONS_NUMERIC.combatLogMinDesktop;\n const maxMessageWidth = width < BREAKPOINTS.mobile \n ? width * COMBAT_UI_DIMENSIONS.combatLogMaxWidthPercentMobile\n : COMBAT_UI_DIMENSIONS_NUMERIC.combatLogMaxDesktop;\n const maxTechniqueBarWidth = width < BREAKPOINTS.mobile \n ? COMBAT_UI_DIMENSIONS.techniqueBarWidthMobile \n : COMBAT_UI_DIMENSIONS.techniqueBarWidthDesktop;\n \n const messagePadding = width < BREAKPOINTS.mobile \n ? `${SPACING_ADJUSTMENTS.compact} ${SPACING.sm}` \n : `${SPACING.xs} ${SPACING.md}`;\n\n const usableWidth = width - padding * 2;\n const maxBarWidthPx = usableWidth * parsePercentageToRatio(maxTechniqueBarWidth);\n const volumeReserve = showMobileControls\n ? HUD_SIDE_CONTROL_RESERVES.TECHNIQUE_BAR_MOBILE\n : HUD_SIDE_CONTROL_RESERVES.VOLUME_CONTROL;\n\n const techniqueBarContainerWidth = Math.max(\n 0,\n Math.min(\n maxBarWidthPx,\n usableWidth - volumeReserve,\n ),\n );\n\n return {\n hudHeight,\n padding,\n titleFontSize,\n messageFontSize,\n minMessageWidth,\n maxMessageWidth,\n maxTechniqueBarWidth,\n messagePadding,\n volumeReserve,\n techniqueBarContainerWidth,\n };\n }, [width, height, positionScale, showMobileControls]);\n\n const recentMessages = combatMessages.slice(-3);\n\n return (\n <div\n style={{\n position: \"absolute\",\n bottom: 0,\n left: 0,\n width: \"100%\",\n height: `${layout.hudHeight}px`,\n display: \"flex\",\n flexDirection: \"column\",\n justifyContent: \"flex-end\",\n alignItems: \"center\",\n pointerEvents: \"none\",\n padding: `${layout.padding}px`,\n boxSizing: \"border-box\",\n borderTop: BORDERS.default,\n background: GRADIENTS.verticalReverse(0.9),\n backdropFilter: HUD_STYLE.backdropFilter,\n }}\n data-testid=\"combat-bottom-hud\"\n >\n {/* Combat Messages - styled box above technique bar */}\n {recentMessages.length > 0 && (\n <div\n style={{\n position: \"absolute\",\n top: `${layout.padding}px`,\n left: \"50%\",\n transform: \"translateX(-50%)\",\n display: \"flex\",\n flexDirection: \"column\",\n alignItems: \"center\",\n gap: SPACING_ADJUSTMENTS.micro,\n zIndex: Z_INDEX.HUD,\n padding: layout.messagePadding,\n background: HUD_STYLE.background,\n border: BORDERS.muted,\n borderRadius: BORDER_RADIUS.md,\n boxShadow: HUD_STYLE.shadow,\n minWidth: `${layout.minMessageWidth}px`,\n maxWidth: typeof layout.maxMessageWidth === 'number' \n ? `${layout.maxMessageWidth}px` \n : layout.maxMessageWidth,\n }}\n data-testid=\"combat-bottom-hud-messages\"\n >\n <div\n style={{\n fontSize: `${layout.titleFontSize}px`,\n fontFamily: TYPOGRAPHY.caption.fontFamily,\n color: HIERARCHY.accent70.color,\n textTransform: \"uppercase\",\n letterSpacing: \"1px\",\n marginBottom: SPACING_ADJUSTMENTS.tiny,\n }}\n >\n 전투 기록 | Combat Log\n </div>\n {recentMessages.map((message, index) => (\n <div\n key={index}\n style={{\n fontSize: `${layout.messageFontSize}px`,\n fontFamily: TYPOGRAPHY.bodySmall.fontFamily,\n color: HIERARCHY.primary.color,\n textShadow: TEXT_EFFECTS.darkShadow,\n opacity: OPACITY.base + index * OPACITY.increment,\n textAlign: \"center\",\n }}\n >\n {message}\n </div>\n ))}\n </div>\n )}\n\n {/* Technique Bar - centered, embedded mode for proper containment.\n Reserve space on the right for the absolute Volume Control so cards\n never visually overlap the volume button. */}\n {visible && (\n <div\n style={{\n pointerEvents: \"all\",\n display: \"flex\",\n justifyContent: \"center\",\n alignItems: \"center\",\n width: \"100%\",\n maxWidth: layout.maxTechniqueBarWidth,\n marginRight: layout.volumeReserve,\n }}\n data-testid=\"combat-bottom-hud-technique-section\"\n >\n <TechniqueBar\n techniques={techniques as Technique[]}\n player={player}\n selectedIndex={selectedIndex}\n cooldowns={cooldowns}\n onTechniqueSelect={onTechniqueSelect}\n onTechniqueHover={(_tech) => {}}\n isMobile={showMobileControls}\n screenWidth={width}\n screenHeight={height}\n embedded={true}\n containerWidth={layout.techniqueBarContainerWidth}\n />\n </div>\n )}\n\n {/* Volume Control - bottom right corner */}\n <div\n style={{\n position: \"absolute\",\n right: `${layout.padding * 1.5}px`,\n bottom: `${layout.padding}px`,\n pointerEvents: \"all\",\n }}\n data-testid=\"combat-bottom-hud-volume-section\"\n >\n <VolumeControl position=\"custom\" compact={true} />\n </div>\n </div>\n );\n};\n\nexport default CombatBottomHUD;\n"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"CombatLeftHUD.js","names":[],"sources":["../../../../../../src/components/screens/combat/components/hud/CombatLeftHUD.tsx"],"sourcesContent":["/**\n * CombatLeftHUD - Left side HUD for combat screen (Player 1)\n *\n * REUSES existing components:\n * - PlayerHUD: Archetype image, name, health/stamina bars\n * - SpeedIndicatorHUD: Movement speed percentage\n * - BodyPartHealthDisplay: Individual body part health bars\n * - GuardIndicator: Current stance guard status\n *\n * Gaming Layout Best Practice:\n * - Width: Resolution-based 14-18% of screen\n * - Height: 100% minus top/bottom HUD heights\n * - Leaves approximately 64–72% center for arena depending on resolution (≈72% on desktop)\n *\n * Now uses shared HUD utilities with resolution-based sizing.\n *\n * @korean 전투화면 왼쪽 HUD - 플레이어 1 상태\n */\n\nimport React from \"react\";\nimport { useHUDLayout } from \"../../../../../hooks/useHUDLayout\";\nimport { PlayerState } from \"../../../../../systems\";\nimport type { StanceLaterality } from \"../../../../../systems/trigram/types\";\nimport { BaseHUDContainer } from \"../../../../shared/ui/BaseHUDContainer\";\nimport { GuardIndicator } from \"../../../../shared/three/indicators/GuardIndicator\";\nimport { PlayerHUD } from \"../../../../shared/three/ui/PlayerHUD\";\nimport { SpeedIndicatorHUD } from \"../../../../shared/three/ui/SpeedIndicatorHUD\";\nimport { BodyPartHealthDisplay } from \"../../../../shared/three/ui/BodyPartHealthDisplay\";\nimport { BreathingIndicator } from \"../../../../shared/three/ui/BreathingIndicator\";\n\nexport interface CombatLeftHUDProps {\n /** Screen width for layout calculations */\n readonly width: number;\n /** Screen height for layout calculations */\n readonly height: number;\n /** Whether mobile layout is active */\n readonly isMobile: boolean;\n /** Position scale multiplier for large displays */\n readonly positionScale: number;\n /** Player 1 state */\n readonly player: PlayerState;\n /** Player laterality (left/right foot forward) */\n readonly laterality: StanceLaterality;\n /** Whether player is in guard stance */\n readonly isInGuard: boolean;\n /** Player speed modifiers */\n readonly speedModifiers: {\n finalSpeed: number;\n baseSpeed: number;\n };\n}\n\n/**\n * CombatLeftHUD Component\n *\n * Left side of the combat screen containing Player 1's stats.\n * Occupies approximately 14–18% of screen width based on resolution breakpoints/interpolation,\n * positioned between the top and bottom HUDs.\n * REUSES existing PlayerHUD, SpeedIndicatorHUD, BodyPartHealthDisplay components.\n * Uses shared HUD utilities for consistent, resolution-based layout and styling.\n */\nexport const CombatLeftHUD: React.FC<CombatLeftHUDProps> = ({\n width,\n height,\n isMobile,\n positionScale,\n player,\n laterality,\n isInGuard,\n speedModifiers,\n}) => {\n const layout = useHUDLayout(\n width,\n height,\n positionScale,\n 'left',\n 'combat'\n );\n\n return (\n <BaseHUDContainer\n position=\"left\"\n width={layout.hudWidth}\n height={layout.availableHeight}\n topOffset={layout.topOffset}\n padding={layout.padding}\n gap={layout.gap}\n style={{ overflow: \"hidden\" }}\n dataTestId=\"combat-left-hud\"\n >\n {/* Player 1 Stats - REUSING PlayerHUD component with embedded positioning */}\n <div\n style={{\n pointerEvents: \"none\",\n position: \"relative\",\n }}\n data-testid=\"combat-left-hud-player-section\"\n >\n <PlayerHUD\n player={player}\n position=\"left\"\n isMobile={isMobile}\n laterality={laterality}\n />\n </div>\n\n {/* Speed Indicator - REUSING SpeedIndicatorHUD component */}\n <div\n style={{\n pointerEvents: \"none\",\n position: \"relative\",\n }}\n data-testid=\"combat-left-hud-speed-section\"\n >\n <SpeedIndicatorHUD\n finalSpeed={speedModifiers.finalSpeed}\n baseSpeed={speedModifiers.baseSpeed}\n position=\"left\"\n isMobile={isMobile}\n visible={true}\n />\n </div>\n\n {/* Body Part Health - REUSING BodyPartHealthDisplay component */}\n {player.bodyPartHealth && (\n <div\n style={{\n pointerEvents: \"none\",\n position: \"relative\",\n }}\n data-testid=\"combat-left-hud-bodypart-section\"\n >\n <BodyPartHealthDisplay\n bodyPartHealth={player.bodyPartHealth}\n playerId={player.id}\n position=\"left\"\n isMobile={isMobile}\n />\n </div>\n )}\n\n {/* Breathing Disruption Indicator - Shows breathing difficulty status */}\n <div\n style={{\n pointerEvents: \"none\",\n position: \"relative\",\n }}\n data-testid=\"combat-left-hud-breathing-section\"\n >\n <BreathingIndicator\n player={player}\n isMobile={isMobile}\n />\n </div>\n\n {/* Guard Indicator - at bottom of HUD */}\n <div\n style={{\n pointerEvents: \"none\",\n marginTop: \"auto\",\n position: \"relative\",\n }}\n data-testid=\"combat-left-hud-guard-section\"\n >\n <GuardIndicator\n currentStance={player.currentStance}\n isInGuard={isInGuard}\n position=\"left\"\n isMobile={isMobile}\n />\n </div>\n </BaseHUDContainer>\n );\n};\n\nexport default CombatLeftHUD;\n"],"mappings":";;;;;;;;;;;;;;;;;;;AA6DA,IAAa,iBAA+C,EAC1D,OACA,QACA,UACA,eACA,QACA,YACA,WACA,qBACI;CACJ,MAAM,SAAS,aACb,OACA,QACA,eACA,QACA,
|
|
1
|
+
{"version":3,"file":"CombatLeftHUD.js","names":[],"sources":["../../../../../../src/components/screens/combat/components/hud/CombatLeftHUD.tsx"],"sourcesContent":["/**\n * CombatLeftHUD - Left side HUD for combat screen (Player 1)\n *\n * REUSES existing components:\n * - PlayerHUD: Archetype image, name, health/stamina bars\n * - SpeedIndicatorHUD: Movement speed percentage\n * - BodyPartHealthDisplay: Individual body part health bars\n * - GuardIndicator: Current stance guard status\n *\n * Gaming Layout Best Practice:\n * - Width: Resolution-based 14-18% of screen\n * - Height: 100% minus top/bottom HUD heights\n * - Leaves approximately 64–72% center for arena depending on resolution (≈72% on desktop)\n *\n * Now uses shared HUD utilities with resolution-based sizing.\n *\n * @korean 전투화면 왼쪽 HUD - 플레이어 1 상태\n */\n\nimport React from \"react\";\nimport { useHUDLayout } from \"../../../../../hooks/useHUDLayout\";\nimport { PlayerState } from \"../../../../../systems\";\nimport type { StanceLaterality } from \"../../../../../systems/trigram/types\";\nimport { BaseHUDContainer } from \"../../../../shared/ui/BaseHUDContainer\";\nimport { GuardIndicator } from \"../../../../shared/three/indicators/GuardIndicator\";\nimport { PlayerHUD } from \"../../../../shared/three/ui/PlayerHUD\";\nimport { SpeedIndicatorHUD } from \"../../../../shared/three/ui/SpeedIndicatorHUD\";\nimport { BodyPartHealthDisplay } from \"../../../../shared/three/ui/BodyPartHealthDisplay\";\nimport { BreathingIndicator } from \"../../../../shared/three/ui/BreathingIndicator\";\n\nexport interface CombatLeftHUDProps {\n /** Screen width for layout calculations */\n readonly width: number;\n /** Screen height for layout calculations */\n readonly height: number;\n /** Whether mobile layout is active */\n readonly isMobile: boolean;\n /** Position scale multiplier for large displays */\n readonly positionScale: number;\n /** Player 1 state */\n readonly player: PlayerState;\n /** Player laterality (left/right foot forward) */\n readonly laterality: StanceLaterality;\n /** Whether player is in guard stance */\n readonly isInGuard: boolean;\n /** Player speed modifiers */\n readonly speedModifiers: {\n finalSpeed: number;\n baseSpeed: number;\n };\n}\n\n/**\n * CombatLeftHUD Component\n *\n * Left side of the combat screen containing Player 1's stats.\n * Occupies approximately 14–18% of screen width based on resolution breakpoints/interpolation,\n * positioned between the top and bottom HUDs.\n * REUSES existing PlayerHUD, SpeedIndicatorHUD, BodyPartHealthDisplay components.\n * Uses shared HUD utilities for consistent, resolution-based layout and styling.\n */\nexport const CombatLeftHUD: React.FC<CombatLeftHUDProps> = ({\n width,\n height,\n isMobile,\n positionScale,\n player,\n laterality,\n isInGuard,\n speedModifiers,\n}) => {\n const layout = useHUDLayout(\n width,\n height,\n positionScale,\n 'left',\n 'combat'\n );\n\n return (\n <BaseHUDContainer\n position=\"left\"\n width={layout.hudWidth}\n height={layout.availableHeight}\n topOffset={layout.topOffset}\n padding={layout.padding}\n gap={layout.gap}\n style={{ overflow: \"hidden\" }}\n dataTestId=\"combat-left-hud\"\n >\n {/* Player 1 Stats - REUSING PlayerHUD component with embedded positioning */}\n <div\n style={{\n pointerEvents: \"none\",\n position: \"relative\",\n }}\n data-testid=\"combat-left-hud-player-section\"\n >\n <PlayerHUD\n player={player}\n position=\"left\"\n isMobile={isMobile}\n laterality={laterality}\n />\n </div>\n\n {/* Speed Indicator - REUSING SpeedIndicatorHUD component */}\n <div\n style={{\n pointerEvents: \"none\",\n position: \"relative\",\n }}\n data-testid=\"combat-left-hud-speed-section\"\n >\n <SpeedIndicatorHUD\n finalSpeed={speedModifiers.finalSpeed}\n baseSpeed={speedModifiers.baseSpeed}\n position=\"left\"\n isMobile={isMobile}\n visible={true}\n />\n </div>\n\n {/* Body Part Health - REUSING BodyPartHealthDisplay component */}\n {player.bodyPartHealth && (\n <div\n style={{\n pointerEvents: \"none\",\n position: \"relative\",\n }}\n data-testid=\"combat-left-hud-bodypart-section\"\n >\n <BodyPartHealthDisplay\n bodyPartHealth={player.bodyPartHealth}\n playerId={player.id}\n position=\"left\"\n isMobile={isMobile}\n />\n </div>\n )}\n\n {/* Breathing Disruption Indicator - Shows breathing difficulty status */}\n <div\n style={{\n pointerEvents: \"none\",\n position: \"relative\",\n }}\n data-testid=\"combat-left-hud-breathing-section\"\n >\n <BreathingIndicator\n player={player}\n isMobile={isMobile}\n />\n </div>\n\n {/* Guard Indicator - at bottom of HUD */}\n <div\n style={{\n pointerEvents: \"none\",\n marginTop: \"auto\",\n position: \"relative\",\n }}\n data-testid=\"combat-left-hud-guard-section\"\n >\n <GuardIndicator\n currentStance={player.currentStance}\n isInGuard={isInGuard}\n position=\"left\"\n isMobile={isMobile}\n />\n </div>\n </BaseHUDContainer>\n );\n};\n\nexport default CombatLeftHUD;\n"],"mappings":";;;;;;;;;;;;;;;;;;;AA6DA,IAAa,iBAA+C,EAC1D,OACA,QACA,UACA,eACA,QACA,YACA,WACA,qBACI;CACJ,MAAM,SAAS,aACb,OACA,QACA,eACA,QACA,QACF;CAEA,OACE,qBAAC,kBAAD;EACE,UAAS;EACT,OAAO,OAAO;EACd,QAAQ,OAAO;EACf,WAAW,OAAO;EAClB,SAAS,OAAO;EAChB,KAAK,OAAO;EACZ,OAAO,EAAE,UAAU,SAAS;EAC5B,YAAW;YARb;GAWE,oBAAC,OAAD;IACE,OAAO;KACL,eAAe;KACf,UAAU;IACZ;IACA,eAAY;cAEZ,oBAAC,WAAD;KACU;KACR,UAAS;KACC;KACE;IACb,CAAA;GACE,CAAA;GAGL,oBAAC,OAAD;IACE,OAAO;KACL,eAAe;KACf,UAAU;IACZ;IACA,eAAY;cAEZ,oBAAC,mBAAD;KACE,YAAY,eAAe;KAC3B,WAAW,eAAe;KAC1B,UAAS;KACC;KACV,SAAS;IACV,CAAA;GACE,CAAA;GAGJ,OAAO,kBACN,oBAAC,OAAD;IACE,OAAO;KACL,eAAe;KACf,UAAU;IACZ;IACA,eAAY;cAEZ,oBAAC,uBAAD;KACE,gBAAgB,OAAO;KACvB,UAAU,OAAO;KACjB,UAAS;KACC;IACX,CAAA;GACE,CAAA;GAIP,oBAAC,OAAD;IACE,OAAO;KACL,eAAe;KACf,UAAU;IACZ;IACA,eAAY;cAEZ,oBAAC,oBAAD;KACU;KACE;IACX,CAAA;GACE,CAAA;GAGL,oBAAC,OAAD;IACE,OAAO;KACL,eAAe;KACf,WAAW;KACX,UAAU;IACZ;IACA,eAAY;cAEZ,oBAAC,gBAAD;KACE,eAAe,OAAO;KACX;KACX,UAAS;KACC;IACX,CAAA;GACE,CAAA;EACW;;AAEtB"}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { KOREAN_COLORS } from "../../../../../types/constants/colors.js";
|
|
2
2
|
import { BORDERS, FONT_SIZE_MULTIPLIERS, GRADIENTS, HIERARCHY, HUD_STYLE, TYPOGRAPHY, TYPOGRAPHY_NUMERIC } from "../../../../../types/constants/designSystem.js";
|
|
3
|
-
import { hexToRgbaString } from "../../../../../utils/colorUtils.js";
|
|
4
3
|
import { getResponsiveFontSize, getResponsivePadding } from "../../../../../utils/responsiveLayout.js";
|
|
4
|
+
import { hexToRgbaString } from "../../../../../utils/colorUtils.js";
|
|
5
5
|
import { useMemo } from "react";
|
|
6
6
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
7
7
|
//#region src/components/screens/combat/components/hud/CombatPortraitStatusStrip.tsx
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"CombatPortraitStatusStrip.js","names":[],"sources":["../../../../../../src/components/screens/combat/components/hud/CombatPortraitStatusStrip.tsx"],"sourcesContent":["/**\n * CombatPortraitStatusStrip - Compact two-player HP/stamina strip for portrait mobile.\n *\n * When the viewport is portrait mobile we hide `CombatLeftHUD` and\n * `CombatRightHUD` (they occlude the 3D arena on narrow screens). This strip\n * replaces them with a minimal, **always visible** health+stamina bar for\n * both players, rendered as a single horizontal band directly under the\n * top HUD.\n *\n * UX goals (game-UI best practice for narrow portrait):\n * - Both bars fit on a single row on viewports as narrow as 320 px.\n * - Clear left/right grouping (P1 on the left, P2 on the right) mirrors the\n * fighters' on-screen positions.\n * - Low visual weight (no side HUD backgrounds, no icons) so the strip does\n * not re-introduce the occlusion problem it was meant to solve.\n * - Korean + English bilingual micro-labels (체 / ST).\n *\n * 세로 모드 휴대폰 전용 상/하단 상태 바\n *\n * @module components/screens/combat/components/hud/CombatPortraitStatusStrip\n */\n\nimport React, { useMemo } from \"react\";\nimport { PlayerState } from \"../../../../../systems\";\nimport {\n BORDERS,\n FONT_SIZE_MULTIPLIERS,\n GRADIENTS,\n HIERARCHY,\n HUD_STYLE,\n TYPOGRAPHY,\n TYPOGRAPHY_NUMERIC,\n} from \"../../../../../types/constants/designSystem\";\nimport { KOREAN_COLORS } from \"../../../../../types/constants/colors\";\nimport { hexToRgbaString } from \"../../../../../utils/colorUtils\";\nimport {\n getResponsiveFontSize,\n getResponsivePadding,\n} from \"../../../../../utils/responsiveLayout\";\n\nexport interface CombatPortraitStatusStripProps {\n /** Screen width for layout calculations */\n readonly width: number;\n /** Screen height for layout calculations */\n readonly height: number;\n /** Player 1 state */\n readonly player1: PlayerState;\n /** Player 2 (AI) state */\n readonly player2: PlayerState;\n /** Position scale multiplier for large displays */\n readonly positionScale: number;\n /** Y offset from top of screen (should equal top HUD height) */\n readonly topOffset: number;\n}\n\n/** Single compact HP + stamina pill for one fighter. */\nconst PlayerPill: React.FC<{\n readonly player: PlayerState;\n readonly side: \"left\" | \"right\";\n readonly fontSize: number;\n readonly barHeight: number;\n readonly testId: string;\n}> = ({ player, side, fontSize, barHeight, testId }) => {\n const healthPercent =\n player.maxHealth > 0\n ? Math.max(\n 0,\n Math.min(100, (player.health / player.maxHealth) * 100),\n )\n : 0;\n const staminaPercent =\n player.maxStamina > 0\n ? Math.max(\n 0,\n Math.min(100, (player.stamina / player.maxStamina) * 100),\n )\n : 0;\n\n const healthColor =\n healthPercent > 50\n ? KOREAN_COLORS.HEALTH_FULL\n : healthPercent > 25\n ? KOREAN_COLORS.HEALTH_MEDIUM\n : KOREAN_COLORS.HEALTH_LOW;\n\n return (\n <div\n data-testid={testId}\n style={{\n display: \"flex\",\n flex: 1,\n flexDirection: \"column\",\n alignItems: side === \"left\" ? \"flex-start\" : \"flex-end\",\n gap: \"2px\",\n minWidth: 0,\n }}\n >\n {/* Name + side indicator */}\n <div\n style={{\n fontSize: `${fontSize}px`,\n color: HIERARCHY.accent.color,\n fontFamily: TYPOGRAPHY.body.fontFamily,\n fontWeight: 600,\n whiteSpace: \"nowrap\",\n overflow: \"hidden\",\n textOverflow: \"ellipsis\",\n maxWidth: \"100%\",\n }}\n >\n {side === \"left\" ? \"P1\" : \"P2\"}\n </div>\n {/* HP bar */}\n <div\n role=\"progressbar\"\n aria-label={`Player ${side === \"left\" ? 1 : 2} health`}\n aria-valuenow={Math.max(\n 0,\n Math.min(player.maxHealth, Math.ceil(player.health)),\n )}\n aria-valuemin={0}\n aria-valuemax={player.maxHealth}\n style={{\n width: \"100%\",\n height: `${barHeight}px`,\n backgroundColor: hexToRgbaString(\n KOREAN_COLORS.UI_BACKGROUND_MEDIUM,\n 1,\n ),\n borderRadius: \"2px\",\n overflow: \"hidden\",\n border: `1px solid ${hexToRgbaString(KOREAN_COLORS.PRIMARY_CYAN, 0.5)}`,\n }}\n >\n <div\n data-testid={`${testId}-hp-fill`}\n style={{\n width: `${healthPercent}%`,\n height: \"100%\",\n backgroundColor: hexToRgbaString(healthColor, 1),\n transition: \"width 0.2s ease-out, background-color 0.3s ease-out\",\n }}\n />\n </div>\n {/* Stamina bar (thinner) */}\n <div\n role=\"progressbar\"\n aria-label={`Player ${side === \"left\" ? 1 : 2} stamina`}\n aria-valuenow={Math.max(\n 0,\n Math.min(player.maxStamina, Math.ceil(player.stamina)),\n )}\n aria-valuemin={0}\n aria-valuemax={player.maxStamina}\n style={{\n width: \"100%\",\n height: `${Math.max(3, Math.floor(barHeight * 0.5))}px`,\n backgroundColor: hexToRgbaString(\n KOREAN_COLORS.UI_BACKGROUND_MEDIUM,\n 1,\n ),\n borderRadius: \"2px\",\n overflow: \"hidden\",\n border: `1px solid ${hexToRgbaString(KOREAN_COLORS.ACCENT_GOLD, 0.4)}`,\n }}\n >\n <div\n data-testid={`${testId}-sp-fill`}\n style={{\n width: `${staminaPercent}%`,\n height: \"100%\",\n backgroundColor: hexToRgbaString(KOREAN_COLORS.ACCENT_GOLD, 1),\n transition: \"width 0.2s ease-out\",\n }}\n />\n </div>\n </div>\n );\n};\n\n/**\n * Compact two-player HP/stamina strip, only rendered for portrait mobile.\n */\nexport const CombatPortraitStatusStrip: React.FC<\n CombatPortraitStatusStripProps\n> = ({ width, height, player1, player2, positionScale, topOffset }) => {\n const layout = useMemo(() => {\n const isExtraSmall = width < 380;\n const minStripHeight = isExtraSmall ? 28 : 36;\n const stripHeight = Math.max(\n minStripHeight,\n Math.round(height * 0.055 * positionScale),\n );\n const padding = getResponsivePadding(width) * positionScale;\n const fontSize = Math.max(\n TYPOGRAPHY_NUMERIC.caption,\n getResponsiveFontSize(width) *\n positionScale *\n FONT_SIZE_MULTIPLIERS.bodySmall,\n );\n const centerGap = Math.max(6, padding);\n const barHeight = Math.max(6, Math.floor(stripHeight * 0.28));\n\n return { stripHeight, padding, fontSize, centerGap, barHeight };\n }, [width, height, positionScale]);\n\n return (\n <div\n data-testid=\"combat-portrait-status-strip\"\n style={{\n position: \"absolute\",\n top: `${topOffset}px`,\n left: 0,\n width: \"100%\",\n height: `${layout.stripHeight}px`,\n display: \"flex\",\n flexDirection: \"row\",\n alignItems: \"center\",\n justifyContent: \"space-between\",\n gap: `${layout.centerGap}px`,\n padding: `4px ${layout.padding}px`,\n pointerEvents: \"none\",\n boxSizing: \"border-box\",\n borderBottom: BORDERS.muted,\n background: GRADIENTS.vertical(0.85),\n backdropFilter: HUD_STYLE.backdropFilter,\n }}\n >\n <PlayerPill\n player={player1}\n side=\"left\"\n fontSize={layout.fontSize}\n barHeight={layout.barHeight}\n testId=\"combat-portrait-p1\"\n />\n <PlayerPill\n player={player2}\n side=\"right\"\n fontSize={layout.fontSize}\n barHeight={layout.barHeight}\n testId=\"combat-portrait-p2\"\n />\n </div>\n );\n};\n\nexport default CombatPortraitStatusStrip;\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"CombatPortraitStatusStrip.js","names":[],"sources":["../../../../../../src/components/screens/combat/components/hud/CombatPortraitStatusStrip.tsx"],"sourcesContent":["/**\n * CombatPortraitStatusStrip - Compact two-player HP/stamina strip for portrait mobile.\n *\n * When the viewport is portrait mobile we hide `CombatLeftHUD` and\n * `CombatRightHUD` (they occlude the 3D arena on narrow screens). This strip\n * replaces them with a minimal, **always visible** health+stamina bar for\n * both players, rendered as a single horizontal band directly under the\n * top HUD.\n *\n * UX goals (game-UI best practice for narrow portrait):\n * - Both bars fit on a single row on viewports as narrow as 320 px.\n * - Clear left/right grouping (P1 on the left, P2 on the right) mirrors the\n * fighters' on-screen positions.\n * - Low visual weight (no side HUD backgrounds, no icons) so the strip does\n * not re-introduce the occlusion problem it was meant to solve.\n * - Korean + English bilingual micro-labels (체 / ST).\n *\n * 세로 모드 휴대폰 전용 상/하단 상태 바\n *\n * @module components/screens/combat/components/hud/CombatPortraitStatusStrip\n */\n\nimport React, { useMemo } from \"react\";\nimport { PlayerState } from \"../../../../../systems\";\nimport {\n BORDERS,\n FONT_SIZE_MULTIPLIERS,\n GRADIENTS,\n HIERARCHY,\n HUD_STYLE,\n TYPOGRAPHY,\n TYPOGRAPHY_NUMERIC,\n} from \"../../../../../types/constants/designSystem\";\nimport { KOREAN_COLORS } from \"../../../../../types/constants/colors\";\nimport { hexToRgbaString } from \"../../../../../utils/colorUtils\";\nimport {\n getResponsiveFontSize,\n getResponsivePadding,\n} from \"../../../../../utils/responsiveLayout\";\n\nexport interface CombatPortraitStatusStripProps {\n /** Screen width for layout calculations */\n readonly width: number;\n /** Screen height for layout calculations */\n readonly height: number;\n /** Player 1 state */\n readonly player1: PlayerState;\n /** Player 2 (AI) state */\n readonly player2: PlayerState;\n /** Position scale multiplier for large displays */\n readonly positionScale: number;\n /** Y offset from top of screen (should equal top HUD height) */\n readonly topOffset: number;\n}\n\n/** Single compact HP + stamina pill for one fighter. */\nconst PlayerPill: React.FC<{\n readonly player: PlayerState;\n readonly side: \"left\" | \"right\";\n readonly fontSize: number;\n readonly barHeight: number;\n readonly testId: string;\n}> = ({ player, side, fontSize, barHeight, testId }) => {\n const healthPercent =\n player.maxHealth > 0\n ? Math.max(\n 0,\n Math.min(100, (player.health / player.maxHealth) * 100),\n )\n : 0;\n const staminaPercent =\n player.maxStamina > 0\n ? Math.max(\n 0,\n Math.min(100, (player.stamina / player.maxStamina) * 100),\n )\n : 0;\n\n const healthColor =\n healthPercent > 50\n ? KOREAN_COLORS.HEALTH_FULL\n : healthPercent > 25\n ? KOREAN_COLORS.HEALTH_MEDIUM\n : KOREAN_COLORS.HEALTH_LOW;\n\n return (\n <div\n data-testid={testId}\n style={{\n display: \"flex\",\n flex: 1,\n flexDirection: \"column\",\n alignItems: side === \"left\" ? \"flex-start\" : \"flex-end\",\n gap: \"2px\",\n minWidth: 0,\n }}\n >\n {/* Name + side indicator */}\n <div\n style={{\n fontSize: `${fontSize}px`,\n color: HIERARCHY.accent.color,\n fontFamily: TYPOGRAPHY.body.fontFamily,\n fontWeight: 600,\n whiteSpace: \"nowrap\",\n overflow: \"hidden\",\n textOverflow: \"ellipsis\",\n maxWidth: \"100%\",\n }}\n >\n {side === \"left\" ? \"P1\" : \"P2\"}\n </div>\n {/* HP bar */}\n <div\n role=\"progressbar\"\n aria-label={`Player ${side === \"left\" ? 1 : 2} health`}\n aria-valuenow={Math.max(\n 0,\n Math.min(player.maxHealth, Math.ceil(player.health)),\n )}\n aria-valuemin={0}\n aria-valuemax={player.maxHealth}\n style={{\n width: \"100%\",\n height: `${barHeight}px`,\n backgroundColor: hexToRgbaString(\n KOREAN_COLORS.UI_BACKGROUND_MEDIUM,\n 1,\n ),\n borderRadius: \"2px\",\n overflow: \"hidden\",\n border: `1px solid ${hexToRgbaString(KOREAN_COLORS.PRIMARY_CYAN, 0.5)}`,\n }}\n >\n <div\n data-testid={`${testId}-hp-fill`}\n style={{\n width: `${healthPercent}%`,\n height: \"100%\",\n backgroundColor: hexToRgbaString(healthColor, 1),\n transition: \"width 0.2s ease-out, background-color 0.3s ease-out\",\n }}\n />\n </div>\n {/* Stamina bar (thinner) */}\n <div\n role=\"progressbar\"\n aria-label={`Player ${side === \"left\" ? 1 : 2} stamina`}\n aria-valuenow={Math.max(\n 0,\n Math.min(player.maxStamina, Math.ceil(player.stamina)),\n )}\n aria-valuemin={0}\n aria-valuemax={player.maxStamina}\n style={{\n width: \"100%\",\n height: `${Math.max(3, Math.floor(barHeight * 0.5))}px`,\n backgroundColor: hexToRgbaString(\n KOREAN_COLORS.UI_BACKGROUND_MEDIUM,\n 1,\n ),\n borderRadius: \"2px\",\n overflow: \"hidden\",\n border: `1px solid ${hexToRgbaString(KOREAN_COLORS.ACCENT_GOLD, 0.4)}`,\n }}\n >\n <div\n data-testid={`${testId}-sp-fill`}\n style={{\n width: `${staminaPercent}%`,\n height: \"100%\",\n backgroundColor: hexToRgbaString(KOREAN_COLORS.ACCENT_GOLD, 1),\n transition: \"width 0.2s ease-out\",\n }}\n />\n </div>\n </div>\n );\n};\n\n/**\n * Compact two-player HP/stamina strip, only rendered for portrait mobile.\n */\nexport const CombatPortraitStatusStrip: React.FC<\n CombatPortraitStatusStripProps\n> = ({ width, height, player1, player2, positionScale, topOffset }) => {\n const layout = useMemo(() => {\n const isExtraSmall = width < 380;\n const minStripHeight = isExtraSmall ? 28 : 36;\n const stripHeight = Math.max(\n minStripHeight,\n Math.round(height * 0.055 * positionScale),\n );\n const padding = getResponsivePadding(width) * positionScale;\n const fontSize = Math.max(\n TYPOGRAPHY_NUMERIC.caption,\n getResponsiveFontSize(width) *\n positionScale *\n FONT_SIZE_MULTIPLIERS.bodySmall,\n );\n const centerGap = Math.max(6, padding);\n const barHeight = Math.max(6, Math.floor(stripHeight * 0.28));\n\n return { stripHeight, padding, fontSize, centerGap, barHeight };\n }, [width, height, positionScale]);\n\n return (\n <div\n data-testid=\"combat-portrait-status-strip\"\n style={{\n position: \"absolute\",\n top: `${topOffset}px`,\n left: 0,\n width: \"100%\",\n height: `${layout.stripHeight}px`,\n display: \"flex\",\n flexDirection: \"row\",\n alignItems: \"center\",\n justifyContent: \"space-between\",\n gap: `${layout.centerGap}px`,\n padding: `4px ${layout.padding}px`,\n pointerEvents: \"none\",\n boxSizing: \"border-box\",\n borderBottom: BORDERS.muted,\n background: GRADIENTS.vertical(0.85),\n backdropFilter: HUD_STYLE.backdropFilter,\n }}\n >\n <PlayerPill\n player={player1}\n side=\"left\"\n fontSize={layout.fontSize}\n barHeight={layout.barHeight}\n testId=\"combat-portrait-p1\"\n />\n <PlayerPill\n player={player2}\n side=\"right\"\n fontSize={layout.fontSize}\n barHeight={layout.barHeight}\n testId=\"combat-portrait-p2\"\n />\n </div>\n );\n};\n\nexport default CombatPortraitStatusStrip;\n"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"CombatRightHUD.js","names":[],"sources":["../../../../../../src/components/screens/combat/components/hud/CombatRightHUD.tsx"],"sourcesContent":["/**\n * CombatRightHUD - Right side HUD for combat screen (Player 2 / AI)\n *\n * REUSES existing components:\n * - PlayerHUD: Archetype image, name, health/stamina bars\n * - SpeedIndicatorHUD: Movement speed percentage\n * - BodyPartHealthDisplay: Individual body part health bars\n * - DifficultyIndicator: AI difficulty tier display\n *\n * Gaming Layout Best Practice:\n * - Width: Resolution-based 14-18% of screen\n * - Height: 100% minus top/bottom HUD heights\n * - Leaves remaining center region for arena (width varies by resolution)\n *\n * Now uses shared HUD utilities with resolution-based sizing.\n *\n * @korean 전투화면 오른쪽 HUD - 플레이어 2/AI 상태\n */\n\nimport React from \"react\";\nimport { useHUDLayout } from \"../../../../../hooks/useHUDLayout\";\nimport { PlayerState } from \"../../../../../systems\";\nimport type { StanceLaterality } from \"../../../../../systems/trigram/types\";\nimport { BaseHUDContainer } from \"../../../../shared/ui/BaseHUDContainer\";\nimport { BodyPartHealthDisplay } from \"../../../../shared/three/ui/BodyPartHealthDisplay\";\nimport { PlayerHUD } from \"../../../../shared/three/ui/PlayerHUD\";\nimport { SpeedIndicatorHUD } from \"../../../../shared/three/ui/SpeedIndicatorHUD\";\nimport { BreathingIndicator } from \"../../../../shared/three/ui/BreathingIndicator\";\nimport { DifficultyIndicator } from \"./DifficultyIndicator\";\n\nexport interface CombatRightHUDProps {\n /** Screen width for layout calculations */\n readonly width: number;\n /** Screen height for layout calculations */\n readonly height: number;\n /** Whether mobile layout is active */\n readonly isMobile: boolean;\n /** Position scale multiplier for large displays */\n readonly positionScale: number;\n /** Player 2/AI state */\n readonly player: PlayerState;\n /** Player laterality (left/right foot forward) */\n readonly laterality: StanceLaterality;\n /** Current AI difficulty tier (1-5) */\n readonly difficultyTier: number;\n /** Player speed modifiers */\n readonly speedModifiers: {\n finalSpeed: number;\n baseSpeed: number;\n };\n}\n\n/**\n * CombatRightHUD Component\n *\n * Right side of the combat screen containing Player 2/AI stats.\n * Occupies approximately 14–18% of screen width based on resolution breakpoints/interpolation,\n * positioned between top and bottom HUDs.\n * REUSES existing PlayerHUD, SpeedIndicatorHUD, BodyPartHealthDisplay, DifficultyIndicator.\n * Uses shared HUD utilities for consistent, resolution-based layout and styling.\n */\nexport const CombatRightHUD: React.FC<CombatRightHUDProps> = ({\n width,\n height,\n isMobile,\n positionScale,\n player,\n laterality,\n difficultyTier,\n speedModifiers,\n}) => {\n const layout = useHUDLayout(\n width,\n height,\n positionScale,\n 'right',\n 'combat'\n );\n\n return (\n <BaseHUDContainer\n position=\"right\"\n width={layout.hudWidth}\n height={layout.availableHeight}\n topOffset={layout.topOffset}\n padding={layout.padding}\n gap={layout.gap}\n style={{ overflow: \"hidden\" }}\n dataTestId=\"combat-right-hud\"\n >\n {/* Player 2/AI Stats - REUSING PlayerHUD component */}\n <div\n style={{\n pointerEvents: \"none\",\n position: \"relative\",\n }}\n data-testid=\"combat-right-hud-player-section\"\n >\n <PlayerHUD\n player={player}\n position=\"right\"\n isMobile={isMobile}\n laterality={laterality}\n />\n </div>\n\n {/* Difficulty Tier - REUSING DifficultyIndicator component */}\n <div\n style={{\n pointerEvents: \"none\",\n position: \"relative\",\n }}\n data-testid=\"combat-right-hud-difficulty-section\"\n >\n <DifficultyIndicator tier={difficultyTier} isMobile={isMobile} />\n </div>\n\n {/* Speed Indicator - REUSING SpeedIndicatorHUD component */}\n <div\n style={{\n pointerEvents: \"none\",\n position: \"relative\",\n }}\n data-testid=\"combat-right-hud-speed-section\"\n >\n <SpeedIndicatorHUD\n finalSpeed={speedModifiers.finalSpeed}\n baseSpeed={speedModifiers.baseSpeed}\n position=\"right\"\n isMobile={isMobile}\n visible={true}\n />\n </div>\n\n {/* Body Part Health - REUSING BodyPartHealthDisplay component */}\n {player.bodyPartHealth && (\n <div\n style={{\n pointerEvents: \"none\",\n position: \"relative\",\n marginTop: \"auto\",\n }}\n data-testid=\"combat-right-hud-bodypart-section\"\n >\n <BodyPartHealthDisplay\n bodyPartHealth={player.bodyPartHealth}\n playerId={player.id}\n position=\"right\"\n isMobile={isMobile}\n />\n </div>\n )}\n\n {/* Breathing Disruption Indicator - Shows breathing difficulty status */}\n <div\n style={{\n pointerEvents: \"none\",\n position: \"relative\",\n }}\n data-testid=\"combat-right-hud-breathing-section\"\n >\n <BreathingIndicator\n player={player}\n isMobile={isMobile}\n />\n </div>\n </BaseHUDContainer>\n );\n};\n\nexport default CombatRightHUD;\n"],"mappings":";;;;;;;;;;;;;;;;;;;AA6DA,IAAa,kBAAiD,EAC5D,OACA,QACA,UACA,eACA,QACA,YACA,gBACA,qBACI;CACJ,MAAM,SAAS,aACb,OACA,QACA,eACA,SACA,
|
|
1
|
+
{"version":3,"file":"CombatRightHUD.js","names":[],"sources":["../../../../../../src/components/screens/combat/components/hud/CombatRightHUD.tsx"],"sourcesContent":["/**\n * CombatRightHUD - Right side HUD for combat screen (Player 2 / AI)\n *\n * REUSES existing components:\n * - PlayerHUD: Archetype image, name, health/stamina bars\n * - SpeedIndicatorHUD: Movement speed percentage\n * - BodyPartHealthDisplay: Individual body part health bars\n * - DifficultyIndicator: AI difficulty tier display\n *\n * Gaming Layout Best Practice:\n * - Width: Resolution-based 14-18% of screen\n * - Height: 100% minus top/bottom HUD heights\n * - Leaves remaining center region for arena (width varies by resolution)\n *\n * Now uses shared HUD utilities with resolution-based sizing.\n *\n * @korean 전투화면 오른쪽 HUD - 플레이어 2/AI 상태\n */\n\nimport React from \"react\";\nimport { useHUDLayout } from \"../../../../../hooks/useHUDLayout\";\nimport { PlayerState } from \"../../../../../systems\";\nimport type { StanceLaterality } from \"../../../../../systems/trigram/types\";\nimport { BaseHUDContainer } from \"../../../../shared/ui/BaseHUDContainer\";\nimport { BodyPartHealthDisplay } from \"../../../../shared/three/ui/BodyPartHealthDisplay\";\nimport { PlayerHUD } from \"../../../../shared/three/ui/PlayerHUD\";\nimport { SpeedIndicatorHUD } from \"../../../../shared/three/ui/SpeedIndicatorHUD\";\nimport { BreathingIndicator } from \"../../../../shared/three/ui/BreathingIndicator\";\nimport { DifficultyIndicator } from \"./DifficultyIndicator\";\n\nexport interface CombatRightHUDProps {\n /** Screen width for layout calculations */\n readonly width: number;\n /** Screen height for layout calculations */\n readonly height: number;\n /** Whether mobile layout is active */\n readonly isMobile: boolean;\n /** Position scale multiplier for large displays */\n readonly positionScale: number;\n /** Player 2/AI state */\n readonly player: PlayerState;\n /** Player laterality (left/right foot forward) */\n readonly laterality: StanceLaterality;\n /** Current AI difficulty tier (1-5) */\n readonly difficultyTier: number;\n /** Player speed modifiers */\n readonly speedModifiers: {\n finalSpeed: number;\n baseSpeed: number;\n };\n}\n\n/**\n * CombatRightHUD Component\n *\n * Right side of the combat screen containing Player 2/AI stats.\n * Occupies approximately 14–18% of screen width based on resolution breakpoints/interpolation,\n * positioned between top and bottom HUDs.\n * REUSES existing PlayerHUD, SpeedIndicatorHUD, BodyPartHealthDisplay, DifficultyIndicator.\n * Uses shared HUD utilities for consistent, resolution-based layout and styling.\n */\nexport const CombatRightHUD: React.FC<CombatRightHUDProps> = ({\n width,\n height,\n isMobile,\n positionScale,\n player,\n laterality,\n difficultyTier,\n speedModifiers,\n}) => {\n const layout = useHUDLayout(\n width,\n height,\n positionScale,\n 'right',\n 'combat'\n );\n\n return (\n <BaseHUDContainer\n position=\"right\"\n width={layout.hudWidth}\n height={layout.availableHeight}\n topOffset={layout.topOffset}\n padding={layout.padding}\n gap={layout.gap}\n style={{ overflow: \"hidden\" }}\n dataTestId=\"combat-right-hud\"\n >\n {/* Player 2/AI Stats - REUSING PlayerHUD component */}\n <div\n style={{\n pointerEvents: \"none\",\n position: \"relative\",\n }}\n data-testid=\"combat-right-hud-player-section\"\n >\n <PlayerHUD\n player={player}\n position=\"right\"\n isMobile={isMobile}\n laterality={laterality}\n />\n </div>\n\n {/* Difficulty Tier - REUSING DifficultyIndicator component */}\n <div\n style={{\n pointerEvents: \"none\",\n position: \"relative\",\n }}\n data-testid=\"combat-right-hud-difficulty-section\"\n >\n <DifficultyIndicator tier={difficultyTier} isMobile={isMobile} />\n </div>\n\n {/* Speed Indicator - REUSING SpeedIndicatorHUD component */}\n <div\n style={{\n pointerEvents: \"none\",\n position: \"relative\",\n }}\n data-testid=\"combat-right-hud-speed-section\"\n >\n <SpeedIndicatorHUD\n finalSpeed={speedModifiers.finalSpeed}\n baseSpeed={speedModifiers.baseSpeed}\n position=\"right\"\n isMobile={isMobile}\n visible={true}\n />\n </div>\n\n {/* Body Part Health - REUSING BodyPartHealthDisplay component */}\n {player.bodyPartHealth && (\n <div\n style={{\n pointerEvents: \"none\",\n position: \"relative\",\n marginTop: \"auto\",\n }}\n data-testid=\"combat-right-hud-bodypart-section\"\n >\n <BodyPartHealthDisplay\n bodyPartHealth={player.bodyPartHealth}\n playerId={player.id}\n position=\"right\"\n isMobile={isMobile}\n />\n </div>\n )}\n\n {/* Breathing Disruption Indicator - Shows breathing difficulty status */}\n <div\n style={{\n pointerEvents: \"none\",\n position: \"relative\",\n }}\n data-testid=\"combat-right-hud-breathing-section\"\n >\n <BreathingIndicator\n player={player}\n isMobile={isMobile}\n />\n </div>\n </BaseHUDContainer>\n );\n};\n\nexport default CombatRightHUD;\n"],"mappings":";;;;;;;;;;;;;;;;;;;AA6DA,IAAa,kBAAiD,EAC5D,OACA,QACA,UACA,eACA,QACA,YACA,gBACA,qBACI;CACJ,MAAM,SAAS,aACb,OACA,QACA,eACA,SACA,QACF;CAEA,OACE,qBAAC,kBAAD;EACE,UAAS;EACT,OAAO,OAAO;EACd,QAAQ,OAAO;EACf,WAAW,OAAO;EAClB,SAAS,OAAO;EAChB,KAAK,OAAO;EACZ,OAAO,EAAE,UAAU,SAAS;EAC5B,YAAW;YARb;GAWE,oBAAC,OAAD;IACE,OAAO;KACL,eAAe;KACf,UAAU;IACZ;IACA,eAAY;cAEZ,oBAAC,WAAD;KACU;KACR,UAAS;KACC;KACE;IACb,CAAA;GACE,CAAA;GAGL,oBAAC,OAAD;IACE,OAAO;KACL,eAAe;KACf,UAAU;IACZ;IACA,eAAY;cAEZ,oBAAC,qBAAD;KAAqB,MAAM;KAA0B;IAAW,CAAA;GAC7D,CAAA;GAGL,oBAAC,OAAD;IACE,OAAO;KACL,eAAe;KACf,UAAU;IACZ;IACA,eAAY;cAEZ,oBAAC,mBAAD;KACE,YAAY,eAAe;KAC3B,WAAW,eAAe;KAC1B,UAAS;KACC;KACV,SAAS;IACV,CAAA;GACE,CAAA;GAGJ,OAAO,kBACN,oBAAC,OAAD;IACE,OAAO;KACL,eAAe;KACf,UAAU;KACV,WAAW;IACb;IACA,eAAY;cAEZ,oBAAC,uBAAD;KACE,gBAAgB,OAAO;KACvB,UAAU,OAAO;KACjB,UAAS;KACC;IACX,CAAA;GACE,CAAA;GAIP,oBAAC,OAAD;IACE,OAAO;KACL,eAAe;KACf,UAAU;IACZ;IACA,eAAY;cAEZ,oBAAC,oBAAD;KACU;KACE;IACX,CAAA;GACE,CAAA;EACW;;AAEtB"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"CombatTopHUD.d.ts","sourceRoot":"","sources":["../../../../../../src/components/screens/combat/components/hud/CombatTopHUD.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAEH,OAAO,KAAK,MAAM,OAAO,CAAC;AAC1B,OAAO,EAAE,oBAAoB,EAAE,MAAM,qCAAqC,CAAC;
|
|
1
|
+
{"version":3,"file":"CombatTopHUD.d.ts","sourceRoot":"","sources":["../../../../../../src/components/screens/combat/components/hud/CombatTopHUD.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAEH,OAAO,KAAK,MAAM,OAAO,CAAC;AAC1B,OAAO,EAAE,oBAAoB,EAAE,MAAM,qCAAqC,CAAC;AAqB3E,MAAM,WAAW,iBAAiB;IAChC,2CAA2C;IAC3C,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,4CAA4C;IAC5C,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,+DAA+D;IAC/D,QAAQ,CAAC,QAAQ,CAAC,EAAE,OAAO,CAAC;IAC5B,mDAAmD;IACnD,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAC;IAC/B,2BAA2B;IAC3B,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC;IAC9B,0BAA0B;IAC1B,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,2CAA2C;IAC3C,QAAQ,CAAC,UAAU,EAAE,oBAAoB,CAAC;IAC1C,4BAA4B;IAC5B,QAAQ,CAAC,SAAS,EAAE,OAAO,CAAC;IAC5B,oCAAoC;IACpC,QAAQ,CAAC,cAAc,EAAE,MAAM,IAAI,CAAC;IACpC,iCAAiC;IACjC,QAAQ,CAAC,QAAQ,EAAE,OAAO,CAAC;CAC5B;AAED;;;;;GAKG;AACH,eAAO,MAAM,YAAY,EAAE,KAAK,CAAC,EAAE,CAAC,iBAAiB,CAwIpD,CAAC;AAEF,eAAe,YAAY,CAAC"}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { BORDERS, FONT_SIZE_MULTIPLIERS, GRADIENTS, HIERARCHY, HUD_STYLE, LAYOUT_MULTIPLIERS, TYPOGRAPHY, TYPOGRAPHY_NUMERIC } from "../../../../../types/constants/designSystem.js";
|
|
2
|
+
import { COMBAT_TOP_HUD_HEIGHT_PERCENT } from "../../../../../types/constants/layout.js";
|
|
2
3
|
import { getHUDHeight, getResponsiveFontSize, getResponsivePadding, shouldShowMobileControls } from "../../../../../utils/responsiveLayout.js";
|
|
3
4
|
import CombatTimer from "../../../../shared/ui/CombatTimer.js";
|
|
4
5
|
import CombatReturnToMenuButton from "../controls/CombatButtons.js";
|
|
@@ -28,7 +29,7 @@ import { jsx, jsxs } from "react/jsx-runtime";
|
|
|
28
29
|
var CombatTopHUD = ({ width, height, isMobile = false, positionScale, currentRound, totalRounds, timerState, showTimer, onReturnToMenu, isPaused: _isPaused }) => {
|
|
29
30
|
const showMobileControls = shouldShowMobileControls(width, isMobile);
|
|
30
31
|
const layout = React.useMemo(() => {
|
|
31
|
-
const hudHeight = getHUDHeight(height,
|
|
32
|
+
const hudHeight = getHUDHeight(height, COMBAT_TOP_HUD_HEIGHT_PERCENT) * positionScale;
|
|
32
33
|
const padding = getResponsivePadding(width) * positionScale;
|
|
33
34
|
const gap = padding * LAYOUT_MULTIPLIERS.gapToPadding;
|
|
34
35
|
const baseFontSize = getResponsiveFontSize(width) * positionScale;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"CombatTopHUD.js","names":[],"sources":["../../../../../../src/components/screens/combat/components/hud/CombatTopHUD.tsx"],"sourcesContent":["/**\n * CombatTopHUD - Slim top bar for combat screen\n *\n * Gaming Best Practice - Minimal Top Bar:\n * - Round indicator (left)\n * - Timer (center)\n * - Return to Menu button (right)\n *\n * Layout:\n * - Width: 100% of screen\n * - Height: Resolution-based ~6% of screen height (40-80px)\n *\n * @korean 전투화면 상단 바 - 라운드, 타이머, 메뉴 복귀\n */\n\nimport React from \"react\";\nimport { UseCombatTimerReturn } from \"../../../../../hooks/useCombatTimer\";\nimport {\n BORDERS,\n FONT_SIZE_MULTIPLIERS,\n GRADIENTS,\n HIERARCHY,\n HUD_STYLE,\n LAYOUT_MULTIPLIERS,\n TYPOGRAPHY,\n TYPOGRAPHY_NUMERIC,\n} from \"../../../../../types/constants/designSystem\";\nimport {\n getHUDHeight,\n getResponsiveFontSize,\n getResponsivePadding,\n shouldShowMobileControls,\n} from \"../../../../../utils/responsiveLayout\";\nimport { CombatTimer } from \"../../../../shared/ui/CombatTimer\";\nimport { CombatReturnToMenuButton } from \"../controls/CombatButtons\";\n\nexport interface CombatTopHUDProps {\n /** Screen width for layout calculations */\n readonly width: number;\n /** Screen height for layout calculations */\n readonly height: number;\n /** Whether mobile controls should be shown (NOT for sizing) */\n readonly isMobile?: boolean;\n /** Position scale multiplier for large displays */\n readonly positionScale: number;\n /** Current round number */\n readonly currentRound: number;\n /** Max rounds in match */\n readonly totalRounds: number;\n /** Timer state from useCombatTimer hook */\n readonly timerState: UseCombatTimerReturn;\n /** Whether to show timer */\n readonly showTimer: boolean;\n /** Handler for returning to menu */\n readonly onReturnToMenu: () => void;\n /** Whether the game is paused */\n readonly isPaused: boolean;\n}\n\n/**\n * CombatTopHUD Component\n *\n * Slim top bar containing round info, timer, and return to menu button.\n * Uses resolution-based sizing for all dimensions.\n */\nexport const CombatTopHUD: React.FC<CombatTopHUDProps> = ({\n width,\n height,\n isMobile = false,\n positionScale,\n currentRound,\n totalRounds,\n timerState,\n showTimer,\n onReturnToMenu,\n isPaused: _isPaused, // Reserved for future pause indicator\n}) => {\n const showMobileControls = shouldShowMobileControls(width, isMobile);\n\n const layout = React.useMemo(() => {\n const hudHeight = getHUDHeight(height,
|
|
1
|
+
{"version":3,"file":"CombatTopHUD.js","names":[],"sources":["../../../../../../src/components/screens/combat/components/hud/CombatTopHUD.tsx"],"sourcesContent":["/**\n * CombatTopHUD - Slim top bar for combat screen\n *\n * Gaming Best Practice - Minimal Top Bar:\n * - Round indicator (left)\n * - Timer (center)\n * - Return to Menu button (right)\n *\n * Layout:\n * - Width: 100% of screen\n * - Height: Resolution-based ~6% of screen height (40-80px)\n *\n * @korean 전투화면 상단 바 - 라운드, 타이머, 메뉴 복귀\n */\n\nimport React from \"react\";\nimport { UseCombatTimerReturn } from \"../../../../../hooks/useCombatTimer\";\nimport { COMBAT_TOP_HUD_HEIGHT_PERCENT } from \"../../../../../types/constants/layout\";\nimport {\n BORDERS,\n FONT_SIZE_MULTIPLIERS,\n GRADIENTS,\n HIERARCHY,\n HUD_STYLE,\n LAYOUT_MULTIPLIERS,\n TYPOGRAPHY,\n TYPOGRAPHY_NUMERIC,\n} from \"../../../../../types/constants/designSystem\";\nimport {\n getHUDHeight,\n getResponsiveFontSize,\n getResponsivePadding,\n shouldShowMobileControls,\n} from \"../../../../../utils/responsiveLayout\";\nimport { CombatTimer } from \"../../../../shared/ui/CombatTimer\";\nimport { CombatReturnToMenuButton } from \"../controls/CombatButtons\";\n\nexport interface CombatTopHUDProps {\n /** Screen width for layout calculations */\n readonly width: number;\n /** Screen height for layout calculations */\n readonly height: number;\n /** Whether mobile controls should be shown (NOT for sizing) */\n readonly isMobile?: boolean;\n /** Position scale multiplier for large displays */\n readonly positionScale: number;\n /** Current round number */\n readonly currentRound: number;\n /** Max rounds in match */\n readonly totalRounds: number;\n /** Timer state from useCombatTimer hook */\n readonly timerState: UseCombatTimerReturn;\n /** Whether to show timer */\n readonly showTimer: boolean;\n /** Handler for returning to menu */\n readonly onReturnToMenu: () => void;\n /** Whether the game is paused */\n readonly isPaused: boolean;\n}\n\n/**\n * CombatTopHUD Component\n *\n * Slim top bar containing round info, timer, and return to menu button.\n * Uses resolution-based sizing for all dimensions.\n */\nexport const CombatTopHUD: React.FC<CombatTopHUDProps> = ({\n width,\n height,\n isMobile = false,\n positionScale,\n currentRound,\n totalRounds,\n timerState,\n showTimer,\n onReturnToMenu,\n isPaused: _isPaused, // Reserved for future pause indicator\n}) => {\n const showMobileControls = shouldShowMobileControls(width, isMobile);\n\n const layout = React.useMemo(() => {\n const hudHeight = getHUDHeight(height, COMBAT_TOP_HUD_HEIGHT_PERCENT) * positionScale;\n\n const padding = getResponsivePadding(width) * positionScale;\n \n const gap = padding * LAYOUT_MULTIPLIERS.gapToPadding;\n \n const baseFontSize = getResponsiveFontSize(width) * positionScale;\n const fontSize = Math.max(TYPOGRAPHY_NUMERIC.bodySmall, baseFontSize * FONT_SIZE_MULTIPLIERS.bodySmall);\n const titleSize = Math.max(TYPOGRAPHY_NUMERIC.body, baseFontSize * FONT_SIZE_MULTIPLIERS.titleLarge);\n\n\n return {\n hudHeight,\n padding,\n gap,\n fontSize,\n titleSize,\n hudWidth: width,\n };\n }, [width, height, positionScale]);\n\n return (\n <div\n style={{\n position: \"absolute\",\n top: 0,\n left: 0,\n width: \"100%\",\n height: `${layout.hudHeight}px`,\n display: \"flex\",\n flexDirection: \"row\",\n justifyContent: \"space-between\",\n alignItems: \"center\",\n padding: `${layout.padding}px`,\n pointerEvents: \"none\",\n boxSizing: \"border-box\",\n borderBottom: BORDERS.default,\n background: GRADIENTS.vertical(0.9),\n backdropFilter: HUD_STYLE.backdropFilter,\n }}\n data-testid=\"combat-top-hud\"\n >\n {/* Left Section - Round Info */}\n <div\n style={{\n display: \"flex\",\n flexDirection: \"column\",\n gap: \"2px\",\n pointerEvents: \"none\",\n alignItems: \"flex-start\",\n }}\n data-testid=\"combat-top-hud-left-section\"\n >\n {/* Title */}\n <div\n style={{\n fontSize: `${layout.titleSize}px`,\n fontWeight: 600,\n fontFamily: TYPOGRAPHY.heading2.fontFamily,\n color: HIERARCHY.gold.color,\n textShadow: HUD_STYLE.accentGlow,\n }}\n >\n 전투 | Combat\n </div>\n\n {/* Round indicator */}\n <div\n style={{\n display: \"flex\",\n gap: `${layout.gap}px`,\n alignItems: \"center\",\n fontSize: `${layout.fontSize}px`,\n fontFamily: TYPOGRAPHY.body.fontFamily,\n color: HIERARCHY.accent.color,\n }}\n >\n <span>\n 라운드 {currentRound}/{totalRounds}\n </span>\n </div>\n </div>\n\n {/* Center Section - Timer */}\n <div\n style={{\n display: \"flex\",\n flexDirection: \"column\",\n alignItems: \"center\",\n gap: \"2px\",\n }}\n data-testid=\"combat-top-hud-center-section\"\n >\n {showTimer && (\n <CombatTimer\n formattedTime={timerState.formattedTime}\n warningLevel={timerState.warningLevel}\n isTimeUp={timerState.isTimeUp}\n isMobile={showMobileControls}\n style={{ position: \"relative\", top: 0 }}\n />\n )}\n </div>\n\n {/* Right Section - Return to Menu */}\n <div\n style={{\n display: \"flex\",\n flexDirection: \"row\",\n alignItems: \"center\",\n pointerEvents: \"all\",\n }}\n data-testid=\"combat-top-hud-right-section\"\n >\n <CombatReturnToMenuButton\n onClick={onReturnToMenu}\n isMobile={showMobileControls}\n />\n </div>\n </div>\n );\n};\n\nexport default CombatTopHUD;\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;AAkEA,IAAa,gBAA6C,EACxD,OACA,QACA,WAAW,OACX,eACA,cACA,aACA,YACA,WACA,gBACA,UAAU,gBACN;CACJ,MAAM,qBAAqB,yBAAyB,OAAO,QAAQ;CAEnE,MAAM,SAAS,MAAM,cAAc;EACjC,MAAM,YAAY,aAAa,QAAQ,6BAA6B,IAAI;EAExE,MAAM,UAAU,qBAAqB,KAAK,IAAI;EAE9C,MAAM,MAAM,UAAU,mBAAmB;EAEzC,MAAM,eAAe,sBAAsB,KAAK,IAAI;EAKpD,OAAO;GACL;GACA;GACA;GACA,UARe,KAAK,IAAI,mBAAmB,WAAW,eAAe,sBAAsB,SAQ3F;GACA,WARgB,KAAK,IAAI,mBAAmB,MAAM,eAAe,sBAAsB,UAQvF;GACA,UAAU;EACZ;CACF,GAAG;EAAC;EAAO;EAAQ;CAAa,CAAC;CAEjC,OACE,qBAAC,OAAD;EACE,OAAO;GACL,UAAU;GACV,KAAK;GACL,MAAM;GACN,OAAO;GACP,QAAQ,GAAG,OAAO,UAAU;GAC5B,SAAS;GACT,eAAe;GACf,gBAAgB;GAChB,YAAY;GACZ,SAAS,GAAG,OAAO,QAAQ;GAC3B,eAAe;GACf,WAAW;GACX,cAAc,QAAQ;GACtB,YAAY,UAAU,SAAS,EAAG;GAClC,gBAAgB,UAAU;EAC5B;EACA,eAAY;YAlBd;GAqBE,qBAAC,OAAD;IACE,OAAO;KACL,SAAS;KACT,eAAe;KACf,KAAK;KACL,eAAe;KACf,YAAY;IACd;IACA,eAAY;cARd,CAWE,oBAAC,OAAD;KACE,OAAO;MACL,UAAU,GAAG,OAAO,UAAU;MAC9B,YAAY;MACZ,YAAY,WAAW,SAAS;MAChC,OAAO,UAAU,KAAK;MACtB,YAAY,UAAU;KACxB;eACD;IAEI,CAAA,GAGL,oBAAC,OAAD;KACE,OAAO;MACL,SAAS;MACT,KAAK,GAAG,OAAO,IAAI;MACnB,YAAY;MACZ,UAAU,GAAG,OAAO,SAAS;MAC7B,YAAY,WAAW,KAAK;MAC5B,OAAO,UAAU,OAAO;KAC1B;eAEA,qBAAC,QAAD,EAAA,UAAA;MAAM;MACC;MAAa;MAAE;KAChB,EAAA,CAAA;IACH,CAAA,CACF;;GAGL,oBAAC,OAAD;IACE,OAAO;KACL,SAAS;KACT,eAAe;KACf,YAAY;KACZ,KAAK;IACP;IACA,eAAY;cAEX,aACC,oBAAC,aAAD;KACE,eAAe,WAAW;KAC1B,cAAc,WAAW;KACzB,UAAU,WAAW;KACrB,UAAU;KACV,OAAO;MAAE,UAAU;MAAY,KAAK;KAAE;IACvC,CAAA;GAEA,CAAA;GAGL,oBAAC,OAAD;IACE,OAAO;KACL,SAAS;KACT,eAAe;KACf,YAAY;KACZ,eAAe;IACjB;IACA,eAAY;cAEZ,oBAAC,0BAAD;KACE,SAAS;KACT,UAAU;IACX,CAAA;GACE,CAAA;EACF;;AAET"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"DifficultyIndicator.js","names":[],"sources":["../../../../../../src/components/screens/combat/components/hud/DifficultyIndicator.tsx"],"sourcesContent":["/**\n * DifficultyIndicator Component - AI Difficulty Tier Display\n *\n * Shows the current AI difficulty tier with Korean-English bilingual text\n * and color-coded visual feedback based on tier level.\n *\n * **Korean Philosophy (난이도 표시)**:\n * Provides transparent feedback to player about AI challenge level,\n * helping them understand their skill progression.\n */\n\nimport React, { useMemo } from \"react\";\nimport { DifficultyTier } from \"../../../../../systems/ai\";\nimport {\n hexColorToCSS,\n hexToRgbaString,\n} from \"../../../../../utils/colorUtils\";\nimport { useKoreanTheme } from \"../../../../shared/base/useKoreanTheme\";\n\nexport interface DifficultyIndicatorProps {\n /** Current difficulty tier (1-5) */\n readonly tier: DifficultyTier;\n /** Whether to use mobile-optimized sizing */\n readonly isMobile: boolean;\n}\n\n/**\n * Get tier display name with Korean and English\n */\nfunction getTierName(tier: DifficultyTier): {\n korean: string;\n english: string;\n} {\n switch (tier) {\n case DifficultyTier.BEGINNER:\n return { korean: \"초보\", english: \"Beginner\" };\n case DifficultyTier.NOVICE:\n return { korean: \"입문\", english: \"Novice\" };\n case DifficultyTier.INTERMEDIATE:\n return { korean: \"중급\", english: \"Intermediate\" };\n case DifficultyTier.ADVANCED:\n return { korean: \"고급\", english: \"Advanced\" };\n case DifficultyTier.EXPERT:\n return { korean: \"전문\", english: \"Expert\" };\n default:\n return { korean: \"중급\", english: \"Intermediate\" };\n }\n}\n\n/**\n * Get numeric color for difficulty tier using theme colors\n * Maps tiers to existing color scheme for consistency\n */\nfunction getTierColorValue(\n tier: DifficultyTier,\n theme: ReturnType<typeof useKoreanTheme>,\n): number {\n switch (tier) {\n case DifficultyTier.BEGINNER:\n return theme.colors.POSITIVE_GREEN; // Green - Easy\n case DifficultyTier.NOVICE:\n return theme.colors.ACCENT_GREEN; // Light Green\n case DifficultyTier.INTERMEDIATE:\n return theme.colors.ACCENT_GOLD; // Gold - Medium\n case DifficultyTier.ADVANCED:\n return theme.colors.SECONDARY_ORANGE; // Orange\n case DifficultyTier.EXPERT:\n return theme.colors.NEGATIVE_RED; // Red - Hard\n default:\n return theme.colors.ACCENT_GOLD; // Default to medium\n }\n}\n\n/**\n * DifficultyIndicator - Visual feedback for current AI difficulty tier\n *\n * Uses relative positioning for embedding in container HUDs\n */\nexport const DifficultyIndicator: React.FC<DifficultyIndicatorProps> = ({\n tier,\n isMobile,\n}) => {\n const theme = useKoreanTheme({ variant: \"primary\", size: \"small\", isMobile });\n const tierName = getTierName(tier);\n const tierColorValue = getTierColorValue(tier, theme);\n\n const tierColor = useMemo(\n () => hexColorToCSS(tierColorValue),\n [tierColorValue],\n );\n const tierBackground = useMemo(\n () => hexToRgbaString(tierColorValue, 0.13),\n [tierColorValue],\n );\n const tierBoxShadow = useMemo(\n () => hexToRgbaString(tierColorValue, 0.27),\n [tierColorValue],\n );\n\n const fontSize = isMobile ? 11 : 13;\n const padding = isMobile ? \"6px 10px\" : \"8px 12px\";\n\n return (\n <div\n data-testid=\"difficulty-indicator\"\n style={{\n position: \"relative\",\n padding,\n background: tierBackground, // 13% opacity background\n border: `2px solid ${tierColor}`,\n borderRadius: \"4px\",\n fontFamily: theme.fontFamily.KOREAN,\n fontSize: `${fontSize}px`,\n color: tierColor,\n textShadow: \"0 0 4px rgba(0,0,0,0.8)\",\n pointerEvents: \"none\",\n display: \"flex\",\n flexDirection: \"column\",\n alignItems: \"center\",\n gap: \"2px\",\n transition: \"all 0.3s ease-in-out\", // Smooth color/border transitions\n boxShadow: `0 0 8px ${tierBoxShadow}`, // Subtle glow effect (27% opacity)\n width: \"100%\",\n boxSizing: \"border-box\" as const,\n }}\n >\n <div\n data-testid=\"difficulty-label\"\n style={{\n fontSize: isMobile ? \"9px\" : \"10px\",\n opacity: 0.8,\n letterSpacing: \"0.5px\",\n }}\n >\n AI Difficulty\n </div>\n <div\n data-testid=\"difficulty-tier\"\n style={{\n fontWeight: \"bold\",\n whiteSpace: \"nowrap\",\n letterSpacing: \"0.5px\",\n }}\n >\n {tierName.korean} | {tierName.english}\n </div>\n </div>\n );\n};\n"],"mappings":";;;;;;;;;;;;;;;;;;;AA6BA,SAAS,YAAY,MAGnB;CACA,QAAQ,MAAR;EACE,KAAK,eAAe,UAClB,OAAO;GAAE,QAAQ;GAAM,SAAS;
|
|
1
|
+
{"version":3,"file":"DifficultyIndicator.js","names":[],"sources":["../../../../../../src/components/screens/combat/components/hud/DifficultyIndicator.tsx"],"sourcesContent":["/**\n * DifficultyIndicator Component - AI Difficulty Tier Display\n *\n * Shows the current AI difficulty tier with Korean-English bilingual text\n * and color-coded visual feedback based on tier level.\n *\n * **Korean Philosophy (난이도 표시)**:\n * Provides transparent feedback to player about AI challenge level,\n * helping them understand their skill progression.\n */\n\nimport React, { useMemo } from \"react\";\nimport { DifficultyTier } from \"../../../../../systems/ai\";\nimport {\n hexColorToCSS,\n hexToRgbaString,\n} from \"../../../../../utils/colorUtils\";\nimport { useKoreanTheme } from \"../../../../shared/base/useKoreanTheme\";\n\nexport interface DifficultyIndicatorProps {\n /** Current difficulty tier (1-5) */\n readonly tier: DifficultyTier;\n /** Whether to use mobile-optimized sizing */\n readonly isMobile: boolean;\n}\n\n/**\n * Get tier display name with Korean and English\n */\nfunction getTierName(tier: DifficultyTier): {\n korean: string;\n english: string;\n} {\n switch (tier) {\n case DifficultyTier.BEGINNER:\n return { korean: \"초보\", english: \"Beginner\" };\n case DifficultyTier.NOVICE:\n return { korean: \"입문\", english: \"Novice\" };\n case DifficultyTier.INTERMEDIATE:\n return { korean: \"중급\", english: \"Intermediate\" };\n case DifficultyTier.ADVANCED:\n return { korean: \"고급\", english: \"Advanced\" };\n case DifficultyTier.EXPERT:\n return { korean: \"전문\", english: \"Expert\" };\n default:\n return { korean: \"중급\", english: \"Intermediate\" };\n }\n}\n\n/**\n * Get numeric color for difficulty tier using theme colors\n * Maps tiers to existing color scheme for consistency\n */\nfunction getTierColorValue(\n tier: DifficultyTier,\n theme: ReturnType<typeof useKoreanTheme>,\n): number {\n switch (tier) {\n case DifficultyTier.BEGINNER:\n return theme.colors.POSITIVE_GREEN; // Green - Easy\n case DifficultyTier.NOVICE:\n return theme.colors.ACCENT_GREEN; // Light Green\n case DifficultyTier.INTERMEDIATE:\n return theme.colors.ACCENT_GOLD; // Gold - Medium\n case DifficultyTier.ADVANCED:\n return theme.colors.SECONDARY_ORANGE; // Orange\n case DifficultyTier.EXPERT:\n return theme.colors.NEGATIVE_RED; // Red - Hard\n default:\n return theme.colors.ACCENT_GOLD; // Default to medium\n }\n}\n\n/**\n * DifficultyIndicator - Visual feedback for current AI difficulty tier\n *\n * Uses relative positioning for embedding in container HUDs\n */\nexport const DifficultyIndicator: React.FC<DifficultyIndicatorProps> = ({\n tier,\n isMobile,\n}) => {\n const theme = useKoreanTheme({ variant: \"primary\", size: \"small\", isMobile });\n const tierName = getTierName(tier);\n const tierColorValue = getTierColorValue(tier, theme);\n\n const tierColor = useMemo(\n () => hexColorToCSS(tierColorValue),\n [tierColorValue],\n );\n const tierBackground = useMemo(\n () => hexToRgbaString(tierColorValue, 0.13),\n [tierColorValue],\n );\n const tierBoxShadow = useMemo(\n () => hexToRgbaString(tierColorValue, 0.27),\n [tierColorValue],\n );\n\n const fontSize = isMobile ? 11 : 13;\n const padding = isMobile ? \"6px 10px\" : \"8px 12px\";\n\n return (\n <div\n data-testid=\"difficulty-indicator\"\n style={{\n position: \"relative\",\n padding,\n background: tierBackground, // 13% opacity background\n border: `2px solid ${tierColor}`,\n borderRadius: \"4px\",\n fontFamily: theme.fontFamily.KOREAN,\n fontSize: `${fontSize}px`,\n color: tierColor,\n textShadow: \"0 0 4px rgba(0,0,0,0.8)\",\n pointerEvents: \"none\",\n display: \"flex\",\n flexDirection: \"column\",\n alignItems: \"center\",\n gap: \"2px\",\n transition: \"all 0.3s ease-in-out\", // Smooth color/border transitions\n boxShadow: `0 0 8px ${tierBoxShadow}`, // Subtle glow effect (27% opacity)\n width: \"100%\",\n boxSizing: \"border-box\" as const,\n }}\n >\n <div\n data-testid=\"difficulty-label\"\n style={{\n fontSize: isMobile ? \"9px\" : \"10px\",\n opacity: 0.8,\n letterSpacing: \"0.5px\",\n }}\n >\n AI Difficulty\n </div>\n <div\n data-testid=\"difficulty-tier\"\n style={{\n fontWeight: \"bold\",\n whiteSpace: \"nowrap\",\n letterSpacing: \"0.5px\",\n }}\n >\n {tierName.korean} | {tierName.english}\n </div>\n </div>\n );\n};\n"],"mappings":";;;;;;;;;;;;;;;;;;;AA6BA,SAAS,YAAY,MAGnB;CACA,QAAQ,MAAR;EACE,KAAK,eAAe,UAClB,OAAO;GAAE,QAAQ;GAAM,SAAS;EAAW;EAC7C,KAAK,eAAe,QAClB,OAAO;GAAE,QAAQ;GAAM,SAAS;EAAS;EAC3C,KAAK,eAAe,cAClB,OAAO;GAAE,QAAQ;GAAM,SAAS;EAAe;EACjD,KAAK,eAAe,UAClB,OAAO;GAAE,QAAQ;GAAM,SAAS;EAAW;EAC7C,KAAK,eAAe,QAClB,OAAO;GAAE,QAAQ;GAAM,SAAS;EAAS;EAC3C,SACE,OAAO;GAAE,QAAQ;GAAM,SAAS;EAAe;CACnD;AACF;;;;;AAMA,SAAS,kBACP,MACA,OACQ;CACR,QAAQ,MAAR;EACE,KAAK,eAAe,UAClB,OAAO,MAAM,OAAO;EACtB,KAAK,eAAe,QAClB,OAAO,MAAM,OAAO;EACtB,KAAK,eAAe,cAClB,OAAO,MAAM,OAAO;EACtB,KAAK,eAAe,UAClB,OAAO,MAAM,OAAO;EACtB,KAAK,eAAe,QAClB,OAAO,MAAM,OAAO;EACtB,SACE,OAAO,MAAM,OAAO;CACxB;AACF;;;;;;AAOA,IAAa,uBAA2D,EACtE,MACA,eACI;CACJ,MAAM,QAAQ,eAAe;EAAE,SAAS;EAAW,MAAM;EAAS;CAAS,CAAC;CAC5E,MAAM,WAAW,YAAY,IAAI;CACjC,MAAM,iBAAiB,kBAAkB,MAAM,KAAK;CAEpD,MAAM,YAAY,cACV,cAAc,cAAc,GAClC,CAAC,cAAc,CACjB;CACA,MAAM,iBAAiB,cACf,gBAAgB,gBAAgB,GAAI,GAC1C,CAAC,cAAc,CACjB;CACA,MAAM,gBAAgB,cACd,gBAAgB,gBAAgB,GAAI,GAC1C,CAAC,cAAc,CACjB;CAEA,MAAM,WAAW,WAAW,KAAK;CAGjC,OACE,qBAAC,OAAD;EACE,eAAY;EACZ,OAAO;GACL,UAAU;GACV,SAPU,WAAW,aAAa;GAQlC,YAAY;GACZ,QAAQ,aAAa;GACrB,cAAc;GACd,YAAY,MAAM,WAAW;GAC7B,UAAU,GAAG,SAAS;GACtB,OAAO;GACP,YAAY;GACZ,eAAe;GACf,SAAS;GACT,eAAe;GACf,YAAY;GACZ,KAAK;GACL,YAAY;GACZ,WAAW,WAAW;GACtB,OAAO;GACP,WAAW;EACb;YArBF,CAuBE,oBAAC,OAAD;GACE,eAAY;GACZ,OAAO;IACL,UAAU,WAAW,QAAQ;IAC7B,SAAS;IACT,eAAe;GACjB;aACD;EAEI,CAAA,GACL,qBAAC,OAAD;GACE,eAAY;GACZ,OAAO;IACL,YAAY;IACZ,YAAY;IACZ,eAAe;GACjB;aANF;IAQG,SAAS;IAAO;IAAI,SAAS;GAC3B;IACF;;AAET"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"FPSMonitor.js","names":[],"sources":["../../../../../../src/components/screens/combat/components/hud/FPSMonitor.tsx"],"sourcesContent":["/**\n * FPSMonitor - Real-time performance monitoring component\n *\n * Displays current FPS (frames per second) and provides performance warnings\n * when frame rate drops below target thresholds. Helps verify 60fps target\n * and identify performance bottlenecks during combat.\n *\n * @module components/combat/components/FPSMonitor\n * @category Performance Monitoring\n * @korean FPS모니터\n */\n\nimport { useFrame } from \"@react-three/fiber\";\nimport { Html } from \"@react-three/drei\";\nimport React, { useRef, useState, useCallback, useEffect, useMemo } from \"react\";\nimport { useKoreanTheme } from \"../../../../shared/base/useKoreanTheme\";\n\nexport interface FPSMonitorProps {\n /** Whether to show the FPS monitor */\n readonly enabled?: boolean;\n /** FPS threshold for warning (default: 50) */\n readonly warningThreshold?: number;\n /** FPS threshold for critical warning (default: 30) */\n readonly criticalThreshold?: number;\n /** Callback when FPS drops below threshold */\n readonly onFPSDrop?: (fps: number) => void;\n /** Position from top in pixels (default: 10) */\n readonly top?: number;\n /** Position from right in pixels (default: 10) */\n readonly right?: number;\n}\n\n/**\n * FPSMonitor Component\n *\n * Monitors and displays real-time FPS during combat. Uses Three.js useFrame\n * to calculate frames per second and provides visual feedback:\n * - Green: 60+ fps (excellent)\n * - Yellow: 50-59 fps (warning)\n * - Orange: 30-49 fps (poor)\n * - Red: <30 fps (critical)\n *\n * @example\n * ```tsx\n * <FPSMonitor\n * enabled={showPerformanceStats}\n * warningThreshold={50}\n * onFPSDrop={(fps) => console.warn(`Performance drop: ${fps} fps`)}\n * />\n * ```\n */\nexport const FPSMonitor: React.FC<FPSMonitorProps> = ({\n enabled = true,\n warningThreshold = 50,\n criticalThreshold = 30,\n onFPSDrop,\n top = 10,\n right = 10,\n}) => {\n const theme = useKoreanTheme({ variant: \"primary\", size: \"small\", isMobile: false });\n const [fps, setFps] = useState(60);\n const frameCountRef = useRef(0);\n const lastTimeRef = useRef(0);\n const lastWarningTimeRef = useRef(0);\n const initializedRef = useRef(false);\n \n useEffect(() => {\n lastTimeRef.current = performance.now();\n initializedRef.current = true;\n }, []);\n\n const updateFPS = useCallback(() => {\n if (!initializedRef.current) return;\n \n frameCountRef.current++;\n const currentTime = performance.now();\n const deltaTime = currentTime - lastTimeRef.current;\n\n if (deltaTime >= 1000) {\n const currentFps = Math.round((frameCountRef.current * 1000) / deltaTime);\n setFps(currentFps);\n \n if (onFPSDrop && currentFps < warningThreshold) {\n const timeSinceLastWarning = currentTime - lastWarningTimeRef.current;\n if (timeSinceLastWarning >= 3000) {\n onFPSDrop(currentFps);\n lastWarningTimeRef.current = currentTime;\n }\n }\n\n frameCountRef.current = 0;\n lastTimeRef.current = currentTime;\n }\n }, [onFPSDrop, warningThreshold]);\n\n useFrame(() => {\n if (enabled) {\n updateFPS();\n }\n });\n\n const colorHex = useMemo(() => {\n let color: number;\n if (fps >= 60) {\n color = theme.colors.ACCENT_GOLD; // Excellent\n } else if (fps >= warningThreshold) {\n color = 0xffff00; // Warning (yellow)\n } else if (fps >= criticalThreshold) {\n color = 0xff8800; // Poor (orange)\n } else {\n color = theme.colors.PRIMARY_RED; // Critical (red)\n }\n return `#${color.toString(16).padStart(6, \"0\")}`;\n }, [fps, warningThreshold, criticalThreshold, theme.colors.ACCENT_GOLD, theme.colors.PRIMARY_RED]);\n\n const getStatus = () => {\n if (fps >= 60) return \"우수 | Excellent\";\n if (fps >= warningThreshold) return \"경고 | Warning\";\n if (fps >= criticalThreshold) return \"저하 | Poor\";\n return \"심각 | Critical\";\n };\n\n if (!enabled) {\n return null;\n }\n\n return (\n <Html fullscreen>\n <div\n data-testid=\"fps-monitor\"\n style={{\n position: \"absolute\",\n top: `${top}px`,\n right: `${right}px`,\n padding: \"8px 12px\",\n background: \"rgba(0, 0, 0, 0.8)\",\n border: `2px solid ${colorHex}`,\n borderRadius: \"4px\",\n fontFamily: theme.fontFamily.KOREAN,\n fontSize: \"12px\",\n color: colorHex,\n pointerEvents: \"none\",\n zIndex: 9999,\n }}\n >\n <div style={{ fontWeight: \"bold\", marginBottom: \"2px\" }}>\n FPS: {fps}\n </div>\n <div style={{ fontSize: \"10px\", opacity: 0.9 }}>\n {getStatus()}\n </div>\n </div>\n </Html>\n );\n};\n\nexport default FPSMonitor;\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAmDA,IAAa,cAAyC,EACpD,UAAU,MACV,mBAAmB,IACnB,oBAAoB,IACpB,WACA,MAAM,IACN,QAAQ,SACJ;CACJ,MAAM,QAAQ,eAAe;EAAE,SAAS;EAAW,MAAM;EAAS,UAAU;
|
|
1
|
+
{"version":3,"file":"FPSMonitor.js","names":[],"sources":["../../../../../../src/components/screens/combat/components/hud/FPSMonitor.tsx"],"sourcesContent":["/**\n * FPSMonitor - Real-time performance monitoring component\n *\n * Displays current FPS (frames per second) and provides performance warnings\n * when frame rate drops below target thresholds. Helps verify 60fps target\n * and identify performance bottlenecks during combat.\n *\n * @module components/combat/components/FPSMonitor\n * @category Performance Monitoring\n * @korean FPS모니터\n */\n\nimport { useFrame } from \"@react-three/fiber\";\nimport { Html } from \"@react-three/drei\";\nimport React, { useRef, useState, useCallback, useEffect, useMemo } from \"react\";\nimport { useKoreanTheme } from \"../../../../shared/base/useKoreanTheme\";\n\nexport interface FPSMonitorProps {\n /** Whether to show the FPS monitor */\n readonly enabled?: boolean;\n /** FPS threshold for warning (default: 50) */\n readonly warningThreshold?: number;\n /** FPS threshold for critical warning (default: 30) */\n readonly criticalThreshold?: number;\n /** Callback when FPS drops below threshold */\n readonly onFPSDrop?: (fps: number) => void;\n /** Position from top in pixels (default: 10) */\n readonly top?: number;\n /** Position from right in pixels (default: 10) */\n readonly right?: number;\n}\n\n/**\n * FPSMonitor Component\n *\n * Monitors and displays real-time FPS during combat. Uses Three.js useFrame\n * to calculate frames per second and provides visual feedback:\n * - Green: 60+ fps (excellent)\n * - Yellow: 50-59 fps (warning)\n * - Orange: 30-49 fps (poor)\n * - Red: <30 fps (critical)\n *\n * @example\n * ```tsx\n * <FPSMonitor\n * enabled={showPerformanceStats}\n * warningThreshold={50}\n * onFPSDrop={(fps) => console.warn(`Performance drop: ${fps} fps`)}\n * />\n * ```\n */\nexport const FPSMonitor: React.FC<FPSMonitorProps> = ({\n enabled = true,\n warningThreshold = 50,\n criticalThreshold = 30,\n onFPSDrop,\n top = 10,\n right = 10,\n}) => {\n const theme = useKoreanTheme({ variant: \"primary\", size: \"small\", isMobile: false });\n const [fps, setFps] = useState(60);\n const frameCountRef = useRef(0);\n const lastTimeRef = useRef(0);\n const lastWarningTimeRef = useRef(0);\n const initializedRef = useRef(false);\n \n useEffect(() => {\n lastTimeRef.current = performance.now();\n initializedRef.current = true;\n }, []);\n\n const updateFPS = useCallback(() => {\n if (!initializedRef.current) return;\n \n frameCountRef.current++;\n const currentTime = performance.now();\n const deltaTime = currentTime - lastTimeRef.current;\n\n if (deltaTime >= 1000) {\n const currentFps = Math.round((frameCountRef.current * 1000) / deltaTime);\n setFps(currentFps);\n \n if (onFPSDrop && currentFps < warningThreshold) {\n const timeSinceLastWarning = currentTime - lastWarningTimeRef.current;\n if (timeSinceLastWarning >= 3000) {\n onFPSDrop(currentFps);\n lastWarningTimeRef.current = currentTime;\n }\n }\n\n frameCountRef.current = 0;\n lastTimeRef.current = currentTime;\n }\n }, [onFPSDrop, warningThreshold]);\n\n useFrame(() => {\n if (enabled) {\n updateFPS();\n }\n });\n\n const colorHex = useMemo(() => {\n let color: number;\n if (fps >= 60) {\n color = theme.colors.ACCENT_GOLD; // Excellent\n } else if (fps >= warningThreshold) {\n color = 0xffff00; // Warning (yellow)\n } else if (fps >= criticalThreshold) {\n color = 0xff8800; // Poor (orange)\n } else {\n color = theme.colors.PRIMARY_RED; // Critical (red)\n }\n return `#${color.toString(16).padStart(6, \"0\")}`;\n }, [fps, warningThreshold, criticalThreshold, theme.colors.ACCENT_GOLD, theme.colors.PRIMARY_RED]);\n\n const getStatus = () => {\n if (fps >= 60) return \"우수 | Excellent\";\n if (fps >= warningThreshold) return \"경고 | Warning\";\n if (fps >= criticalThreshold) return \"저하 | Poor\";\n return \"심각 | Critical\";\n };\n\n if (!enabled) {\n return null;\n }\n\n return (\n <Html fullscreen>\n <div\n data-testid=\"fps-monitor\"\n style={{\n position: \"absolute\",\n top: `${top}px`,\n right: `${right}px`,\n padding: \"8px 12px\",\n background: \"rgba(0, 0, 0, 0.8)\",\n border: `2px solid ${colorHex}`,\n borderRadius: \"4px\",\n fontFamily: theme.fontFamily.KOREAN,\n fontSize: \"12px\",\n color: colorHex,\n pointerEvents: \"none\",\n zIndex: 9999,\n }}\n >\n <div style={{ fontWeight: \"bold\", marginBottom: \"2px\" }}>\n FPS: {fps}\n </div>\n <div style={{ fontSize: \"10px\", opacity: 0.9 }}>\n {getStatus()}\n </div>\n </div>\n </Html>\n );\n};\n\nexport default FPSMonitor;\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAmDA,IAAa,cAAyC,EACpD,UAAU,MACV,mBAAmB,IACnB,oBAAoB,IACpB,WACA,MAAM,IACN,QAAQ,SACJ;CACJ,MAAM,QAAQ,eAAe;EAAE,SAAS;EAAW,MAAM;EAAS,UAAU;CAAM,CAAC;CACnF,MAAM,CAAC,KAAK,UAAU,SAAS,EAAE;CACjC,MAAM,gBAAgB,OAAO,CAAC;CAC9B,MAAM,cAAc,OAAO,CAAC;CAC5B,MAAM,qBAAqB,OAAO,CAAC;CACnC,MAAM,iBAAiB,OAAO,KAAK;CAEnC,gBAAgB;EACd,YAAY,UAAU,YAAY,IAAI;EACtC,eAAe,UAAU;CAC3B,GAAG,CAAC,CAAC;CAEL,MAAM,YAAY,kBAAkB;EAClC,IAAI,CAAC,eAAe,SAAS;EAE7B,cAAc;EACd,MAAM,cAAc,YAAY,IAAI;EACpC,MAAM,YAAY,cAAc,YAAY;EAE5C,IAAI,aAAa,KAAM;GACrB,MAAM,aAAa,KAAK,MAAO,cAAc,UAAU,MAAQ,SAAS;GACxE,OAAO,UAAU;GAEjB,IAAI,aAAa,aAAa;QACC,cAAc,mBAAmB,WAClC,KAAM;KAChC,UAAU,UAAU;KACpB,mBAAmB,UAAU;IAC/B;;GAGF,cAAc,UAAU;GACxB,YAAY,UAAU;EACxB;CACF,GAAG,CAAC,WAAW,gBAAgB,CAAC;CAEhC,eAAe;EACb,IAAI,SACF,UAAU;CAEd,CAAC;CAED,MAAM,WAAW,cAAc;EAC7B,IAAI;EACJ,IAAI,OAAO,IACT,QAAQ,MAAM,OAAO;OAChB,IAAI,OAAO,kBAChB,QAAQ;OACH,IAAI,OAAO,mBAChB,QAAQ;OAER,QAAQ,MAAM,OAAO;EAEvB,OAAO,IAAI,MAAM,SAAS,EAAE,EAAE,SAAS,GAAG,GAAG;CAC/C,GAAG;EAAC;EAAK;EAAkB;EAAmB,MAAM,OAAO;EAAa,MAAM,OAAO;CAAW,CAAC;CAEjG,MAAM,kBAAkB;EACtB,IAAI,OAAO,IAAI,OAAO;EACtB,IAAI,OAAO,kBAAkB,OAAO;EACpC,IAAI,OAAO,mBAAmB,OAAO;EACrC,OAAO;CACT;CAEA,IAAI,CAAC,SACH,OAAO;CAGT,OACE,oBAAC,MAAD;EAAM,YAAA;YACJ,qBAAC,OAAD;GACE,eAAY;GACZ,OAAO;IACL,UAAU;IACV,KAAK,GAAG,IAAI;IACZ,OAAO,GAAG,MAAM;IAChB,SAAS;IACT,YAAY;IACZ,QAAQ,aAAa;IACrB,cAAc;IACd,YAAY,MAAM,WAAW;IAC7B,UAAU;IACV,OAAO;IACP,eAAe;IACf,QAAQ;GACV;aAfF,CAiBE,qBAAC,OAAD;IAAK,OAAO;KAAE,YAAY;KAAQ,cAAc;IAAM;cAAtD,CAAyD,SACjD,GACH;OACL,oBAAC,OAAD;IAAK,OAAO;KAAE,UAAU;KAAQ,SAAS;IAAI;cAC1C,UAAU;GACR,CAAA,CACF;;CACD,CAAA;AAEV"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"MobileControlsWrapper.js","names":[],"sources":["../../../../../../src/components/screens/combat/components/hud/MobileControlsWrapper.tsx"],"sourcesContent":["/**\n * MobileControlsWrapper - Mobile touch controls container\n *\n * Wraps all mobile-specific touch controls including:\n * - Virtual D-Pad for movement\n * - Action buttons (attack/block)\n * - Stance wheel for trigram stance selection\n * - Gesture recognizer for swipe actions\n *\n * PRIORITY SUPPORT: Optimized for high-end mobile devices (Super HD, 2K+)\n * - Enhanced touch targets (56px on Super HD vs 48px standard)\n * - Larger fonts and better spacing on high-resolution displays\n * - Full feature parity with desktop experience\n *\n * Integrated with:\n * - CombatScreen3D: Full combat gameplay with mobile controls\n * - TrainingScreen3D: Training mode with mobile controls\n * - deviceDetection.ts: Robust mobile detection (handles 2712x1220 Super HD)\n * - mobileUIUtils.ts: Touch target and font sizing utilities\n *\n * Only rendered on mobile devices (isMobile=true)\n *\n * @module components/combat/components/MobileControlsWrapper\n * @category Combat UI\n * @korean 모바일컨트롤래퍼\n */\n\nimport React from \"react\";\nimport {\n ActionButtons,\n GestureRecognizerPure,\n StanceWheelPure,\n VirtualDPad,\n} from \"../../../../shared/mobile\";\nimport { ButtonEventType } from \"../../../../shared/mobile/ActionButtons\";\nimport { Direction, DPadEventType } from \"../../../../shared/mobile/VirtualDPad\";\nimport { GestureEvent } from \"../../../../../hooks/useTouchControls\";\nimport { getMobileControlsBottom } from \"../../../../../types/constants/layout\";\n\nexport interface MobileControlsWrapperProps {\n /** Whether mobile controls are enabled */\n readonly enabled: boolean;\n /** Current stance index (0-7) */\n readonly currentStanceIndex: number;\n /** Whether stance wheel is expanded */\n readonly stanceWheelExpanded: boolean;\n /** D-Pad movement handler */\n readonly onMove: (direction: Direction | null, eventType: DPadEventType) => void;\n /** Attack button handler */\n readonly onAttack: () => void;\n /** Block button handler */\n readonly onBlock: (eventType: ButtonEventType) => void;\n /** Stance change handler */\n readonly onStanceChange: (stanceIndex: number) => void;\n /** Stance wheel toggle handler */\n readonly onStanceWheelToggle: () => void;\n /** Gesture handler */\n readonly onGesture: (gesture: GestureEvent) => void;\n /**\n * Current viewport height in pixels.\n *\n * Forwarded to `getMobileControlsBottom()` so the D-Pad / ActionButtons\n * drop closer to the bottom edge on short viewports (e.g. landscape\n * phones < 500 px tall). When omitted, the default 200 px band is used.\n */\n readonly viewportHeight?: number;\n}\n\n/**\n * MobileControlsWrapper - Container for all mobile touch controls\n *\n * Provides virtual controls for mobile combat gameplay:\n * - Virtual D-Pad (bottom-left) for 8-direction movement\n * - Action Buttons (bottom-right) for attack/block\n * - Stance Wheel (right-center) for trigram stance selection\n * - Gesture Recognition for swipe-based actions\n *\n * Positioning Strategy:\n * - All controls positioned using centralized layout constants\n * - D-Pad and ActionButtons use getMobileControlsBottom() for consistency\n * - Z-index: MOBILE_CONTROLS (50) to ensure visibility over HUD elements\n *\n * @example\n * ```tsx\n * <MobileControlsWrapper\n * enabled={mobileControlsEnabled}\n * currentStanceIndex={currentStanceIndex}\n * stanceWheelExpanded={stanceWheelExpanded}\n * onMove={handleMobileMove}\n * onAttack={handleMobileAttack}\n * onBlock={handleMobileBlock}\n * onStanceChange={handleMobileStanceChange}\n * onStanceWheelToggle={() => setStanceWheelExpanded(!stanceWheelExpanded)}\n * onGesture={handleMobileGesture}\n * />\n * ```\n */\nexport const MobileControlsWrapper: React.FC<MobileControlsWrapperProps> = ({\n enabled,\n currentStanceIndex,\n stanceWheelExpanded,\n onMove,\n onAttack,\n onBlock,\n onStanceChange,\n onStanceWheelToggle,\n onGesture,\n viewportHeight,\n}) => {\n const controlsBottom = getMobileControlsBottom(viewportHeight);\n return (\n <>\n {/* Virtual D-Pad - Bottom-left for movement */}\n {/* Positioned using centralized constant for consistency */}\n <VirtualDPad\n onMove={onMove}\n disabled={!enabled}\n opacity={0.8}\n bottom={controlsBottom}\n />\n\n {/* Action Buttons - Bottom-right for attack/block */}\n {/* Positioned using centralized constant for consistency */}\n <ActionButtons\n onAttack={onAttack}\n onBlock={onBlock}\n disabled={!enabled}\n opacity={0.8}\n bottom={controlsBottom}\n />\n\n {/* Stance Wheel - Right-center for trigram stance selection */}\n <StanceWheelPure\n currentStance={currentStanceIndex}\n onStanceChange={onStanceChange}\n expanded={stanceWheelExpanded}\n onToggle={onStanceWheelToggle}\n disabled={!enabled}\n opacity={0.8}\n />\n\n {/* Gesture Recognizer - Full-screen swipe detection */}\n <GestureRecognizerPure\n onGesture={onGesture}\n enabled={enabled}\n showFeedback={true}\n minSwipeDistance={50}\n />\n </>\n );\n};\n\nexport default MobileControlsWrapper;\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAiGA,IAAa,yBAA+D,EAC1E,SACA,oBACA,qBACA,QACA,UACA,SACA,gBACA,qBACA,WACA,qBACI;CACJ,MAAM,iBAAiB,wBAAwB,
|
|
1
|
+
{"version":3,"file":"MobileControlsWrapper.js","names":[],"sources":["../../../../../../src/components/screens/combat/components/hud/MobileControlsWrapper.tsx"],"sourcesContent":["/**\n * MobileControlsWrapper - Mobile touch controls container\n *\n * Wraps all mobile-specific touch controls including:\n * - Virtual D-Pad for movement\n * - Action buttons (attack/block)\n * - Stance wheel for trigram stance selection\n * - Gesture recognizer for swipe actions\n *\n * PRIORITY SUPPORT: Optimized for high-end mobile devices (Super HD, 2K+)\n * - Enhanced touch targets (56px on Super HD vs 48px standard)\n * - Larger fonts and better spacing on high-resolution displays\n * - Full feature parity with desktop experience\n *\n * Integrated with:\n * - CombatScreen3D: Full combat gameplay with mobile controls\n * - TrainingScreen3D: Training mode with mobile controls\n * - deviceDetection.ts: Robust mobile detection (handles 2712x1220 Super HD)\n * - mobileUIUtils.ts: Touch target and font sizing utilities\n *\n * Only rendered on mobile devices (isMobile=true)\n *\n * @module components/combat/components/MobileControlsWrapper\n * @category Combat UI\n * @korean 모바일컨트롤래퍼\n */\n\nimport React from \"react\";\nimport {\n ActionButtons,\n GestureRecognizerPure,\n StanceWheelPure,\n VirtualDPad,\n} from \"../../../../shared/mobile\";\nimport { ButtonEventType } from \"../../../../shared/mobile/ActionButtons\";\nimport { Direction, DPadEventType } from \"../../../../shared/mobile/VirtualDPad\";\nimport { GestureEvent } from \"../../../../../hooks/useTouchControls\";\nimport { getMobileControlsBottom } from \"../../../../../types/constants/layout\";\n\nexport interface MobileControlsWrapperProps {\n /** Whether mobile controls are enabled */\n readonly enabled: boolean;\n /** Current stance index (0-7) */\n readonly currentStanceIndex: number;\n /** Whether stance wheel is expanded */\n readonly stanceWheelExpanded: boolean;\n /** D-Pad movement handler */\n readonly onMove: (direction: Direction | null, eventType: DPadEventType) => void;\n /** Attack button handler */\n readonly onAttack: () => void;\n /** Block button handler */\n readonly onBlock: (eventType: ButtonEventType) => void;\n /** Stance change handler */\n readonly onStanceChange: (stanceIndex: number) => void;\n /** Stance wheel toggle handler */\n readonly onStanceWheelToggle: () => void;\n /** Gesture handler */\n readonly onGesture: (gesture: GestureEvent) => void;\n /**\n * Current viewport height in pixels.\n *\n * Forwarded to `getMobileControlsBottom()` so the D-Pad / ActionButtons\n * drop closer to the bottom edge on short viewports (e.g. landscape\n * phones < 500 px tall). When omitted, the default 200 px band is used.\n */\n readonly viewportHeight?: number;\n}\n\n/**\n * MobileControlsWrapper - Container for all mobile touch controls\n *\n * Provides virtual controls for mobile combat gameplay:\n * - Virtual D-Pad (bottom-left) for 8-direction movement\n * - Action Buttons (bottom-right) for attack/block\n * - Stance Wheel (right-center) for trigram stance selection\n * - Gesture Recognition for swipe-based actions\n *\n * Positioning Strategy:\n * - All controls positioned using centralized layout constants\n * - D-Pad and ActionButtons use getMobileControlsBottom() for consistency\n * - Z-index: MOBILE_CONTROLS (50) to ensure visibility over HUD elements\n *\n * @example\n * ```tsx\n * <MobileControlsWrapper\n * enabled={mobileControlsEnabled}\n * currentStanceIndex={currentStanceIndex}\n * stanceWheelExpanded={stanceWheelExpanded}\n * onMove={handleMobileMove}\n * onAttack={handleMobileAttack}\n * onBlock={handleMobileBlock}\n * onStanceChange={handleMobileStanceChange}\n * onStanceWheelToggle={() => setStanceWheelExpanded(!stanceWheelExpanded)}\n * onGesture={handleMobileGesture}\n * />\n * ```\n */\nexport const MobileControlsWrapper: React.FC<MobileControlsWrapperProps> = ({\n enabled,\n currentStanceIndex,\n stanceWheelExpanded,\n onMove,\n onAttack,\n onBlock,\n onStanceChange,\n onStanceWheelToggle,\n onGesture,\n viewportHeight,\n}) => {\n const controlsBottom = getMobileControlsBottom(viewportHeight);\n return (\n <>\n {/* Virtual D-Pad - Bottom-left for movement */}\n {/* Positioned using centralized constant for consistency */}\n <VirtualDPad\n onMove={onMove}\n disabled={!enabled}\n opacity={0.8}\n bottom={controlsBottom}\n />\n\n {/* Action Buttons - Bottom-right for attack/block */}\n {/* Positioned using centralized constant for consistency */}\n <ActionButtons\n onAttack={onAttack}\n onBlock={onBlock}\n disabled={!enabled}\n opacity={0.8}\n bottom={controlsBottom}\n />\n\n {/* Stance Wheel - Right-center for trigram stance selection */}\n <StanceWheelPure\n currentStance={currentStanceIndex}\n onStanceChange={onStanceChange}\n expanded={stanceWheelExpanded}\n onToggle={onStanceWheelToggle}\n disabled={!enabled}\n opacity={0.8}\n />\n\n {/* Gesture Recognizer - Full-screen swipe detection */}\n <GestureRecognizerPure\n onGesture={onGesture}\n enabled={enabled}\n showFeedback={true}\n minSwipeDistance={50}\n />\n </>\n );\n};\n\nexport default MobileControlsWrapper;\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAiGA,IAAa,yBAA+D,EAC1E,SACA,oBACA,qBACA,QACA,UACA,SACA,gBACA,qBACA,WACA,qBACI;CACJ,MAAM,iBAAiB,wBAAwB,cAAc;CAC7D,OACE,qBAAA,UAAA,EAAA,UAAA;EAGE,oBAAC,aAAD;GACU;GACR,UAAU,CAAC;GACX,SAAS;GACT,QAAQ;EACT,CAAA;EAID,oBAAC,eAAD;GACY;GACD;GACT,UAAU,CAAC;GACX,SAAS;GACT,QAAQ;EACT,CAAA;EAGD,oBAAC,iBAAD;GACE,eAAe;GACC;GAChB,UAAU;GACV,UAAU;GACV,UAAU,CAAC;GACX,SAAS;EACV,CAAA;EAGD,oBAAC,uBAAD;GACa;GACF;GACT,cAAc;GACd,kBAAkB;EACnB,CAAA;CACD,EAAA,CAAA;AAEN"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"PlayerStateOverlayHtml.js","names":[],"sources":["../../../../../../src/components/screens/combat/components/hud/PlayerStateOverlayHtml.tsx"],"sourcesContent":["/**\n * PlayerStateOverlayHtml Component - Unified player state visual indicators\n * \n * Combines all player state visual effects into a single overlay:\n * - Pain vignette\n * - Balance indicator\n * - Consciousness blur\n * - Blood loss warning\n * - Stamina warning\n * \n * @module components/combat/PlayerStateOverlayHtml\n * @category Combat UI\n * @korean 플레이어상태오버레이\n */\n\nimport React from \"react\";\nimport { PainVignette } from \"../effects/PainVignette\";\nimport { BalanceIndicator } from \"../indicators/BalanceIndicator\";\nimport { ConsciousnessBlur } from \"../effects/ConsciousnessBlur\";\nimport { BloodLossOverlayHtml } from \"../effects/BloodLossOverlayHtml\";\nimport { StaminaWarning } from \"../indicators/StaminaWarning\";\nimport type { BalanceState } from \"../../../../../types/player-visual\";\n\nexport interface PlayerStateOverlayProps {\n /**\n * Current pain level (0-100)\n * @korean 통증\n */\n readonly pain: number;\n\n /**\n * Current balance state\n * @korean 균형상태\n */\n readonly balanceState: BalanceState;\n\n /**\n * Player position ('left' or 'right')\n * @korean 플레이어위치\n */\n readonly position: \"left\" | \"right\";\n\n /**\n * Current consciousness level (0-100)\n * @korean 의식\n */\n readonly consciousness: number;\n\n /**\n * Current blood loss (0-100, optional)\n * @korean 출혈\n */\n readonly bloodLoss?: number;\n\n /**\n * Current stamina (0-100)\n * @korean 체력\n */\n readonly stamina: number;\n\n /**\n * Mobile responsive mode\n * @korean 모바일여부\n */\n readonly isMobile: boolean;\n\n /**\n * Multiplier applied to every fullscreen effect's visual weight (0.0-1.0).\n *\n * Used by the parent screen to attenuate fullscreen vignette / blur / flash\n * effects when the 3D arena is already visually compressed (e.g. portrait\n * mobile, where the arena is rendered in a 3:4 aspect ratio and consumes\n * the majority of the viewport height). Default is `1.0` (no attenuation).\n *\n * Cascades to `PainVignette`, `ConsciousnessBlur`, `BloodLossOverlayHtml`\n * and `StaminaWarning`. Does not affect `BalanceIndicator`, which is an\n * informational indicator rather than a fullscreen overlay.\n *\n * @korean 효과강도배수\n */\n readonly intensityScale?: number;\n}\n\n/**\n * PlayerStateOverlayHtml - Unified visual effects for player state\n * \n * Combines all player state visual indicators into a single component\n * with optimal performance and consistent rendering. All effects use\n * smooth 0.5s transitions and are optimized for 60fps.\n *\n * Optimized with React.memo for performance:\n * - Prevents re-renders when props haven't changed\n * - Custom comparison function for precise control\n * - Reduces DOM updates for 60fps target\n * \n * @example\n * ```tsx\n * <PlayerStateOverlayHtml\n * pain={65}\n * balanceState=\"SHAKEN\"\n * position=\"left\"\n * consciousness={80}\n * bloodLoss={45}\n * stamina={15}\n * isMobile={false}\n * />\n * ```\n */\nexport const PlayerStateOverlayHtml = React.memo<PlayerStateOverlayProps>(\n ({\n pain,\n balanceState,\n position,\n consciousness,\n bloodLoss = 0,\n stamina,\n isMobile,\n intensityScale = 1,\n }) => {\n return (\n <div data-testid=\"player-state-overlay\" style={{ display: 'contents' }}>\n {/* Pain vignette - shows when pain >= 5 (see PainVignette.tsx) */}\n <PainVignette\n pain={pain}\n isMobile={isMobile}\n intensityScale={intensityScale}\n />\n\n {/* Balance indicator - always visible, color-coded by state (see BalanceIndicator.tsx) */}\n <BalanceIndicator\n balanceState={balanceState}\n position={position}\n isMobile={isMobile}\n />\n\n {/* Consciousness blur - shows when consciousness <= 90 (see ConsciousnessBlur.tsx) */}\n <ConsciousnessBlur\n consciousness={consciousness}\n isMobile={isMobile}\n intensityScale={intensityScale}\n />\n\n {/* Blood loss warning - pulses when bloodLoss >= 50 (see BloodLossOverlayHtml.tsx) */}\n <BloodLossOverlayHtml\n bloodLoss={bloodLoss}\n isMobile={isMobile}\n intensityScale={intensityScale}\n />\n\n {/* Stamina warning - flashes when stamina < 20 (see StaminaWarning.tsx) */}\n <StaminaWarning\n stamina={stamina}\n isMobile={isMobile}\n intensityScale={intensityScale}\n />\n </div>\n );\n },\n (prevProps, nextProps) => {\n return (\n prevProps.pain === nextProps.pain &&\n prevProps.balanceState === nextProps.balanceState &&\n prevProps.position === nextProps.position &&\n prevProps.consciousness === nextProps.consciousness &&\n prevProps.bloodLoss === nextProps.bloodLoss &&\n prevProps.stamina === nextProps.stamina &&\n prevProps.isMobile === nextProps.isMobile &&\n prevProps.intensityScale === nextProps.intensityScale\n );\n },\n);\n\nPlayerStateOverlayHtml.displayName = \"PlayerStateOverlayHtml\";\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA4GA,IAAa,yBAAyB,MAAM,MACzC,EACC,MACA,cACA,UACA,eACA,YAAY,GACZ,SACA,UACA,iBAAiB,QACb;CACJ,OACE,qBAAC,OAAD;EAAK,eAAY;EAAuB,OAAO,EAAE,SAAS,
|
|
1
|
+
{"version":3,"file":"PlayerStateOverlayHtml.js","names":[],"sources":["../../../../../../src/components/screens/combat/components/hud/PlayerStateOverlayHtml.tsx"],"sourcesContent":["/**\n * PlayerStateOverlayHtml Component - Unified player state visual indicators\n * \n * Combines all player state visual effects into a single overlay:\n * - Pain vignette\n * - Balance indicator\n * - Consciousness blur\n * - Blood loss warning\n * - Stamina warning\n * \n * @module components/combat/PlayerStateOverlayHtml\n * @category Combat UI\n * @korean 플레이어상태오버레이\n */\n\nimport React from \"react\";\nimport { PainVignette } from \"../effects/PainVignette\";\nimport { BalanceIndicator } from \"../indicators/BalanceIndicator\";\nimport { ConsciousnessBlur } from \"../effects/ConsciousnessBlur\";\nimport { BloodLossOverlayHtml } from \"../effects/BloodLossOverlayHtml\";\nimport { StaminaWarning } from \"../indicators/StaminaWarning\";\nimport type { BalanceState } from \"../../../../../types/player-visual\";\n\nexport interface PlayerStateOverlayProps {\n /**\n * Current pain level (0-100)\n * @korean 통증\n */\n readonly pain: number;\n\n /**\n * Current balance state\n * @korean 균형상태\n */\n readonly balanceState: BalanceState;\n\n /**\n * Player position ('left' or 'right')\n * @korean 플레이어위치\n */\n readonly position: \"left\" | \"right\";\n\n /**\n * Current consciousness level (0-100)\n * @korean 의식\n */\n readonly consciousness: number;\n\n /**\n * Current blood loss (0-100, optional)\n * @korean 출혈\n */\n readonly bloodLoss?: number;\n\n /**\n * Current stamina (0-100)\n * @korean 체력\n */\n readonly stamina: number;\n\n /**\n * Mobile responsive mode\n * @korean 모바일여부\n */\n readonly isMobile: boolean;\n\n /**\n * Multiplier applied to every fullscreen effect's visual weight (0.0-1.0).\n *\n * Used by the parent screen to attenuate fullscreen vignette / blur / flash\n * effects when the 3D arena is already visually compressed (e.g. portrait\n * mobile, where the arena is rendered in a 3:4 aspect ratio and consumes\n * the majority of the viewport height). Default is `1.0` (no attenuation).\n *\n * Cascades to `PainVignette`, `ConsciousnessBlur`, `BloodLossOverlayHtml`\n * and `StaminaWarning`. Does not affect `BalanceIndicator`, which is an\n * informational indicator rather than a fullscreen overlay.\n *\n * @korean 효과강도배수\n */\n readonly intensityScale?: number;\n}\n\n/**\n * PlayerStateOverlayHtml - Unified visual effects for player state\n * \n * Combines all player state visual indicators into a single component\n * with optimal performance and consistent rendering. All effects use\n * smooth 0.5s transitions and are optimized for 60fps.\n *\n * Optimized with React.memo for performance:\n * - Prevents re-renders when props haven't changed\n * - Custom comparison function for precise control\n * - Reduces DOM updates for 60fps target\n * \n * @example\n * ```tsx\n * <PlayerStateOverlayHtml\n * pain={65}\n * balanceState=\"SHAKEN\"\n * position=\"left\"\n * consciousness={80}\n * bloodLoss={45}\n * stamina={15}\n * isMobile={false}\n * />\n * ```\n */\nexport const PlayerStateOverlayHtml = React.memo<PlayerStateOverlayProps>(\n ({\n pain,\n balanceState,\n position,\n consciousness,\n bloodLoss = 0,\n stamina,\n isMobile,\n intensityScale = 1,\n }) => {\n return (\n <div data-testid=\"player-state-overlay\" style={{ display: 'contents' }}>\n {/* Pain vignette - shows when pain >= 5 (see PainVignette.tsx) */}\n <PainVignette\n pain={pain}\n isMobile={isMobile}\n intensityScale={intensityScale}\n />\n\n {/* Balance indicator - always visible, color-coded by state (see BalanceIndicator.tsx) */}\n <BalanceIndicator\n balanceState={balanceState}\n position={position}\n isMobile={isMobile}\n />\n\n {/* Consciousness blur - shows when consciousness <= 90 (see ConsciousnessBlur.tsx) */}\n <ConsciousnessBlur\n consciousness={consciousness}\n isMobile={isMobile}\n intensityScale={intensityScale}\n />\n\n {/* Blood loss warning - pulses when bloodLoss >= 50 (see BloodLossOverlayHtml.tsx) */}\n <BloodLossOverlayHtml\n bloodLoss={bloodLoss}\n isMobile={isMobile}\n intensityScale={intensityScale}\n />\n\n {/* Stamina warning - flashes when stamina < 20 (see StaminaWarning.tsx) */}\n <StaminaWarning\n stamina={stamina}\n isMobile={isMobile}\n intensityScale={intensityScale}\n />\n </div>\n );\n },\n (prevProps, nextProps) => {\n return (\n prevProps.pain === nextProps.pain &&\n prevProps.balanceState === nextProps.balanceState &&\n prevProps.position === nextProps.position &&\n prevProps.consciousness === nextProps.consciousness &&\n prevProps.bloodLoss === nextProps.bloodLoss &&\n prevProps.stamina === nextProps.stamina &&\n prevProps.isMobile === nextProps.isMobile &&\n prevProps.intensityScale === nextProps.intensityScale\n );\n },\n);\n\nPlayerStateOverlayHtml.displayName = \"PlayerStateOverlayHtml\";\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA4GA,IAAa,yBAAyB,MAAM,MACzC,EACC,MACA,cACA,UACA,eACA,YAAY,GACZ,SACA,UACA,iBAAiB,QACb;CACJ,OACE,qBAAC,OAAD;EAAK,eAAY;EAAuB,OAAO,EAAE,SAAS,WAAW;YAArE;GAEE,oBAAC,cAAD;IACQ;IACI;IACM;GACjB,CAAA;GAGD,oBAAC,kBAAD;IACgB;IACJ;IACA;GACX,CAAA;GAGD,oBAAC,mBAAD;IACiB;IACL;IACM;GACjB,CAAA;GAGD,oBAAC,sBAAD;IACa;IACD;IACM;GACjB,CAAA;GAGD,oBAAC,gBAAD;IACW;IACC;IACM;GACjB,CAAA;EACE;;AAET,IACC,WAAW,cAAc;CACxB,OACE,UAAU,SAAS,UAAU,QAC7B,UAAU,iBAAiB,UAAU,gBACrC,UAAU,aAAa,UAAU,YACjC,UAAU,kBAAkB,UAAU,iBACtC,UAAU,cAAc,UAAU,aAClC,UAAU,YAAY,UAAU,WAChC,UAAU,aAAa,UAAU,YACjC,UAAU,mBAAmB,UAAU;AAE3C,CACF;AAEA,uBAAuB,cAAc"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"BalanceIndicator.js","names":[],"sources":["../../../../../../src/components/screens/combat/components/indicators/BalanceIndicator.tsx"],"sourcesContent":["/**\n * BalanceIndicator Component - Visual indicator for player balance state\n *\n * Displays a color-coded border around the player representing their\n * combat balance state: READY (green), SHAKEN (yellow), VULNERABLE (orange), HELPLESS (red).\n *\n * NOTE: This component is rendered OUTSIDE the Canvas as part of the HTML overlay.\n * It does NOT use Html from drei - it's a standard React component.\n *\n * Refactored to use useKoreanTheme for consistent styling.\n *\n * @module components/combat/BalanceIndicator\n * @category Combat UI\n * @korean 균형표시기\n */\n\nimport React, { useMemo } from \"react\";\nimport { useKoreanTheme } from \"../../../../shared/base/useKoreanTheme\";\nimport type { BalanceState } from \"../../../../../types/player-visual\";\n\nexport interface BalanceIndicatorProps {\n /**\n * Current balance state\n * @korean 균형상태\n */\n readonly balanceState: BalanceState;\n\n /**\n * Player position ('left' or 'right' side of screen)\n * @korean 플레이어위치\n */\n readonly position: \"left\" | \"right\";\n\n /**\n * Mobile responsive mode (thinner borders)\n * @korean 모바일여부\n */\n readonly isMobile: boolean;\n}\n\n/**\n * Get color for balance state using theme colors\n */\nfunction getBalanceColor(theme: ReturnType<typeof useKoreanTheme>, state: BalanceState): number {\n switch (state) {\n case \"READY\":\n return theme.colors.POSITIVE_GREEN; // 🟢 Green\n case \"SHAKEN\":\n return theme.colors.WARNING_YELLOW; // 🟡 Yellow\n case \"VULNERABLE\":\n return theme.colors.WARNING_ORANGE; // 🟠 Orange\n case \"HELPLESS\":\n return theme.colors.ACCENT_RED; // 🔴 Red\n }\n}\n\n/**\n * Get Korean label for balance state\n */\nfunction getBalanceLabel(state: BalanceState): {\n korean: string;\n english: string;\n} {\n switch (state) {\n case \"READY\":\n return { korean: \"준비완료\", english: \"READY\" };\n case \"SHAKEN\":\n return { korean: \"동요상태\", english: \"SHAKEN\" };\n case \"VULNERABLE\":\n return { korean: \"취약상태\", english: \"VULNERABLE\" };\n case \"HELPLESS\":\n return { korean: \"무력상태\", english: \"HELPLESS\" };\n }\n}\n\n/**\n * BalanceIndicator - Color-coded border indicator for player balance state\n *\n * Renders a border around the player HUD area with color matching the\n * current balance state. Uses smooth transitions for state changes.\n * Uses useKoreanTheme for consistent color scheme.\n *\n * @example\n * ```tsx\n * <BalanceIndicator\n * balanceState=\"SHAKEN\"\n * position=\"left\"\n * isMobile={false}\n * />\n * ```\n */\nexport const BalanceIndicator: React.FC<BalanceIndicatorProps> = ({\n balanceState,\n position,\n isMobile,\n}) => {\n const theme = useKoreanTheme({ variant: \"primary\", size: \"md\", isMobile });\n \n const indicatorStyle = useMemo(() => {\n const color = getBalanceColor(theme, balanceState);\n const colorHex = `#${color.toString(16).padStart(6, \"0\")}`;\n\n const borderWidth = isMobile ? \"3px\" : \"4px\";\n\n const isLeft = position === \"left\";\n\n return {\n position: \"absolute\" as const,\n top: isMobile ? \"8px\" : \"12px\",\n left: isLeft ? (isMobile ? \"8px\" : \"12px\") : \"auto\",\n right: isLeft ? \"auto\" : isMobile ? \"8px\" : \"12px\",\n width: isMobile ? \"180px\" : \"220px\",\n height: isMobile ? \"80px\" : \"100px\",\n border: `${borderWidth} solid ${colorHex}`,\n borderRadius: \"8px\",\n boxShadow: `0 0 12px ${colorHex}`,\n pointerEvents: \"none\" as const,\n transition: \"border-color 0.5s ease-out, box-shadow 0.5s ease-out\",\n zIndex: 90, // Below HUD text but above game content\n };\n }, [balanceState, position, isMobile, theme]);\n\n const label = useMemo(() => getBalanceLabel(balanceState), [balanceState]);\n\n return (\n <div\n data-testid={`balance-indicator-${position}`}\n style={indicatorStyle}\n aria-label={`${label.korean} | ${label.english}`}\n role=\"status\"\n aria-live=\"polite\"\n />\n );\n};\n\nBalanceIndicator.displayName = \"BalanceIndicator\";\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;AA2CA,SAAS,gBAAgB,OAA0C,OAA6B;CAC9F,QAAQ,OAAR;EACE,KAAK,SACH,OAAO,MAAM,OAAO;EACtB,KAAK,UACH,OAAO,MAAM,OAAO;EACtB,KAAK,cACH,OAAO,MAAM,OAAO;EACtB,KAAK,YACH,OAAO,MAAM,OAAO
|
|
1
|
+
{"version":3,"file":"BalanceIndicator.js","names":[],"sources":["../../../../../../src/components/screens/combat/components/indicators/BalanceIndicator.tsx"],"sourcesContent":["/**\n * BalanceIndicator Component - Visual indicator for player balance state\n *\n * Displays a color-coded border around the player representing their\n * combat balance state: READY (green), SHAKEN (yellow), VULNERABLE (orange), HELPLESS (red).\n *\n * NOTE: This component is rendered OUTSIDE the Canvas as part of the HTML overlay.\n * It does NOT use Html from drei - it's a standard React component.\n *\n * Refactored to use useKoreanTheme for consistent styling.\n *\n * @module components/combat/BalanceIndicator\n * @category Combat UI\n * @korean 균형표시기\n */\n\nimport React, { useMemo } from \"react\";\nimport { useKoreanTheme } from \"../../../../shared/base/useKoreanTheme\";\nimport type { BalanceState } from \"../../../../../types/player-visual\";\n\nexport interface BalanceIndicatorProps {\n /**\n * Current balance state\n * @korean 균형상태\n */\n readonly balanceState: BalanceState;\n\n /**\n * Player position ('left' or 'right' side of screen)\n * @korean 플레이어위치\n */\n readonly position: \"left\" | \"right\";\n\n /**\n * Mobile responsive mode (thinner borders)\n * @korean 모바일여부\n */\n readonly isMobile: boolean;\n}\n\n/**\n * Get color for balance state using theme colors\n */\nfunction getBalanceColor(theme: ReturnType<typeof useKoreanTheme>, state: BalanceState): number {\n switch (state) {\n case \"READY\":\n return theme.colors.POSITIVE_GREEN; // 🟢 Green\n case \"SHAKEN\":\n return theme.colors.WARNING_YELLOW; // 🟡 Yellow\n case \"VULNERABLE\":\n return theme.colors.WARNING_ORANGE; // 🟠 Orange\n case \"HELPLESS\":\n return theme.colors.ACCENT_RED; // 🔴 Red\n }\n}\n\n/**\n * Get Korean label for balance state\n */\nfunction getBalanceLabel(state: BalanceState): {\n korean: string;\n english: string;\n} {\n switch (state) {\n case \"READY\":\n return { korean: \"준비완료\", english: \"READY\" };\n case \"SHAKEN\":\n return { korean: \"동요상태\", english: \"SHAKEN\" };\n case \"VULNERABLE\":\n return { korean: \"취약상태\", english: \"VULNERABLE\" };\n case \"HELPLESS\":\n return { korean: \"무력상태\", english: \"HELPLESS\" };\n }\n}\n\n/**\n * BalanceIndicator - Color-coded border indicator for player balance state\n *\n * Renders a border around the player HUD area with color matching the\n * current balance state. Uses smooth transitions for state changes.\n * Uses useKoreanTheme for consistent color scheme.\n *\n * @example\n * ```tsx\n * <BalanceIndicator\n * balanceState=\"SHAKEN\"\n * position=\"left\"\n * isMobile={false}\n * />\n * ```\n */\nexport const BalanceIndicator: React.FC<BalanceIndicatorProps> = ({\n balanceState,\n position,\n isMobile,\n}) => {\n const theme = useKoreanTheme({ variant: \"primary\", size: \"md\", isMobile });\n \n const indicatorStyle = useMemo(() => {\n const color = getBalanceColor(theme, balanceState);\n const colorHex = `#${color.toString(16).padStart(6, \"0\")}`;\n\n const borderWidth = isMobile ? \"3px\" : \"4px\";\n\n const isLeft = position === \"left\";\n\n return {\n position: \"absolute\" as const,\n top: isMobile ? \"8px\" : \"12px\",\n left: isLeft ? (isMobile ? \"8px\" : \"12px\") : \"auto\",\n right: isLeft ? \"auto\" : isMobile ? \"8px\" : \"12px\",\n width: isMobile ? \"180px\" : \"220px\",\n height: isMobile ? \"80px\" : \"100px\",\n border: `${borderWidth} solid ${colorHex}`,\n borderRadius: \"8px\",\n boxShadow: `0 0 12px ${colorHex}`,\n pointerEvents: \"none\" as const,\n transition: \"border-color 0.5s ease-out, box-shadow 0.5s ease-out\",\n zIndex: 90, // Below HUD text but above game content\n };\n }, [balanceState, position, isMobile, theme]);\n\n const label = useMemo(() => getBalanceLabel(balanceState), [balanceState]);\n\n return (\n <div\n data-testid={`balance-indicator-${position}`}\n style={indicatorStyle}\n aria-label={`${label.korean} | ${label.english}`}\n role=\"status\"\n aria-live=\"polite\"\n />\n );\n};\n\nBalanceIndicator.displayName = \"BalanceIndicator\";\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;AA2CA,SAAS,gBAAgB,OAA0C,OAA6B;CAC9F,QAAQ,OAAR;EACE,KAAK,SACH,OAAO,MAAM,OAAO;EACtB,KAAK,UACH,OAAO,MAAM,OAAO;EACtB,KAAK,cACH,OAAO,MAAM,OAAO;EACtB,KAAK,YACH,OAAO,MAAM,OAAO;CACxB;AACF;;;;AAKA,SAAS,gBAAgB,OAGvB;CACA,QAAQ,OAAR;EACE,KAAK,SACH,OAAO;GAAE,QAAQ;GAAQ,SAAS;EAAQ;EAC5C,KAAK,UACH,OAAO;GAAE,QAAQ;GAAQ,SAAS;EAAS;EAC7C,KAAK,cACH,OAAO;GAAE,QAAQ;GAAQ,SAAS;EAAa;EACjD,KAAK,YACH,OAAO;GAAE,QAAQ;GAAQ,SAAS;EAAW;CACjD;AACF;;;;;;;;;;;;;;;;;AAkBA,IAAa,oBAAqD,EAChE,cACA,UACA,eACI;CACJ,MAAM,QAAQ,eAAe;EAAE,SAAS;EAAW,MAAM;EAAM;CAAS,CAAC;CAEzE,MAAM,iBAAiB,cAAc;EAEnC,MAAM,WAAW,IADH,gBAAgB,OAAO,YAChB,EAAM,SAAS,EAAE,EAAE,SAAS,GAAG,GAAG;EAEvD,MAAM,cAAc,WAAW,QAAQ;EAEvC,MAAM,SAAS,aAAa;EAE5B,OAAO;GACL,UAAU;GACV,KAAK,WAAW,QAAQ;GACxB,MAAM,SAAU,WAAW,QAAQ,SAAU;GAC7C,OAAO,SAAS,SAAS,WAAW,QAAQ;GAC5C,OAAO,WAAW,UAAU;GAC5B,QAAQ,WAAW,SAAS;GAC5B,QAAQ,GAAG,YAAY,SAAS;GAChC,cAAc;GACd,WAAW,YAAY;GACvB,eAAe;GACf,YAAY;GACZ,QAAQ;EACV;CACF,GAAG;EAAC;EAAc;EAAU;EAAU;CAAK,CAAC;CAE5C,MAAM,QAAQ,cAAc,gBAAgB,YAAY,GAAG,CAAC,YAAY,CAAC;CAEzE,OACE,oBAAC,OAAD;EACE,eAAa,qBAAqB;EAClC,OAAO;EACP,cAAY,GAAG,MAAM,OAAO,KAAK,MAAM;EACvC,MAAK;EACL,aAAU;CACX,CAAA;AAEL;AAEA,iBAAiB,cAAc"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"InputBufferDisplay.js","names":[],"sources":["../../../../../../src/components/screens/combat/components/indicators/InputBufferDisplay.tsx"],"sourcesContent":["/**\n * InputBufferDisplay - Input queue visualization\n * Shows queued combat actions for player feedback\n * \n * Refactored to use useKoreanTheme for consistent theming.\n * \n * @module components/combat/components/InputBufferDisplay\n * @category Combat UI\n * @korean 입력버퍼표시\n */\n\nimport { Html } from \"@react-three/drei\";\nimport React, { useMemo } from \"react\";\nimport { useKoreanTheme } from \"../../../../shared/base/useKoreanTheme\";\nimport { hexToRgbaString } from \"../../../../../utils/colorUtils\";\nimport { QueuedInput } from \"../../../../../hooks/useKeyboardControls\";\n\n/**\n * Props for InputBufferDisplay component\n */\nexport interface InputBufferDisplayProps {\n /** Queued inputs to display */\n readonly queuedInputs: readonly QueuedInput[];\n /** Mobile layout flag */\n readonly isMobile?: boolean;\n}\n\n/**\n * InputBufferDisplay Component\n * \n * Displays a list of recently queued combat inputs in the top-right corner.\n * Useful for showing input confirmation and debugging input lag issues.\n * Uses useKoreanTheme for consistent styling.\n * \n * Features:\n * - Displays up to 3 recent inputs\n * - Fades older inputs with reduced opacity\n * - Shows action name and key pressed\n * - Responsive mobile layout\n * - Korean cyberpunk styling via theme\n * - Automatically clears after 2 seconds\n * \n * @example\n * ```tsx\n * <InputBufferDisplay\n * queuedInputs={queuedInputs}\n * isMobile={isMobile}\n * />\n * ```\n * \n * @korean 입력버퍼표시\n */\nexport const InputBufferDisplay: React.FC<InputBufferDisplayProps> = ({\n queuedInputs,\n isMobile = false,\n}) => {\n const theme = useKoreanTheme({ variant: \"primary\", size: \"sm\", isMobile });\n \n const animationStyles = useMemo(() => (\n <style>\n {`\n @keyframes slideIn {\n 0% {\n opacity: 0;\n transform: translateX(10px);\n }\n 100% {\n opacity: 1;\n transform: translateX(0);\n }\n }\n `}\n </style>\n ), []);\n\n if (queuedInputs.length === 0) return null;\n\n const fontSize = isMobile ? 10 : 12;\n const labelFontSize = isMobile ? 8 : 10;\n const top = isMobile ? \"10px\" : \"20px\";\n const right = isMobile ? \"10px\" : \"20px\";\n\n return (\n <Html fullscreen>\n <div\n data-testid=\"input-buffer-display\"\n role=\"log\"\n aria-live=\"polite\"\n aria-label=\"Input queue\"\n style={{\n position: \"absolute\",\n top,\n right,\n background: hexToRgbaString(theme.colors.UI_BACKGROUND_DARK, 0.8),\n border: `1px solid ${hexToRgbaString(theme.colors.ACCENT_BLUE, 0.6)}`,\n borderRadius: \"4px\",\n padding: isMobile ? \"4px\" : \"8px\",\n minWidth: isMobile ? \"100px\" : \"150px\",\n pointerEvents: \"none\",\n zIndex: 998,\n boxShadow: `0 0 10px ${hexToRgbaString(theme.colors.ACCENT_BLUE, 0.2)}`,\n }}\n >\n {/* Title */}\n <div\n style={{\n fontSize: `${labelFontSize}px`,\n color: hexToRgbaString(theme.colors.ACCENT_BLUE, 0.9),\n fontFamily: theme.fontFamily.KOREAN,\n marginBottom: \"4px\",\n fontWeight: \"bold\",\n textTransform: \"uppercase\",\n }}\n >\n Input Queue\n </div>\n\n {/* Input list */}\n <div style={{ display: \"flex\", flexDirection: \"column\", gap: \"2px\" }}>\n {queuedInputs.map((input, index) => {\n const opacity = 1 - index * 0.3;\n\n return (\n <div\n key={`${input.timestamp}-${input.key}`}\n data-testid={`queued-input-${index}`}\n style={{\n fontSize: `${fontSize}px`,\n color: hexToRgbaString(theme.colors.TEXT_PRIMARY, opacity),\n fontFamily: theme.fontFamily.KOREAN,\n display: \"flex\",\n justifyContent: \"space-between\",\n alignItems: \"center\",\n gap: \"8px\",\n animation: index === 0 ? \"slideIn 0.2s ease-out\" : \"none\",\n }}\n >\n {/* Action name */}\n <span\n style={{\n flex: 1,\n overflow: \"hidden\",\n textOverflow: \"ellipsis\",\n whiteSpace: \"nowrap\",\n }}\n >\n {input.action}\n </span>\n\n {/* Key badge */}\n <span\n style={{\n padding: \"2px 4px\",\n background: hexToRgbaString(\n theme.colors.ACCENT_BLUE,\n 0.2 * opacity\n ),\n border: `1px solid ${hexToRgbaString(theme.colors.ACCENT_BLUE, 0.5 * opacity)}`,\n borderRadius: \"2px\",\n fontSize: `${fontSize - 2}px`,\n fontWeight: \"bold\",\n minWidth: isMobile ? \"20px\" : \"24px\",\n textAlign: \"center\",\n }}\n >\n {input.key.toUpperCase()}\n </span>\n </div>\n );\n })}\n </div>\n\n {/* CSS Animation - Memoized to prevent redefinition */}\n {animationStyles}\n </div>\n </Html>\n );\n};\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAoDA,IAAa,sBAAyD,EACpE,cACA,WAAW,YACP;CACJ,MAAM,QAAQ,eAAe;EAAE,SAAS;EAAW,MAAM;EAAM;
|
|
1
|
+
{"version":3,"file":"InputBufferDisplay.js","names":[],"sources":["../../../../../../src/components/screens/combat/components/indicators/InputBufferDisplay.tsx"],"sourcesContent":["/**\n * InputBufferDisplay - Input queue visualization\n * Shows queued combat actions for player feedback\n * \n * Refactored to use useKoreanTheme for consistent theming.\n * \n * @module components/combat/components/InputBufferDisplay\n * @category Combat UI\n * @korean 입력버퍼표시\n */\n\nimport { Html } from \"@react-three/drei\";\nimport React, { useMemo } from \"react\";\nimport { useKoreanTheme } from \"../../../../shared/base/useKoreanTheme\";\nimport { hexToRgbaString } from \"../../../../../utils/colorUtils\";\nimport { QueuedInput } from \"../../../../../hooks/useKeyboardControls\";\n\n/**\n * Props for InputBufferDisplay component\n */\nexport interface InputBufferDisplayProps {\n /** Queued inputs to display */\n readonly queuedInputs: readonly QueuedInput[];\n /** Mobile layout flag */\n readonly isMobile?: boolean;\n}\n\n/**\n * InputBufferDisplay Component\n * \n * Displays a list of recently queued combat inputs in the top-right corner.\n * Useful for showing input confirmation and debugging input lag issues.\n * Uses useKoreanTheme for consistent styling.\n * \n * Features:\n * - Displays up to 3 recent inputs\n * - Fades older inputs with reduced opacity\n * - Shows action name and key pressed\n * - Responsive mobile layout\n * - Korean cyberpunk styling via theme\n * - Automatically clears after 2 seconds\n * \n * @example\n * ```tsx\n * <InputBufferDisplay\n * queuedInputs={queuedInputs}\n * isMobile={isMobile}\n * />\n * ```\n * \n * @korean 입력버퍼표시\n */\nexport const InputBufferDisplay: React.FC<InputBufferDisplayProps> = ({\n queuedInputs,\n isMobile = false,\n}) => {\n const theme = useKoreanTheme({ variant: \"primary\", size: \"sm\", isMobile });\n \n const animationStyles = useMemo(() => (\n <style>\n {`\n @keyframes slideIn {\n 0% {\n opacity: 0;\n transform: translateX(10px);\n }\n 100% {\n opacity: 1;\n transform: translateX(0);\n }\n }\n `}\n </style>\n ), []);\n\n if (queuedInputs.length === 0) return null;\n\n const fontSize = isMobile ? 10 : 12;\n const labelFontSize = isMobile ? 8 : 10;\n const top = isMobile ? \"10px\" : \"20px\";\n const right = isMobile ? \"10px\" : \"20px\";\n\n return (\n <Html fullscreen>\n <div\n data-testid=\"input-buffer-display\"\n role=\"log\"\n aria-live=\"polite\"\n aria-label=\"Input queue\"\n style={{\n position: \"absolute\",\n top,\n right,\n background: hexToRgbaString(theme.colors.UI_BACKGROUND_DARK, 0.8),\n border: `1px solid ${hexToRgbaString(theme.colors.ACCENT_BLUE, 0.6)}`,\n borderRadius: \"4px\",\n padding: isMobile ? \"4px\" : \"8px\",\n minWidth: isMobile ? \"100px\" : \"150px\",\n pointerEvents: \"none\",\n zIndex: 998,\n boxShadow: `0 0 10px ${hexToRgbaString(theme.colors.ACCENT_BLUE, 0.2)}`,\n }}\n >\n {/* Title */}\n <div\n style={{\n fontSize: `${labelFontSize}px`,\n color: hexToRgbaString(theme.colors.ACCENT_BLUE, 0.9),\n fontFamily: theme.fontFamily.KOREAN,\n marginBottom: \"4px\",\n fontWeight: \"bold\",\n textTransform: \"uppercase\",\n }}\n >\n Input Queue\n </div>\n\n {/* Input list */}\n <div style={{ display: \"flex\", flexDirection: \"column\", gap: \"2px\" }}>\n {queuedInputs.map((input, index) => {\n const opacity = 1 - index * 0.3;\n\n return (\n <div\n key={`${input.timestamp}-${input.key}`}\n data-testid={`queued-input-${index}`}\n style={{\n fontSize: `${fontSize}px`,\n color: hexToRgbaString(theme.colors.TEXT_PRIMARY, opacity),\n fontFamily: theme.fontFamily.KOREAN,\n display: \"flex\",\n justifyContent: \"space-between\",\n alignItems: \"center\",\n gap: \"8px\",\n animation: index === 0 ? \"slideIn 0.2s ease-out\" : \"none\",\n }}\n >\n {/* Action name */}\n <span\n style={{\n flex: 1,\n overflow: \"hidden\",\n textOverflow: \"ellipsis\",\n whiteSpace: \"nowrap\",\n }}\n >\n {input.action}\n </span>\n\n {/* Key badge */}\n <span\n style={{\n padding: \"2px 4px\",\n background: hexToRgbaString(\n theme.colors.ACCENT_BLUE,\n 0.2 * opacity\n ),\n border: `1px solid ${hexToRgbaString(theme.colors.ACCENT_BLUE, 0.5 * opacity)}`,\n borderRadius: \"2px\",\n fontSize: `${fontSize - 2}px`,\n fontWeight: \"bold\",\n minWidth: isMobile ? \"20px\" : \"24px\",\n textAlign: \"center\",\n }}\n >\n {input.key.toUpperCase()}\n </span>\n </div>\n );\n })}\n </div>\n\n {/* CSS Animation - Memoized to prevent redefinition */}\n {animationStyles}\n </div>\n </Html>\n );\n};\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAoDA,IAAa,sBAAyD,EACpE,cACA,WAAW,YACP;CACJ,MAAM,QAAQ,eAAe;EAAE,SAAS;EAAW,MAAM;EAAM;CAAS,CAAC;CAEzE,MAAM,kBAAkB,cACtB,oBAAC,SAAD,EAAA,UACG;;;;;;;;;;;QAYI,CAAA,GACN,CAAC,CAAC;CAEL,IAAI,aAAa,WAAW,GAAG,OAAO;CAEtC,MAAM,WAAW,WAAW,KAAK;CACjC,MAAM,gBAAgB,WAAW,IAAI;CAIrC,OACE,oBAAC,MAAD;EAAM,YAAA;YACJ,qBAAC,OAAD;GACE,eAAY;GACZ,MAAK;GACL,aAAU;GACV,cAAW;GACX,OAAO;IACL,UAAU;IACV,KAZI,WAAW,SAAS;IAaxB,OAZM,WAAW,SAAS;IAa1B,YAAY,gBAAgB,MAAM,OAAO,oBAAoB,EAAG;IAChE,QAAQ,aAAa,gBAAgB,MAAM,OAAO,aAAa,EAAG;IAClE,cAAc;IACd,SAAS,WAAW,QAAQ;IAC5B,UAAU,WAAW,UAAU;IAC/B,eAAe;IACf,QAAQ;IACR,WAAW,YAAY,gBAAgB,MAAM,OAAO,aAAa,EAAG;GACtE;aAjBF;IAoBE,oBAAC,OAAD;KACE,OAAO;MACL,UAAU,GAAG,cAAc;MAC3B,OAAO,gBAAgB,MAAM,OAAO,aAAa,EAAG;MACpD,YAAY,MAAM,WAAW;MAC7B,cAAc;MACd,YAAY;MACZ,eAAe;KACjB;eACD;IAEI,CAAA;IAGL,oBAAC,OAAD;KAAK,OAAO;MAAE,SAAS;MAAQ,eAAe;MAAU,KAAK;KAAM;eAChE,aAAa,KAAK,OAAO,UAAU;MAClC,MAAM,UAAU,IAAI,QAAQ;MAE5B,OACE,qBAAC,OAAD;OAEE,eAAa,gBAAgB;OAC7B,OAAO;QACL,UAAU,GAAG,SAAS;QACtB,OAAO,gBAAgB,MAAM,OAAO,cAAc,OAAO;QACzD,YAAY,MAAM,WAAW;QAC7B,SAAS;QACT,gBAAgB;QAChB,YAAY;QACZ,KAAK;QACL,WAAW,UAAU,IAAI,0BAA0B;OACrD;iBAZF,CAeE,oBAAC,QAAD;QACE,OAAO;SACL,MAAM;SACN,UAAU;SACV,cAAc;SACd,YAAY;QACd;kBAEC,MAAM;OACH,CAAA,GAGN,oBAAC,QAAD;QACE,OAAO;SACL,SAAS;SACT,YAAY,gBACV,MAAM,OAAO,aACb,KAAM,OACR;SACA,QAAQ,aAAa,gBAAgB,MAAM,OAAO,aAAa,KAAM,OAAO;SAC5E,cAAc;SACd,UAAU,GAAG,WAAW,EAAE;SAC1B,YAAY;SACZ,UAAU,WAAW,SAAS;SAC9B,WAAW;QACb;kBAEC,MAAM,IAAI,YAAY;OACnB,CAAA,CACH;SA3CE,GAAG,MAAM,UAAU,GAAG,MAAM,KA2C9B;KAET,CAAC;IACE,CAAA;IAGJ;GACE;;CACD,CAAA;AAEV"}
|