blacktrigram 0.7.47 → 0.7.49
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/App2.js.map +1 -1
- package/lib/audio/AudioAssetLoader.js.map +1 -1
- package/lib/audio/AudioAssetRegistry.js.map +1 -1
- package/lib/audio/AudioCache.js.map +1 -1
- package/lib/audio/AudioManager.js.map +1 -1
- package/lib/audio/AudioMonitor.js.map +1 -1
- package/lib/audio/AudioPool.js.map +1 -1
- package/lib/audio/AudioProvider.js.map +1 -1
- package/lib/audio/AudioUtils.js.map +1 -1
- package/lib/audio/BoneImpactAudioMap.js.map +1 -1
- package/lib/audio/VariantSelector.js.map +1 -1
- package/lib/audio/types.js.map +1 -1
- package/lib/components/screens/combat/CombatScreen3D.d.ts.map +1 -1
- package/lib/components/screens/combat/CombatScreen3D.js +29 -25
- package/lib/components/screens/combat/CombatScreen3D.js.map +1 -1
- package/lib/components/screens/combat/components/controls/CombatButtons.js.map +1 -1
- package/lib/components/screens/combat/components/controls/CombatControlsPanel.js.map +1 -1
- package/lib/components/screens/combat/components/controls/ControlsGuide.js.map +1 -1
- package/lib/components/screens/combat/components/controls/KeyboardHints.js.map +1 -1
- package/lib/components/screens/combat/components/controls/PauseMenu.js.map +1 -1
- package/lib/components/screens/combat/components/controls/PauseMenuButton.js.map +1 -1
- package/lib/components/screens/combat/components/controls/QuickSettings.js.map +1 -1
- package/lib/components/screens/combat/components/effects/BloodDecals3D.js.map +1 -1
- package/lib/components/screens/combat/components/effects/BloodLossOverlayHtml.js.map +1 -1
- package/lib/components/screens/combat/components/effects/BloodParticles3D.js.map +1 -1
- package/lib/components/screens/combat/components/effects/BloodViscosity3D.js.map +1 -1
- package/lib/components/screens/combat/components/effects/CombatParticleEffects3D.js.map +1 -1
- package/lib/components/screens/combat/components/effects/ConsciousnessBlur.js.map +1 -1
- package/lib/components/screens/combat/components/effects/InternalDamage3D.js.map +1 -1
- package/lib/components/screens/combat/components/effects/PainVignette.js.map +1 -1
- package/lib/components/screens/combat/components/effects/ParticleAudio3D.js.map +1 -1
- package/lib/components/screens/combat/components/effects/TraumaOverlay3D.js.map +1 -1
- package/lib/components/screens/combat/components/feedback/MatchCountdown.js.map +1 -1
- package/lib/components/screens/combat/components/feedback/RoundAnnouncementOverlayHtml.js.map +1 -1
- package/lib/components/screens/combat/components/feedback/RoundDisplayStatus.js.map +1 -1
- package/lib/components/screens/combat/components/feedback/RoundStartAnnouncementOverlayHtml.js.map +1 -1
- package/lib/components/screens/combat/components/hud/CombatBottomHUD.d.ts.map +1 -1
- package/lib/components/screens/combat/components/hud/CombatBottomHUD.js +2 -2
- package/lib/components/screens/combat/components/hud/CombatBottomHUD.js.map +1 -1
- package/lib/components/screens/combat/components/hud/CombatLeftHUD.js.map +1 -1
- package/lib/components/screens/combat/components/hud/CombatPortraitStatusStrip.js +1 -1
- package/lib/components/screens/combat/components/hud/CombatPortraitStatusStrip.js.map +1 -1
- package/lib/components/screens/combat/components/hud/CombatRightHUD.js.map +1 -1
- package/lib/components/screens/combat/components/hud/CombatTopHUD.d.ts.map +1 -1
- package/lib/components/screens/combat/components/hud/CombatTopHUD.js +2 -1
- package/lib/components/screens/combat/components/hud/CombatTopHUD.js.map +1 -1
- package/lib/components/screens/combat/components/hud/DifficultyIndicator.js.map +1 -1
- package/lib/components/screens/combat/components/hud/FPSMonitor.js.map +1 -1
- package/lib/components/screens/combat/components/hud/MobileControlsWrapper.js.map +1 -1
- package/lib/components/screens/combat/components/hud/PlayerStateOverlayHtml.js.map +1 -1
- package/lib/components/screens/combat/components/indicators/BalanceIndicator.js.map +1 -1
- package/lib/components/screens/combat/components/indicators/InputBufferDisplay.js.map +1 -1
- package/lib/components/screens/combat/components/indicators/StaminaWarning.js.map +1 -1
- package/lib/components/screens/combat/components/indicators/TechniqueNameDisplay.js.map +1 -1
- package/lib/components/screens/combat/helpers/AnimationUpdater.d.ts.map +1 -1
- package/lib/components/screens/combat/helpers/AnimationUpdater.js +4 -2
- package/lib/components/screens/combat/helpers/AnimationUpdater.js.map +1 -1
- package/lib/components/screens/combat/helpers/combatHelpers.js.map +1 -1
- package/lib/components/screens/combat/hooks/useAICombat.js.map +1 -1
- package/lib/components/screens/combat/hooks/useCombatActions.js.map +1 -1
- package/lib/components/screens/combat/hooks/useCombatAttackMovement.js.map +1 -1
- package/lib/components/screens/combat/hooks/useCombatAudio.js.map +1 -1
- package/lib/components/screens/combat/hooks/useCombatLayout.d.ts.map +1 -1
- package/lib/components/screens/combat/hooks/useCombatLayout.js +11 -5
- package/lib/components/screens/combat/hooks/useCombatLayout.js.map +1 -1
- package/lib/components/screens/combat/hooks/useCombatState.js.map +1 -1
- package/lib/components/screens/controls/ControlsScreen3D.js +1 -1
- package/lib/components/screens/controls/ControlsScreen3D.js.map +1 -1
- package/lib/components/screens/controls/components/ControlBindingsOverlayHtml.js.map +1 -1
- package/lib/components/screens/controls/components/ControlCategoryTabsOverlayHtml.js.map +1 -1
- package/lib/components/screens/controls/components/GamepadVisualization3D.js.map +1 -1
- package/lib/components/screens/controls/components/InteractiveControlDemoOverlayHtml.js.map +1 -1
- package/lib/components/screens/controls/components/Key3D.js.map +1 -1
- package/lib/components/screens/controls/components/VisualKeyboard3D.js.map +1 -1
- package/lib/components/screens/controls/constants/ControlsConstants.js.map +1 -1
- package/lib/components/screens/controls/hooks/useControlsState.js.map +1 -1
- package/lib/components/screens/endscreen/EndScreen3D.js.map +1 -1
- package/lib/components/screens/endscreen/components/DefeatAnimation3D.js.map +1 -1
- package/lib/components/screens/endscreen/components/MatchStatisticsDisplayOverlayHtml.js.map +1 -1
- package/lib/components/screens/endscreen/components/NavigationButtonsOverlayHtml.js.map +1 -1
- package/lib/components/screens/endscreen/components/PerformanceBreakdownOverlayHtml.js.map +1 -1
- package/lib/components/screens/endscreen/components/PerformanceRatingOverlayHtml.js.map +1 -1
- package/lib/components/screens/endscreen/components/VictoryAnimation3D.js.map +1 -1
- package/lib/components/screens/endscreen/components/WinnerDisplayOverlayHtml.js.map +1 -1
- package/lib/components/screens/intro/IntroScreen3D.js +1 -1
- package/lib/components/screens/intro/IntroScreen3D.js.map +1 -1
- package/lib/components/screens/intro/components/AbilityListOverlayHtml.js.map +1 -1
- package/lib/components/screens/intro/components/ArchetypeCardGridOverlayHtml.js.map +1 -1
- package/lib/components/screens/intro/components/ArchetypeCardOverlayHtml.js.map +1 -1
- package/lib/components/screens/intro/components/ArchetypeDisplayOverlayHtml.js.map +1 -1
- package/lib/components/screens/intro/components/EnhancedArchetypeDisplayOverlayHtml.js.map +1 -1
- package/lib/components/screens/intro/components/MenuButtonsOverlayHtml.js.map +1 -1
- package/lib/components/screens/intro/components/MenuSectionOverlayHtml.js.map +1 -1
- package/lib/components/screens/intro/components/StatBarOverlayHtml.js.map +1 -1
- package/lib/components/screens/philosophy/PhilosophyScreen3D.js +1 -1
- package/lib/components/screens/philosophy/PhilosophyScreen3D.js.map +1 -1
- package/lib/components/screens/training/TrainingScreen3D.d.ts.map +1 -1
- package/lib/components/screens/training/TrainingScreen3D.js +3 -11
- package/lib/components/screens/training/TrainingScreen3D.js.map +1 -1
- package/lib/components/screens/training/components/AnatomyControlsOverlayHtml.js.map +1 -1
- package/lib/components/screens/training/components/AnatomyOverlay3D.js.map +1 -1
- package/lib/components/screens/training/components/FootPlacementMarkers3D.js.map +1 -1
- package/lib/components/screens/training/components/FootworkDrillsOverlayHtml.js.map +1 -1
- package/lib/components/screens/training/components/HitFeedbackEffect3D.js.map +1 -1
- package/lib/components/screens/training/components/TrainingButtonsOverlayHtml.js.map +1 -1
- package/lib/components/screens/training/components/TrainingControlsOverlayHtml.js.map +1 -1
- package/lib/components/screens/training/components/TrainingDummy3D.js.map +1 -1
- package/lib/components/screens/training/components/TrainingFeedbackOverlayHtml.js.map +1 -1
- package/lib/components/screens/training/components/TrainingModeSelectorOverlayHtml.js.map +1 -1
- package/lib/components/screens/training/components/TrainingStatsOverlayHtml.js.map +1 -1
- package/lib/components/screens/training/components/VitalPointMarker3D.js.map +1 -1
- package/lib/components/screens/training/components/VitalPointTrainingOverlayHtml.js.map +1 -1
- package/lib/components/screens/training/components/hud/TrainingBottomHUD.d.ts.map +1 -1
- package/lib/components/screens/training/components/hud/TrainingBottomHUD.js +2 -2
- package/lib/components/screens/training/components/hud/TrainingBottomHUD.js.map +1 -1
- package/lib/components/screens/training/components/hud/TrainingLeftHUD.js.map +1 -1
- package/lib/components/screens/training/components/hud/TrainingRightHUD.js.map +1 -1
- package/lib/components/screens/training/components/hud/TrainingTopHUD.js.map +1 -1
- package/lib/components/screens/training/hooks/useAttackMovement.js.map +1 -1
- package/lib/components/screens/training/hooks/useTrainingActions.d.ts +1 -0
- package/lib/components/screens/training/hooks/useTrainingActions.d.ts.map +1 -1
- package/lib/components/screens/training/hooks/useTrainingActions.js +6 -4
- package/lib/components/screens/training/hooks/useTrainingActions.js.map +1 -1
- package/lib/components/screens/training/hooks/useTrainingLayout.d.ts.map +1 -1
- package/lib/components/screens/training/hooks/useTrainingLayout.js +11 -5
- package/lib/components/screens/training/hooks/useTrainingLayout.js.map +1 -1
- package/lib/components/screens/training/hooks/useTrainingState.js.map +1 -1
- package/lib/components/shared/base/BaseButton.js.map +1 -1
- package/lib/components/shared/base/BaseButtonOverlayHtml.js.map +1 -1
- package/lib/components/shared/base/BasePanel.js.map +1 -1
- package/lib/components/shared/base/BaseText.js.map +1 -1
- package/lib/components/shared/base/useKoreanTheme.js.map +1 -1
- package/lib/components/shared/debug/PerformanceDebugOverlayHtml.js.map +1 -1
- package/lib/components/shared/mobile/ActionButtons.js.map +1 -1
- package/lib/components/shared/mobile/GestureRecognizerPure.js.map +1 -1
- package/lib/components/shared/mobile/HapticController.js.map +1 -1
- package/lib/components/shared/mobile/MobileControlsPure.js.map +1 -1
- package/lib/components/shared/mobile/StanceWheelPure.js.map +1 -1
- package/lib/components/shared/mobile/TouchOptimizer.js.map +1 -1
- package/lib/components/shared/mobile/VirtualDPad.js.map +1 -1
- package/lib/components/shared/three/anatomy/BodySurface.js.map +1 -1
- package/lib/components/shared/three/anatomy/BoneAttachedMuscles.js.map +1 -1
- package/lib/components/shared/three/anatomy/BoneClothing.js.map +1 -1
- package/lib/components/shared/three/anatomy/BoneRenderer.js.map +1 -1
- package/lib/components/shared/three/anatomy/Face3D.js.map +1 -1
- package/lib/components/shared/three/anatomy/Foot3D.js.map +1 -1
- package/lib/components/shared/three/anatomy/Hand3D.js.map +1 -1
- package/lib/components/shared/three/effects/ActionFeedback.js.map +1 -1
- package/lib/components/shared/three/effects/DamageNumbers.js.map +1 -1
- package/lib/components/shared/three/effects/HitEffects3D.js.map +1 -1
- package/lib/components/shared/three/effects/PlayerStateIndicators.js.map +1 -1
- package/lib/components/shared/three/effects/StanceSymbol3D.js.map +1 -1
- package/lib/components/shared/three/effects/StanceTransitionEffect.js.map +1 -1
- package/lib/components/shared/three/effects/VitalPointMarkers3D.js.map +1 -1
- package/lib/components/shared/three/indicators/ElementalColorSystem.js.map +1 -1
- package/lib/components/shared/three/indicators/GuardIndicator.js.map +1 -1
- package/lib/components/shared/three/indicators/HapticFeedback.js.map +1 -1
- package/lib/components/shared/three/indicators/StanceChangeIndicator.js.map +1 -1
- package/lib/components/shared/three/models/Player3DWithTransitions.js.map +1 -1
- package/lib/components/shared/three/models/SkeletalPlayer3D.d.ts.map +1 -1
- package/lib/components/shared/three/models/SkeletalPlayer3D.js +7 -5
- package/lib/components/shared/three/models/SkeletalPlayer3D.js.map +1 -1
- package/lib/components/shared/three/optimization/AdaptiveQuality.js.map +1 -1
- package/lib/components/shared/three/scene/AtmosphericParticles3D.js.map +1 -1
- package/lib/components/shared/three/scene/BackgroundScene3D.js.map +1 -1
- package/lib/components/shared/three/scene/CombatArena3D.js.map +1 -1
- package/lib/components/shared/three/scene/KoreanSignage3D.js.map +1 -1
- package/lib/components/shared/three/ui/ArchetypeCard.js.map +1 -1
- package/lib/components/shared/three/ui/BodyPartHealthDisplay.js.map +1 -1
- package/lib/components/shared/three/ui/BreathingIndicator2.js.map +1 -1
- package/lib/components/shared/three/ui/CombatReadinessBar.js.map +1 -1
- package/lib/components/shared/three/ui/ComboCounter.js.map +1 -1
- package/lib/components/shared/three/ui/HealthBar.js.map +1 -1
- package/lib/components/shared/three/ui/KoreanButton.js.map +1 -1
- package/lib/components/shared/three/ui/KoreanPanel.js.map +1 -1
- package/lib/components/shared/three/ui/KoreanText.js.map +1 -1
- package/lib/components/shared/three/ui/MenuList.js.map +1 -1
- package/lib/components/shared/three/ui/PlayerHUD.js.map +1 -1
- package/lib/components/shared/three/ui/ProgressBar.js.map +1 -1
- package/lib/components/shared/three/ui/SpeedIndicatorHUD.js.map +1 -1
- package/lib/components/shared/three/ui/StaminaBar.js.map +1 -1
- package/lib/components/shared/three/ui/TechniqueBar.js.map +1 -1
- package/lib/components/shared/three/ui/TechniqueCard.js.map +1 -1
- package/lib/components/shared/three/ui/VitalPointOverlayControlsHtml.js.map +1 -1
- package/lib/components/shared/ui/BackButton.js.map +1 -1
- package/lib/components/shared/ui/BaseHUDContainer.js.map +1 -1
- package/lib/components/shared/ui/CombatTimer.js.map +1 -1
- package/lib/components/shared/ui/ErrorModal.js.map +1 -1
- package/lib/components/shared/ui/LoadingState.js.map +1 -1
- package/lib/components/shared/ui/SplashScreen.js +2 -2
- package/lib/components/shared/ui/SplashScreen.js.map +1 -1
- package/lib/components/shared/ui/VitalPointOverlayControlsPure.js.map +1 -1
- package/lib/components/shared/ui/VolumeControl.js.map +1 -1
- package/lib/components/shared/ui/shared/ConfirmDialog.js.map +1 -1
- package/lib/components/ui/combat/BalanceIndicatorOverlayHtml.js.map +1 -1
- package/lib/constants/bodyDimensions.js.map +1 -1
- package/lib/constants/bodyRenderingConstants.js.map +1 -1
- package/lib/data/archetypeClothing.js.map +1 -1
- package/lib/data/archetypePhysicalAttributes.js.map +1 -1
- package/lib/data/techniqueMappings.js.map +1 -1
- package/lib/data/techniques.js.map +1 -1
- package/lib/hooks/useActionFeedback.js.map +1 -1
- package/lib/hooks/useBalanceAnimations.js.map +1 -1
- package/lib/hooks/useCombatTimer.js.map +1 -1
- package/lib/hooks/useDebounce.js.map +1 -1
- package/lib/hooks/useHUDLayout.d.ts.map +1 -1
- package/lib/hooks/useHUDLayout.js +3 -2
- package/lib/hooks/useHUDLayout.js.map +1 -1
- package/lib/hooks/useHandPoseTransitions.js.map +1 -1
- package/lib/hooks/useKeyboardControls.js.map +1 -1
- package/lib/hooks/useMatchCountdown.js.map +1 -1
- package/lib/hooks/useMuscleActivation.js.map +1 -1
- package/lib/hooks/usePauseMenu.js.map +1 -1
- package/lib/hooks/usePlayerAnimation.js.map +1 -1
- package/lib/hooks/useResponsiveLayout.js.map +1 -1
- package/lib/hooks/useRoundTransition.js.map +1 -1
- package/lib/hooks/useSkeletalAnimation.d.ts.map +1 -1
- package/lib/hooks/useSkeletalAnimation.js +1 -1
- package/lib/hooks/useSkeletalAnimation.js.map +1 -1
- package/lib/hooks/useTechniqueSelection.js.map +1 -1
- package/lib/hooks/useThrottle.js.map +1 -1
- package/lib/hooks/useTouchControls.js.map +1 -1
- package/lib/hooks/useWebGLContextLossHandler.js.map +1 -1
- package/lib/hooks/useWindowSize.js.map +1 -1
- package/lib/systems/CombatSystem.js.map +1 -1
- package/lib/systems/EffectCalculator.js.map +1 -1
- package/lib/systems/LayoutSystem.js.map +1 -1
- package/lib/systems/PlayerEffectManager.js.map +1 -1
- package/lib/systems/ResponsiveScaling.js.map +1 -1
- package/lib/systems/TrigramSystem.js.map +1 -1
- package/lib/systems/VitalPointSystem.js.map +1 -1
- package/lib/systems/ai/AIPersonality.js.map +1 -1
- package/lib/systems/ai/AdaptiveDifficulty.js +16 -16
- package/lib/systems/ai/AdaptiveDifficulty.js.map +1 -1
- package/lib/systems/ai/ArchetypeEnforcer.js.map +1 -1
- package/lib/systems/ai/ComboSystem.js.map +1 -1
- package/lib/systems/ai/DecisionTree.js.map +1 -1
- package/lib/systems/ai/TrainingAI.js.map +1 -1
- package/lib/systems/ai/types.js.map +1 -1
- package/lib/systems/animation/builders/AnimationBuilder.js.map +1 -1
- package/lib/systems/animation/builders/HandPoseApplicator.js.map +1 -1
- package/lib/systems/animation/builders/HandPoses.js.map +1 -1
- package/lib/systems/animation/builders/KeyframeConfig.js.map +1 -1
- package/lib/systems/animation/builders/KeyframeInterpolation.js.map +1 -1
- package/lib/systems/animation/builders/KickPhaseApplicator.d.ts +6 -0
- package/lib/systems/animation/builders/KickPhaseApplicator.d.ts.map +1 -1
- package/lib/systems/animation/builders/KickPhaseApplicator.js +16 -9
- package/lib/systems/animation/builders/KickPhaseApplicator.js.map +1 -1
- package/lib/systems/animation/builders/KoreanGuardPositions.d.ts +4 -4
- package/lib/systems/animation/builders/KoreanGuardPositions.js.map +1 -1
- package/lib/systems/animation/builders/MartialArtsAnimationBuilder.d.ts +1 -1
- package/lib/systems/animation/builders/MartialArtsAnimationBuilder.d.ts.map +1 -1
- package/lib/systems/animation/builders/MartialArtsAnimationBuilder.js +5 -5
- package/lib/systems/animation/builders/MartialArtsAnimationBuilder.js.map +1 -1
- package/lib/systems/animation/builders/MartialArtsConstants.d.ts +112 -71
- package/lib/systems/animation/builders/MartialArtsConstants.d.ts.map +1 -1
- package/lib/systems/animation/builders/MartialArtsConstants.js +113 -72
- package/lib/systems/animation/builders/MartialArtsConstants.js.map +1 -1
- package/lib/systems/animation/builders/MartialPoseApplicator.js.map +1 -1
- package/lib/systems/animation/builders/PunchPhaseApplicator.js.map +1 -1
- package/lib/systems/animation/builders/SkeletonRig.js.map +1 -1
- package/lib/systems/animation/builders/TrigramGuardApplicator.js.map +1 -1
- package/lib/systems/animation/catalogs/AttackAnimations.js.map +1 -1
- package/lib/systems/animation/catalogs/BasicAnimations.js.map +1 -1
- package/lib/systems/animation/catalogs/ComboAnimations.js.map +1 -1
- package/lib/systems/animation/catalogs/DarkOpsAnimations.js.map +1 -1
- package/lib/systems/animation/catalogs/DefensiveAnimations.js.map +1 -1
- package/lib/systems/animation/catalogs/ElbowKneeAnimations.js.map +1 -1
- package/lib/systems/animation/catalogs/EnhancedAttackAnimations.js.map +1 -1
- package/lib/systems/animation/catalogs/EnhancedElbowKneeAnimations.js.map +1 -1
- package/lib/systems/animation/catalogs/FootworkSkeletalAnimations.js.map +1 -1
- package/lib/systems/animation/catalogs/GamRedirectionAnimations.js.map +1 -1
- package/lib/systems/animation/catalogs/GamStanceAnimations.js +21 -0
- package/lib/systems/animation/catalogs/GamStanceAnimations.js.map +1 -0
- package/lib/systems/animation/catalogs/GamTechniqueAnimations.js +34 -2
- package/lib/systems/animation/catalogs/GamTechniqueAnimations.js.map +1 -1
- package/lib/systems/animation/catalogs/GanStanceAnimations.js.map +1 -1
- package/lib/systems/animation/catalogs/GanTechniqueAnimations.js.map +1 -1
- package/lib/systems/animation/catalogs/GeonStanceAnimations.js.map +1 -1
- package/lib/systems/animation/catalogs/GonTechniqueAnimations.d.ts +9 -0
- package/lib/systems/animation/catalogs/GonTechniqueAnimations.d.ts.map +1 -1
- package/lib/systems/animation/catalogs/GonTechniqueAnimations.js +288 -0
- package/lib/systems/animation/catalogs/GonTechniqueAnimations.js.map +1 -0
- package/lib/systems/animation/catalogs/GrapplingAnimations.js.map +1 -1
- package/lib/systems/animation/catalogs/JinStanceAnimations.js.map +1 -1
- package/lib/systems/animation/catalogs/JinTechniqueAnimations.js.map +1 -1
- package/lib/systems/animation/catalogs/KickAnimations.d.ts +2 -2
- package/lib/systems/animation/catalogs/KickAnimations.js +2 -2
- package/lib/systems/animation/catalogs/KickAnimations.js.map +1 -1
- package/lib/systems/animation/catalogs/LiStanceAnimations.js +14 -1
- package/lib/systems/animation/catalogs/LiStanceAnimations.js.map +1 -1
- package/lib/systems/animation/catalogs/LiTechniqueAnimations.js.map +1 -1
- package/lib/systems/animation/catalogs/MovementAnimations.js.map +1 -1
- package/lib/systems/animation/catalogs/PunchAnimations.d.ts +1 -1
- package/lib/systems/animation/catalogs/PunchAnimations.js +1 -1
- package/lib/systems/animation/catalogs/PunchAnimations.js.map +1 -1
- package/lib/systems/animation/catalogs/RecoveryAnimations.js.map +1 -1
- package/lib/systems/animation/catalogs/SonStanceAnimations.js.map +1 -1
- package/lib/systems/animation/catalogs/SonTechniqueAnimations.js.map +1 -1
- package/lib/systems/animation/catalogs/SpecializedPunchAnimations.js.map +1 -1
- package/lib/systems/animation/catalogs/StanceAnimations.js.map +1 -1
- package/lib/systems/animation/catalogs/StanceAttackAnimations.js.map +1 -1
- package/lib/systems/animation/catalogs/StanceGuardPoses.d.ts +6 -6
- package/lib/systems/animation/catalogs/StanceGuardPoses.js +36 -36
- package/lib/systems/animation/catalogs/StanceGuardPoses.js.map +1 -1
- package/lib/systems/animation/catalogs/StanceIdleAnimations.js.map +1 -1
- package/lib/systems/animation/catalogs/StanceLocomotionAnimations.js.map +1 -1
- package/lib/systems/animation/catalogs/StepSkeletalAnimations.js.map +1 -1
- package/lib/systems/animation/catalogs/TaeJointLockAnimations.js.map +1 -1
- package/lib/systems/animation/catalogs/TaeStanceAnimations.js.map +1 -1
- package/lib/systems/animation/constants/AnatomicalLimits.js.map +1 -1
- package/lib/systems/animation/core/AnimationHitTiming.js.map +1 -1
- package/lib/systems/animation/core/AnimationOptimizations.js.map +1 -1
- package/lib/systems/animation/core/AnimationPriority.js +15 -15
- package/lib/systems/animation/core/AnimationPriority.js.map +1 -1
- package/lib/systems/animation/core/AnimationRegistry.d.ts +30 -0
- package/lib/systems/animation/core/AnimationRegistry.d.ts.map +1 -1
- package/lib/systems/animation/core/AnimationRegistry.js +74 -12
- package/lib/systems/animation/core/AnimationRegistry.js.map +1 -1
- package/lib/systems/animation/core/AnimationStateMachine.js +16 -16
- package/lib/systems/animation/core/AnimationStateMachine.js.map +1 -1
- package/lib/systems/animation/core/AnimationTransitions.d.ts.map +1 -1
- package/lib/systems/animation/core/AnimationTransitions.js +34 -0
- package/lib/systems/animation/core/AnimationTransitions.js.map +1 -1
- package/lib/systems/animation/core/LateralityTransform.js.map +1 -1
- package/lib/systems/animation/core/RecoveryPhaseEnhancer.js.map +1 -1
- package/lib/systems/animation/core/TechniqueAnimationMapper.js.map +1 -1
- package/lib/systems/animation/core/TechniqueAnimationMapping.js.map +1 -1
- package/lib/systems/animation/core/index.d.ts +1 -1
- package/lib/systems/animation/core/index.d.ts.map +1 -1
- package/lib/systems/animation/core/types.d.ts +24 -0
- package/lib/systems/animation/core/types.d.ts.map +1 -1
- package/lib/systems/animation/core/types.js +27 -11
- package/lib/systems/animation/core/types.js.map +1 -1
- package/lib/systems/animation/systems/AdvancedJointMovements.js.map +1 -1
- package/lib/systems/animation/systems/BodyFacingSystem.js.map +1 -1
- package/lib/systems/animation/systems/FacialExpressions.js.map +1 -1
- package/lib/systems/animation/systems/FallAnimations.js.map +1 -1
- package/lib/systems/animation/systems/MuscleActivation.js.map +1 -1
- package/lib/systems/bodypart/BodyPartDamageIntegration.js.map +1 -1
- package/lib/systems/bodypart/BodyPartHealthSystem.js.map +1 -1
- package/lib/systems/bodypart/BodyPartPositionMapping.js.map +1 -1
- package/lib/systems/bodypart/CombatInjuryIntegration.js.map +1 -1
- package/lib/systems/bodypart/InjuryIntegration.js.map +1 -1
- package/lib/systems/bodypart/InjuryTracker.js.map +1 -1
- package/lib/systems/bodypart/MovementPenaltySystem.js.map +1 -1
- package/lib/systems/bodypart/PlayerInjuryTrackingManager.js.map +1 -1
- package/lib/systems/bodypart/types.js.map +1 -1
- package/lib/systems/breathing/BreathingDisruptionSystem.js +19 -19
- package/lib/systems/breathing/BreathingDisruptionSystem.js.map +1 -1
- package/lib/systems/breathing/feedback.js.map +1 -1
- package/lib/systems/breathing/integration.js.map +1 -1
- package/lib/systems/combat/BalanceSystem.js +19 -19
- package/lib/systems/combat/BalanceSystem.js.map +1 -1
- package/lib/systems/combat/BreakingStatusEffects.js.map +1 -1
- package/lib/systems/combat/CombatStateSystem.js +17 -17
- package/lib/systems/combat/CombatStateSystem.js.map +1 -1
- package/lib/systems/combat/ConsciousnessSystem.js +24 -24
- package/lib/systems/combat/ConsciousnessSystem.js.map +1 -1
- package/lib/systems/combat/FallIntegration.js.map +1 -1
- package/lib/systems/combat/GrappleSystem.js.map +1 -1
- package/lib/systems/combat/LimbExposureSystem.js.map +1 -1
- package/lib/systems/combat/PainResponseSystem.js +21 -21
- package/lib/systems/combat/PainResponseSystem.js.map +1 -1
- package/lib/systems/combat/TrainingCombatSystem.js.map +1 -1
- package/lib/systems/combat/painConsciousnessUtils.js.map +1 -1
- package/lib/systems/combat/typeGuards.js.map +1 -1
- package/lib/systems/effects.js.map +1 -1
- package/lib/systems/game.js.map +1 -1
- package/lib/systems/movement/InjuryMovementModifier.js.map +1 -1
- package/lib/systems/movement/helpers/AccelerationUpdater.js.map +1 -1
- package/lib/systems/movement/helpers/accelerationUtils.js.map +1 -1
- package/lib/systems/movement/integration.js.map +1 -1
- package/lib/systems/physics/AttackMovementPhysics.js.map +1 -1
- package/lib/systems/physics/CollisionDetection.js.map +1 -1
- package/lib/systems/physics/CoordinateMapper.js.map +1 -1
- package/lib/systems/physics/KnockbackPhysics.js.map +1 -1
- package/lib/systems/physics/MovementPhysics.js.map +1 -1
- package/lib/systems/physics/PhysicalReachCalculator.js.map +1 -1
- package/lib/systems/physics/SpeedModifierSystem.js +6 -6
- package/lib/systems/physics/SpeedModifierSystem.js.map +1 -1
- package/lib/systems/trigram/KoreanCulture.js.map +1 -1
- package/lib/systems/trigram/KoreanTechniques.js.map +1 -1
- package/lib/systems/trigram/StanceManager.js.map +1 -1
- package/lib/systems/trigram/TransitionCalculator.js.map +1 -1
- package/lib/systems/trigram/TrigramCalculator.js.map +1 -1
- package/lib/systems/trigram/techniques/DarkOpsTechniques.js.map +1 -1
- package/lib/systems/trigram/techniques/GamTechniques.js.map +1 -1
- package/lib/systems/trigram/techniques/GanTechniques.js.map +1 -1
- package/lib/systems/trigram/techniques/GeonTechniques.js.map +1 -1
- package/lib/systems/trigram/techniques/GonTechniques.js.map +1 -1
- package/lib/systems/trigram/techniques/JinTechniques.js.map +1 -1
- package/lib/systems/trigram/techniques/LiTechniques.js.map +1 -1
- package/lib/systems/trigram/techniques/SonTechniques.js.map +1 -1
- package/lib/systems/trigram/techniques/TaeTechniques.js.map +1 -1
- package/lib/systems/trigram/techniques/TechniqueConfig.js.map +1 -1
- package/lib/systems/trigram/techniques/index.js.map +1 -1
- package/lib/systems/trigram/types/GonTechniqueExtensions.js.map +1 -1
- package/lib/systems/trigram/types.js.map +1 -1
- package/lib/systems/types.js.map +1 -1
- package/lib/systems/vitalpoint/DamageCalculator.js.map +1 -1
- package/lib/systems/vitalpoint/HitDetection.js.map +1 -1
- package/lib/systems/vitalpoint/KoreanAnatomy.js.map +1 -1
- package/lib/systems/vitalpoint/KoreanVitalPoints.js.map +1 -1
- package/lib/systems/vitalpoint/MeridianVitalPointMapping.js.map +1 -1
- package/lib/systems/vitalpoint/VitalPointsData.js.map +1 -1
- package/lib/types/AccessibilityTypes.js.map +1 -1
- package/lib/types/LayoutTypes.js.map +1 -1
- package/lib/types/PhysicsTypes.js.map +1 -1
- package/lib/types/common.js.map +1 -1
- package/lib/types/constants/animations.js.map +1 -1
- package/lib/types/constants/colors.js.map +1 -1
- package/lib/types/constants/designSystem.js.map +1 -1
- package/lib/types/constants/index.js.map +1 -1
- package/lib/types/constants/layout.d.ts +21 -0
- package/lib/types/constants/layout.d.ts.map +1 -1
- package/lib/types/constants/layout.js +22 -1
- package/lib/types/constants/layout.js.map +1 -1
- package/lib/types/constants/performance.js.map +1 -1
- package/lib/types/constants/typography.js.map +1 -1
- package/lib/types/constants/ui.js.map +1 -1
- package/lib/types/facial.js +19 -19
- package/lib/types/facial.js.map +1 -1
- package/lib/types/hand-animation.js.map +1 -1
- package/lib/types/injury.js.map +1 -1
- package/lib/types/muscle.js.map +1 -1
- package/lib/types/physics.js.map +1 -1
- package/lib/types/physicsConstants.js.map +1 -1
- package/lib/types/player-visual.d.ts +1 -1
- package/lib/types/player-visual.d.ts.map +1 -1
- package/lib/types/skeletal.js.map +1 -1
- package/lib/types/techniqueId.js.map +1 -1
- package/lib/utils/accessibility.js.map +1 -1
- package/lib/utils/arenaWorldDimensions.js.map +1 -1
- package/lib/utils/assetConfig.js.map +1 -1
- package/lib/utils/characterScaling.js.map +1 -1
- package/lib/utils/colorHelpers.js.map +1 -1
- package/lib/utils/colorUtils.js.map +1 -1
- package/lib/utils/combatReadiness.js.map +1 -1
- package/lib/utils/controlMapping.js.map +1 -1
- package/lib/utils/deviceDetection.js +6 -7
- package/lib/utils/deviceDetection.js.map +1 -1
- package/lib/utils/effectUtils.js.map +1 -1
- package/lib/utils/fabricTextures.js.map +1 -1
- package/lib/utils/hapticFeedback.js.map +1 -1
- package/lib/utils/haptics.js.map +1 -1
- package/lib/utils/htmlOverlayHelpers.js.map +1 -1
- package/lib/utils/inputSystem.js.map +1 -1
- package/lib/utils/koreanThemeHelpers.js.map +1 -1
- package/lib/utils/math.js.map +1 -1
- package/lib/utils/mobileLayoutHelpers.js.map +1 -1
- package/lib/utils/mobileUIUtils.js.map +1 -1
- package/lib/utils/performance/PerformanceMonitor.js.map +1 -1
- package/lib/utils/performance/PerformanceOverlay3D.js.map +1 -1
- package/lib/utils/performance/usePerformanceMonitor.js.map +1 -1
- package/lib/utils/performanceOptimization.js.map +1 -1
- package/lib/utils/player3DHelpers.js.map +1 -1
- package/lib/utils/playerUtils.js.map +1 -1
- package/lib/utils/responsiveLayout.js.map +1 -1
- package/lib/utils/responsiveLayoutHelpers.d.ts +7 -0
- package/lib/utils/responsiveLayoutHelpers.d.ts.map +1 -1
- package/lib/utils/responsiveLayoutHelpers.js +16 -2
- package/lib/utils/responsiveLayoutHelpers.js.map +1 -1
- package/lib/utils/responsiveOrientationConstants.js.map +1 -1
- package/lib/utils/safeAreaUtils.js.map +1 -1
- package/lib/utils/sharedPhysicsConfig.js.map +1 -1
- package/lib/utils/skeletonScaling.js.map +1 -1
- package/lib/utils/stanceHelpers.js.map +1 -1
- package/lib/utils/threeObjectPool.js.map +1 -1
- package/lib/utils/visualEffects.js.map +1 -1
- package/package.json +7 -7
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"GamepadVisualization3D.js","names":[],"sources":["../../../../../src/components/screens/controls/components/GamepadVisualization3D.tsx"],"sourcesContent":["/**\n * GamepadVisualization3D - 3D gamepad visualization with button labels\n *\n * Renders a simplified 3D gamepad shape with colored buttons and\n * bilingual Korean-English labels for each button.\n *\n * @module components/screens/controls/components\n */\n\nimport { Html } from \"@react-three/drei\";\nimport React, { useEffect, useMemo } from \"react\";\nimport * as THREE from \"three\";\nimport { FONT_FAMILY, KOREAN_COLORS } from \"../../../../types/constants\";\nimport { hexToRgbaString } from \"../../../../utils/colorUtils\";\nimport { GAMEPAD_BUTTONS } from \"../constants/ControlsConstants\";\n\n/**\n * Props for GamepadVisualization3D component\n */\nexport interface GamepadVisualization3DProps {\n /** Whether on mobile device (affects sizing) */\n readonly isMobile: boolean;\n}\n\n/**\n * GamepadVisualization3D Component\n *\n * 3D visualization of a gamepad with labeled buttons.\n * Displays simplified gamepad shape with colored circles for buttons.\n *\n * @example\n * ```tsx\n * <Canvas>\n * <GamepadVisualization3D isMobile={false} />\n * </Canvas>\n * ```\n */\nexport const GamepadVisualization3D: React.FC<GamepadVisualization3DProps> = ({\n isMobile,\n}) => {\n const bodyMaterial = useMemo(\n () =>\n new THREE.MeshStandardMaterial({\n color: KOREAN_COLORS.UI_STEEL_GRAY_DARK,\n metalness: 0.7,\n roughness: 0.3,\n transparent: true,\n opacity: 0.95,\n }),\n [],\n );\n\n useEffect(() => {\n return () => {\n bodyMaterial.dispose();\n };\n }, [bodyMaterial]);\n\n const buttonPositions = useMemo(\n () => ({\n 0: { x: 1.5, y: 0.5, z: 0.15 }, // A\n 1: { x: 2.0, y: 0.0, z: 0.15 }, // B\n 2: { x: 1.0, y: 0.0, z: 0.15 }, // X\n 3: { x: 1.5, y: -0.5, z: 0.15 }, // Y\n 4: { x: -2.0, y: 1.0, z: 0.2 }, // LB\n 5: { x: 2.0, y: 1.0, z: 0.2 }, // RB\n 6: { x: -2.0, y: 1.4, z: 0.2 }, // LT\n 7: { x: 2.0, y: 1.4, z: 0.2 }, // RT\n 8: { x: -0.6, y: 0.0, z: 0.15 }, // Back\n 9: { x: 0.6, y: 0.0, z: 0.15 }, // Start\n 10: { x: -1.5, y: -0.8, z: 0.15 }, // L3\n 11: { x: 1.5, y: -0.8, z: 0.15 }, // R3\n }),\n [],\n );\n\n return (\n <group position={[0, 0, 0]} data-testid=\"gamepad-visualization\">\n {/* Ambient light */}\n <ambientLight intensity={0.5} />\n\n {/* Directional lights */}\n <directionalLight position={[3, 3, 3]} intensity={0.8} />\n <directionalLight position={[-3, 2, 2]} intensity={0.4} />\n\n {/* Left body section (rounded rectangle) */}\n <mesh position={[-1.5, 0, 0]}>\n <boxGeometry args={[2.5, 2.5, 0.4]} />\n <primitive object={bodyMaterial} attach=\"material\" />\n </mesh>\n\n {/* Right body section (rounded rectangle) */}\n <mesh position={[1.5, 0, 0]}>\n <boxGeometry args={[2.5, 2.5, 0.4]} />\n <primitive object={bodyMaterial} attach=\"material\" />\n </mesh>\n\n {/* Center connector */}\n <mesh position={[0, 0, 0]}>\n <boxGeometry args={[1.5, 1.5, 0.35]} />\n <primitive object={bodyMaterial} attach=\"material\" />\n </mesh>\n\n {/* Render all buttons */}\n {GAMEPAD_BUTTONS.map((button) => {\n const pos =\n buttonPositions[button.index as keyof typeof buttonPositions];\n if (!pos) return null;\n\n return (\n <group key={button.index} position={[pos.x, pos.y, pos.z]}>\n {/* Button sphere */}\n <mesh>\n <sphereGeometry args={[0.15, 16, 16]} />\n <meshStandardMaterial\n color={button.color}\n emissive={button.color}\n emissiveIntensity={1.5}\n metalness={0.5}\n roughness={0.3}\n />\n </mesh>\n\n {/* Button label overlay */}\n <Html\n position={[0, 0.3, 0]}\n center\n distanceFactor={isMobile ? 3 : 2.5}\n style={{ pointerEvents: \"none\" }}\n >\n <div\n style={{\n fontFamily: FONT_FAMILY.KOREAN,\n fontSize: isMobile ? \"10px\" : \"11px\",\n fontWeight: \"bold\",\n color: hexToRgbaString(KOREAN_COLORS.TEXT_PRIMARY),\n textAlign: \"center\",\n whiteSpace: \"nowrap\",\n background: hexToRgbaString(\n KOREAN_COLORS.UI_BACKGROUND_DARK,\n 0.8,\n ),\n padding: \"4px 6px\",\n borderRadius: \"4px\",\n border: `1px solid ${hexToRgbaString(button.color, 0.6)}`,\n boxShadow: `0 0 8px ${hexToRgbaString(button.color, 0.4)}`,\n }}\n >\n {/* Button name */}\n <div\n style={{\n color: hexToRgbaString(button.color),\n marginBottom: \"2px\",\n }}\n >\n {button.korean} | {button.english}\n </div>\n {/* Action */}\n <div\n style={{\n fontSize: isMobile ? \"9px\" : \"10px\",\n color: hexToRgbaString(KOREAN_COLORS.TEXT_SECONDARY),\n }}\n >\n {button.actionKorean} | {button.action}\n </div>\n </div>\n </Html>\n </group>\n );\n })}\n </group>\n );\n};\n\nexport default GamepadVisualization3D;\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAqCA,IAAa,0BAAiE,EAC5E,eACI;CACJ,MAAM,eAAe,cAEjB,IAAI,MAAM,qBAAqB;EAC7B,OAAO,cAAc;EACrB,WAAW;EACX,WAAW;EACX,aAAa;EACb,SAAS;
|
|
1
|
+
{"version":3,"file":"GamepadVisualization3D.js","names":[],"sources":["../../../../../src/components/screens/controls/components/GamepadVisualization3D.tsx"],"sourcesContent":["/**\n * GamepadVisualization3D - 3D gamepad visualization with button labels\n *\n * Renders a simplified 3D gamepad shape with colored buttons and\n * bilingual Korean-English labels for each button.\n *\n * @module components/screens/controls/components\n */\n\nimport { Html } from \"@react-three/drei\";\nimport React, { useEffect, useMemo } from \"react\";\nimport * as THREE from \"three\";\nimport { FONT_FAMILY, KOREAN_COLORS } from \"../../../../types/constants\";\nimport { hexToRgbaString } from \"../../../../utils/colorUtils\";\nimport { GAMEPAD_BUTTONS } from \"../constants/ControlsConstants\";\n\n/**\n * Props for GamepadVisualization3D component\n */\nexport interface GamepadVisualization3DProps {\n /** Whether on mobile device (affects sizing) */\n readonly isMobile: boolean;\n}\n\n/**\n * GamepadVisualization3D Component\n *\n * 3D visualization of a gamepad with labeled buttons.\n * Displays simplified gamepad shape with colored circles for buttons.\n *\n * @example\n * ```tsx\n * <Canvas>\n * <GamepadVisualization3D isMobile={false} />\n * </Canvas>\n * ```\n */\nexport const GamepadVisualization3D: React.FC<GamepadVisualization3DProps> = ({\n isMobile,\n}) => {\n const bodyMaterial = useMemo(\n () =>\n new THREE.MeshStandardMaterial({\n color: KOREAN_COLORS.UI_STEEL_GRAY_DARK,\n metalness: 0.7,\n roughness: 0.3,\n transparent: true,\n opacity: 0.95,\n }),\n [],\n );\n\n useEffect(() => {\n return () => {\n bodyMaterial.dispose();\n };\n }, [bodyMaterial]);\n\n const buttonPositions = useMemo(\n () => ({\n 0: { x: 1.5, y: 0.5, z: 0.15 }, // A\n 1: { x: 2.0, y: 0.0, z: 0.15 }, // B\n 2: { x: 1.0, y: 0.0, z: 0.15 }, // X\n 3: { x: 1.5, y: -0.5, z: 0.15 }, // Y\n 4: { x: -2.0, y: 1.0, z: 0.2 }, // LB\n 5: { x: 2.0, y: 1.0, z: 0.2 }, // RB\n 6: { x: -2.0, y: 1.4, z: 0.2 }, // LT\n 7: { x: 2.0, y: 1.4, z: 0.2 }, // RT\n 8: { x: -0.6, y: 0.0, z: 0.15 }, // Back\n 9: { x: 0.6, y: 0.0, z: 0.15 }, // Start\n 10: { x: -1.5, y: -0.8, z: 0.15 }, // L3\n 11: { x: 1.5, y: -0.8, z: 0.15 }, // R3\n }),\n [],\n );\n\n return (\n <group position={[0, 0, 0]} data-testid=\"gamepad-visualization\">\n {/* Ambient light */}\n <ambientLight intensity={0.5} />\n\n {/* Directional lights */}\n <directionalLight position={[3, 3, 3]} intensity={0.8} />\n <directionalLight position={[-3, 2, 2]} intensity={0.4} />\n\n {/* Left body section (rounded rectangle) */}\n <mesh position={[-1.5, 0, 0]}>\n <boxGeometry args={[2.5, 2.5, 0.4]} />\n <primitive object={bodyMaterial} attach=\"material\" />\n </mesh>\n\n {/* Right body section (rounded rectangle) */}\n <mesh position={[1.5, 0, 0]}>\n <boxGeometry args={[2.5, 2.5, 0.4]} />\n <primitive object={bodyMaterial} attach=\"material\" />\n </mesh>\n\n {/* Center connector */}\n <mesh position={[0, 0, 0]}>\n <boxGeometry args={[1.5, 1.5, 0.35]} />\n <primitive object={bodyMaterial} attach=\"material\" />\n </mesh>\n\n {/* Render all buttons */}\n {GAMEPAD_BUTTONS.map((button) => {\n const pos =\n buttonPositions[button.index as keyof typeof buttonPositions];\n if (!pos) return null;\n\n return (\n <group key={button.index} position={[pos.x, pos.y, pos.z]}>\n {/* Button sphere */}\n <mesh>\n <sphereGeometry args={[0.15, 16, 16]} />\n <meshStandardMaterial\n color={button.color}\n emissive={button.color}\n emissiveIntensity={1.5}\n metalness={0.5}\n roughness={0.3}\n />\n </mesh>\n\n {/* Button label overlay */}\n <Html\n position={[0, 0.3, 0]}\n center\n distanceFactor={isMobile ? 3 : 2.5}\n style={{ pointerEvents: \"none\" }}\n >\n <div\n style={{\n fontFamily: FONT_FAMILY.KOREAN,\n fontSize: isMobile ? \"10px\" : \"11px\",\n fontWeight: \"bold\",\n color: hexToRgbaString(KOREAN_COLORS.TEXT_PRIMARY),\n textAlign: \"center\",\n whiteSpace: \"nowrap\",\n background: hexToRgbaString(\n KOREAN_COLORS.UI_BACKGROUND_DARK,\n 0.8,\n ),\n padding: \"4px 6px\",\n borderRadius: \"4px\",\n border: `1px solid ${hexToRgbaString(button.color, 0.6)}`,\n boxShadow: `0 0 8px ${hexToRgbaString(button.color, 0.4)}`,\n }}\n >\n {/* Button name */}\n <div\n style={{\n color: hexToRgbaString(button.color),\n marginBottom: \"2px\",\n }}\n >\n {button.korean} | {button.english}\n </div>\n {/* Action */}\n <div\n style={{\n fontSize: isMobile ? \"9px\" : \"10px\",\n color: hexToRgbaString(KOREAN_COLORS.TEXT_SECONDARY),\n }}\n >\n {button.actionKorean} | {button.action}\n </div>\n </div>\n </Html>\n </group>\n );\n })}\n </group>\n );\n};\n\nexport default GamepadVisualization3D;\n"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"InteractiveControlDemoOverlayHtml.js","names":[],"sources":["../../../../../src/components/screens/controls/components/InteractiveControlDemoOverlayHtml.tsx"],"sourcesContent":["/**\n * InteractiveControlDemo - Display recently pressed keys and their actions\n * \n * Shows the last 5 pressed keys with their descriptions in a bottom overlay.\n * Keys auto-fade after 2 seconds for a clean, non-intrusive display.\n * \n * @module components/screens/controls/components\n */\n\nimport React, { useEffect, useMemo, useState } from \"react\";\nimport { FONT_FAMILY } from \"../../../../types/constants\";\nimport { hexToRgbaString } from \"../../../../utils/colorUtils\";\nimport { useKoreanTheme } from \"../../../shared/base/useKoreanTheme\";\nimport { getKeyCategoryColor, KEYBOARD_LAYOUT, type KeyData } from \"../constants/ControlsConstants\";\n\n/**\n * Props for InteractiveControlDemo component\n */\nexport interface InteractiveControlDemoProps {\n /** Set of currently pressed key codes */\n readonly pressedKeys: Set<string>;\n /** Whether on mobile device */\n readonly isMobile: boolean;\n}\n\n/**\n * Key press entry with timestamp\n */\ninterface KeyPressEntry {\n readonly keyData: KeyData;\n readonly timestamp: number;\n readonly id: string;\n}\n\n/**\n * Props for KeyPressEntryDisplay component\n */\ninterface KeyPressEntryDisplayProps {\n readonly entry: KeyPressEntry;\n readonly opacity: number;\n readonly categoryColor: number;\n readonly isMobile: boolean;\n readonly theme: ReturnType<typeof useKoreanTheme>;\n}\n\n/**\n * Memoized KeyPressEntryDisplay Component\n * \n * Displays a single key press entry with optimized inline styles.\n */\nconst KeyPressEntryDisplay = React.memo<KeyPressEntryDisplayProps>(({\n entry,\n opacity,\n categoryColor,\n isMobile,\n theme,\n}) => {\n const containerStyle = useMemo(() => ({\n display: 'flex',\n alignItems: 'center',\n gap: isMobile ? '8px' : '12px',\n padding: isMobile ? '8px' : '10px',\n background: hexToRgbaString(theme.colors.UI_BACKGROUND_MEDIUM, opacity * 0.9),\n borderRadius: '8px',\n border: `2px solid ${hexToRgbaString(categoryColor, opacity * 0.7)}`,\n transition: 'opacity 0.3s ease',\n opacity,\n }), [isMobile, theme.colors.UI_BACKGROUND_MEDIUM, opacity, categoryColor]);\n\n const keyLabelStyle = useMemo(() => ({\n fontFamily: FONT_FAMILY.KOREAN,\n fontSize: isMobile ? '16px' : '18px',\n fontWeight: 'bold' as const,\n color: hexToRgbaString(categoryColor),\n padding: '6px 12px',\n background: hexToRgbaString(categoryColor, 0.2),\n borderRadius: '6px',\n minWidth: isMobile ? '50px' : '60px',\n textAlign: 'center' as const,\n boxShadow: `0 0 10px ${hexToRgbaString(categoryColor, opacity * 0.5)}`,\n }), [isMobile, categoryColor, opacity]);\n\n const koreanLabelStyle = useMemo(() => ({\n fontFamily: FONT_FAMILY.KOREAN,\n fontSize: isMobile ? '13px' : '14px',\n fontWeight: 'bold' as const,\n color: hexToRgbaString(theme.colors.ACCENT_GOLD, opacity),\n marginBottom: '2px',\n }), [isMobile, theme.colors.ACCENT_GOLD, opacity]);\n\n const descriptionStyle = useMemo(() => ({\n fontFamily: FONT_FAMILY.KOREAN,\n fontSize: isMobile ? '11px' : '12px',\n color: hexToRgbaString(theme.colors.TEXT_SECONDARY, opacity),\n lineHeight: 1.3,\n }), [isMobile, theme.colors.TEXT_SECONDARY, opacity]);\n\n return (\n <div\n style={containerStyle}\n data-testid={`key-press-${entry.keyData.code}`}\n >\n {/* Key label */}\n <div style={keyLabelStyle}>\n {entry.keyData.label}\n </div>\n\n {/* Key description */}\n <div style={{ flex: 1 }}>\n {/* Korean label */}\n {entry.keyData.labelKorean && (\n <div style={koreanLabelStyle}>\n {entry.keyData.labelKorean}\n </div>\n )}\n\n {/* Description (Korean | English) */}\n {entry.keyData.descriptionKorean && entry.keyData.description && (\n <div style={descriptionStyle}>\n {entry.keyData.descriptionKorean} | {entry.keyData.description}\n </div>\n )}\n </div>\n </div>\n );\n});\n\nKeyPressEntryDisplay.displayName = 'KeyPressEntryDisplay';\n\n/**\n * InteractiveControlDemo Component\n * \n * Displays recently pressed keys with their actions.\n * Auto-fades entries after 2 seconds and maintains last 5 keys.\n * \n * @example\n * ```tsx\n * <InteractiveControlDemo\n * pressedKeys={new Set(['Space', 'KeyW'])}\n * isMobile={false}\n * />\n * ```\n */\nexport const InteractiveControlDemo: React.FC<InteractiveControlDemoProps> = ({\n pressedKeys,\n isMobile,\n}) => {\n const theme = useKoreanTheme({ variant: 'primary', size: 'md', isMobile });\n const [keyPresses, setKeyPresses] = useState<KeyPressEntry[]>([]);\n const [currentTime, setCurrentTime] = useState(() => Date.now());\n\n useEffect(() => {\n let frameId: number;\n\n const updateTime = () => {\n setCurrentTime(Date.now());\n frameId = window.requestAnimationFrame(updateTime);\n };\n\n frameId = window.requestAnimationFrame(updateTime);\n\n return () => {\n window.cancelAnimationFrame(frameId);\n };\n }, []);\n\n useEffect(() => {\n const currentCodes = Array.from(pressedKeys);\n\n if (currentCodes.length === 0) {\n return;\n }\n\n const now = Date.now();\n\n setKeyPresses((prev) => {\n let updated = prev;\n\n currentCodes.forEach((code) => {\n const alreadyRecorded = updated.some(\n (entry) => entry.keyData.code === code && now - entry.timestamp < 100\n );\n\n if (!alreadyRecorded) {\n const keyData = KEYBOARD_LAYOUT.find((k) => k.code === code);\n if (keyData) {\n const newEntry: KeyPressEntry = {\n keyData,\n timestamp: now,\n id: `${code}-${now}`,\n };\n updated = [newEntry, ...updated];\n }\n }\n });\n\n if (updated.length > 5) {\n updated = updated.slice(0, 5);\n }\n\n return updated === prev ? prev : updated;\n });\n }, [pressedKeys]);\n\n useEffect(() => {\n setKeyPresses((prev) =>\n prev.filter((entry) => currentTime - entry.timestamp < 2000)\n );\n }, [currentTime]);\n\n const getOpacity = (timestamp: number): number => {\n const age = currentTime - timestamp;\n const maxAge = 2000;\n return Math.max(0, 1 - age / maxAge);\n };\n\n const containerStyle = useMemo(() => ({\n position: 'fixed' as const,\n bottom: isMobile ? '15px' : '20px',\n left: '50%',\n transform: 'translateX(-50%)',\n display: 'flex',\n flexDirection: 'column' as const,\n gap: isMobile ? '8px' : '10px',\n maxWidth: isMobile ? '90%' : '600px',\n width: '100%',\n padding: isMobile ? '12px' : '15px',\n background: hexToRgbaString(theme.colors.UI_BACKGROUND_DARK, 0.95),\n borderRadius: '12px',\n border: `2px solid ${hexToRgbaString(theme.colors.PRIMARY_CYAN, 0.6)}`,\n boxShadow: `\n 0 4px 20px ${hexToRgbaString(theme.colors.BLACK_SOLID, 0.5)},\n 0 0 15px ${hexToRgbaString(theme.colors.PRIMARY_CYAN, 0.3)}\n `,\n pointerEvents: 'none' as const,\n zIndex: 1000,\n }), [isMobile, theme]);\n\n const titleStyle = useMemo(() => ({\n fontFamily: FONT_FAMILY.KOREAN,\n fontSize: isMobile ? '12px' : '13px',\n fontWeight: 'bold' as const,\n color: hexToRgbaString(theme.colors.ACCENT_GOLD),\n textAlign: 'center' as const,\n marginBottom: '4px',\n textShadow: `0 0 8px ${hexToRgbaString(theme.colors.ACCENT_GOLD, 0.5)}`,\n }), [isMobile, theme]);\n\n return (\n <div style={containerStyle} data-testid=\"interactive-demo\">\n {/* Title */}\n <div style={titleStyle}>\n 최근 입력 | Recent Input\n </div>\n\n {/* Key press entries */}\n {keyPresses.map((entry) => {\n const opacity = getOpacity(entry.timestamp);\n const categoryColor = getKeyCategoryColor(entry.keyData.category);\n\n return (\n <KeyPressEntryDisplay\n key={entry.id}\n entry={entry}\n opacity={opacity}\n categoryColor={categoryColor}\n isMobile={isMobile}\n theme={theme}\n />\n );\n })}\n\n {/* Empty state */}\n {keyPresses.length === 0 && (\n <div\n style={{\n fontFamily: FONT_FAMILY.KOREAN,\n fontSize: isMobile ? '12px' : '13px',\n color: hexToRgbaString(theme.colors.TEXT_TERTIARY),\n textAlign: 'center' as const,\n padding: '15px',\n }}\n >\n 키를 눌러서 액션을 확인하세요 | Press keys to see actions\n </div>\n )}\n </div>\n );\n};\n\nexport default InteractiveControlDemo;\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"InteractiveControlDemoOverlayHtml.js","names":[],"sources":["../../../../../src/components/screens/controls/components/InteractiveControlDemoOverlayHtml.tsx"],"sourcesContent":["/**\n * InteractiveControlDemo - Display recently pressed keys and their actions\n * \n * Shows the last 5 pressed keys with their descriptions in a bottom overlay.\n * Keys auto-fade after 2 seconds for a clean, non-intrusive display.\n * \n * @module components/screens/controls/components\n */\n\nimport React, { useEffect, useMemo, useState } from \"react\";\nimport { FONT_FAMILY } from \"../../../../types/constants\";\nimport { hexToRgbaString } from \"../../../../utils/colorUtils\";\nimport { useKoreanTheme } from \"../../../shared/base/useKoreanTheme\";\nimport { getKeyCategoryColor, KEYBOARD_LAYOUT, type KeyData } from \"../constants/ControlsConstants\";\n\n/**\n * Props for InteractiveControlDemo component\n */\nexport interface InteractiveControlDemoProps {\n /** Set of currently pressed key codes */\n readonly pressedKeys: Set<string>;\n /** Whether on mobile device */\n readonly isMobile: boolean;\n}\n\n/**\n * Key press entry with timestamp\n */\ninterface KeyPressEntry {\n readonly keyData: KeyData;\n readonly timestamp: number;\n readonly id: string;\n}\n\n/**\n * Props for KeyPressEntryDisplay component\n */\ninterface KeyPressEntryDisplayProps {\n readonly entry: KeyPressEntry;\n readonly opacity: number;\n readonly categoryColor: number;\n readonly isMobile: boolean;\n readonly theme: ReturnType<typeof useKoreanTheme>;\n}\n\n/**\n * Memoized KeyPressEntryDisplay Component\n * \n * Displays a single key press entry with optimized inline styles.\n */\nconst KeyPressEntryDisplay = React.memo<KeyPressEntryDisplayProps>(({\n entry,\n opacity,\n categoryColor,\n isMobile,\n theme,\n}) => {\n const containerStyle = useMemo(() => ({\n display: 'flex',\n alignItems: 'center',\n gap: isMobile ? '8px' : '12px',\n padding: isMobile ? '8px' : '10px',\n background: hexToRgbaString(theme.colors.UI_BACKGROUND_MEDIUM, opacity * 0.9),\n borderRadius: '8px',\n border: `2px solid ${hexToRgbaString(categoryColor, opacity * 0.7)}`,\n transition: 'opacity 0.3s ease',\n opacity,\n }), [isMobile, theme.colors.UI_BACKGROUND_MEDIUM, opacity, categoryColor]);\n\n const keyLabelStyle = useMemo(() => ({\n fontFamily: FONT_FAMILY.KOREAN,\n fontSize: isMobile ? '16px' : '18px',\n fontWeight: 'bold' as const,\n color: hexToRgbaString(categoryColor),\n padding: '6px 12px',\n background: hexToRgbaString(categoryColor, 0.2),\n borderRadius: '6px',\n minWidth: isMobile ? '50px' : '60px',\n textAlign: 'center' as const,\n boxShadow: `0 0 10px ${hexToRgbaString(categoryColor, opacity * 0.5)}`,\n }), [isMobile, categoryColor, opacity]);\n\n const koreanLabelStyle = useMemo(() => ({\n fontFamily: FONT_FAMILY.KOREAN,\n fontSize: isMobile ? '13px' : '14px',\n fontWeight: 'bold' as const,\n color: hexToRgbaString(theme.colors.ACCENT_GOLD, opacity),\n marginBottom: '2px',\n }), [isMobile, theme.colors.ACCENT_GOLD, opacity]);\n\n const descriptionStyle = useMemo(() => ({\n fontFamily: FONT_FAMILY.KOREAN,\n fontSize: isMobile ? '11px' : '12px',\n color: hexToRgbaString(theme.colors.TEXT_SECONDARY, opacity),\n lineHeight: 1.3,\n }), [isMobile, theme.colors.TEXT_SECONDARY, opacity]);\n\n return (\n <div\n style={containerStyle}\n data-testid={`key-press-${entry.keyData.code}`}\n >\n {/* Key label */}\n <div style={keyLabelStyle}>\n {entry.keyData.label}\n </div>\n\n {/* Key description */}\n <div style={{ flex: 1 }}>\n {/* Korean label */}\n {entry.keyData.labelKorean && (\n <div style={koreanLabelStyle}>\n {entry.keyData.labelKorean}\n </div>\n )}\n\n {/* Description (Korean | English) */}\n {entry.keyData.descriptionKorean && entry.keyData.description && (\n <div style={descriptionStyle}>\n {entry.keyData.descriptionKorean} | {entry.keyData.description}\n </div>\n )}\n </div>\n </div>\n );\n});\n\nKeyPressEntryDisplay.displayName = 'KeyPressEntryDisplay';\n\n/**\n * InteractiveControlDemo Component\n * \n * Displays recently pressed keys with their actions.\n * Auto-fades entries after 2 seconds and maintains last 5 keys.\n * \n * @example\n * ```tsx\n * <InteractiveControlDemo\n * pressedKeys={new Set(['Space', 'KeyW'])}\n * isMobile={false}\n * />\n * ```\n */\nexport const InteractiveControlDemo: React.FC<InteractiveControlDemoProps> = ({\n pressedKeys,\n isMobile,\n}) => {\n const theme = useKoreanTheme({ variant: 'primary', size: 'md', isMobile });\n const [keyPresses, setKeyPresses] = useState<KeyPressEntry[]>([]);\n const [currentTime, setCurrentTime] = useState(() => Date.now());\n\n useEffect(() => {\n let frameId: number;\n\n const updateTime = () => {\n setCurrentTime(Date.now());\n frameId = window.requestAnimationFrame(updateTime);\n };\n\n frameId = window.requestAnimationFrame(updateTime);\n\n return () => {\n window.cancelAnimationFrame(frameId);\n };\n }, []);\n\n useEffect(() => {\n const currentCodes = Array.from(pressedKeys);\n\n if (currentCodes.length === 0) {\n return;\n }\n\n const now = Date.now();\n\n setKeyPresses((prev) => {\n let updated = prev;\n\n currentCodes.forEach((code) => {\n const alreadyRecorded = updated.some(\n (entry) => entry.keyData.code === code && now - entry.timestamp < 100\n );\n\n if (!alreadyRecorded) {\n const keyData = KEYBOARD_LAYOUT.find((k) => k.code === code);\n if (keyData) {\n const newEntry: KeyPressEntry = {\n keyData,\n timestamp: now,\n id: `${code}-${now}`,\n };\n updated = [newEntry, ...updated];\n }\n }\n });\n\n if (updated.length > 5) {\n updated = updated.slice(0, 5);\n }\n\n return updated === prev ? prev : updated;\n });\n }, [pressedKeys]);\n\n useEffect(() => {\n setKeyPresses((prev) =>\n prev.filter((entry) => currentTime - entry.timestamp < 2000)\n );\n }, [currentTime]);\n\n const getOpacity = (timestamp: number): number => {\n const age = currentTime - timestamp;\n const maxAge = 2000;\n return Math.max(0, 1 - age / maxAge);\n };\n\n const containerStyle = useMemo(() => ({\n position: 'fixed' as const,\n bottom: isMobile ? '15px' : '20px',\n left: '50%',\n transform: 'translateX(-50%)',\n display: 'flex',\n flexDirection: 'column' as const,\n gap: isMobile ? '8px' : '10px',\n maxWidth: isMobile ? '90%' : '600px',\n width: '100%',\n padding: isMobile ? '12px' : '15px',\n background: hexToRgbaString(theme.colors.UI_BACKGROUND_DARK, 0.95),\n borderRadius: '12px',\n border: `2px solid ${hexToRgbaString(theme.colors.PRIMARY_CYAN, 0.6)}`,\n boxShadow: `\n 0 4px 20px ${hexToRgbaString(theme.colors.BLACK_SOLID, 0.5)},\n 0 0 15px ${hexToRgbaString(theme.colors.PRIMARY_CYAN, 0.3)}\n `,\n pointerEvents: 'none' as const,\n zIndex: 1000,\n }), [isMobile, theme]);\n\n const titleStyle = useMemo(() => ({\n fontFamily: FONT_FAMILY.KOREAN,\n fontSize: isMobile ? '12px' : '13px',\n fontWeight: 'bold' as const,\n color: hexToRgbaString(theme.colors.ACCENT_GOLD),\n textAlign: 'center' as const,\n marginBottom: '4px',\n textShadow: `0 0 8px ${hexToRgbaString(theme.colors.ACCENT_GOLD, 0.5)}`,\n }), [isMobile, theme]);\n\n return (\n <div style={containerStyle} data-testid=\"interactive-demo\">\n {/* Title */}\n <div style={titleStyle}>\n 최근 입력 | Recent Input\n </div>\n\n {/* Key press entries */}\n {keyPresses.map((entry) => {\n const opacity = getOpacity(entry.timestamp);\n const categoryColor = getKeyCategoryColor(entry.keyData.category);\n\n return (\n <KeyPressEntryDisplay\n key={entry.id}\n entry={entry}\n opacity={opacity}\n categoryColor={categoryColor}\n isMobile={isMobile}\n theme={theme}\n />\n );\n })}\n\n {/* Empty state */}\n {keyPresses.length === 0 && (\n <div\n style={{\n fontFamily: FONT_FAMILY.KOREAN,\n fontSize: isMobile ? '12px' : '13px',\n color: hexToRgbaString(theme.colors.TEXT_TERTIARY),\n textAlign: 'center' as const,\n padding: '15px',\n }}\n >\n 키를 눌러서 액션을 확인하세요 | Press keys to see actions\n </div>\n )}\n </div>\n );\n};\n\nexport default InteractiveControlDemo;\n"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Key3D.js","names":[],"sources":["../../../../../src/components/screens/controls/components/Key3D.tsx"],"sourcesContent":["/**\n * Key3D - Individual 3D keyboard key component with press animation\n * \n * Renders a 3D box mesh for a keyboard key with category-based coloring,\n * press animation, and bilingual label overlay.\n * \n * @module components/screens/controls/components\n */\n\nimport { Html } from \"@react-three/drei\";\nimport { useFrame } from \"@react-three/fiber\";\nimport React, { useEffect, useMemo, useRef } from \"react\";\nimport * as THREE from \"three\";\nimport { FONT_FAMILY, KOREAN_COLORS } from \"../../../../types/constants\";\nimport { hexToRgbaString } from \"../../../../utils/colorUtils\";\nimport { getKeyCategoryColor, type KeyData } from \"../constants/ControlsConstants\";\n\n/**\n * Props for Key3D component\n */\nexport interface Key3DProps {\n /** Key data containing position, label, and category */\n readonly keyData: KeyData;\n /** Whether this key is currently pressed */\n readonly isPressed: boolean;\n}\n\n/**\n * Key3D Component\n * \n * Individual 3D keyboard key with press animation and label overlay.\n * Uses category-based coloring and emissive glow when pressed.\n * \n * @example\n * ```tsx\n * <Key3D\n * keyData={{ code: 'Space', label: 'Space', row: 4, col: 2, width: 3, category: 'combat' }}\n * isPressed={pressedKeys.has('Space')}\n * />\n * ```\n */\nexport const Key3D: React.FC<Key3DProps> = ({ keyData, isPressed }) => {\n const meshRef = useRef<THREE.Mesh>(null);\n const targetScale = useRef(new THREE.Vector3(1, 1, 1));\n\n const keyWidth = (keyData.width ?? 1) * 0.6; // 0.6 units per key width\n const keyHeight = 0.6;\n const keyDepth = 0.2;\n\n const position = useMemo<[number, number, number]>(() => {\n const x = keyData.col * 0.6 + (keyWidth / 2) - 3.0; // Center around origin\n const y = -keyData.row * 0.6;\n const z = isPressed ? -0.05 : 0; // Press down slightly when pressed\n return [x, y, z];\n }, [keyData.col, keyData.row, keyWidth, isPressed]);\n\n const categoryColor = useMemo(() => getKeyCategoryColor(keyData.category), [keyData.category]);\n\n useFrame(() => {\n if (!meshRef.current) return;\n\n const targetScaleValue = isPressed ? 0.9 : 1.0;\n targetScale.current.set(targetScaleValue, targetScaleValue, 1.0);\n\n meshRef.current.scale.lerp(targetScale.current, 0.2);\n });\n\n const materials = useMemo(\n () => ({\n unpressed: new THREE.MeshStandardMaterial({\n color: categoryColor,\n emissive: categoryColor,\n emissiveIntensity: 0.3,\n metalness: 0.6,\n roughness: 0.4,\n transparent: true,\n opacity: 0.9,\n }),\n pressed: new THREE.MeshStandardMaterial({\n color: categoryColor,\n emissive: categoryColor,\n emissiveIntensity: 2.5,\n metalness: 0.6,\n roughness: 0.4,\n transparent: true,\n opacity: 0.9,\n }),\n }),\n [categoryColor]\n );\n\n const keyMaterial = isPressed ? materials.pressed : materials.unpressed;\n\n useEffect(() => {\n return () => {\n materials.unpressed.dispose();\n materials.pressed.dispose();\n };\n }, [materials]);\n\n const labelStyle = useMemo(() => ({\n fontFamily: FONT_FAMILY.KOREAN,\n fontSize: keyData.width && keyData.width > 2 ? '11px' : '13px',\n fontWeight: 'bold' as const,\n color: isPressed ? hexToRgbaString(KOREAN_COLORS.ACCENT_GOLD) : hexToRgbaString(KOREAN_COLORS.TEXT_PRIMARY),\n textAlign: 'center' as const,\n pointerEvents: 'none' as const,\n userSelect: 'none' as const,\n textShadow: isPressed ? `0 0 8px ${hexToRgbaString(categoryColor, 0.8)}` : 'none',\n whiteSpace: 'nowrap' as const,\n lineHeight: 1.2,\n }), [isPressed, categoryColor, keyData.width]);\n\n return (\n <group position={position}>\n {/* 3D key box */}\n <mesh\n ref={meshRef}\n castShadow\n receiveShadow\n name={`key-${keyData.code}`}\n data-testid={`key-${keyData.code}`}\n >\n <boxGeometry args={[keyWidth, keyHeight, keyDepth]} />\n <primitive object={keyMaterial} attach=\"material\" />\n </mesh>\n\n {/* Label overlay */}\n <Html\n position={[0, 0, keyDepth / 2 + 0.01]}\n center\n distanceFactor={2}\n style={{ pointerEvents: 'none' }}\n >\n <div style={labelStyle}>\n {/* Main label */}\n <div>{keyData.label}</div>\n {/* Korean label (if available) */}\n {keyData.labelKorean && (\n <div style={{\n fontSize: '10px',\n color: hexToRgbaString(KOREAN_COLORS.ACCENT_GOLD),\n marginTop: '2px',\n }}>\n {keyData.labelKorean}\n </div>\n )}\n </div>\n </Html>\n\n {/* Glow ring when pressed */}\n {isPressed && (\n <mesh position={[0, 0, -keyDepth / 2 - 0.02]} rotation={[0, 0, 0]}>\n <ringGeometry args={[Math.max(keyWidth, keyHeight) * 0.6, Math.max(keyWidth, keyHeight) * 0.7, 32]} />\n <meshBasicMaterial\n color={categoryColor}\n transparent\n opacity={0.6}\n side={THREE.DoubleSide}\n />\n </mesh>\n )}\n </group>\n );\n};\n\nexport default Key3D;\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAyCA,IAAa,SAA+B,EAAE,SAAS,gBAAgB;CACrE,MAAM,UAAU,OAAmB,
|
|
1
|
+
{"version":3,"file":"Key3D.js","names":[],"sources":["../../../../../src/components/screens/controls/components/Key3D.tsx"],"sourcesContent":["/**\n * Key3D - Individual 3D keyboard key component with press animation\n * \n * Renders a 3D box mesh for a keyboard key with category-based coloring,\n * press animation, and bilingual label overlay.\n * \n * @module components/screens/controls/components\n */\n\nimport { Html } from \"@react-three/drei\";\nimport { useFrame } from \"@react-three/fiber\";\nimport React, { useEffect, useMemo, useRef } from \"react\";\nimport * as THREE from \"three\";\nimport { FONT_FAMILY, KOREAN_COLORS } from \"../../../../types/constants\";\nimport { hexToRgbaString } from \"../../../../utils/colorUtils\";\nimport { getKeyCategoryColor, type KeyData } from \"../constants/ControlsConstants\";\n\n/**\n * Props for Key3D component\n */\nexport interface Key3DProps {\n /** Key data containing position, label, and category */\n readonly keyData: KeyData;\n /** Whether this key is currently pressed */\n readonly isPressed: boolean;\n}\n\n/**\n * Key3D Component\n * \n * Individual 3D keyboard key with press animation and label overlay.\n * Uses category-based coloring and emissive glow when pressed.\n * \n * @example\n * ```tsx\n * <Key3D\n * keyData={{ code: 'Space', label: 'Space', row: 4, col: 2, width: 3, category: 'combat' }}\n * isPressed={pressedKeys.has('Space')}\n * />\n * ```\n */\nexport const Key3D: React.FC<Key3DProps> = ({ keyData, isPressed }) => {\n const meshRef = useRef<THREE.Mesh>(null);\n const targetScale = useRef(new THREE.Vector3(1, 1, 1));\n\n const keyWidth = (keyData.width ?? 1) * 0.6; // 0.6 units per key width\n const keyHeight = 0.6;\n const keyDepth = 0.2;\n\n const position = useMemo<[number, number, number]>(() => {\n const x = keyData.col * 0.6 + (keyWidth / 2) - 3.0; // Center around origin\n const y = -keyData.row * 0.6;\n const z = isPressed ? -0.05 : 0; // Press down slightly when pressed\n return [x, y, z];\n }, [keyData.col, keyData.row, keyWidth, isPressed]);\n\n const categoryColor = useMemo(() => getKeyCategoryColor(keyData.category), [keyData.category]);\n\n useFrame(() => {\n if (!meshRef.current) return;\n\n const targetScaleValue = isPressed ? 0.9 : 1.0;\n targetScale.current.set(targetScaleValue, targetScaleValue, 1.0);\n\n meshRef.current.scale.lerp(targetScale.current, 0.2);\n });\n\n const materials = useMemo(\n () => ({\n unpressed: new THREE.MeshStandardMaterial({\n color: categoryColor,\n emissive: categoryColor,\n emissiveIntensity: 0.3,\n metalness: 0.6,\n roughness: 0.4,\n transparent: true,\n opacity: 0.9,\n }),\n pressed: new THREE.MeshStandardMaterial({\n color: categoryColor,\n emissive: categoryColor,\n emissiveIntensity: 2.5,\n metalness: 0.6,\n roughness: 0.4,\n transparent: true,\n opacity: 0.9,\n }),\n }),\n [categoryColor]\n );\n\n const keyMaterial = isPressed ? materials.pressed : materials.unpressed;\n\n useEffect(() => {\n return () => {\n materials.unpressed.dispose();\n materials.pressed.dispose();\n };\n }, [materials]);\n\n const labelStyle = useMemo(() => ({\n fontFamily: FONT_FAMILY.KOREAN,\n fontSize: keyData.width && keyData.width > 2 ? '11px' : '13px',\n fontWeight: 'bold' as const,\n color: isPressed ? hexToRgbaString(KOREAN_COLORS.ACCENT_GOLD) : hexToRgbaString(KOREAN_COLORS.TEXT_PRIMARY),\n textAlign: 'center' as const,\n pointerEvents: 'none' as const,\n userSelect: 'none' as const,\n textShadow: isPressed ? `0 0 8px ${hexToRgbaString(categoryColor, 0.8)}` : 'none',\n whiteSpace: 'nowrap' as const,\n lineHeight: 1.2,\n }), [isPressed, categoryColor, keyData.width]);\n\n return (\n <group position={position}>\n {/* 3D key box */}\n <mesh\n ref={meshRef}\n castShadow\n receiveShadow\n name={`key-${keyData.code}`}\n data-testid={`key-${keyData.code}`}\n >\n <boxGeometry args={[keyWidth, keyHeight, keyDepth]} />\n <primitive object={keyMaterial} attach=\"material\" />\n </mesh>\n\n {/* Label overlay */}\n <Html\n position={[0, 0, keyDepth / 2 + 0.01]}\n center\n distanceFactor={2}\n style={{ pointerEvents: 'none' }}\n >\n <div style={labelStyle}>\n {/* Main label */}\n <div>{keyData.label}</div>\n {/* Korean label (if available) */}\n {keyData.labelKorean && (\n <div style={{\n fontSize: '10px',\n color: hexToRgbaString(KOREAN_COLORS.ACCENT_GOLD),\n marginTop: '2px',\n }}>\n {keyData.labelKorean}\n </div>\n )}\n </div>\n </Html>\n\n {/* Glow ring when pressed */}\n {isPressed && (\n <mesh position={[0, 0, -keyDepth / 2 - 0.02]} rotation={[0, 0, 0]}>\n <ringGeometry args={[Math.max(keyWidth, keyHeight) * 0.6, Math.max(keyWidth, keyHeight) * 0.7, 32]} />\n <meshBasicMaterial\n color={categoryColor}\n transparent\n opacity={0.6}\n side={THREE.DoubleSide}\n />\n </mesh>\n )}\n </group>\n );\n};\n\nexport default Key3D;\n"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"VisualKeyboard3D.js","names":[],"sources":["../../../../../src/components/screens/controls/components/VisualKeyboard3D.tsx"],"sourcesContent":["/**\n * VisualKeyboard3D - 3D keyboard visualization with all keys\n * \n * Renders a complete 3D keyboard with keys filtered by selected category.\n * Uses grid layout with proper spacing and lighting for visual clarity.\n * \n * @module components/screens/controls/components\n */\n\nimport React, { useMemo } from \"react\";\nimport { KOREAN_COLORS } from \"../../../../types/constants/colors\";\nimport { filterKeysByCategory, KEYBOARD_LAYOUT, type KeyData } from \"../constants/ControlsConstants\";\nimport { Key3D } from \"./Key3D\";\n\n/**\n * Props for VisualKeyboard3D component\n */\nexport interface VisualKeyboard3DProps {\n /** Set of currently pressed key codes */\n readonly pressedKeys: Set<string>;\n /** Selected control category to filter keys */\n readonly selectedTab: 'combat' | 'movement' | 'system';\n}\n\n/**\n * VisualKeyboard3D Component\n * \n * 3D keyboard visualization showing all keys filtered by selected category.\n * Keys are positioned in a grid layout with proper spacing.\n * \n * @example\n * ```tsx\n * <Canvas>\n * <VisualKeyboard3D\n * pressedKeys={new Set(['Space', 'KeyW'])}\n * selectedTab=\"combat\"\n * />\n * </Canvas>\n * ```\n */\nexport const VisualKeyboard3D: React.FC<VisualKeyboard3DProps> = ({\n pressedKeys,\n selectedTab,\n}) => {\n const filteredKeys = useMemo<readonly KeyData[]>(() => {\n return filterKeysByCategory(KEYBOARD_LAYOUT, selectedTab);\n }, [selectedTab]);\n\n return (\n <group\n position={[0, -1, 0]}\n rotation={[-Math.PI / 6, 0, 0]}\n data-testid=\"visual-keyboard\"\n >\n {/* Ambient light for overall illumination */}\n <ambientLight intensity={0.4} />\n\n {/* Directional light for depth and shadows */}\n <directionalLight\n position={[5, 5, 5]}\n intensity={1.0}\n castShadow\n shadow-mapSize-width={2048}\n shadow-mapSize-height={2048}\n />\n\n {/* Additional directional light from the side */}\n <directionalLight\n position={[-3, 3, 2]}\n intensity={0.5}\n />\n\n {/* Point light for accent highlights */}\n <pointLight\n position={[0, 2, 2]}\n intensity={0.6}\n distance={10}\n decay={2}\n />\n\n {/* Render all filtered keys */}\n {filteredKeys.map((keyData) => (\n <Key3D\n key={keyData.code}\n keyData={keyData}\n isPressed={pressedKeys.has(keyData.code)}\n />\n ))}\n\n {/* Background plane for keyboard base */}\n <mesh\n position={[0, 0, -0.2]}\n receiveShadow\n >\n <planeGeometry args={[12, 6]} />\n <meshStandardMaterial\n color={KOREAN_COLORS.ARENA_BACKGROUND}\n metalness={0.8}\n roughness={0.3}\n opacity={0.9}\n transparent\n />\n </mesh>\n\n {/* Grid helper for visual reference (subtle) */}\n <gridHelper\n args={[12, 20, KOREAN_COLORS.UI_STEEL_GRAY_DARK, KOREAN_COLORS.UI_BACKGROUND_MEDIUM]}\n position={[0, 0, -0.19]}\n rotation={[0, 0, 0]}\n />\n </group>\n );\n};\n\nexport default VisualKeyboard3D;\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAwCA,IAAa,oBAAqD,EAChE,aACA,kBACI;CACJ,MAAM,eAAe,cAAkC;EACrD,OAAO,qBAAqB,iBAAiB,
|
|
1
|
+
{"version":3,"file":"VisualKeyboard3D.js","names":[],"sources":["../../../../../src/components/screens/controls/components/VisualKeyboard3D.tsx"],"sourcesContent":["/**\n * VisualKeyboard3D - 3D keyboard visualization with all keys\n * \n * Renders a complete 3D keyboard with keys filtered by selected category.\n * Uses grid layout with proper spacing and lighting for visual clarity.\n * \n * @module components/screens/controls/components\n */\n\nimport React, { useMemo } from \"react\";\nimport { KOREAN_COLORS } from \"../../../../types/constants/colors\";\nimport { filterKeysByCategory, KEYBOARD_LAYOUT, type KeyData } from \"../constants/ControlsConstants\";\nimport { Key3D } from \"./Key3D\";\n\n/**\n * Props for VisualKeyboard3D component\n */\nexport interface VisualKeyboard3DProps {\n /** Set of currently pressed key codes */\n readonly pressedKeys: Set<string>;\n /** Selected control category to filter keys */\n readonly selectedTab: 'combat' | 'movement' | 'system';\n}\n\n/**\n * VisualKeyboard3D Component\n * \n * 3D keyboard visualization showing all keys filtered by selected category.\n * Keys are positioned in a grid layout with proper spacing.\n * \n * @example\n * ```tsx\n * <Canvas>\n * <VisualKeyboard3D\n * pressedKeys={new Set(['Space', 'KeyW'])}\n * selectedTab=\"combat\"\n * />\n * </Canvas>\n * ```\n */\nexport const VisualKeyboard3D: React.FC<VisualKeyboard3DProps> = ({\n pressedKeys,\n selectedTab,\n}) => {\n const filteredKeys = useMemo<readonly KeyData[]>(() => {\n return filterKeysByCategory(KEYBOARD_LAYOUT, selectedTab);\n }, [selectedTab]);\n\n return (\n <group\n position={[0, -1, 0]}\n rotation={[-Math.PI / 6, 0, 0]}\n data-testid=\"visual-keyboard\"\n >\n {/* Ambient light for overall illumination */}\n <ambientLight intensity={0.4} />\n\n {/* Directional light for depth and shadows */}\n <directionalLight\n position={[5, 5, 5]}\n intensity={1.0}\n castShadow\n shadow-mapSize-width={2048}\n shadow-mapSize-height={2048}\n />\n\n {/* Additional directional light from the side */}\n <directionalLight\n position={[-3, 3, 2]}\n intensity={0.5}\n />\n\n {/* Point light for accent highlights */}\n <pointLight\n position={[0, 2, 2]}\n intensity={0.6}\n distance={10}\n decay={2}\n />\n\n {/* Render all filtered keys */}\n {filteredKeys.map((keyData) => (\n <Key3D\n key={keyData.code}\n keyData={keyData}\n isPressed={pressedKeys.has(keyData.code)}\n />\n ))}\n\n {/* Background plane for keyboard base */}\n <mesh\n position={[0, 0, -0.2]}\n receiveShadow\n >\n <planeGeometry args={[12, 6]} />\n <meshStandardMaterial\n color={KOREAN_COLORS.ARENA_BACKGROUND}\n metalness={0.8}\n roughness={0.3}\n opacity={0.9}\n transparent\n />\n </mesh>\n\n {/* Grid helper for visual reference (subtle) */}\n <gridHelper\n args={[12, 20, KOREAN_COLORS.UI_STEEL_GRAY_DARK, KOREAN_COLORS.UI_BACKGROUND_MEDIUM]}\n position={[0, 0, -0.19]}\n rotation={[0, 0, 0]}\n />\n </group>\n );\n};\n\nexport default VisualKeyboard3D;\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAwCA,IAAa,oBAAqD,EAChE,aACA,kBACI;CACJ,MAAM,eAAe,cAAkC;EACrD,OAAO,qBAAqB,iBAAiB,WAAW;CAC1D,GAAG,CAAC,WAAW,CAAC;CAEhB,OACE,qBAAC,SAAD;EACE,UAAU;GAAC;GAAG;GAAI;EAAC;EACnB,UAAU;GAAC,CAAC,KAAK,KAAK;GAAG;GAAG;EAAC;EAC7B,eAAY;YAHd;GAME,oBAAC,gBAAD,EAAc,WAAW,GAAM,CAAA;GAG/B,oBAAC,oBAAD;IACE,UAAU;KAAC;KAAG;KAAG;IAAC;IAClB,WAAW;IACX,YAAA;IACA,wBAAsB;IACtB,yBAAuB;GACxB,CAAA;GAGD,oBAAC,oBAAD;IACE,UAAU;KAAC;KAAI;KAAG;IAAC;IACnB,WAAW;GACZ,CAAA;GAGD,oBAAC,cAAD;IACE,UAAU;KAAC;KAAG;KAAG;IAAC;IAClB,WAAW;IACX,UAAU;IACV,OAAO;GACR,CAAA;GAGA,aAAa,KAAK,YACjB,oBAAC,OAAD;IAEW;IACT,WAAW,YAAY,IAAI,QAAQ,IAAI;GACxC,GAHM,QAAQ,IAGd,CACF;GAGD,qBAAC,QAAD;IACE,UAAU;KAAC;KAAG;KAAG;IAAI;IACrB,eAAA;cAFF,CAIE,oBAAC,iBAAD,EAAe,MAAM,CAAC,IAAI,CAAC,EAAI,CAAA,GAC/B,oBAAC,wBAAD;KACE,OAAO,cAAc;KACrB,WAAW;KACX,WAAW;KACX,SAAS;KACT,aAAA;IACD,CAAA,CACG;;GAGN,oBAAC,cAAD;IACE,MAAM;KAAC;KAAI;KAAI,cAAc;KAAoB,cAAc;IAAoB;IACnF,UAAU;KAAC;KAAG;KAAG;IAAK;IACtB,UAAU;KAAC;KAAG;KAAG;IAAC;GACnB,CAAA;EACI;;AAEX"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ControlsConstants.js","names":[],"sources":["../../../../../src/components/screens/controls/constants/ControlsConstants.ts"],"sourcesContent":["/**\n * Control constants and layouts for Black Trigram (흑괘)\n * Defines keyboard layouts, gamepad mappings, and control categories\n * \n * @module components/screens/controls/constants\n */\n\nimport { KOREAN_COLORS } from \"../../../../types/constants/colors\";\n\n/**\n * Keyboard key data structure\n */\nexport interface KeyData {\n readonly code: string;\n readonly label: string;\n readonly labelKorean?: string;\n readonly row: number;\n readonly col: number;\n readonly width?: number; // Default is 1\n readonly category: KeyCategory;\n readonly description?: string;\n readonly descriptionKorean?: string;\n}\n\n/**\n * Control categories for organizing controls\n */\nexport type KeyCategory = 'stance' | 'movement' | 'combat' | 'system' | 'technique' | 'modifier' | 'normal';\n\n/**\n * Control category configuration\n */\nexport interface ControlCategory {\n readonly id: string;\n readonly korean: string;\n readonly english: string;\n readonly icon: string;\n readonly color: number;\n}\n\n/**\n * Gamepad button mapping\n */\nexport interface GamepadButton {\n readonly index: number;\n readonly korean: string;\n readonly english: string;\n readonly action: string;\n readonly actionKorean: string;\n readonly color: number;\n}\n\n/**\n * Control categories for tab navigation\n */\nexport const CONTROL_CATEGORIES: readonly ControlCategory[] = [\n {\n id: 'combat',\n korean: '전투',\n english: 'Combat',\n icon: '⚔️',\n color: KOREAN_COLORS.ACCENT_GOLD,\n },\n {\n id: 'movement',\n korean: '이동',\n english: 'Movement',\n icon: '🏃',\n color: KOREAN_COLORS.PRIMARY_CYAN,\n },\n {\n id: 'system',\n korean: '시스템',\n english: 'System',\n icon: '⚙️',\n color: KOREAN_COLORS.ACCENT_PURPLE,\n },\n] as const;\n\n/**\n * Keyboard layout - all important keys for the game\n * Positioned based on physical keyboard layout\n */\nexport const KEYBOARD_LAYOUT: readonly KeyData[] = [\n { code: 'Digit1', label: '1', labelKorean: '건', row: 0, col: 0, category: 'stance', description: 'Geon (Heaven)', descriptionKorean: '건 (Heaven)' },\n { code: 'Digit2', label: '2', labelKorean: '태', row: 0, col: 1, category: 'stance', description: 'Tae (Lake)', descriptionKorean: '태 (Lake)' },\n { code: 'Digit3', label: '3', labelKorean: '리', row: 0, col: 2, category: 'stance', description: 'Li (Fire)', descriptionKorean: '리 (Fire)' },\n { code: 'Digit4', label: '4', labelKorean: '진', row: 0, col: 3, category: 'stance', description: 'Jin (Thunder)', descriptionKorean: '진 (Thunder)' },\n { code: 'Digit5', label: '5', labelKorean: '손', row: 0, col: 4, category: 'stance', description: 'Son (Wind)', descriptionKorean: '손 (Wind)' },\n { code: 'Digit6', label: '6', labelKorean: '감', row: 0, col: 5, category: 'stance', description: 'Gam (Water)', descriptionKorean: '감 (Water)' },\n { code: 'Digit7', label: '7', labelKorean: '간', row: 0, col: 6, category: 'stance', description: 'Gan (Mountain)', descriptionKorean: '간 (Mountain)' },\n { code: 'Digit8', label: '8', labelKorean: '곤', row: 0, col: 7, category: 'stance', description: 'Gon (Earth)', descriptionKorean: '곤 (Earth)' },\n \n { code: 'KeyQ', label: 'Q', row: 1, col: 0, category: 'technique', description: 'Technique 1', descriptionKorean: '기술 1' },\n { code: 'KeyW', label: 'W', labelKorean: '전진', row: 1, col: 1, category: 'movement', description: 'Move Forward', descriptionKorean: '전진' },\n { code: 'KeyE', label: 'E', row: 1, col: 2, category: 'technique', description: 'Technique 2', descriptionKorean: '기술 2' },\n { code: 'KeyR', label: 'R', row: 1, col: 3, category: 'technique', description: 'Technique 3', descriptionKorean: '기술 3' },\n { code: 'KeyT', label: 'T', row: 1, col: 4, category: 'technique', description: 'Technique 4', descriptionKorean: '기술 4' },\n { code: 'KeyY', label: 'Y', row: 1, col: 5, category: 'technique', description: 'Technique 5', descriptionKorean: '기술 5' },\n \n { code: 'KeyA', label: 'A', labelKorean: '좌', row: 2, col: 0, category: 'movement', description: 'Move Left', descriptionKorean: '좌측 이동' },\n { code: 'KeyS', label: 'S', labelKorean: '후퇴', row: 2, col: 1, category: 'movement', description: 'Move Back', descriptionKorean: '후퇴' },\n { code: 'KeyD', label: 'D', labelKorean: '우', row: 2, col: 2, category: 'movement', description: 'Move Right', descriptionKorean: '우측 이동' },\n { code: 'KeyF', label: 'F', row: 2, col: 3, category: 'technique', description: 'Technique 6', descriptionKorean: '기술 6' },\n { code: 'KeyG', label: 'G', row: 2, col: 4, category: 'technique', description: 'Technique 7', descriptionKorean: '기술 7' },\n \n { code: 'KeyZ', label: 'Z', row: 3, col: 0, category: 'technique', description: 'Technique 8', descriptionKorean: '기술 8' },\n { code: 'KeyX', label: 'X', row: 3, col: 1, category: 'technique', description: 'Technique 9', descriptionKorean: '기술 9' },\n { code: 'KeyC', label: 'C', row: 3, col: 2, category: 'technique', description: 'Technique 10', descriptionKorean: '기술 10' },\n { code: 'KeyV', label: 'V', labelKorean: '급소', row: 3, col: 3, category: 'combat', description: 'Vital Points', descriptionKorean: '급소 표시' },\n { code: 'KeyB', label: 'B', labelKorean: '방어', row: 3, col: 4, category: 'combat', description: 'Block/Guard', descriptionKorean: '방어' },\n \n { code: 'Space', label: 'Space', labelKorean: '공격', row: 4, col: 2, width: 3, category: 'combat', description: 'Attack', descriptionKorean: '공격' },\n \n { code: 'ShiftLeft', label: 'Shift', labelKorean: '전술', row: 3, col: -1, width: 1, category: 'modifier', description: 'Tactical Step', descriptionKorean: '전술 보법' },\n { code: 'ControlLeft', label: 'Ctrl', labelKorean: '보법', row: 4, col: -1, width: 1, category: 'modifier', description: 'Advanced Footwork', descriptionKorean: '고급 보법' },\n \n { code: 'ArrowUp', label: '↑', labelKorean: '전진', row: 1, col: 10, category: 'movement', description: 'Move Forward', descriptionKorean: '전진' },\n { code: 'ArrowLeft', label: '←', labelKorean: '좌', row: 2, col: 9, category: 'movement', description: 'Move Left', descriptionKorean: '좌측 이동' },\n { code: 'ArrowDown', label: '↓', labelKorean: '후퇴', row: 2, col: 10, category: 'movement', description: 'Move Back', descriptionKorean: '후퇴' },\n { code: 'ArrowRight', label: '→', labelKorean: '우', row: 2, col: 11, category: 'movement', description: 'Move Right', descriptionKorean: '우측 이동' },\n \n { code: 'Escape', label: 'ESC', labelKorean: '일시정지', row: 0, col: -2, category: 'system', description: 'Pause Menu', descriptionKorean: '일시정지' },\n { code: 'KeyM', label: 'M', labelKorean: '메뉴', row: 2, col: 6, category: 'system', description: 'Menu', descriptionKorean: '메뉴' },\n { code: 'Tab', label: 'Tab', labelKorean: '전환', row: 1, col: -1, width: 1, category: 'system', description: 'Switch Archetype', descriptionKorean: '전환' },\n] as const;\n\n/**\n * Gamepad button mappings (Xbox-style controller)\n */\nexport const GAMEPAD_BUTTONS: readonly GamepadButton[] = [\n { index: 0, korean: 'A', english: 'A', action: 'Attack', actionKorean: '공격', color: KOREAN_COLORS.ACCENT_GREEN },\n { index: 1, korean: 'B', english: 'B', action: 'Block', actionKorean: '방어', color: KOREAN_COLORS.ACCENT_RED },\n { index: 2, korean: 'X', english: 'X', action: 'Technique 1', actionKorean: '기술 1', color: KOREAN_COLORS.ACCENT_BLUE },\n { index: 3, korean: 'Y', english: 'Y', action: 'Technique 2', actionKorean: '기술 2', color: KOREAN_COLORS.ACCENT_YELLOW },\n { index: 4, korean: 'LB', english: 'LB', action: 'Previous Stance', actionKorean: '이전 자세', color: KOREAN_COLORS.UI_STEEL_GRAY },\n { index: 5, korean: 'RB', english: 'RB', action: 'Next Stance', actionKorean: '다음 자세', color: KOREAN_COLORS.UI_STEEL_GRAY },\n { index: 6, korean: 'LT', english: 'LT', action: 'Vital Points', actionKorean: '급소 표시', color: KOREAN_COLORS.UI_STEEL_GRAY_DARK },\n { index: 7, korean: 'RT', english: 'RT', action: 'Special Attack', actionKorean: '특수 공격', color: KOREAN_COLORS.UI_STEEL_GRAY_DARK },\n { index: 8, korean: 'Back', english: 'Back', action: 'Menu', actionKorean: '메뉴', color: KOREAN_COLORS.UI_BORDER },\n { index: 9, korean: 'Start', english: 'Start', action: 'Pause', actionKorean: '일시정지', color: KOREAN_COLORS.UI_BORDER },\n { index: 10, korean: 'L3', english: 'L3', action: 'Lock Target', actionKorean: '목표 고정', color: KOREAN_COLORS.UI_DISABLED_TEXT },\n { index: 11, korean: 'R3', english: 'R3', action: 'Camera Reset', actionKorean: '카메라 리셋', color: KOREAN_COLORS.UI_DISABLED_TEXT },\n] as const;\n\n/**\n * Get key category color\n */\nexport function getKeyCategoryColor(category: KeyCategory): number {\n switch (category) {\n case 'stance':\n return KOREAN_COLORS.ACCENT_GOLD;\n case 'movement':\n return KOREAN_COLORS.PRIMARY_CYAN;\n case 'combat':\n return KOREAN_COLORS.ACCENT_RED;\n case 'technique':\n return KOREAN_COLORS.ACCENT_PURPLE;\n case 'system':\n return KOREAN_COLORS.ACCENT_ORANGE;\n case 'modifier':\n return KOREAN_COLORS.ACCENT_BLUE;\n default:\n return KOREAN_COLORS.UI_STEEL_GRAY;\n }\n}\n\n/**\n * Filter keys by category\n */\nexport function filterKeysByCategory(keys: readonly KeyData[], category: string): readonly KeyData[] {\n return keys.filter(key => {\n if (category === 'combat') {\n return key.category === 'combat' || key.category === 'stance' || key.category === 'technique';\n }\n if (category === 'movement') {\n return key.category === 'movement' || key.category === 'modifier';\n }\n if (category === 'system') {\n return key.category === 'system';\n }\n return false;\n });\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"ControlsConstants.js","names":[],"sources":["../../../../../src/components/screens/controls/constants/ControlsConstants.ts"],"sourcesContent":["/**\n * Control constants and layouts for Black Trigram (흑괘)\n * Defines keyboard layouts, gamepad mappings, and control categories\n * \n * @module components/screens/controls/constants\n */\n\nimport { KOREAN_COLORS } from \"../../../../types/constants/colors\";\n\n/**\n * Keyboard key data structure\n */\nexport interface KeyData {\n readonly code: string;\n readonly label: string;\n readonly labelKorean?: string;\n readonly row: number;\n readonly col: number;\n readonly width?: number; // Default is 1\n readonly category: KeyCategory;\n readonly description?: string;\n readonly descriptionKorean?: string;\n}\n\n/**\n * Control categories for organizing controls\n */\nexport type KeyCategory = 'stance' | 'movement' | 'combat' | 'system' | 'technique' | 'modifier' | 'normal';\n\n/**\n * Control category configuration\n */\nexport interface ControlCategory {\n readonly id: string;\n readonly korean: string;\n readonly english: string;\n readonly icon: string;\n readonly color: number;\n}\n\n/**\n * Gamepad button mapping\n */\nexport interface GamepadButton {\n readonly index: number;\n readonly korean: string;\n readonly english: string;\n readonly action: string;\n readonly actionKorean: string;\n readonly color: number;\n}\n\n/**\n * Control categories for tab navigation\n */\nexport const CONTROL_CATEGORIES: readonly ControlCategory[] = [\n {\n id: 'combat',\n korean: '전투',\n english: 'Combat',\n icon: '⚔️',\n color: KOREAN_COLORS.ACCENT_GOLD,\n },\n {\n id: 'movement',\n korean: '이동',\n english: 'Movement',\n icon: '🏃',\n color: KOREAN_COLORS.PRIMARY_CYAN,\n },\n {\n id: 'system',\n korean: '시스템',\n english: 'System',\n icon: '⚙️',\n color: KOREAN_COLORS.ACCENT_PURPLE,\n },\n] as const;\n\n/**\n * Keyboard layout - all important keys for the game\n * Positioned based on physical keyboard layout\n */\nexport const KEYBOARD_LAYOUT: readonly KeyData[] = [\n { code: 'Digit1', label: '1', labelKorean: '건', row: 0, col: 0, category: 'stance', description: 'Geon (Heaven)', descriptionKorean: '건 (Heaven)' },\n { code: 'Digit2', label: '2', labelKorean: '태', row: 0, col: 1, category: 'stance', description: 'Tae (Lake)', descriptionKorean: '태 (Lake)' },\n { code: 'Digit3', label: '3', labelKorean: '리', row: 0, col: 2, category: 'stance', description: 'Li (Fire)', descriptionKorean: '리 (Fire)' },\n { code: 'Digit4', label: '4', labelKorean: '진', row: 0, col: 3, category: 'stance', description: 'Jin (Thunder)', descriptionKorean: '진 (Thunder)' },\n { code: 'Digit5', label: '5', labelKorean: '손', row: 0, col: 4, category: 'stance', description: 'Son (Wind)', descriptionKorean: '손 (Wind)' },\n { code: 'Digit6', label: '6', labelKorean: '감', row: 0, col: 5, category: 'stance', description: 'Gam (Water)', descriptionKorean: '감 (Water)' },\n { code: 'Digit7', label: '7', labelKorean: '간', row: 0, col: 6, category: 'stance', description: 'Gan (Mountain)', descriptionKorean: '간 (Mountain)' },\n { code: 'Digit8', label: '8', labelKorean: '곤', row: 0, col: 7, category: 'stance', description: 'Gon (Earth)', descriptionKorean: '곤 (Earth)' },\n \n { code: 'KeyQ', label: 'Q', row: 1, col: 0, category: 'technique', description: 'Technique 1', descriptionKorean: '기술 1' },\n { code: 'KeyW', label: 'W', labelKorean: '전진', row: 1, col: 1, category: 'movement', description: 'Move Forward', descriptionKorean: '전진' },\n { code: 'KeyE', label: 'E', row: 1, col: 2, category: 'technique', description: 'Technique 2', descriptionKorean: '기술 2' },\n { code: 'KeyR', label: 'R', row: 1, col: 3, category: 'technique', description: 'Technique 3', descriptionKorean: '기술 3' },\n { code: 'KeyT', label: 'T', row: 1, col: 4, category: 'technique', description: 'Technique 4', descriptionKorean: '기술 4' },\n { code: 'KeyY', label: 'Y', row: 1, col: 5, category: 'technique', description: 'Technique 5', descriptionKorean: '기술 5' },\n \n { code: 'KeyA', label: 'A', labelKorean: '좌', row: 2, col: 0, category: 'movement', description: 'Move Left', descriptionKorean: '좌측 이동' },\n { code: 'KeyS', label: 'S', labelKorean: '후퇴', row: 2, col: 1, category: 'movement', description: 'Move Back', descriptionKorean: '후퇴' },\n { code: 'KeyD', label: 'D', labelKorean: '우', row: 2, col: 2, category: 'movement', description: 'Move Right', descriptionKorean: '우측 이동' },\n { code: 'KeyF', label: 'F', row: 2, col: 3, category: 'technique', description: 'Technique 6', descriptionKorean: '기술 6' },\n { code: 'KeyG', label: 'G', row: 2, col: 4, category: 'technique', description: 'Technique 7', descriptionKorean: '기술 7' },\n \n { code: 'KeyZ', label: 'Z', row: 3, col: 0, category: 'technique', description: 'Technique 8', descriptionKorean: '기술 8' },\n { code: 'KeyX', label: 'X', row: 3, col: 1, category: 'technique', description: 'Technique 9', descriptionKorean: '기술 9' },\n { code: 'KeyC', label: 'C', row: 3, col: 2, category: 'technique', description: 'Technique 10', descriptionKorean: '기술 10' },\n { code: 'KeyV', label: 'V', labelKorean: '급소', row: 3, col: 3, category: 'combat', description: 'Vital Points', descriptionKorean: '급소 표시' },\n { code: 'KeyB', label: 'B', labelKorean: '방어', row: 3, col: 4, category: 'combat', description: 'Block/Guard', descriptionKorean: '방어' },\n \n { code: 'Space', label: 'Space', labelKorean: '공격', row: 4, col: 2, width: 3, category: 'combat', description: 'Attack', descriptionKorean: '공격' },\n \n { code: 'ShiftLeft', label: 'Shift', labelKorean: '전술', row: 3, col: -1, width: 1, category: 'modifier', description: 'Tactical Step', descriptionKorean: '전술 보법' },\n { code: 'ControlLeft', label: 'Ctrl', labelKorean: '보법', row: 4, col: -1, width: 1, category: 'modifier', description: 'Advanced Footwork', descriptionKorean: '고급 보법' },\n \n { code: 'ArrowUp', label: '↑', labelKorean: '전진', row: 1, col: 10, category: 'movement', description: 'Move Forward', descriptionKorean: '전진' },\n { code: 'ArrowLeft', label: '←', labelKorean: '좌', row: 2, col: 9, category: 'movement', description: 'Move Left', descriptionKorean: '좌측 이동' },\n { code: 'ArrowDown', label: '↓', labelKorean: '후퇴', row: 2, col: 10, category: 'movement', description: 'Move Back', descriptionKorean: '후퇴' },\n { code: 'ArrowRight', label: '→', labelKorean: '우', row: 2, col: 11, category: 'movement', description: 'Move Right', descriptionKorean: '우측 이동' },\n \n { code: 'Escape', label: 'ESC', labelKorean: '일시정지', row: 0, col: -2, category: 'system', description: 'Pause Menu', descriptionKorean: '일시정지' },\n { code: 'KeyM', label: 'M', labelKorean: '메뉴', row: 2, col: 6, category: 'system', description: 'Menu', descriptionKorean: '메뉴' },\n { code: 'Tab', label: 'Tab', labelKorean: '전환', row: 1, col: -1, width: 1, category: 'system', description: 'Switch Archetype', descriptionKorean: '전환' },\n] as const;\n\n/**\n * Gamepad button mappings (Xbox-style controller)\n */\nexport const GAMEPAD_BUTTONS: readonly GamepadButton[] = [\n { index: 0, korean: 'A', english: 'A', action: 'Attack', actionKorean: '공격', color: KOREAN_COLORS.ACCENT_GREEN },\n { index: 1, korean: 'B', english: 'B', action: 'Block', actionKorean: '방어', color: KOREAN_COLORS.ACCENT_RED },\n { index: 2, korean: 'X', english: 'X', action: 'Technique 1', actionKorean: '기술 1', color: KOREAN_COLORS.ACCENT_BLUE },\n { index: 3, korean: 'Y', english: 'Y', action: 'Technique 2', actionKorean: '기술 2', color: KOREAN_COLORS.ACCENT_YELLOW },\n { index: 4, korean: 'LB', english: 'LB', action: 'Previous Stance', actionKorean: '이전 자세', color: KOREAN_COLORS.UI_STEEL_GRAY },\n { index: 5, korean: 'RB', english: 'RB', action: 'Next Stance', actionKorean: '다음 자세', color: KOREAN_COLORS.UI_STEEL_GRAY },\n { index: 6, korean: 'LT', english: 'LT', action: 'Vital Points', actionKorean: '급소 표시', color: KOREAN_COLORS.UI_STEEL_GRAY_DARK },\n { index: 7, korean: 'RT', english: 'RT', action: 'Special Attack', actionKorean: '특수 공격', color: KOREAN_COLORS.UI_STEEL_GRAY_DARK },\n { index: 8, korean: 'Back', english: 'Back', action: 'Menu', actionKorean: '메뉴', color: KOREAN_COLORS.UI_BORDER },\n { index: 9, korean: 'Start', english: 'Start', action: 'Pause', actionKorean: '일시정지', color: KOREAN_COLORS.UI_BORDER },\n { index: 10, korean: 'L3', english: 'L3', action: 'Lock Target', actionKorean: '목표 고정', color: KOREAN_COLORS.UI_DISABLED_TEXT },\n { index: 11, korean: 'R3', english: 'R3', action: 'Camera Reset', actionKorean: '카메라 리셋', color: KOREAN_COLORS.UI_DISABLED_TEXT },\n] as const;\n\n/**\n * Get key category color\n */\nexport function getKeyCategoryColor(category: KeyCategory): number {\n switch (category) {\n case 'stance':\n return KOREAN_COLORS.ACCENT_GOLD;\n case 'movement':\n return KOREAN_COLORS.PRIMARY_CYAN;\n case 'combat':\n return KOREAN_COLORS.ACCENT_RED;\n case 'technique':\n return KOREAN_COLORS.ACCENT_PURPLE;\n case 'system':\n return KOREAN_COLORS.ACCENT_ORANGE;\n case 'modifier':\n return KOREAN_COLORS.ACCENT_BLUE;\n default:\n return KOREAN_COLORS.UI_STEEL_GRAY;\n }\n}\n\n/**\n * Filter keys by category\n */\nexport function filterKeysByCategory(keys: readonly KeyData[], category: string): readonly KeyData[] {\n return keys.filter(key => {\n if (category === 'combat') {\n return key.category === 'combat' || key.category === 'stance' || key.category === 'technique';\n }\n if (category === 'movement') {\n return key.category === 'movement' || key.category === 'modifier';\n }\n if (category === 'system') {\n return key.category === 'system';\n }\n return false;\n });\n}\n"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useControlsState.js","names":[],"sources":["../../../../../src/components/screens/controls/hooks/useControlsState.ts"],"sourcesContent":["/**\n * useControlsState - State management hook for Controls Screen\n * \n * Manages keyboard press detection, gamepad state, and category selection\n * \n * @module components/screens/controls/hooks\n */\n\nimport { useCallback, useEffect, useState } from \"react\";\n\n/**\n * Controls state interface\n */\nexport interface ControlsState {\n readonly pressedKeys: Set<string>;\n readonly category: 'keyboard' | 'gamepad';\n readonly selectedTab: 'combat' | 'movement' | 'system';\n}\n\n/**\n * Hook return type\n */\nexport interface UseControlsStateReturn {\n readonly pressedKeys: Set<string>;\n readonly category: 'keyboard' | 'gamepad';\n readonly selectedTab: 'combat' | 'movement' | 'system';\n readonly setCategory: (category: 'keyboard' | 'gamepad') => void;\n readonly setSelectedTab: (tab: 'combat' | 'movement' | 'system') => void;\n}\n\n/**\n * Custom hook for managing controls screen state\n * \n * Features:\n * - Keyboard press detection with cleanup\n * - Category switching (keyboard/gamepad)\n * - Tab selection for control categories\n * \n * @example\n * ```tsx\n * const { pressedKeys, category, selectedTab, setCategory, setSelectedTab } = useControlsState();\n * \n * // Check if key is pressed\n * const isSpacePressed = pressedKeys.has('Space');\n * \n * // Switch to gamepad view\n * setCategory('gamepad');\n * ```\n */\nexport function useControlsState(): UseControlsStateReturn {\n const [pressedKeys, setPressedKeys] = useState<Set<string>>(new Set());\n const [category, setCategory] = useState<'keyboard' | 'gamepad'>('keyboard');\n const [selectedTab, setSelectedTab] = useState<'combat' | 'movement' | 'system'>('combat');\n\n useEffect(() => {\n const handleKeyDown = (event: KeyboardEvent) => {\n if (event.target instanceof HTMLInputElement || event.target instanceof HTMLTextAreaElement) {\n return;\n }\n\n setPressedKeys(prev => {\n const next = new Set(prev);\n next.add(event.code);\n return next;\n });\n };\n\n const handleKeyUp = (event: KeyboardEvent) => {\n setPressedKeys(prev => {\n const next = new Set(prev);\n next.delete(event.code);\n return next;\n });\n };\n\n window.addEventListener('keydown', handleKeyDown);\n window.addEventListener('keyup', handleKeyUp);\n\n return () => {\n window.removeEventListener('keydown', handleKeyDown);\n window.removeEventListener('keyup', handleKeyUp);\n };\n }, []);\n\n const handleSetCategory = useCallback((newCategory: 'keyboard' | 'gamepad') => {\n setCategory(newCategory);\n }, []);\n\n const handleSetSelectedTab = useCallback((tab: 'combat' | 'movement' | 'system') => {\n setSelectedTab(tab);\n }, []);\n\n return {\n pressedKeys,\n category,\n selectedTab,\n setCategory: handleSetCategory,\n setSelectedTab: handleSetSelectedTab,\n };\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;AAiDA,SAAgB,mBAA2C;CACzD,MAAM,CAAC,aAAa,kBAAkB,yBAAsB,IAAI,
|
|
1
|
+
{"version":3,"file":"useControlsState.js","names":[],"sources":["../../../../../src/components/screens/controls/hooks/useControlsState.ts"],"sourcesContent":["/**\n * useControlsState - State management hook for Controls Screen\n * \n * Manages keyboard press detection, gamepad state, and category selection\n * \n * @module components/screens/controls/hooks\n */\n\nimport { useCallback, useEffect, useState } from \"react\";\n\n/**\n * Controls state interface\n */\nexport interface ControlsState {\n readonly pressedKeys: Set<string>;\n readonly category: 'keyboard' | 'gamepad';\n readonly selectedTab: 'combat' | 'movement' | 'system';\n}\n\n/**\n * Hook return type\n */\nexport interface UseControlsStateReturn {\n readonly pressedKeys: Set<string>;\n readonly category: 'keyboard' | 'gamepad';\n readonly selectedTab: 'combat' | 'movement' | 'system';\n readonly setCategory: (category: 'keyboard' | 'gamepad') => void;\n readonly setSelectedTab: (tab: 'combat' | 'movement' | 'system') => void;\n}\n\n/**\n * Custom hook for managing controls screen state\n * \n * Features:\n * - Keyboard press detection with cleanup\n * - Category switching (keyboard/gamepad)\n * - Tab selection for control categories\n * \n * @example\n * ```tsx\n * const { pressedKeys, category, selectedTab, setCategory, setSelectedTab } = useControlsState();\n * \n * // Check if key is pressed\n * const isSpacePressed = pressedKeys.has('Space');\n * \n * // Switch to gamepad view\n * setCategory('gamepad');\n * ```\n */\nexport function useControlsState(): UseControlsStateReturn {\n const [pressedKeys, setPressedKeys] = useState<Set<string>>(new Set());\n const [category, setCategory] = useState<'keyboard' | 'gamepad'>('keyboard');\n const [selectedTab, setSelectedTab] = useState<'combat' | 'movement' | 'system'>('combat');\n\n useEffect(() => {\n const handleKeyDown = (event: KeyboardEvent) => {\n if (event.target instanceof HTMLInputElement || event.target instanceof HTMLTextAreaElement) {\n return;\n }\n\n setPressedKeys(prev => {\n const next = new Set(prev);\n next.add(event.code);\n return next;\n });\n };\n\n const handleKeyUp = (event: KeyboardEvent) => {\n setPressedKeys(prev => {\n const next = new Set(prev);\n next.delete(event.code);\n return next;\n });\n };\n\n window.addEventListener('keydown', handleKeyDown);\n window.addEventListener('keyup', handleKeyUp);\n\n return () => {\n window.removeEventListener('keydown', handleKeyDown);\n window.removeEventListener('keyup', handleKeyUp);\n };\n }, []);\n\n const handleSetCategory = useCallback((newCategory: 'keyboard' | 'gamepad') => {\n setCategory(newCategory);\n }, []);\n\n const handleSetSelectedTab = useCallback((tab: 'combat' | 'movement' | 'system') => {\n setSelectedTab(tab);\n }, []);\n\n return {\n pressedKeys,\n category,\n selectedTab,\n setCategory: handleSetCategory,\n setSelectedTab: handleSetSelectedTab,\n };\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;AAiDA,SAAgB,mBAA2C;CACzD,MAAM,CAAC,aAAa,kBAAkB,yBAAsB,IAAI,IAAI,CAAC;CACrE,MAAM,CAAC,UAAU,eAAe,SAAiC,UAAU;CAC3E,MAAM,CAAC,aAAa,kBAAkB,SAA2C,QAAQ;CAEzF,gBAAgB;EACd,MAAM,iBAAiB,UAAyB;GAC9C,IAAI,MAAM,kBAAkB,oBAAoB,MAAM,kBAAkB,qBACtE;GAGF,gBAAe,SAAQ;IACrB,MAAM,OAAO,IAAI,IAAI,IAAI;IACzB,KAAK,IAAI,MAAM,IAAI;IACnB,OAAO;GACT,CAAC;EACH;EAEA,MAAM,eAAe,UAAyB;GAC5C,gBAAe,SAAQ;IACrB,MAAM,OAAO,IAAI,IAAI,IAAI;IACzB,KAAK,OAAO,MAAM,IAAI;IACtB,OAAO;GACT,CAAC;EACH;EAEA,OAAO,iBAAiB,WAAW,aAAa;EAChD,OAAO,iBAAiB,SAAS,WAAW;EAE5C,aAAa;GACX,OAAO,oBAAoB,WAAW,aAAa;GACnD,OAAO,oBAAoB,SAAS,WAAW;EACjD;CACF,GAAG,CAAC,CAAC;CAUL,OAAO;EACL;EACA;EACA;EACA,aAZwB,aAAa,gBAAwC;GAC7E,YAAY,WAAW;EACzB,GAAG,CAAC,CAUW;EACb,gBAT2B,aAAa,QAA0C;GAClF,eAAe,GAAG;EACpB,GAAG,CAAC,CAOc;CAClB;AACF"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"EndScreen3D.js","names":[],"sources":["../../../../src/components/screens/endscreen/EndScreen3D.tsx"],"sourcesContent":["import { PerspectiveCamera } from \"@react-three/drei\";\nimport { Canvas, useFrame } from \"@react-three/fiber\";\nimport React, {\n useCallback,\n useEffect,\n useMemo,\n useRef,\n useState,\n} from \"react\";\nimport * as THREE from \"three\";\nimport { useAudio } from \"../../../audio/AudioProvider\";\nimport { useWebGLContextLossHandler } from \"../../../hooks/useWebGLContextLossHandler\";\nimport { useWindowSize } from \"../../../hooks/useWindowSize\";\nimport { PlayerState } from \"../../../systems\";\nimport { MatchStatistics } from \"../../../systems/combat\";\nimport { Z_INDEX } from \"../../../types/LayoutTypes\";\nimport { hexToRgbaString } from \"../../../utils/colorUtils\";\nimport {\n detectPlatform,\n shouldUseMobileControls,\n} from \"../../../utils/deviceDetection\";\nimport { BaseButtonOverlayHtml } from \"../../shared/base/BaseButtonOverlayHtml\";\nimport { useKoreanTheme } from \"../../shared/base/useKoreanTheme\";\nimport { VolumeControl } from \"../../shared/ui/VolumeControl\";\nimport { DefeatAnimation3D } from \"./components/DefeatAnimation3D\";\nimport { MatchStatisticsDisplay } from \"./components/MatchStatisticsDisplayOverlayHtml\";\nimport { NavigationButtons } from \"./components/NavigationButtonsOverlayHtml\";\nimport { PerformanceBreakdown } from \"./components/PerformanceBreakdownOverlayHtml\";\nimport { PerformanceRating } from \"./components/PerformanceRatingOverlayHtml\";\nimport { VictoryAnimation3D } from \"./components/VictoryAnimation3D\";\nimport { WinnerDisplay } from \"./components/WinnerDisplayOverlayHtml\";\n\nexport interface EndScreen3DProps {\n readonly winner: PlayerState;\n readonly matchStats: MatchStatistics;\n readonly onReturnToMenu: () => void;\n readonly onRematch?: () => void;\n readonly onViewReplay?: () => void;\n readonly width?: number;\n readonly height?: number;\n}\n\n/**\n * Helper to convert hex color to CSS string\n */\nconst toCssColor = (hex: number): string => hexToRgbaString(hex, 1);\n\n/**\n * Three.js-based End Screen Component\n * Displays victory/defeat screen with match statistics and 3D effects\n */\nconst BackgroundParticles3D: React.FC<{ color: number }> = ({ color }) => {\n const pointsRef = useRef<THREE.Points>(null);\n\n const [particleData] = useState(() => {\n const count = 100;\n const pos = new Float32Array(count * 3);\n const vel = new Float32Array(count * 3);\n\n for (let i = 0; i < count; i++) {\n const i3 = i * 3;\n pos[i3] = (Math.random() - 0.5) * 40;\n pos[i3 + 1] = (Math.random() - 0.5) * 30;\n pos[i3 + 2] = (Math.random() - 0.5) * 20;\n\n vel[i3] = (Math.random() - 0.5) * 0.5;\n vel[i3 + 1] = Math.random() * 0.3;\n vel[i3 + 2] = (Math.random() - 0.5) * 0.5;\n }\n return { positions: pos, velocities: vel };\n });\n\n const { positions, velocities } = particleData;\n\n useFrame((_state, delta) => {\n if (!pointsRef.current) return;\n\n const attr = pointsRef.current.geometry.attributes.position;\n const array = attr.array as Float32Array;\n\n for (let i = 0; i < 100; i++) {\n const i3 = i * 3;\n\n array[i3] += velocities[i3] * delta;\n array[i3 + 1] += velocities[i3 + 1] * delta;\n array[i3 + 2] += velocities[i3 + 2] * delta;\n\n if (array[i3 + 1] > 15) {\n array[i3 + 1] = -15;\n }\n if (Math.abs(array[i3]) > 20) {\n array[i3] = -array[i3];\n }\n if (Math.abs(array[i3 + 2]) > 10) {\n array[i3 + 2] = -array[i3 + 2];\n }\n }\n\n attr.needsUpdate = true;\n });\n\n return (\n <points ref={pointsRef}>\n <bufferGeometry>\n <bufferAttribute\n attach=\"attributes-position\"\n count={100}\n itemSize={3}\n args={[positions, 3]}\n />\n </bufferGeometry>\n <pointsMaterial\n size={0.15}\n color={new THREE.Color(color)}\n transparent\n opacity={0.6}\n sizeAttenuation\n depthWrite={false}\n />\n </points>\n );\n};\n\n/**\n * Main Three.js background scene\n */\nconst EndScreenBackground3D: React.FC<{ \n isVictory: boolean;\n isMobile: boolean;\n}> = ({\n isVictory,\n isMobile,\n}) => {\n const gridRef = useRef<THREE.GridHelper>(null);\n const theme = useKoreanTheme({ variant: \"primary\", size: \"md\", isMobile });\n\n useFrame(() => {\n if (gridRef.current) {\n gridRef.current.rotation.y += 0.0005;\n }\n });\n\n const primaryColor = isVictory\n ? theme.colors.ACCENT_GOLD\n : theme.colors.ACCENT_RED;\n\n return (\n <>\n {/* Ambient lighting */}\n <ambientLight intensity={0.3} color={theme.colors.PRIMARY_CYAN} />\n\n {/* Directional lights */}\n <directionalLight\n position={[10, 15, 10]}\n intensity={isVictory ? 1.2 : 0.8}\n color={primaryColor}\n />\n <directionalLight\n position={[-10, 10, -5]}\n intensity={0.5}\n color={theme.colors.PRIMARY_CYAN}\n />\n\n {/* Point light for dramatic effect */}\n <pointLight\n position={[0, 5, 0]}\n intensity={isVictory ? 2 : 1}\n distance={30}\n color={primaryColor}\n />\n\n {/* Grid for cyberpunk aesthetic */}\n <gridHelper\n ref={gridRef}\n args={[40, 40, primaryColor, theme.colors.UI_BACKGROUND_MEDIUM]}\n position={[0, -5, 0]}\n />\n\n {/* Background particles */}\n <BackgroundParticles3D color={primaryColor} />\n\n {/* Victory or Defeat animation */}\n {isVictory ? <VictoryAnimation3D /> : <DefeatAnimation3D />}\n </>\n );\n};\n\n/**\n * EndScreen3D Component\n * Three.js-based end screen with Korean theming\n */\nexport const EndScreen3D: React.FC<EndScreen3DProps> = ({\n winner,\n matchStats,\n onReturnToMenu,\n onRematch,\n onViewReplay,\n width: propWidth,\n height: propHeight,\n}) => {\n useWebGLContextLossHandler({\n onContextLost: () => {\n console.warn(\"⚠️ WebGL context lost in EndScreen\");\n },\n autoRestore: true,\n });\n\n const audio = useAudio();\n const [showStats, setShowStats] = useState(false);\n const [showBreakdown, setShowBreakdown] = useState(false);\n\n const { width: windowWidth, height: windowHeight } = useWindowSize();\n const screenWidth = propWidth ?? windowWidth;\n void (propHeight ?? windowHeight); // Consumed but not stored\n\n const winnerId = winner.id;\n const isVictory = winnerId === \"player-0\" || winnerId.endsWith(\"-0\");\n\n const isMobile = useMemo(() => shouldUseMobileControls(), []);\n const platform = useMemo(() => detectPlatform(), []);\n const isTablet = useMemo(() => platform.isTablet, [platform]);\n const isLargeDesktop = useMemo(() => screenWidth >= 1920, [screenWidth]); // 4K/2K displays\n\n const layoutConstants = useMemo(\n () => ({\n titleFontSize: isMobile ? 36 : isTablet ? 44 : isLargeDesktop ? 44 : 54,\n subtitleFontSize: isMobile\n ? 18\n : isTablet\n ? 22\n : isLargeDesktop\n ? 22\n : 28,\n buttonFontSize: isMobile ? 14 : isTablet ? 15 : isLargeDesktop ? 14 : 16,\n padding: isMobile ? 15 : isTablet ? 18 : isLargeDesktop ? 15 : 20,\n buttonPadding: isMobile\n ? \"10px 20px\"\n : isTablet\n ? \"11px 22px\"\n : isLargeDesktop\n ? \"10px 20px\"\n : \"12px 25px\",\n spacing: isMobile ? 15 : isTablet ? 18 : isLargeDesktop ? 15 : 20,\n }),\n [isMobile, isTablet, isLargeDesktop],\n );\n\n const theme = useKoreanTheme({\n variant: \"primary\",\n size: \"md\",\n isMobile,\n });\n\n useEffect(() => {\n if (isVictory) {\n audio.playSFX?.(\"victory_fanfare\");\n const timeoutId = setTimeout(() => {\n audio.playMusic?.(\"victory_theme\");\n }, 1000);\n\n return () => {\n clearTimeout(timeoutId);\n audio.stopMusic?.();\n };\n } else {\n audio.playSFX?.(\"defeat_sound\");\n audio.playMusic?.(\"defeat_theme\");\n\n return () => {\n audio.stopMusic?.();\n };\n }\n }, [audio, isVictory]);\n\n const toggleStats = useCallback(() => {\n audio.playSFX?.(\"menu_hover\");\n setShowStats((prev) => !prev);\n }, [audio]);\n\n const toggleBreakdown = useCallback(() => {\n audio.playSFX?.(\"menu_hover\");\n setShowBreakdown((prev) => !prev);\n }, [audio]);\n\n const handlePlaySelectSound = useCallback(() => {\n audio.playSFX?.(\"menu_select\");\n }, [audio]);\n\n const handlePlayHoverSound = useCallback(() => {\n audio.playSFX?.(\"menu_hover\");\n }, [audio]);\n\n return (\n <div\n data-testid=\"end-screen-3d\"\n style={{\n width: \"100vw\",\n height: \"100vh\",\n position: \"relative\",\n overflow: \"hidden\",\n }}\n >\n {/* Volume Control - outside Canvas to maintain AudioProvider context */}\n <VolumeControl position=\"top-right\" compact={isMobile} />\n\n {/* 3D Background Canvas */}\n <Canvas\n style={{\n width: \"100%\",\n height: \"100%\",\n position: \"absolute\",\n top: 0,\n left: 0,\n zIndex: Z_INDEX.ARENA,\n }}\n dpr={[1, 2]}\n gl={{\n antialias: true,\n alpha: false,\n powerPreference: \"high-performance\",\n }}\n onCreated={({ gl }) => {\n gl.setClearColor(theme.colors.UI_BACKGROUND_DARK, 1);\n }}\n data-testid=\"end-screen-canvas\"\n >\n {/* Camera */}\n <PerspectiveCamera makeDefault position={[0, 5, 15]} fov={60} />\n\n {/* Background scene */}\n <EndScreenBackground3D isVictory={isVictory} isMobile={isMobile} />\n </Canvas>\n\n {/* UI Overlay - outside Canvas for proper layout and AudioProvider context */}\n <div\n data-testid=\"end-screen-overlay\"\n style={{\n position: \"absolute\",\n top: 0,\n left: 0,\n width: \"100%\",\n height: \"100%\",\n display: \"flex\",\n flexDirection: \"column\",\n alignItems: \"center\",\n justifyContent: \"center\",\n fontFamily: theme.koreanTypography.fontFamily,\n lineHeight: theme.koreanTypography.lineHeight,\n letterSpacing: theme.koreanTypography.letterSpacing,\n wordBreak: theme.koreanTypography.wordBreak,\n color: toCssColor(theme.colors.TEXT_PRIMARY),\n padding: layoutConstants.padding,\n boxSizing: \"border-box\",\n overflowY: \"auto\",\n zIndex: Z_INDEX.HUD,\n pointerEvents: \"none\",\n }}\n >\n <div\n style={{\n display: \"flex\",\n flexDirection: \"column\",\n alignItems: \"center\",\n maxHeight: \"100%\",\n pointerEvents: \"auto\",\n }}\n >\n {/* Winner Display Component */}\n <WinnerDisplay\n winner={winner}\n isVictory={isVictory}\n isMobile={isMobile}\n isTablet={isTablet}\n />\n\n {/* Performance Rating Component */}\n <PerformanceRating\n matchStats={matchStats}\n isMobile={isMobile}\n isTablet={isTablet}\n />\n\n {/* Match Statistics Toggle */}\n <div style={{ marginBottom: layoutConstants.spacing }}>\n <BaseButtonOverlayHtml\n korean={showStats ? \"통계 숨기기\" : \"통계 보기\"}\n english={showStats ? \"Hide Stats\" : \"View Stats\"}\n onClick={toggleStats}\n onMouseEnter={() => audio.playSFX?.(\"menu_hover\")}\n variant=\"secondary\"\n size={isMobile ? \"sm\" : \"md\"}\n fullWidth\n testId=\"toggle-stats-button\"\n ariaLabel={showStats ? \"Hide match statistics\" : \"View match statistics\"}\n isMobile={isMobile}\n />\n </div>\n\n {/* Match Statistics Display */}\n {showStats && (\n <MatchStatisticsDisplay\n matchStats={matchStats}\n isMobile={isMobile}\n isTablet={isTablet}\n />\n )}\n\n {/* Performance Breakdown Toggle */}\n <div style={{ marginBottom: layoutConstants.spacing }}>\n <BaseButtonOverlayHtml\n korean={showBreakdown ? \"분석 숨기기\" : \"상세 분석\"}\n english={showBreakdown ? \"Hide Breakdown\" : \"View Breakdown\"}\n onClick={toggleBreakdown}\n onMouseEnter={() => audio.playSFX?.(\"menu_hover\")}\n variant=\"primary\"\n size={isMobile ? \"sm\" : \"md\"}\n fullWidth\n testId=\"toggle-breakdown-button\"\n ariaLabel={showBreakdown ? \"Hide performance breakdown\" : \"View performance breakdown\"}\n isMobile={isMobile}\n />\n </div>\n\n {/* Performance Breakdown Display */}\n {showBreakdown && (\n <PerformanceBreakdown\n matchStats={matchStats}\n isMobile={isMobile}\n isTablet={isTablet}\n />\n )}\n\n {/* Navigation Buttons Component */}\n <NavigationButtons\n onReturnToMenu={onReturnToMenu}\n onRematch={onRematch}\n onViewReplay={onViewReplay}\n isMobile={isMobile}\n isTablet={isTablet}\n width={screenWidth}\n onPlaySelectSound={handlePlaySelectSound}\n onPlayHoverSound={handlePlayHoverSound}\n />\n </div>\n </div>\n </div>\n );\n};\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"EndScreen3D.js","names":[],"sources":["../../../../src/components/screens/endscreen/EndScreen3D.tsx"],"sourcesContent":["import { PerspectiveCamera } from \"@react-three/drei\";\nimport { Canvas, useFrame } from \"@react-three/fiber\";\nimport React, {\n useCallback,\n useEffect,\n useMemo,\n useRef,\n useState,\n} from \"react\";\nimport * as THREE from \"three\";\nimport { useAudio } from \"../../../audio/AudioProvider\";\nimport { useWebGLContextLossHandler } from \"../../../hooks/useWebGLContextLossHandler\";\nimport { useWindowSize } from \"../../../hooks/useWindowSize\";\nimport { PlayerState } from \"../../../systems\";\nimport { MatchStatistics } from \"../../../systems/combat\";\nimport { Z_INDEX } from \"../../../types/LayoutTypes\";\nimport { hexToRgbaString } from \"../../../utils/colorUtils\";\nimport {\n detectPlatform,\n shouldUseMobileControls,\n} from \"../../../utils/deviceDetection\";\nimport { BaseButtonOverlayHtml } from \"../../shared/base/BaseButtonOverlayHtml\";\nimport { useKoreanTheme } from \"../../shared/base/useKoreanTheme\";\nimport { VolumeControl } from \"../../shared/ui/VolumeControl\";\nimport { DefeatAnimation3D } from \"./components/DefeatAnimation3D\";\nimport { MatchStatisticsDisplay } from \"./components/MatchStatisticsDisplayOverlayHtml\";\nimport { NavigationButtons } from \"./components/NavigationButtonsOverlayHtml\";\nimport { PerformanceBreakdown } from \"./components/PerformanceBreakdownOverlayHtml\";\nimport { PerformanceRating } from \"./components/PerformanceRatingOverlayHtml\";\nimport { VictoryAnimation3D } from \"./components/VictoryAnimation3D\";\nimport { WinnerDisplay } from \"./components/WinnerDisplayOverlayHtml\";\n\nexport interface EndScreen3DProps {\n readonly winner: PlayerState;\n readonly matchStats: MatchStatistics;\n readonly onReturnToMenu: () => void;\n readonly onRematch?: () => void;\n readonly onViewReplay?: () => void;\n readonly width?: number;\n readonly height?: number;\n}\n\n/**\n * Helper to convert hex color to CSS string\n */\nconst toCssColor = (hex: number): string => hexToRgbaString(hex, 1);\n\n/**\n * Three.js-based End Screen Component\n * Displays victory/defeat screen with match statistics and 3D effects\n */\nconst BackgroundParticles3D: React.FC<{ color: number }> = ({ color }) => {\n const pointsRef = useRef<THREE.Points>(null);\n\n const [particleData] = useState(() => {\n const count = 100;\n const pos = new Float32Array(count * 3);\n const vel = new Float32Array(count * 3);\n\n for (let i = 0; i < count; i++) {\n const i3 = i * 3;\n pos[i3] = (Math.random() - 0.5) * 40;\n pos[i3 + 1] = (Math.random() - 0.5) * 30;\n pos[i3 + 2] = (Math.random() - 0.5) * 20;\n\n vel[i3] = (Math.random() - 0.5) * 0.5;\n vel[i3 + 1] = Math.random() * 0.3;\n vel[i3 + 2] = (Math.random() - 0.5) * 0.5;\n }\n return { positions: pos, velocities: vel };\n });\n\n const { positions, velocities } = particleData;\n\n useFrame((_state, delta) => {\n if (!pointsRef.current) return;\n\n const attr = pointsRef.current.geometry.attributes.position;\n const array = attr.array as Float32Array;\n\n for (let i = 0; i < 100; i++) {\n const i3 = i * 3;\n\n array[i3] += velocities[i3] * delta;\n array[i3 + 1] += velocities[i3 + 1] * delta;\n array[i3 + 2] += velocities[i3 + 2] * delta;\n\n if (array[i3 + 1] > 15) {\n array[i3 + 1] = -15;\n }\n if (Math.abs(array[i3]) > 20) {\n array[i3] = -array[i3];\n }\n if (Math.abs(array[i3 + 2]) > 10) {\n array[i3 + 2] = -array[i3 + 2];\n }\n }\n\n attr.needsUpdate = true;\n });\n\n return (\n <points ref={pointsRef}>\n <bufferGeometry>\n <bufferAttribute\n attach=\"attributes-position\"\n count={100}\n itemSize={3}\n args={[positions, 3]}\n />\n </bufferGeometry>\n <pointsMaterial\n size={0.15}\n color={new THREE.Color(color)}\n transparent\n opacity={0.6}\n sizeAttenuation\n depthWrite={false}\n />\n </points>\n );\n};\n\n/**\n * Main Three.js background scene\n */\nconst EndScreenBackground3D: React.FC<{ \n isVictory: boolean;\n isMobile: boolean;\n}> = ({\n isVictory,\n isMobile,\n}) => {\n const gridRef = useRef<THREE.GridHelper>(null);\n const theme = useKoreanTheme({ variant: \"primary\", size: \"md\", isMobile });\n\n useFrame(() => {\n if (gridRef.current) {\n gridRef.current.rotation.y += 0.0005;\n }\n });\n\n const primaryColor = isVictory\n ? theme.colors.ACCENT_GOLD\n : theme.colors.ACCENT_RED;\n\n return (\n <>\n {/* Ambient lighting */}\n <ambientLight intensity={0.3} color={theme.colors.PRIMARY_CYAN} />\n\n {/* Directional lights */}\n <directionalLight\n position={[10, 15, 10]}\n intensity={isVictory ? 1.2 : 0.8}\n color={primaryColor}\n />\n <directionalLight\n position={[-10, 10, -5]}\n intensity={0.5}\n color={theme.colors.PRIMARY_CYAN}\n />\n\n {/* Point light for dramatic effect */}\n <pointLight\n position={[0, 5, 0]}\n intensity={isVictory ? 2 : 1}\n distance={30}\n color={primaryColor}\n />\n\n {/* Grid for cyberpunk aesthetic */}\n <gridHelper\n ref={gridRef}\n args={[40, 40, primaryColor, theme.colors.UI_BACKGROUND_MEDIUM]}\n position={[0, -5, 0]}\n />\n\n {/* Background particles */}\n <BackgroundParticles3D color={primaryColor} />\n\n {/* Victory or Defeat animation */}\n {isVictory ? <VictoryAnimation3D /> : <DefeatAnimation3D />}\n </>\n );\n};\n\n/**\n * EndScreen3D Component\n * Three.js-based end screen with Korean theming\n */\nexport const EndScreen3D: React.FC<EndScreen3DProps> = ({\n winner,\n matchStats,\n onReturnToMenu,\n onRematch,\n onViewReplay,\n width: propWidth,\n height: propHeight,\n}) => {\n useWebGLContextLossHandler({\n onContextLost: () => {\n console.warn(\"⚠️ WebGL context lost in EndScreen\");\n },\n autoRestore: true,\n });\n\n const audio = useAudio();\n const [showStats, setShowStats] = useState(false);\n const [showBreakdown, setShowBreakdown] = useState(false);\n\n const { width: windowWidth, height: windowHeight } = useWindowSize();\n const screenWidth = propWidth ?? windowWidth;\n void (propHeight ?? windowHeight); // Consumed but not stored\n\n const winnerId = winner.id;\n const isVictory = winnerId === \"player-0\" || winnerId.endsWith(\"-0\");\n\n const isMobile = useMemo(() => shouldUseMobileControls(), []);\n const platform = useMemo(() => detectPlatform(), []);\n const isTablet = useMemo(() => platform.isTablet, [platform]);\n const isLargeDesktop = useMemo(() => screenWidth >= 1920, [screenWidth]); // 4K/2K displays\n\n const layoutConstants = useMemo(\n () => ({\n titleFontSize: isMobile ? 36 : isTablet ? 44 : isLargeDesktop ? 44 : 54,\n subtitleFontSize: isMobile\n ? 18\n : isTablet\n ? 22\n : isLargeDesktop\n ? 22\n : 28,\n buttonFontSize: isMobile ? 14 : isTablet ? 15 : isLargeDesktop ? 14 : 16,\n padding: isMobile ? 15 : isTablet ? 18 : isLargeDesktop ? 15 : 20,\n buttonPadding: isMobile\n ? \"10px 20px\"\n : isTablet\n ? \"11px 22px\"\n : isLargeDesktop\n ? \"10px 20px\"\n : \"12px 25px\",\n spacing: isMobile ? 15 : isTablet ? 18 : isLargeDesktop ? 15 : 20,\n }),\n [isMobile, isTablet, isLargeDesktop],\n );\n\n const theme = useKoreanTheme({\n variant: \"primary\",\n size: \"md\",\n isMobile,\n });\n\n useEffect(() => {\n if (isVictory) {\n audio.playSFX?.(\"victory_fanfare\");\n const timeoutId = setTimeout(() => {\n audio.playMusic?.(\"victory_theme\");\n }, 1000);\n\n return () => {\n clearTimeout(timeoutId);\n audio.stopMusic?.();\n };\n } else {\n audio.playSFX?.(\"defeat_sound\");\n audio.playMusic?.(\"defeat_theme\");\n\n return () => {\n audio.stopMusic?.();\n };\n }\n }, [audio, isVictory]);\n\n const toggleStats = useCallback(() => {\n audio.playSFX?.(\"menu_hover\");\n setShowStats((prev) => !prev);\n }, [audio]);\n\n const toggleBreakdown = useCallback(() => {\n audio.playSFX?.(\"menu_hover\");\n setShowBreakdown((prev) => !prev);\n }, [audio]);\n\n const handlePlaySelectSound = useCallback(() => {\n audio.playSFX?.(\"menu_select\");\n }, [audio]);\n\n const handlePlayHoverSound = useCallback(() => {\n audio.playSFX?.(\"menu_hover\");\n }, [audio]);\n\n return (\n <div\n data-testid=\"end-screen-3d\"\n style={{\n width: \"100vw\",\n height: \"100vh\",\n position: \"relative\",\n overflow: \"hidden\",\n }}\n >\n {/* Volume Control - outside Canvas to maintain AudioProvider context */}\n <VolumeControl position=\"top-right\" compact={isMobile} />\n\n {/* 3D Background Canvas */}\n <Canvas\n style={{\n width: \"100%\",\n height: \"100%\",\n position: \"absolute\",\n top: 0,\n left: 0,\n zIndex: Z_INDEX.ARENA,\n }}\n dpr={[1, 2]}\n gl={{\n antialias: true,\n alpha: false,\n powerPreference: \"high-performance\",\n }}\n onCreated={({ gl }) => {\n gl.setClearColor(theme.colors.UI_BACKGROUND_DARK, 1);\n }}\n data-testid=\"end-screen-canvas\"\n >\n {/* Camera */}\n <PerspectiveCamera makeDefault position={[0, 5, 15]} fov={60} />\n\n {/* Background scene */}\n <EndScreenBackground3D isVictory={isVictory} isMobile={isMobile} />\n </Canvas>\n\n {/* UI Overlay - outside Canvas for proper layout and AudioProvider context */}\n <div\n data-testid=\"end-screen-overlay\"\n style={{\n position: \"absolute\",\n top: 0,\n left: 0,\n width: \"100%\",\n height: \"100%\",\n display: \"flex\",\n flexDirection: \"column\",\n alignItems: \"center\",\n justifyContent: \"center\",\n fontFamily: theme.koreanTypography.fontFamily,\n lineHeight: theme.koreanTypography.lineHeight,\n letterSpacing: theme.koreanTypography.letterSpacing,\n wordBreak: theme.koreanTypography.wordBreak,\n color: toCssColor(theme.colors.TEXT_PRIMARY),\n padding: layoutConstants.padding,\n boxSizing: \"border-box\",\n overflowY: \"auto\",\n zIndex: Z_INDEX.HUD,\n pointerEvents: \"none\",\n }}\n >\n <div\n style={{\n display: \"flex\",\n flexDirection: \"column\",\n alignItems: \"center\",\n maxHeight: \"100%\",\n pointerEvents: \"auto\",\n }}\n >\n {/* Winner Display Component */}\n <WinnerDisplay\n winner={winner}\n isVictory={isVictory}\n isMobile={isMobile}\n isTablet={isTablet}\n />\n\n {/* Performance Rating Component */}\n <PerformanceRating\n matchStats={matchStats}\n isMobile={isMobile}\n isTablet={isTablet}\n />\n\n {/* Match Statistics Toggle */}\n <div style={{ marginBottom: layoutConstants.spacing }}>\n <BaseButtonOverlayHtml\n korean={showStats ? \"통계 숨기기\" : \"통계 보기\"}\n english={showStats ? \"Hide Stats\" : \"View Stats\"}\n onClick={toggleStats}\n onMouseEnter={() => audio.playSFX?.(\"menu_hover\")}\n variant=\"secondary\"\n size={isMobile ? \"sm\" : \"md\"}\n fullWidth\n testId=\"toggle-stats-button\"\n ariaLabel={showStats ? \"Hide match statistics\" : \"View match statistics\"}\n isMobile={isMobile}\n />\n </div>\n\n {/* Match Statistics Display */}\n {showStats && (\n <MatchStatisticsDisplay\n matchStats={matchStats}\n isMobile={isMobile}\n isTablet={isTablet}\n />\n )}\n\n {/* Performance Breakdown Toggle */}\n <div style={{ marginBottom: layoutConstants.spacing }}>\n <BaseButtonOverlayHtml\n korean={showBreakdown ? \"분석 숨기기\" : \"상세 분석\"}\n english={showBreakdown ? \"Hide Breakdown\" : \"View Breakdown\"}\n onClick={toggleBreakdown}\n onMouseEnter={() => audio.playSFX?.(\"menu_hover\")}\n variant=\"primary\"\n size={isMobile ? \"sm\" : \"md\"}\n fullWidth\n testId=\"toggle-breakdown-button\"\n ariaLabel={showBreakdown ? \"Hide performance breakdown\" : \"View performance breakdown\"}\n isMobile={isMobile}\n />\n </div>\n\n {/* Performance Breakdown Display */}\n {showBreakdown && (\n <PerformanceBreakdown\n matchStats={matchStats}\n isMobile={isMobile}\n isTablet={isTablet}\n />\n )}\n\n {/* Navigation Buttons Component */}\n <NavigationButtons\n onReturnToMenu={onReturnToMenu}\n onRematch={onRematch}\n onViewReplay={onViewReplay}\n isMobile={isMobile}\n isTablet={isTablet}\n width={screenWidth}\n onPlaySelectSound={handlePlaySelectSound}\n onPlayHoverSound={handlePlayHoverSound}\n />\n </div>\n </div>\n </div>\n );\n};\n"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"DefeatAnimation3D.js","names":[],"sources":["../../../../../src/components/screens/endscreen/components/DefeatAnimation3D.tsx"],"sourcesContent":["import { useFrame } from \"@react-three/fiber\";\nimport React, { useEffect, useRef, useState } from \"react\";\nimport * as THREE from \"three\";\nimport { KOREAN_COLORS } from \"../../../../types/constants\";\n\n/**\n * Defeat Animation 3D Component\n * Displays somber 3D particle effects for defeat screen\n * Uses blue/cyan tones to contrast with gold victory effects\n * Optimized for 60fps performance with object reuse\n */\nexport const DefeatAnimation3D: React.FC = () => {\n const groupRef = useRef<THREE.Group>(null);\n const particlesRef = useRef<THREE.Points>(null);\n const spiralRef = useRef<THREE.Group>(null);\n\n const [reusableScale] = useState(() => new THREE.Vector3());\n const [reusablePosition] = useState(() => new THREE.Vector3());\n\n const [particlePositions] = useState(() => {\n const count = 100; // Fewer particles than victory for subdued effect\n const positions = new Float32Array(count * 3);\n\n for (let i = 0; i < count; i++) {\n const i3 = i * 3;\n const radius = 2 + Math.random() * 3;\n const theta = Math.random() * Math.PI * 2;\n const phi = Math.random() * Math.PI;\n\n positions[i3] = radius * Math.sin(phi) * Math.cos(theta);\n positions[i3 + 1] = radius * Math.cos(phi) - 1; // Lower starting position\n positions[i3 + 2] = radius * Math.sin(phi) * Math.sin(theta);\n }\n\n return positions;\n });\n\n useFrame((state) => {\n const time = state.clock.elapsedTime;\n\n if (groupRef.current) {\n groupRef.current.rotation.y = time * 0.15; // Slower than victory\n }\n\n if (particlesRef.current) {\n const scale = 1 + Math.sin(time * 1.5) * 0.1; // Subtle pulsing\n reusableScale.setScalar(scale);\n particlesRef.current.scale.copy(reusableScale);\n \n const yPos = Math.sin(time * 0.5) * 0.3 - 0.2;\n reusablePosition.set(0, yPos, 0);\n particlesRef.current.position.copy(reusablePosition);\n }\n\n if (spiralRef.current) {\n spiralRef.current.rotation.y = -time * 0.2; // Counter-rotation\n spiralRef.current.rotation.z = Math.sin(time * 0.3) * 0.2;\n }\n });\n\n useEffect(() => {\n const group = groupRef.current;\n const particles = particlesRef.current;\n const spiral = spiralRef.current;\n\n return () => {\n if (particles) {\n particles.geometry?.dispose();\n if (particles.material) {\n (particles.material as THREE.Material).dispose();\n }\n }\n if (spiral?.children && Array.isArray(spiral.children)) {\n spiral.children.forEach((child) => {\n if (child instanceof THREE.Mesh) {\n child.geometry?.dispose();\n if (child.material) {\n (child.material as THREE.Material).dispose();\n }\n }\n });\n }\n if (group?.children && Array.isArray(group.children)) {\n group.children.forEach((child) => {\n if (child === particles || child === spiral) {\n return;\n }\n\n if (child instanceof THREE.Mesh) {\n child.geometry?.dispose();\n if (child.material) {\n (child.material as THREE.Material).dispose();\n }\n } else if (child instanceof THREE.Points) {\n child.geometry?.dispose();\n if (child.material) {\n (child.material as THREE.Material).dispose();\n }\n }\n });\n }\n };\n }, []);\n\n return (\n <group\n ref={groupRef}\n position={[0, 1, 0]}\n data-testid=\"defeat-animation-3d\"\n >\n {/* Defeat particles - blue/cyan theme */}\n <points ref={particlesRef}>\n <bufferGeometry>\n <bufferAttribute\n attach=\"attributes-position\"\n count={100}\n itemSize={3}\n args={[particlePositions, 3]}\n />\n </bufferGeometry>\n <pointsMaterial\n size={0.15}\n color={new THREE.Color(KOREAN_COLORS.ACCENT_BLUE)}\n transparent\n opacity={0.6}\n sizeAttenuation\n depthWrite={false}\n />\n </points>\n\n {/* Descending spiral rings */}\n <group ref={spiralRef}>\n <mesh rotation={[Math.PI / 2, 0, 0]}>\n <torusGeometry args={[1.5, 0.03, 12, 64]} />\n <meshBasicMaterial\n color={KOREAN_COLORS.PRIMARY_CYAN}\n transparent\n opacity={0.4}\n />\n </mesh>\n\n <mesh rotation={[Math.PI / 2, Math.PI / 4, 0]} position={[0, -0.3, 0]}>\n <torusGeometry args={[2, 0.03, 12, 64]} />\n <meshBasicMaterial\n color={KOREAN_COLORS.ACCENT_BLUE}\n transparent\n opacity={0.3}\n />\n </mesh>\n\n <mesh rotation={[Math.PI / 2, Math.PI / 2, 0]} position={[0, -0.6, 0]}>\n <torusGeometry args={[2.5, 0.03, 12, 64]} />\n <meshBasicMaterial\n color={KOREAN_COLORS.PRIMARY_CYAN}\n transparent\n opacity={0.2}\n />\n </mesh>\n </group>\n\n {/* Central dimmed sphere */}\n <mesh>\n <sphereGeometry args={[0.4, 32, 32]} />\n <meshStandardMaterial\n color={KOREAN_COLORS.ACCENT_BLUE}\n emissive={KOREAN_COLORS.ACCENT_BLUE}\n emissiveIntensity={0.8}\n transparent\n opacity={0.6}\n />\n </mesh>\n\n {/* Outer faint glow */}\n <mesh>\n <sphereGeometry args={[0.7, 32, 32]} />\n <meshBasicMaterial\n color={KOREAN_COLORS.PRIMARY_CYAN}\n transparent\n opacity={0.15}\n side={THREE.BackSide}\n />\n </mesh>\n\n {/* Point light for subdued glow effect */}\n <pointLight\n position={[0, 0, 0]}\n intensity={1.5}\n distance={8}\n color={KOREAN_COLORS.ACCENT_BLUE}\n />\n </group>\n );\n};\n"],"mappings":";;;;;;;;;;;;AAWA,IAAa,0BAAoC;CAC/C,MAAM,WAAW,OAAoB,
|
|
1
|
+
{"version":3,"file":"DefeatAnimation3D.js","names":[],"sources":["../../../../../src/components/screens/endscreen/components/DefeatAnimation3D.tsx"],"sourcesContent":["import { useFrame } from \"@react-three/fiber\";\nimport React, { useEffect, useRef, useState } from \"react\";\nimport * as THREE from \"three\";\nimport { KOREAN_COLORS } from \"../../../../types/constants\";\n\n/**\n * Defeat Animation 3D Component\n * Displays somber 3D particle effects for defeat screen\n * Uses blue/cyan tones to contrast with gold victory effects\n * Optimized for 60fps performance with object reuse\n */\nexport const DefeatAnimation3D: React.FC = () => {\n const groupRef = useRef<THREE.Group>(null);\n const particlesRef = useRef<THREE.Points>(null);\n const spiralRef = useRef<THREE.Group>(null);\n\n const [reusableScale] = useState(() => new THREE.Vector3());\n const [reusablePosition] = useState(() => new THREE.Vector3());\n\n const [particlePositions] = useState(() => {\n const count = 100; // Fewer particles than victory for subdued effect\n const positions = new Float32Array(count * 3);\n\n for (let i = 0; i < count; i++) {\n const i3 = i * 3;\n const radius = 2 + Math.random() * 3;\n const theta = Math.random() * Math.PI * 2;\n const phi = Math.random() * Math.PI;\n\n positions[i3] = radius * Math.sin(phi) * Math.cos(theta);\n positions[i3 + 1] = radius * Math.cos(phi) - 1; // Lower starting position\n positions[i3 + 2] = radius * Math.sin(phi) * Math.sin(theta);\n }\n\n return positions;\n });\n\n useFrame((state) => {\n const time = state.clock.elapsedTime;\n\n if (groupRef.current) {\n groupRef.current.rotation.y = time * 0.15; // Slower than victory\n }\n\n if (particlesRef.current) {\n const scale = 1 + Math.sin(time * 1.5) * 0.1; // Subtle pulsing\n reusableScale.setScalar(scale);\n particlesRef.current.scale.copy(reusableScale);\n \n const yPos = Math.sin(time * 0.5) * 0.3 - 0.2;\n reusablePosition.set(0, yPos, 0);\n particlesRef.current.position.copy(reusablePosition);\n }\n\n if (spiralRef.current) {\n spiralRef.current.rotation.y = -time * 0.2; // Counter-rotation\n spiralRef.current.rotation.z = Math.sin(time * 0.3) * 0.2;\n }\n });\n\n useEffect(() => {\n const group = groupRef.current;\n const particles = particlesRef.current;\n const spiral = spiralRef.current;\n\n return () => {\n if (particles) {\n particles.geometry?.dispose();\n if (particles.material) {\n (particles.material as THREE.Material).dispose();\n }\n }\n if (spiral?.children && Array.isArray(spiral.children)) {\n spiral.children.forEach((child) => {\n if (child instanceof THREE.Mesh) {\n child.geometry?.dispose();\n if (child.material) {\n (child.material as THREE.Material).dispose();\n }\n }\n });\n }\n if (group?.children && Array.isArray(group.children)) {\n group.children.forEach((child) => {\n if (child === particles || child === spiral) {\n return;\n }\n\n if (child instanceof THREE.Mesh) {\n child.geometry?.dispose();\n if (child.material) {\n (child.material as THREE.Material).dispose();\n }\n } else if (child instanceof THREE.Points) {\n child.geometry?.dispose();\n if (child.material) {\n (child.material as THREE.Material).dispose();\n }\n }\n });\n }\n };\n }, []);\n\n return (\n <group\n ref={groupRef}\n position={[0, 1, 0]}\n data-testid=\"defeat-animation-3d\"\n >\n {/* Defeat particles - blue/cyan theme */}\n <points ref={particlesRef}>\n <bufferGeometry>\n <bufferAttribute\n attach=\"attributes-position\"\n count={100}\n itemSize={3}\n args={[particlePositions, 3]}\n />\n </bufferGeometry>\n <pointsMaterial\n size={0.15}\n color={new THREE.Color(KOREAN_COLORS.ACCENT_BLUE)}\n transparent\n opacity={0.6}\n sizeAttenuation\n depthWrite={false}\n />\n </points>\n\n {/* Descending spiral rings */}\n <group ref={spiralRef}>\n <mesh rotation={[Math.PI / 2, 0, 0]}>\n <torusGeometry args={[1.5, 0.03, 12, 64]} />\n <meshBasicMaterial\n color={KOREAN_COLORS.PRIMARY_CYAN}\n transparent\n opacity={0.4}\n />\n </mesh>\n\n <mesh rotation={[Math.PI / 2, Math.PI / 4, 0]} position={[0, -0.3, 0]}>\n <torusGeometry args={[2, 0.03, 12, 64]} />\n <meshBasicMaterial\n color={KOREAN_COLORS.ACCENT_BLUE}\n transparent\n opacity={0.3}\n />\n </mesh>\n\n <mesh rotation={[Math.PI / 2, Math.PI / 2, 0]} position={[0, -0.6, 0]}>\n <torusGeometry args={[2.5, 0.03, 12, 64]} />\n <meshBasicMaterial\n color={KOREAN_COLORS.PRIMARY_CYAN}\n transparent\n opacity={0.2}\n />\n </mesh>\n </group>\n\n {/* Central dimmed sphere */}\n <mesh>\n <sphereGeometry args={[0.4, 32, 32]} />\n <meshStandardMaterial\n color={KOREAN_COLORS.ACCENT_BLUE}\n emissive={KOREAN_COLORS.ACCENT_BLUE}\n emissiveIntensity={0.8}\n transparent\n opacity={0.6}\n />\n </mesh>\n\n {/* Outer faint glow */}\n <mesh>\n <sphereGeometry args={[0.7, 32, 32]} />\n <meshBasicMaterial\n color={KOREAN_COLORS.PRIMARY_CYAN}\n transparent\n opacity={0.15}\n side={THREE.BackSide}\n />\n </mesh>\n\n {/* Point light for subdued glow effect */}\n <pointLight\n position={[0, 0, 0]}\n intensity={1.5}\n distance={8}\n color={KOREAN_COLORS.ACCENT_BLUE}\n />\n </group>\n );\n};\n"],"mappings":"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"}
|
package/lib/components/screens/endscreen/components/MatchStatisticsDisplayOverlayHtml.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"MatchStatisticsDisplayOverlayHtml.js","names":[],"sources":["../../../../../src/components/screens/endscreen/components/MatchStatisticsDisplayOverlayHtml.tsx"],"sourcesContent":["import React from \"react\";\nimport { MatchStatistics } from \"../../../../systems/combat\";\nimport { PlayerMatchStats } from \"../../../../systems/player\";\nimport { FONT_FAMILY, KOREAN_COLORS } from \"../../../../types/constants\";\nimport { hexToRgbaString } from \"../../../../utils/colorUtils\";\n\nexport interface MatchStatisticsDisplayProps {\n readonly matchStats: MatchStatistics;\n readonly isMobile: boolean;\n readonly isTablet: boolean;\n}\n\n/**\n * Helper to convert hex color to CSS string\n */\nconst toCssColor = (hex: number): string => hexToRgbaString(hex, 1);\n\n/**\n * Props for StatRow component\n */\ninterface StatRowProps {\n readonly label: string;\n readonly value: string | number;\n readonly highlight?: boolean;\n readonly fontSize: number;\n readonly spacing: number;\n}\n\n/**\n * Individual stat row component\n */\nconst StatRow: React.FC<StatRowProps> = ({\n label,\n value,\n highlight = false,\n fontSize,\n spacing,\n}) => (\n <div\n style={{\n display: \"flex\",\n justifyContent: \"space-between\",\n alignItems: \"center\",\n marginBottom: spacing / 2,\n fontSize,\n color: toCssColor(\n highlight ? KOREAN_COLORS.ACCENT_GOLD : KOREAN_COLORS.TEXT_SECONDARY\n ),\n }}\n >\n <span>{label}:</span>\n <span\n style={{\n fontWeight: \"bold\",\n color: toCssColor(KOREAN_COLORS.TEXT_PRIMARY),\n }}\n >\n {value}\n </span>\n </div>\n);\n\n/**\n * Props for PlayerStats component\n */\ninterface PlayerStatsProps {\n readonly playerNum: 1 | 2;\n readonly stats: PlayerMatchStats;\n readonly isWinner: boolean;\n readonly fontSize: number;\n readonly labelFontSize: number;\n readonly padding: number;\n readonly spacing: number;\n}\n\n/**\n * Player statistics panel component\n */\nconst PlayerStats: React.FC<PlayerStatsProps> = ({\n playerNum,\n stats,\n isWinner,\n fontSize,\n labelFontSize,\n padding,\n spacing,\n}) => {\n return (\n <div\n style={{\n flex: 1,\n background: hexToRgbaString(KOREAN_COLORS.UI_BACKGROUND_MEDIUM, 0.6),\n border: `2px solid ${hexToRgbaString(\n isWinner ? KOREAN_COLORS.ACCENT_GOLD : KOREAN_COLORS.UI_BORDER,\n 0.8\n )}`,\n borderRadius: \"8px\",\n padding,\n }}\n data-testid={`player${playerNum}-stats`}\n >\n <div\n style={{\n fontSize: labelFontSize,\n fontWeight: \"bold\",\n color: toCssColor(\n isWinner ? KOREAN_COLORS.ACCENT_GOLD : KOREAN_COLORS.PRIMARY_CYAN\n ),\n marginBottom: spacing,\n textAlign: \"center\",\n }}\n >\n 플레이어 {playerNum} | Player {playerNum}\n {isWinner && \" 🏆\"}\n </div>\n\n <StatRow\n label=\"승리 | Wins\"\n value={stats.wins}\n highlight={stats.wins > 0}\n fontSize={fontSize}\n spacing={spacing}\n />\n <StatRow\n label=\"패배 | Losses\"\n value={stats.losses}\n fontSize={fontSize}\n spacing={spacing}\n />\n <StatRow\n label=\"가한 피해 | Damage Dealt\"\n value={stats.totalDamageDealt}\n fontSize={fontSize}\n spacing={spacing}\n />\n <StatRow\n label=\"받은 피해 | Damage Taken\"\n value={stats.totalDamageReceived}\n fontSize={fontSize}\n spacing={spacing}\n />\n <StatRow\n label=\"명중 | Hits Landed\"\n value={stats.hitsLanded}\n fontSize={fontSize}\n spacing={spacing}\n />\n <StatRow\n label=\"피격 | Hits Taken\"\n value={stats.hitsTaken}\n fontSize={fontSize}\n spacing={spacing}\n />\n <StatRow\n label=\"완벽한 타격 | Perfect Strikes\"\n value={stats.perfectStrikes}\n highlight={stats.perfectStrikes > 0}\n fontSize={fontSize}\n spacing={spacing}\n />\n <StatRow\n label=\"급소 공격 | Vital Point Hits\"\n value={stats.vitalPointHits}\n highlight={stats.vitalPointHits > 0}\n fontSize={fontSize}\n spacing={spacing}\n />\n <StatRow\n label=\"연승 | Consecutive Wins\"\n value={stats.consecutiveWins}\n fontSize={fontSize}\n spacing={spacing}\n />\n\n {stats.techniques && stats.techniques.length > 0 && (\n <div style={{ marginTop: spacing }}>\n <div\n style={{\n fontSize: fontSize - 2,\n color: toCssColor(KOREAN_COLORS.TEXT_TERTIARY),\n marginBottom: spacing / 2,\n }}\n >\n 사용한 기술 | Techniques Used:\n </div>\n <div\n style={{\n fontSize: fontSize - 2,\n color: toCssColor(KOREAN_COLORS.TEXT_SECONDARY),\n paddingLeft: spacing,\n }}\n >\n {stats.techniques.slice(0, 5).join(\", \")}\n {stats.techniques.length > 5 && \"...\"}\n </div>\n </div>\n )}\n </div>\n );\n};\n\n/**\n * Match Statistics Display Component\n * Shows detailed combat statistics for both players\n */\nexport const MatchStatisticsDisplay: React.FC<MatchStatisticsDisplayProps> = ({\n matchStats,\n isMobile,\n isTablet,\n}) => {\n const fontSize = isMobile ? 12 : isTablet ? 14 : 16;\n const labelFontSize = isMobile ? 14 : isTablet ? 16 : 18;\n const padding = isMobile ? 10 : isTablet ? 15 : 20;\n const spacing = isMobile ? 8 : isTablet ? 10 : 12;\n\n return (\n <div\n data-testid=\"match-statistics-display\"\n style={{\n width: isMobile ? \"95%\" : isTablet ? \"80%\" : \"70%\",\n maxWidth: \"900px\",\n background: hexToRgbaString(KOREAN_COLORS.UI_BACKGROUND_DARK, 0.9),\n border: `2px solid ${hexToRgbaString(KOREAN_COLORS.PRIMARY_CYAN, 0.6)}`,\n borderRadius: \"12px\",\n padding: padding * 1.5,\n marginBottom: spacing * 2,\n fontFamily: FONT_FAMILY.KOREAN,\n }}\n >\n {/* Overall Match Stats */}\n <div\n style={{\n fontSize: labelFontSize + 2,\n fontWeight: \"bold\",\n color: toCssColor(KOREAN_COLORS.PRIMARY_CYAN),\n textAlign: \"center\",\n marginBottom: spacing * 1.5,\n }}\n >\n 경기 통계 | Match Statistics\n </div>\n\n <div\n style={{\n display: \"grid\",\n gridTemplateColumns: isMobile ? \"1fr 1fr\" : \"repeat(4, 1fr)\",\n gap: spacing,\n marginBottom: spacing * 1.5,\n padding,\n background: hexToRgbaString(KOREAN_COLORS.UI_BACKGROUND_MEDIUM, 0.4),\n borderRadius: \"8px\",\n }}\n data-testid=\"overall-stats\"\n >\n <div style={{ textAlign: \"center\" }}>\n <div\n style={{\n fontSize: fontSize - 2,\n color: toCssColor(KOREAN_COLORS.TEXT_TERTIARY),\n }}\n >\n 라운드 | Rounds\n </div>\n <div\n style={{\n fontSize: labelFontSize,\n color: toCssColor(KOREAN_COLORS.TEXT_PRIMARY),\n fontWeight: \"bold\",\n }}\n >\n {matchStats.currentRound} / {matchStats.maxRounds}\n </div>\n </div>\n\n <div style={{ textAlign: \"center\" }}>\n <div\n style={{\n fontSize: fontSize - 2,\n color: toCssColor(KOREAN_COLORS.TEXT_TERTIARY),\n }}\n >\n 시간 | Duration\n </div>\n <div\n style={{\n fontSize: labelFontSize,\n color: toCssColor(KOREAN_COLORS.TEXT_PRIMARY),\n fontWeight: \"bold\",\n }}\n >\n {Math.floor(matchStats.matchDuration / 60)}:\n {(matchStats.matchDuration % 60).toString().padStart(2, \"0\")}\n </div>\n </div>\n\n <div style={{ textAlign: \"center\" }}>\n <div\n style={{\n fontSize: fontSize - 2,\n color: toCssColor(KOREAN_COLORS.TEXT_TERTIARY),\n }}\n >\n 치명타 | Critical Hits\n </div>\n <div\n style={{\n fontSize: labelFontSize,\n color: toCssColor(KOREAN_COLORS.CRITICAL_HIT),\n fontWeight: \"bold\",\n }}\n >\n {matchStats.criticalHits}\n </div>\n </div>\n\n <div style={{ textAlign: \"center\" }}>\n <div\n style={{\n fontSize: fontSize - 2,\n color: toCssColor(KOREAN_COLORS.TEXT_TERTIARY),\n }}\n >\n 급소 공격 | Vital Hits\n </div>\n <div\n style={{\n fontSize: labelFontSize,\n color: toCssColor(KOREAN_COLORS.VITAL_POINT_HIT),\n fontWeight: \"bold\",\n }}\n >\n {matchStats.vitalPointHits}\n </div>\n </div>\n </div>\n\n {/* Player Stats Side by Side */}\n <div\n style={{\n display: \"flex\",\n flexDirection: isMobile ? \"column\" : \"row\",\n gap: spacing * 1.5,\n }}\n >\n <PlayerStats\n playerNum={1}\n stats={matchStats.player1}\n isWinner={matchStats.winner === 0}\n fontSize={fontSize}\n labelFontSize={labelFontSize}\n padding={padding}\n spacing={spacing}\n />\n <PlayerStats\n playerNum={2}\n stats={matchStats.player2}\n isWinner={matchStats.winner === 1}\n fontSize={fontSize}\n labelFontSize={labelFontSize}\n padding={padding}\n spacing={spacing}\n />\n </div>\n\n {/* Score Display */}\n <div\n style={{\n marginTop: spacing * 1.5,\n padding,\n background: hexToRgbaString(KOREAN_COLORS.UI_BACKGROUND_MEDIUM, 0.4),\n borderRadius: \"8px\",\n textAlign: \"center\",\n }}\n data-testid=\"final-score\"\n >\n <div\n style={{\n fontSize: labelFontSize,\n color: toCssColor(KOREAN_COLORS.TEXT_SECONDARY),\n marginBottom: spacing / 2,\n }}\n >\n 최종 점수 | Final Score\n </div>\n <div\n style={{\n fontSize: labelFontSize + 4,\n fontWeight: \"bold\",\n color: toCssColor(KOREAN_COLORS.ACCENT_GOLD),\n }}\n >\n {matchStats.finalScore.player1} - {matchStats.finalScore.player2}\n </div>\n </div>\n </div>\n );\n};\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"MatchStatisticsDisplayOverlayHtml.js","names":[],"sources":["../../../../../src/components/screens/endscreen/components/MatchStatisticsDisplayOverlayHtml.tsx"],"sourcesContent":["import React from \"react\";\nimport { MatchStatistics } from \"../../../../systems/combat\";\nimport { PlayerMatchStats } from \"../../../../systems/player\";\nimport { FONT_FAMILY, KOREAN_COLORS } from \"../../../../types/constants\";\nimport { hexToRgbaString } from \"../../../../utils/colorUtils\";\n\nexport interface MatchStatisticsDisplayProps {\n readonly matchStats: MatchStatistics;\n readonly isMobile: boolean;\n readonly isTablet: boolean;\n}\n\n/**\n * Helper to convert hex color to CSS string\n */\nconst toCssColor = (hex: number): string => hexToRgbaString(hex, 1);\n\n/**\n * Props for StatRow component\n */\ninterface StatRowProps {\n readonly label: string;\n readonly value: string | number;\n readonly highlight?: boolean;\n readonly fontSize: number;\n readonly spacing: number;\n}\n\n/**\n * Individual stat row component\n */\nconst StatRow: React.FC<StatRowProps> = ({\n label,\n value,\n highlight = false,\n fontSize,\n spacing,\n}) => (\n <div\n style={{\n display: \"flex\",\n justifyContent: \"space-between\",\n alignItems: \"center\",\n marginBottom: spacing / 2,\n fontSize,\n color: toCssColor(\n highlight ? KOREAN_COLORS.ACCENT_GOLD : KOREAN_COLORS.TEXT_SECONDARY\n ),\n }}\n >\n <span>{label}:</span>\n <span\n style={{\n fontWeight: \"bold\",\n color: toCssColor(KOREAN_COLORS.TEXT_PRIMARY),\n }}\n >\n {value}\n </span>\n </div>\n);\n\n/**\n * Props for PlayerStats component\n */\ninterface PlayerStatsProps {\n readonly playerNum: 1 | 2;\n readonly stats: PlayerMatchStats;\n readonly isWinner: boolean;\n readonly fontSize: number;\n readonly labelFontSize: number;\n readonly padding: number;\n readonly spacing: number;\n}\n\n/**\n * Player statistics panel component\n */\nconst PlayerStats: React.FC<PlayerStatsProps> = ({\n playerNum,\n stats,\n isWinner,\n fontSize,\n labelFontSize,\n padding,\n spacing,\n}) => {\n return (\n <div\n style={{\n flex: 1,\n background: hexToRgbaString(KOREAN_COLORS.UI_BACKGROUND_MEDIUM, 0.6),\n border: `2px solid ${hexToRgbaString(\n isWinner ? KOREAN_COLORS.ACCENT_GOLD : KOREAN_COLORS.UI_BORDER,\n 0.8\n )}`,\n borderRadius: \"8px\",\n padding,\n }}\n data-testid={`player${playerNum}-stats`}\n >\n <div\n style={{\n fontSize: labelFontSize,\n fontWeight: \"bold\",\n color: toCssColor(\n isWinner ? KOREAN_COLORS.ACCENT_GOLD : KOREAN_COLORS.PRIMARY_CYAN\n ),\n marginBottom: spacing,\n textAlign: \"center\",\n }}\n >\n 플레이어 {playerNum} | Player {playerNum}\n {isWinner && \" 🏆\"}\n </div>\n\n <StatRow\n label=\"승리 | Wins\"\n value={stats.wins}\n highlight={stats.wins > 0}\n fontSize={fontSize}\n spacing={spacing}\n />\n <StatRow\n label=\"패배 | Losses\"\n value={stats.losses}\n fontSize={fontSize}\n spacing={spacing}\n />\n <StatRow\n label=\"가한 피해 | Damage Dealt\"\n value={stats.totalDamageDealt}\n fontSize={fontSize}\n spacing={spacing}\n />\n <StatRow\n label=\"받은 피해 | Damage Taken\"\n value={stats.totalDamageReceived}\n fontSize={fontSize}\n spacing={spacing}\n />\n <StatRow\n label=\"명중 | Hits Landed\"\n value={stats.hitsLanded}\n fontSize={fontSize}\n spacing={spacing}\n />\n <StatRow\n label=\"피격 | Hits Taken\"\n value={stats.hitsTaken}\n fontSize={fontSize}\n spacing={spacing}\n />\n <StatRow\n label=\"완벽한 타격 | Perfect Strikes\"\n value={stats.perfectStrikes}\n highlight={stats.perfectStrikes > 0}\n fontSize={fontSize}\n spacing={spacing}\n />\n <StatRow\n label=\"급소 공격 | Vital Point Hits\"\n value={stats.vitalPointHits}\n highlight={stats.vitalPointHits > 0}\n fontSize={fontSize}\n spacing={spacing}\n />\n <StatRow\n label=\"연승 | Consecutive Wins\"\n value={stats.consecutiveWins}\n fontSize={fontSize}\n spacing={spacing}\n />\n\n {stats.techniques && stats.techniques.length > 0 && (\n <div style={{ marginTop: spacing }}>\n <div\n style={{\n fontSize: fontSize - 2,\n color: toCssColor(KOREAN_COLORS.TEXT_TERTIARY),\n marginBottom: spacing / 2,\n }}\n >\n 사용한 기술 | Techniques Used:\n </div>\n <div\n style={{\n fontSize: fontSize - 2,\n color: toCssColor(KOREAN_COLORS.TEXT_SECONDARY),\n paddingLeft: spacing,\n }}\n >\n {stats.techniques.slice(0, 5).join(\", \")}\n {stats.techniques.length > 5 && \"...\"}\n </div>\n </div>\n )}\n </div>\n );\n};\n\n/**\n * Match Statistics Display Component\n * Shows detailed combat statistics for both players\n */\nexport const MatchStatisticsDisplay: React.FC<MatchStatisticsDisplayProps> = ({\n matchStats,\n isMobile,\n isTablet,\n}) => {\n const fontSize = isMobile ? 12 : isTablet ? 14 : 16;\n const labelFontSize = isMobile ? 14 : isTablet ? 16 : 18;\n const padding = isMobile ? 10 : isTablet ? 15 : 20;\n const spacing = isMobile ? 8 : isTablet ? 10 : 12;\n\n return (\n <div\n data-testid=\"match-statistics-display\"\n style={{\n width: isMobile ? \"95%\" : isTablet ? \"80%\" : \"70%\",\n maxWidth: \"900px\",\n background: hexToRgbaString(KOREAN_COLORS.UI_BACKGROUND_DARK, 0.9),\n border: `2px solid ${hexToRgbaString(KOREAN_COLORS.PRIMARY_CYAN, 0.6)}`,\n borderRadius: \"12px\",\n padding: padding * 1.5,\n marginBottom: spacing * 2,\n fontFamily: FONT_FAMILY.KOREAN,\n }}\n >\n {/* Overall Match Stats */}\n <div\n style={{\n fontSize: labelFontSize + 2,\n fontWeight: \"bold\",\n color: toCssColor(KOREAN_COLORS.PRIMARY_CYAN),\n textAlign: \"center\",\n marginBottom: spacing * 1.5,\n }}\n >\n 경기 통계 | Match Statistics\n </div>\n\n <div\n style={{\n display: \"grid\",\n gridTemplateColumns: isMobile ? \"1fr 1fr\" : \"repeat(4, 1fr)\",\n gap: spacing,\n marginBottom: spacing * 1.5,\n padding,\n background: hexToRgbaString(KOREAN_COLORS.UI_BACKGROUND_MEDIUM, 0.4),\n borderRadius: \"8px\",\n }}\n data-testid=\"overall-stats\"\n >\n <div style={{ textAlign: \"center\" }}>\n <div\n style={{\n fontSize: fontSize - 2,\n color: toCssColor(KOREAN_COLORS.TEXT_TERTIARY),\n }}\n >\n 라운드 | Rounds\n </div>\n <div\n style={{\n fontSize: labelFontSize,\n color: toCssColor(KOREAN_COLORS.TEXT_PRIMARY),\n fontWeight: \"bold\",\n }}\n >\n {matchStats.currentRound} / {matchStats.maxRounds}\n </div>\n </div>\n\n <div style={{ textAlign: \"center\" }}>\n <div\n style={{\n fontSize: fontSize - 2,\n color: toCssColor(KOREAN_COLORS.TEXT_TERTIARY),\n }}\n >\n 시간 | Duration\n </div>\n <div\n style={{\n fontSize: labelFontSize,\n color: toCssColor(KOREAN_COLORS.TEXT_PRIMARY),\n fontWeight: \"bold\",\n }}\n >\n {Math.floor(matchStats.matchDuration / 60)}:\n {(matchStats.matchDuration % 60).toString().padStart(2, \"0\")}\n </div>\n </div>\n\n <div style={{ textAlign: \"center\" }}>\n <div\n style={{\n fontSize: fontSize - 2,\n color: toCssColor(KOREAN_COLORS.TEXT_TERTIARY),\n }}\n >\n 치명타 | Critical Hits\n </div>\n <div\n style={{\n fontSize: labelFontSize,\n color: toCssColor(KOREAN_COLORS.CRITICAL_HIT),\n fontWeight: \"bold\",\n }}\n >\n {matchStats.criticalHits}\n </div>\n </div>\n\n <div style={{ textAlign: \"center\" }}>\n <div\n style={{\n fontSize: fontSize - 2,\n color: toCssColor(KOREAN_COLORS.TEXT_TERTIARY),\n }}\n >\n 급소 공격 | Vital Hits\n </div>\n <div\n style={{\n fontSize: labelFontSize,\n color: toCssColor(KOREAN_COLORS.VITAL_POINT_HIT),\n fontWeight: \"bold\",\n }}\n >\n {matchStats.vitalPointHits}\n </div>\n </div>\n </div>\n\n {/* Player Stats Side by Side */}\n <div\n style={{\n display: \"flex\",\n flexDirection: isMobile ? \"column\" : \"row\",\n gap: spacing * 1.5,\n }}\n >\n <PlayerStats\n playerNum={1}\n stats={matchStats.player1}\n isWinner={matchStats.winner === 0}\n fontSize={fontSize}\n labelFontSize={labelFontSize}\n padding={padding}\n spacing={spacing}\n />\n <PlayerStats\n playerNum={2}\n stats={matchStats.player2}\n isWinner={matchStats.winner === 1}\n fontSize={fontSize}\n labelFontSize={labelFontSize}\n padding={padding}\n spacing={spacing}\n />\n </div>\n\n {/* Score Display */}\n <div\n style={{\n marginTop: spacing * 1.5,\n padding,\n background: hexToRgbaString(KOREAN_COLORS.UI_BACKGROUND_MEDIUM, 0.4),\n borderRadius: \"8px\",\n textAlign: \"center\",\n }}\n data-testid=\"final-score\"\n >\n <div\n style={{\n fontSize: labelFontSize,\n color: toCssColor(KOREAN_COLORS.TEXT_SECONDARY),\n marginBottom: spacing / 2,\n }}\n >\n 최종 점수 | Final Score\n </div>\n <div\n style={{\n fontSize: labelFontSize + 4,\n fontWeight: \"bold\",\n color: toCssColor(KOREAN_COLORS.ACCENT_GOLD),\n }}\n >\n {matchStats.finalScore.player1} - {matchStats.finalScore.player2}\n </div>\n </div>\n </div>\n );\n};\n"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"NavigationButtonsOverlayHtml.js","names":[],"sources":["../../../../../src/components/screens/endscreen/components/NavigationButtonsOverlayHtml.tsx"],"sourcesContent":["import React, { useCallback } from \"react\";\nimport { BaseButtonOverlayHtml } from \"../../../shared/base/BaseButtonOverlayHtml\";\nimport { slideUpAnimation } from \"./animations\";\n\nexport interface NavigationButtonsProps {\n readonly onReturnToMenu: () => void;\n readonly onRematch?: () => void;\n readonly onViewReplay?: () => void;\n readonly isMobile: boolean;\n readonly isTablet: boolean;\n readonly width: number;\n /** Optional audio callback for click sounds - passed from parent to avoid Html portal context issues */\n readonly onPlaySelectSound?: () => void;\n /** Optional audio callback for hover sounds - passed from parent to avoid Html portal context issues */\n readonly onPlayHoverSound?: () => void;\n}\n\n/**\n * Navigation Buttons Component\n * Provides action buttons for replay and menu navigation.\n *\n * This component delegates visual styling and accessibility behavior to\n * BaseButtonOverlayHtml, providing:\n * - Consistent Korean / English bilingual theming\n * - Centralized button behavior and standard browser keyboard support\n * - Reduced code duplication (113 lines saved: 237 → 124)\n *\n * Note: Audio callbacks are passed as props since this component is rendered\n * inside a Canvas Html portal which doesn't have access to AudioProvider context.\n */\nexport const NavigationButtons: React.FC<NavigationButtonsProps> = ({\n onReturnToMenu,\n onRematch,\n onViewReplay,\n isMobile,\n isTablet,\n width,\n onPlaySelectSound,\n onPlayHoverSound,\n}) => {\n const spacing = isMobile ? 10 : isTablet ? 12 : 15;\n \n const buttonSize = width < 768 ? \"sm\" : \"md\";\n const buttonMinWidth = width < 768 ? \"200px\" : \"150px\";\n\n const handleReturnToMenu = useCallback(() => {\n onPlaySelectSound?.();\n onReturnToMenu();\n }, [onPlaySelectSound, onReturnToMenu]);\n\n const handleRematch = useCallback(() => {\n if (onRematch) {\n onPlaySelectSound?.();\n onRematch();\n }\n }, [onPlaySelectSound, onRematch]);\n\n const handleViewReplay = useCallback(() => {\n if (onViewReplay) {\n onPlaySelectSound?.();\n onViewReplay();\n }\n }, [onPlaySelectSound, onViewReplay]);\n\n return (\n <div\n data-testid=\"navigation-buttons\"\n style={{\n display: \"flex\",\n flexDirection: isMobile ? \"column\" : \"row\",\n gap: spacing,\n marginTop: spacing * 2,\n animation: \"slideUp 0.6s ease-out 0.3s both\",\n alignItems: \"center\",\n justifyContent: \"center\",\n }}\n >\n {/* Return to Menu Button - Primary Action */}\n <BaseButtonOverlayHtml\n korean=\"메뉴로\"\n english=\"Return to Menu\"\n onClick={handleReturnToMenu}\n onMouseEnter={onPlayHoverSound}\n variant=\"primary\"\n size={buttonSize}\n testId=\"return-to-menu-button\"\n isMobile={isMobile}\n style={{ minWidth: buttonMinWidth }}\n />\n\n {/* Rematch Button - Secondary Action */}\n {onRematch && (\n <BaseButtonOverlayHtml\n korean=\"재대결\"\n english=\"Rematch\"\n onClick={handleRematch}\n onMouseEnter={onPlayHoverSound}\n variant=\"secondary\"\n size={buttonSize}\n testId=\"rematch-button\"\n isMobile={isMobile}\n style={{ minWidth: buttonMinWidth }}\n />\n )}\n\n {/* Training Mode Button - Tertiary Action */}\n {onViewReplay && (\n <BaseButtonOverlayHtml\n korean=\"훈련\"\n english=\"Training\"\n onClick={handleViewReplay}\n onMouseEnter={onPlayHoverSound}\n variant=\"secondary\"\n size={buttonSize}\n testId=\"view-training-button\"\n isMobile={isMobile}\n style={{ minWidth: buttonMinWidth }}\n />\n )}\n\n {/* CSS Animations */}\n <style>{`\n ${slideUpAnimation}\n `}</style>\n </div>\n );\n};\n"],"mappings":";;;;;;;;;;;;;;;;;;AA8BA,IAAa,qBAAuD,EAClE,gBACA,WACA,cACA,UACA,UACA,OACA,mBACA,uBACI;CACJ,MAAM,UAAU,WAAW,KAAK,WAAW,KAAK;CAEhD,MAAM,aAAa,QAAQ,MAAM,OAAO;CACxC,MAAM,iBAAiB,QAAQ,MAAM,UAAU;CAE/C,MAAM,qBAAqB,kBAAkB;EAC3C,
|
|
1
|
+
{"version":3,"file":"NavigationButtonsOverlayHtml.js","names":[],"sources":["../../../../../src/components/screens/endscreen/components/NavigationButtonsOverlayHtml.tsx"],"sourcesContent":["import React, { useCallback } from \"react\";\nimport { BaseButtonOverlayHtml } from \"../../../shared/base/BaseButtonOverlayHtml\";\nimport { slideUpAnimation } from \"./animations\";\n\nexport interface NavigationButtonsProps {\n readonly onReturnToMenu: () => void;\n readonly onRematch?: () => void;\n readonly onViewReplay?: () => void;\n readonly isMobile: boolean;\n readonly isTablet: boolean;\n readonly width: number;\n /** Optional audio callback for click sounds - passed from parent to avoid Html portal context issues */\n readonly onPlaySelectSound?: () => void;\n /** Optional audio callback for hover sounds - passed from parent to avoid Html portal context issues */\n readonly onPlayHoverSound?: () => void;\n}\n\n/**\n * Navigation Buttons Component\n * Provides action buttons for replay and menu navigation.\n *\n * This component delegates visual styling and accessibility behavior to\n * BaseButtonOverlayHtml, providing:\n * - Consistent Korean / English bilingual theming\n * - Centralized button behavior and standard browser keyboard support\n * - Reduced code duplication (113 lines saved: 237 → 124)\n *\n * Note: Audio callbacks are passed as props since this component is rendered\n * inside a Canvas Html portal which doesn't have access to AudioProvider context.\n */\nexport const NavigationButtons: React.FC<NavigationButtonsProps> = ({\n onReturnToMenu,\n onRematch,\n onViewReplay,\n isMobile,\n isTablet,\n width,\n onPlaySelectSound,\n onPlayHoverSound,\n}) => {\n const spacing = isMobile ? 10 : isTablet ? 12 : 15;\n \n const buttonSize = width < 768 ? \"sm\" : \"md\";\n const buttonMinWidth = width < 768 ? \"200px\" : \"150px\";\n\n const handleReturnToMenu = useCallback(() => {\n onPlaySelectSound?.();\n onReturnToMenu();\n }, [onPlaySelectSound, onReturnToMenu]);\n\n const handleRematch = useCallback(() => {\n if (onRematch) {\n onPlaySelectSound?.();\n onRematch();\n }\n }, [onPlaySelectSound, onRematch]);\n\n const handleViewReplay = useCallback(() => {\n if (onViewReplay) {\n onPlaySelectSound?.();\n onViewReplay();\n }\n }, [onPlaySelectSound, onViewReplay]);\n\n return (\n <div\n data-testid=\"navigation-buttons\"\n style={{\n display: \"flex\",\n flexDirection: isMobile ? \"column\" : \"row\",\n gap: spacing,\n marginTop: spacing * 2,\n animation: \"slideUp 0.6s ease-out 0.3s both\",\n alignItems: \"center\",\n justifyContent: \"center\",\n }}\n >\n {/* Return to Menu Button - Primary Action */}\n <BaseButtonOverlayHtml\n korean=\"메뉴로\"\n english=\"Return to Menu\"\n onClick={handleReturnToMenu}\n onMouseEnter={onPlayHoverSound}\n variant=\"primary\"\n size={buttonSize}\n testId=\"return-to-menu-button\"\n isMobile={isMobile}\n style={{ minWidth: buttonMinWidth }}\n />\n\n {/* Rematch Button - Secondary Action */}\n {onRematch && (\n <BaseButtonOverlayHtml\n korean=\"재대결\"\n english=\"Rematch\"\n onClick={handleRematch}\n onMouseEnter={onPlayHoverSound}\n variant=\"secondary\"\n size={buttonSize}\n testId=\"rematch-button\"\n isMobile={isMobile}\n style={{ minWidth: buttonMinWidth }}\n />\n )}\n\n {/* Training Mode Button - Tertiary Action */}\n {onViewReplay && (\n <BaseButtonOverlayHtml\n korean=\"훈련\"\n english=\"Training\"\n onClick={handleViewReplay}\n onMouseEnter={onPlayHoverSound}\n variant=\"secondary\"\n size={buttonSize}\n testId=\"view-training-button\"\n isMobile={isMobile}\n style={{ minWidth: buttonMinWidth }}\n />\n )}\n\n {/* CSS Animations */}\n <style>{`\n ${slideUpAnimation}\n `}</style>\n </div>\n );\n};\n"],"mappings":";;;;;;;;;;;;;;;;;;AA8BA,IAAa,qBAAuD,EAClE,gBACA,WACA,cACA,UACA,UACA,OACA,mBACA,uBACI;CACJ,MAAM,UAAU,WAAW,KAAK,WAAW,KAAK;CAEhD,MAAM,aAAa,QAAQ,MAAM,OAAO;CACxC,MAAM,iBAAiB,QAAQ,MAAM,UAAU;CAE/C,MAAM,qBAAqB,kBAAkB;EAC3C,oBAAoB;EACpB,eAAe;CACjB,GAAG,CAAC,mBAAmB,cAAc,CAAC;CAEtC,MAAM,gBAAgB,kBAAkB;EACtC,IAAI,WAAW;GACb,oBAAoB;GACpB,UAAU;EACZ;CACF,GAAG,CAAC,mBAAmB,SAAS,CAAC;CAEjC,MAAM,mBAAmB,kBAAkB;EACzC,IAAI,cAAc;GAChB,oBAAoB;GACpB,aAAa;EACf;CACF,GAAG,CAAC,mBAAmB,YAAY,CAAC;CAEpC,OACE,qBAAC,OAAD;EACE,eAAY;EACZ,OAAO;GACL,SAAS;GACT,eAAe,WAAW,WAAW;GACrC,KAAK;GACL,WAAW,UAAU;GACrB,WAAW;GACX,YAAY;GACZ,gBAAgB;EAClB;YAVF;GAaE,oBAAC,uBAAD;IACE,QAAO;IACP,SAAQ;IACR,SAAS;IACT,cAAc;IACd,SAAQ;IACR,MAAM;IACN,QAAO;IACG;IACV,OAAO,EAAE,UAAU,eAAe;GACnC,CAAA;GAGA,aACC,oBAAC,uBAAD;IACE,QAAO;IACP,SAAQ;IACR,SAAS;IACT,cAAc;IACd,SAAQ;IACR,MAAM;IACN,QAAO;IACG;IACV,OAAO,EAAE,UAAU,eAAe;GACnC,CAAA;GAIF,gBACC,oBAAC,uBAAD;IACE,QAAO;IACP,SAAQ;IACR,SAAS;IACT,cAAc;IACd,SAAQ;IACR,MAAM;IACN,QAAO;IACG;IACV,OAAO,EAAE,UAAU,eAAe;GACnC,CAAA;GAIH,oBAAC,SAAD,EAAA,UAAQ;UACJ,iBAAiB;QACZ,CAAA;EACN;;AAET"}
|