blacktrigram 0.7.39 → 0.7.41
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/App2.js.map +1 -1
- package/lib/audio/AudioAssetLoader.js.map +1 -1
- package/lib/audio/AudioAssetRegistry.js.map +1 -1
- package/lib/audio/AudioCache.js.map +1 -1
- package/lib/audio/AudioManager.js.map +1 -1
- package/lib/audio/AudioMonitor.js.map +1 -1
- package/lib/audio/AudioPool.js.map +1 -1
- package/lib/audio/AudioProvider.js.map +1 -1
- package/lib/audio/AudioUtils.js.map +1 -1
- package/lib/audio/BoneImpactAudioMap.js.map +1 -1
- package/lib/audio/VariantSelector.js.map +1 -1
- package/lib/audio/types.js.map +1 -1
- package/lib/components/screens/combat/CombatScreen3D.js.map +1 -1
- package/lib/components/screens/combat/components/controls/CombatButtons.js.map +1 -1
- package/lib/components/screens/combat/components/controls/CombatControlsPanel.js.map +1 -1
- package/lib/components/screens/combat/components/controls/ControlsGuide.js.map +1 -1
- package/lib/components/screens/combat/components/controls/KeyboardHints.js.map +1 -1
- package/lib/components/screens/combat/components/controls/PauseMenu.js.map +1 -1
- package/lib/components/screens/combat/components/controls/PauseMenuButton.js.map +1 -1
- package/lib/components/screens/combat/components/controls/QuickSettings.js.map +1 -1
- package/lib/components/screens/combat/components/effects/BloodDecals3D.js.map +1 -1
- package/lib/components/screens/combat/components/effects/BloodLossOverlayHtml.js.map +1 -1
- package/lib/components/screens/combat/components/effects/BloodParticles3D.js.map +1 -1
- package/lib/components/screens/combat/components/effects/BloodViscosity3D.js.map +1 -1
- package/lib/components/screens/combat/components/effects/CombatParticleEffects3D.js.map +1 -1
- package/lib/components/screens/combat/components/effects/ConsciousnessBlur.js.map +1 -1
- package/lib/components/screens/combat/components/effects/InternalDamage3D.js.map +1 -1
- package/lib/components/screens/combat/components/effects/PainVignette.js.map +1 -1
- package/lib/components/screens/combat/components/effects/ParticleAudio3D.js.map +1 -1
- package/lib/components/screens/combat/components/effects/TraumaOverlay3D.js.map +1 -1
- package/lib/components/screens/combat/components/feedback/MatchCountdown.js.map +1 -1
- package/lib/components/screens/combat/components/feedback/RoundAnnouncementOverlayHtml.js.map +1 -1
- package/lib/components/screens/combat/components/feedback/RoundDisplayStatus.js.map +1 -1
- package/lib/components/screens/combat/components/feedback/RoundStartAnnouncementOverlayHtml.js.map +1 -1
- package/lib/components/screens/combat/components/hud/CombatBottomHUD.js.map +1 -1
- package/lib/components/screens/combat/components/hud/CombatLeftHUD.js.map +1 -1
- package/lib/components/screens/combat/components/hud/CombatPortraitStatusStrip.js.map +1 -1
- package/lib/components/screens/combat/components/hud/CombatRightHUD.js.map +1 -1
- package/lib/components/screens/combat/components/hud/CombatTopHUD.js.map +1 -1
- package/lib/components/screens/combat/components/hud/DifficultyIndicator.js.map +1 -1
- package/lib/components/screens/combat/components/hud/FPSMonitor.js.map +1 -1
- package/lib/components/screens/combat/components/hud/MobileControlsWrapper.js.map +1 -1
- package/lib/components/screens/combat/components/hud/PlayerStateOverlayHtml.js.map +1 -1
- package/lib/components/screens/combat/components/indicators/BalanceIndicator.js.map +1 -1
- package/lib/components/screens/combat/components/indicators/InputBufferDisplay.js.map +1 -1
- package/lib/components/screens/combat/components/indicators/StaminaWarning.js.map +1 -1
- package/lib/components/screens/combat/components/indicators/TechniqueNameDisplay.js.map +1 -1
- package/lib/components/screens/combat/helpers/AnimationUpdater.js.map +1 -1
- package/lib/components/screens/combat/helpers/combatHelpers.js.map +1 -1
- package/lib/components/screens/combat/hooks/useAICombat.js.map +1 -1
- package/lib/components/screens/combat/hooks/useCombatActions.js.map +1 -1
- package/lib/components/screens/combat/hooks/useCombatAttackMovement.js.map +1 -1
- package/lib/components/screens/combat/hooks/useCombatAudio.js.map +1 -1
- package/lib/components/screens/combat/hooks/useCombatLayout.js.map +1 -1
- package/lib/components/screens/combat/hooks/useCombatState.js.map +1 -1
- package/lib/components/screens/controls/ControlsScreen3D.js.map +1 -1
- package/lib/components/screens/controls/components/ControlBindingsOverlayHtml.js.map +1 -1
- package/lib/components/screens/controls/components/ControlCategoryTabsOverlayHtml.js.map +1 -1
- package/lib/components/screens/controls/components/GamepadVisualization3D.js.map +1 -1
- package/lib/components/screens/controls/components/InteractiveControlDemoOverlayHtml.js.map +1 -1
- package/lib/components/screens/controls/components/Key3D.js.map +1 -1
- package/lib/components/screens/controls/components/VisualKeyboard3D.js.map +1 -1
- package/lib/components/screens/controls/constants/ControlsConstants.js.map +1 -1
- package/lib/components/screens/controls/hooks/useControlsState.js.map +1 -1
- package/lib/components/screens/endscreen/EndScreen3D.js.map +1 -1
- package/lib/components/screens/endscreen/components/DefeatAnimation3D.js.map +1 -1
- package/lib/components/screens/endscreen/components/MatchStatisticsDisplayOverlayHtml.js.map +1 -1
- package/lib/components/screens/endscreen/components/NavigationButtonsOverlayHtml.js.map +1 -1
- package/lib/components/screens/endscreen/components/PerformanceBreakdownOverlayHtml.js.map +1 -1
- package/lib/components/screens/endscreen/components/PerformanceRatingOverlayHtml.js.map +1 -1
- package/lib/components/screens/endscreen/components/VictoryAnimation3D.js.map +1 -1
- package/lib/components/screens/endscreen/components/WinnerDisplayOverlayHtml.js.map +1 -1
- package/lib/components/screens/intro/IntroScreen3D.js +1 -1
- package/lib/components/screens/intro/IntroScreen3D.js.map +1 -1
- package/lib/components/screens/intro/components/AbilityListOverlayHtml.js.map +1 -1
- package/lib/components/screens/intro/components/ArchetypeCardGridOverlayHtml.js.map +1 -1
- package/lib/components/screens/intro/components/ArchetypeCardOverlayHtml.js.map +1 -1
- package/lib/components/screens/intro/components/ArchetypeDisplayOverlayHtml.js.map +1 -1
- package/lib/components/screens/intro/components/EnhancedArchetypeDisplayOverlayHtml.js.map +1 -1
- package/lib/components/screens/intro/components/MenuButtonsOverlayHtml.js.map +1 -1
- package/lib/components/screens/intro/components/MenuSectionOverlayHtml.js.map +1 -1
- package/lib/components/screens/intro/components/StatBarOverlayHtml.js.map +1 -1
- package/lib/components/screens/philosophy/PhilosophyScreen3D.js.map +1 -1
- package/lib/components/screens/training/TrainingScreen3D.js.map +1 -1
- package/lib/components/screens/training/components/AnatomyControlsOverlayHtml.js.map +1 -1
- package/lib/components/screens/training/components/AnatomyOverlay3D.js.map +1 -1
- package/lib/components/screens/training/components/FootPlacementMarkers3D.js.map +1 -1
- package/lib/components/screens/training/components/FootworkDrillsOverlayHtml.js.map +1 -1
- package/lib/components/screens/training/components/HitFeedbackEffect3D.js.map +1 -1
- package/lib/components/screens/training/components/TrainingButtonsOverlayHtml.js.map +1 -1
- package/lib/components/screens/training/components/TrainingControlsOverlayHtml.js.map +1 -1
- package/lib/components/screens/training/components/TrainingDummy3D.js.map +1 -1
- package/lib/components/screens/training/components/TrainingFeedbackOverlayHtml.js.map +1 -1
- package/lib/components/screens/training/components/TrainingModeSelectorOverlayHtml.js.map +1 -1
- package/lib/components/screens/training/components/TrainingStatsOverlayHtml.js.map +1 -1
- package/lib/components/screens/training/components/VitalPointMarker3D.js.map +1 -1
- package/lib/components/screens/training/components/VitalPointTrainingOverlayHtml.js.map +1 -1
- package/lib/components/screens/training/components/hud/TrainingBottomHUD.js.map +1 -1
- package/lib/components/screens/training/components/hud/TrainingLeftHUD.js.map +1 -1
- package/lib/components/screens/training/components/hud/TrainingRightHUD.js.map +1 -1
- package/lib/components/screens/training/components/hud/TrainingTopHUD.js.map +1 -1
- package/lib/components/screens/training/hooks/useAttackMovement.js.map +1 -1
- package/lib/components/screens/training/hooks/useTrainingActions.js.map +1 -1
- package/lib/components/screens/training/hooks/useTrainingLayout.js.map +1 -1
- package/lib/components/screens/training/hooks/useTrainingState.js.map +1 -1
- package/lib/components/shared/base/BaseButton.js.map +1 -1
- package/lib/components/shared/base/BaseButtonOverlayHtml.js.map +1 -1
- package/lib/components/shared/base/BasePanel.js.map +1 -1
- package/lib/components/shared/base/BaseText.js.map +1 -1
- package/lib/components/shared/base/useKoreanTheme.js.map +1 -1
- package/lib/components/shared/debug/PerformanceDebugOverlayHtml.js.map +1 -1
- package/lib/components/shared/mobile/ActionButtons.js.map +1 -1
- package/lib/components/shared/mobile/GestureRecognizerPure.js.map +1 -1
- package/lib/components/shared/mobile/HapticController.js.map +1 -1
- package/lib/components/shared/mobile/MobileControlsPure.js.map +1 -1
- package/lib/components/shared/mobile/StanceWheelPure.js.map +1 -1
- package/lib/components/shared/mobile/TouchOptimizer.js.map +1 -1
- package/lib/components/shared/mobile/VirtualDPad.js.map +1 -1
- package/lib/components/shared/three/anatomy/BodySurface.js.map +1 -1
- package/lib/components/shared/three/anatomy/BoneAttachedMuscles.js.map +1 -1
- package/lib/components/shared/three/anatomy/BoneClothing.js.map +1 -1
- package/lib/components/shared/three/anatomy/BoneRenderer.js.map +1 -1
- package/lib/components/shared/three/anatomy/Face3D.js.map +1 -1
- package/lib/components/shared/three/anatomy/Foot3D.js.map +1 -1
- package/lib/components/shared/three/anatomy/Hand3D.js.map +1 -1
- package/lib/components/shared/three/effects/ActionFeedback.js.map +1 -1
- package/lib/components/shared/three/effects/DamageNumbers.js.map +1 -1
- package/lib/components/shared/three/effects/HitEffects3D.js.map +1 -1
- package/lib/components/shared/three/effects/PlayerStateIndicators.js.map +1 -1
- package/lib/components/shared/three/effects/StanceSymbol3D.js.map +1 -1
- package/lib/components/shared/three/effects/StanceTransitionEffect.js.map +1 -1
- package/lib/components/shared/three/effects/VitalPointMarkers3D.js.map +1 -1
- package/lib/components/shared/three/indicators/ElementalColorSystem.js.map +1 -1
- package/lib/components/shared/three/indicators/GuardIndicator.js.map +1 -1
- package/lib/components/shared/three/indicators/HapticFeedback.js.map +1 -1
- package/lib/components/shared/three/indicators/StanceChangeIndicator.js.map +1 -1
- package/lib/components/shared/three/models/Player3DWithTransitions.js.map +1 -1
- package/lib/components/shared/three/models/SkeletalPlayer3D.js.map +1 -1
- package/lib/components/shared/three/optimization/AdaptiveQuality.js.map +1 -1
- package/lib/components/shared/three/scene/AtmosphericParticles3D.js.map +1 -1
- package/lib/components/shared/three/scene/BackgroundScene3D.js.map +1 -1
- package/lib/components/shared/three/scene/CombatArena3D.js.map +1 -1
- package/lib/components/shared/three/scene/KoreanSignage3D.js.map +1 -1
- package/lib/components/shared/three/ui/ArchetypeCard.js.map +1 -1
- package/lib/components/shared/three/ui/BodyPartHealthDisplay.js.map +1 -1
- package/lib/components/shared/three/ui/BreathingIndicator2.js.map +1 -1
- package/lib/components/shared/three/ui/CombatReadinessBar.js.map +1 -1
- package/lib/components/shared/three/ui/ComboCounter.js.map +1 -1
- package/lib/components/shared/three/ui/HealthBar.js.map +1 -1
- package/lib/components/shared/three/ui/KoreanButton.js.map +1 -1
- package/lib/components/shared/three/ui/KoreanPanel.js.map +1 -1
- package/lib/components/shared/three/ui/KoreanText.js.map +1 -1
- package/lib/components/shared/three/ui/MenuList.js.map +1 -1
- package/lib/components/shared/three/ui/PlayerHUD.js.map +1 -1
- package/lib/components/shared/three/ui/ProgressBar.js.map +1 -1
- package/lib/components/shared/three/ui/SpeedIndicatorHUD.js.map +1 -1
- package/lib/components/shared/three/ui/StaminaBar.js.map +1 -1
- package/lib/components/shared/three/ui/TechniqueBar.js.map +1 -1
- package/lib/components/shared/three/ui/TechniqueCard.js.map +1 -1
- package/lib/components/shared/three/ui/VitalPointOverlayControlsHtml.js.map +1 -1
- package/lib/components/shared/ui/BackButton.js.map +1 -1
- package/lib/components/shared/ui/BaseHUDContainer.js.map +1 -1
- package/lib/components/shared/ui/CombatTimer.js.map +1 -1
- package/lib/components/shared/ui/ErrorModal.js.map +1 -1
- package/lib/components/shared/ui/LoadingState.js.map +1 -1
- package/lib/components/shared/ui/SplashScreen.js +2 -2
- package/lib/components/shared/ui/SplashScreen.js.map +1 -1
- package/lib/components/shared/ui/VitalPointOverlayControlsPure.js.map +1 -1
- package/lib/components/shared/ui/VolumeControl.js.map +1 -1
- package/lib/components/shared/ui/shared/ConfirmDialog.js.map +1 -1
- package/lib/components/ui/combat/BalanceIndicatorOverlayHtml.js.map +1 -1
- package/lib/constants/bodyDimensions.js.map +1 -1
- package/lib/constants/bodyRenderingConstants.js.map +1 -1
- package/lib/data/archetypeClothing.js.map +1 -1
- package/lib/data/archetypePhysicalAttributes.js.map +1 -1
- package/lib/data/techniqueMappings.js.map +1 -1
- package/lib/data/techniques.js.map +1 -1
- package/lib/hooks/useActionFeedback.js.map +1 -1
- package/lib/hooks/useBalanceAnimations.js.map +1 -1
- package/lib/hooks/useCombatTimer.js.map +1 -1
- package/lib/hooks/useDebounce.js.map +1 -1
- package/lib/hooks/useHUDLayout.js.map +1 -1
- package/lib/hooks/useHandPoseTransitions.js.map +1 -1
- package/lib/hooks/useKeyboardControls.js.map +1 -1
- package/lib/hooks/useMatchCountdown.js.map +1 -1
- package/lib/hooks/useMuscleActivation.js.map +1 -1
- package/lib/hooks/usePauseMenu.js.map +1 -1
- package/lib/hooks/usePlayerAnimation.js.map +1 -1
- package/lib/hooks/useResponsiveLayout.js.map +1 -1
- package/lib/hooks/useRoundTransition.js.map +1 -1
- package/lib/hooks/useSkeletalAnimation.js.map +1 -1
- package/lib/hooks/useTechniqueSelection.js.map +1 -1
- package/lib/hooks/useThrottle.js.map +1 -1
- package/lib/hooks/useTouchControls.js.map +1 -1
- package/lib/hooks/useWebGLContextLossHandler.js.map +1 -1
- package/lib/hooks/useWindowSize.js.map +1 -1
- package/lib/systems/CombatSystem.js.map +1 -1
- package/lib/systems/EffectCalculator.js.map +1 -1
- package/lib/systems/LayoutSystem.js.map +1 -1
- package/lib/systems/PlayerEffectManager.js.map +1 -1
- package/lib/systems/ResponsiveScaling.js.map +1 -1
- package/lib/systems/TrigramSystem.js.map +1 -1
- package/lib/systems/VitalPointSystem.js.map +1 -1
- package/lib/systems/ai/AIPersonality.js.map +1 -1
- package/lib/systems/ai/AdaptiveDifficulty.js.map +1 -1
- package/lib/systems/ai/ArchetypeEnforcer.js.map +1 -1
- package/lib/systems/ai/ComboSystem.js.map +1 -1
- package/lib/systems/ai/DecisionTree.js.map +1 -1
- package/lib/systems/ai/TrainingAI.js.map +1 -1
- package/lib/systems/ai/types.js.map +1 -1
- package/lib/systems/animation/builders/AnimationBuilder.js.map +1 -1
- package/lib/systems/animation/builders/HandPoseApplicator.js.map +1 -1
- package/lib/systems/animation/builders/HandPoses.js.map +1 -1
- package/lib/systems/animation/builders/KeyframeConfig.js.map +1 -1
- package/lib/systems/animation/builders/KeyframeInterpolation.js +3 -90
- package/lib/systems/animation/builders/KeyframeInterpolation.js.map +1 -1
- package/lib/systems/animation/builders/KickPhaseApplicator.js.map +1 -1
- package/lib/systems/animation/builders/KoreanGuardPositions.js.map +1 -1
- package/lib/systems/animation/builders/MartialArtsAnimationBuilder.js.map +1 -1
- package/lib/systems/animation/builders/MartialArtsConstants.js.map +1 -1
- package/lib/systems/animation/builders/MartialPoseApplicator.js.map +1 -1
- package/lib/systems/animation/builders/PunchPhaseApplicator.js.map +1 -1
- package/lib/systems/animation/builders/SkeletonRig.js.map +1 -1
- package/lib/systems/animation/builders/TrigramGuardApplicator.js.map +1 -1
- package/lib/systems/animation/catalogs/DefensiveAnimations.js.map +1 -1
- package/lib/systems/animation/catalogs/FootworkSkeletalAnimations.js.map +1 -1
- package/lib/systems/animation/catalogs/RecoveryAnimations.js.map +1 -1
- package/lib/systems/animation/catalogs/StanceAnimations.js.map +1 -1
- package/lib/systems/animation/catalogs/StanceAttackAnimations.js.map +1 -1
- package/lib/systems/animation/catalogs/StanceGuardPoses.js.map +1 -1
- package/lib/systems/animation/catalogs/StanceIdleAnimations.js.map +1 -1
- package/lib/systems/animation/catalogs/StanceLocomotionAnimations.js.map +1 -1
- package/lib/systems/animation/catalogs/StepSkeletalAnimations.js.map +1 -1
- package/lib/systems/animation/core/AnimationHitTiming.js.map +1 -1
- package/lib/systems/animation/core/AnimationOptimizations.js.map +1 -1
- package/lib/systems/animation/core/AnimationPriority.js.map +1 -1
- package/lib/systems/animation/core/AnimationRegistry.js.map +1 -1
- package/lib/systems/animation/core/AnimationStateMachine.js.map +1 -1
- package/lib/systems/animation/core/AnimationTransitions.js.map +1 -1
- package/lib/systems/animation/core/LateralityTransform.js.map +1 -1
- package/lib/systems/animation/core/RecoveryPhaseEnhancer.js.map +1 -1
- package/lib/systems/animation/core/TechniqueAnimationMapper.js.map +1 -1
- package/lib/systems/animation/core/TechniqueAnimationMapping.js.map +1 -1
- package/lib/systems/animation/core/types.js.map +1 -1
- package/lib/systems/animation/systems/AdvancedJointMovements.js.map +1 -1
- package/lib/systems/animation/systems/BodyFacingSystem.js.map +1 -1
- package/lib/systems/animation/systems/FacialExpressions.js.map +1 -1
- package/lib/systems/animation/systems/FallAnimations.js.map +1 -1
- package/lib/systems/animation/systems/MuscleActivation.js.map +1 -1
- package/lib/systems/bodypart/BodyPartDamageIntegration.js.map +1 -1
- package/lib/systems/bodypart/BodyPartHealthSystem.js.map +1 -1
- package/lib/systems/bodypart/BodyPartPositionMapping.js.map +1 -1
- package/lib/systems/bodypart/CombatInjuryIntegration.js.map +1 -1
- package/lib/systems/bodypart/InjuryIntegration.js.map +1 -1
- package/lib/systems/bodypart/InjuryTracker.js.map +1 -1
- package/lib/systems/bodypart/MovementPenaltySystem.js.map +1 -1
- package/lib/systems/bodypart/PlayerInjuryTrackingManager.js.map +1 -1
- package/lib/systems/bodypart/types.js.map +1 -1
- package/lib/systems/breathing/BreathingDisruptionSystem.js.map +1 -1
- package/lib/systems/breathing/feedback.js.map +1 -1
- package/lib/systems/breathing/integration.js.map +1 -1
- package/lib/systems/combat/BalanceSystem.js.map +1 -1
- package/lib/systems/combat/BreakingStatusEffects.js.map +1 -1
- package/lib/systems/combat/CombatStateSystem.js.map +1 -1
- package/lib/systems/combat/ConsciousnessSystem.js.map +1 -1
- package/lib/systems/combat/FallIntegration.js.map +1 -1
- package/lib/systems/combat/GrappleSystem.js.map +1 -1
- package/lib/systems/combat/LimbExposureSystem.js.map +1 -1
- package/lib/systems/combat/PainResponseSystem.js.map +1 -1
- package/lib/systems/combat/TrainingCombatSystem.js.map +1 -1
- package/lib/systems/combat/painConsciousnessUtils.js.map +1 -1
- package/lib/systems/combat/typeGuards.js.map +1 -1
- package/lib/systems/effects.js.map +1 -1
- package/lib/systems/game.js.map +1 -1
- package/lib/systems/movement/InjuryMovementModifier.js.map +1 -1
- package/lib/systems/movement/helpers/AccelerationUpdater.js.map +1 -1
- package/lib/systems/movement/helpers/accelerationUtils.js.map +1 -1
- package/lib/systems/movement/integration.js.map +1 -1
- package/lib/systems/physics/AttackMovementPhysics.js.map +1 -1
- package/lib/systems/physics/CollisionDetection.js.map +1 -1
- package/lib/systems/physics/CoordinateMapper.js.map +1 -1
- package/lib/systems/physics/KnockbackPhysics.js.map +1 -1
- package/lib/systems/physics/MovementPhysics.js.map +1 -1
- package/lib/systems/physics/PhysicalReachCalculator.js.map +1 -1
- package/lib/systems/physics/SpeedModifierSystem.js.map +1 -1
- package/lib/systems/trigram/KoreanCulture.js.map +1 -1
- package/lib/systems/trigram/KoreanTechniques.js.map +1 -1
- package/lib/systems/trigram/StanceManager.js.map +1 -1
- package/lib/systems/trigram/TransitionCalculator.js.map +1 -1
- package/lib/systems/trigram/TrigramCalculator.js.map +1 -1
- package/lib/systems/trigram/techniques/DarkOpsTechniques.js.map +1 -1
- package/lib/systems/trigram/techniques/GamTechniques.js.map +1 -1
- package/lib/systems/trigram/techniques/GanTechniques.js.map +1 -1
- package/lib/systems/trigram/techniques/GonTechniques.js.map +1 -1
- package/lib/systems/trigram/techniques/SonTechniques.js.map +1 -1
- package/lib/systems/trigram/techniques/TechniqueConfig.js.map +1 -1
- package/lib/systems/trigram/techniques/index.js.map +1 -1
- package/lib/systems/trigram/types/GonTechniqueExtensions.js.map +1 -1
- package/lib/systems/trigram/types.js.map +1 -1
- package/lib/systems/types.js.map +1 -1
- package/lib/systems/vitalpoint/DamageCalculator.js.map +1 -1
- package/lib/systems/vitalpoint/HitDetection.js.map +1 -1
- package/lib/systems/vitalpoint/KoreanAnatomy.js.map +1 -1
- package/lib/systems/vitalpoint/KoreanVitalPoints.js.map +1 -1
- package/lib/systems/vitalpoint/MeridianVitalPointMapping.js.map +1 -1
- package/lib/types/AccessibilityTypes.js.map +1 -1
- package/lib/types/PhysicsTypes.js.map +1 -1
- package/lib/types/common.js.map +1 -1
- package/lib/types/constants/colors.js.map +1 -1
- package/lib/types/constants/designSystem.js.map +1 -1
- package/lib/types/constants/layout.js.map +1 -1
- package/lib/types/constants/performance.js.map +1 -1
- package/lib/types/constants/typography.js.map +1 -1
- package/lib/types/facial.js.map +1 -1
- package/lib/types/hand-animation.js.map +1 -1
- package/lib/types/injury.js.map +1 -1
- package/lib/types/physics.js.map +1 -1
- package/lib/types/skeletal.js.map +1 -1
- package/lib/types/techniqueId.js.map +1 -1
- package/lib/utils/accessibility.js.map +1 -1
- package/lib/utils/arenaWorldDimensions.js.map +1 -1
- package/lib/utils/assetConfig.js.map +1 -1
- package/lib/utils/characterScaling.js.map +1 -1
- package/lib/utils/colorHelpers.js.map +1 -1
- package/lib/utils/colorUtils.js.map +1 -1
- package/lib/utils/combatReadiness.js.map +1 -1
- package/lib/utils/controlMapping.js.map +1 -1
- package/lib/utils/deviceDetection.js.map +1 -1
- package/lib/utils/effectUtils.js.map +1 -1
- package/lib/utils/fabricTextures.js.map +1 -1
- package/lib/utils/hapticFeedback.js.map +1 -1
- package/lib/utils/haptics.js.map +1 -1
- package/lib/utils/htmlOverlayHelpers.js.map +1 -1
- package/lib/utils/inputSystem.js.map +1 -1
- package/lib/utils/koreanThemeHelpers.js.map +1 -1
- package/lib/utils/math.js.map +1 -1
- package/lib/utils/mobileLayoutHelpers.js.map +1 -1
- package/lib/utils/mobileUIUtils.js.map +1 -1
- package/lib/utils/performance/PerformanceMonitor.js.map +1 -1
- package/lib/utils/performance/PerformanceOverlay3D.js.map +1 -1
- package/lib/utils/performance/usePerformanceMonitor.js.map +1 -1
- package/lib/utils/performanceOptimization.js.map +1 -1
- package/lib/utils/player3DHelpers.js.map +1 -1
- package/lib/utils/playerUtils.js.map +1 -1
- package/lib/utils/responsiveLayout.js.map +1 -1
- package/lib/utils/responsiveLayoutHelpers.js.map +1 -1
- package/lib/utils/responsiveOrientationConstants.js.map +1 -1
- package/lib/utils/safeAreaUtils.js.map +1 -1
- package/lib/utils/sharedPhysicsConfig.js.map +1 -1
- package/lib/utils/skeletonScaling.js.map +1 -1
- package/lib/utils/stanceHelpers.js.map +1 -1
- package/lib/utils/threeObjectPool.js.map +1 -1
- package/lib/utils/visualEffects.js.map +1 -1
- package/package.json +8 -8
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useCombatState.js","names":[],"sources":["../../../../../src/components/screens/combat/hooks/useCombatState.ts"],"sourcesContent":["/**\n * useCombatState Hook - Consolidated Combat State Management\n * \n * Custom hook for managing complex combat state using useReducer.\n * Consolidates multiple useState calls into a single reducer for better\n * performance and maintainability.\n *\n * Performance:\n * - Uses useReducer to batch state updates\n * - Reduces number of re-renders\n * - Provides clear action-based state updates\n *\n * @returns Combat state and dispatch actions\n * \n * @example\n * ```typescript\n * const { state, actions } = useCombatState();\n * actions.setHitEffects([effect1, effect2]);\n * actions.incrementCombo();\n * ```\n */\n\nimport { HitEffect } from \"@/systems\";\nimport type { StanceLaterality } from \"@/systems/trigram/types\";\nimport { useCallback, useReducer } from \"react\";\n\n/**\n * Combat screen state managed by the reducer\n * Note: Renamed from CombatState to avoid conflict with CombatState enum in types/common.ts\n */\nexport interface CombatScreenState {\n readonly hitEffects: HitEffect[];\n readonly isExecutingTechnique: boolean;\n readonly combatMessages: string[];\n readonly roundStarted: boolean;\n readonly roundEnded: boolean;\n readonly roundDisplayStatus: \"start\" | \"fight\" | \"ko\" | \"end\" | null;\n readonly comboCount: number;\n readonly lastHitTime: number;\n readonly screenShake: { x: number; y: number };\n readonly playerLaterality: readonly [StanceLaterality, StanceLaterality];\n}\n\n/**\n * Combat state actions\n */\ntype CombatAction =\n | { type: \"SET_HIT_EFFECTS\"; payload: HitEffect[] }\n | { type: \"ADD_HIT_EFFECT\"; payload: HitEffect }\n | { type: \"REMOVE_HIT_EFFECT\"; payload: string }\n | { type: \"SET_EXECUTING_TECHNIQUE\"; payload: boolean }\n | { type: \"ADD_COMBAT_MESSAGE\"; payload: string }\n | { type: \"SET_ROUND_STARTED\"; payload: boolean }\n | { type: \"SET_ROUND_ENDED\"; payload: boolean }\n | { type: \"SET_ROUND_DISPLAY_STATUS\"; payload: \"start\" | \"fight\" | \"ko\" | \"end\" | null }\n | { type: \"SET_COMBO_COUNT\"; payload: number }\n | { type: \"INCREMENT_COMBO\" }\n | { type: \"RESET_COMBO\" }\n | { type: \"SET_LAST_HIT_TIME\"; payload: number }\n | { type: \"SET_SCREEN_SHAKE\"; payload: { x: number; y: number } }\n | { type: \"RESET_SCREEN_SHAKE\" }\n | { type: \"RESET_ROUND_STATE\" }\n | { type: \"SET_PLAYER_LATERALITY\"; payload: readonly [StanceLaterality, StanceLaterality] }\n | { type: \"SET_PLAYER_LATERALITY_INDEX\"; payload: { index: 0 | 1; laterality: StanceLaterality } };\n\n/**\n * Initial combat state\n */\nconst initialState: CombatScreenState = {\n hitEffects: [],\n isExecutingTechnique: false,\n combatMessages: [],\n roundStarted: false,\n roundEnded: false,\n roundDisplayStatus: null,\n comboCount: 0,\n lastHitTime: 0,\n screenShake: { x: 0, y: 0 },\n playerLaterality: [\"right\", \"right\"],\n};\n\n/**\n * Combat state reducer\n * Handles all combat state updates in a centralized, performant way\n */\nfunction combatReducer(state: CombatScreenState, action: CombatAction): CombatScreenState {\n switch (action.type) {\n case \"SET_HIT_EFFECTS\":\n return { ...state, hitEffects: action.payload };\n\n case \"ADD_HIT_EFFECT\":\n return { ...state, hitEffects: [...state.hitEffects, action.payload] };\n\n case \"REMOVE_HIT_EFFECT\":\n return {\n ...state,\n hitEffects: state.hitEffects.filter((effect) => effect.id !== action.payload),\n };\n\n case \"SET_EXECUTING_TECHNIQUE\":\n return { ...state, isExecutingTechnique: action.payload };\n\n case \"ADD_COMBAT_MESSAGE\":\n return {\n ...state,\n combatMessages: [action.payload, ...state.combatMessages.slice(0, 4)],\n };\n\n case \"SET_ROUND_STARTED\":\n return { ...state, roundStarted: action.payload };\n\n case \"SET_ROUND_ENDED\":\n return { ...state, roundEnded: action.payload };\n\n case \"SET_ROUND_DISPLAY_STATUS\":\n return { ...state, roundDisplayStatus: action.payload };\n\n case \"SET_COMBO_COUNT\":\n return { ...state, comboCount: action.payload };\n\n case \"INCREMENT_COMBO\":\n return { ...state, comboCount: state.comboCount + 1 };\n\n case \"RESET_COMBO\":\n return { ...state, comboCount: 0 };\n\n case \"SET_LAST_HIT_TIME\":\n return { ...state, lastHitTime: action.payload };\n\n case \"SET_SCREEN_SHAKE\":\n return { ...state, screenShake: action.payload };\n\n case \"RESET_SCREEN_SHAKE\":\n return { ...state, screenShake: { x: 0, y: 0 } };\n\n case \"RESET_ROUND_STATE\":\n return {\n ...state,\n roundStarted: false,\n roundEnded: false,\n roundDisplayStatus: null,\n comboCount: 0,\n hitEffects: [],\n combatMessages: [],\n };\n\n case \"SET_PLAYER_LATERALITY\":\n return { ...state, playerLaterality: action.payload };\n\n case \"SET_PLAYER_LATERALITY_INDEX\":\n const newLaterality: [StanceLaterality, StanceLaterality] = [...state.playerLaterality] as [StanceLaterality, StanceLaterality];\n newLaterality[action.payload.index] = action.payload.laterality;\n return { ...state, playerLaterality: newLaterality };\n\n default:\n return state;\n }\n}\n\n/**\n * Combat state action creators\n */\nexport interface CombatActions {\n readonly setHitEffects: (effects: HitEffect[]) => void;\n readonly addHitEffect: (effect: HitEffect) => void;\n readonly removeHitEffect: (effectId: string) => void;\n readonly setExecutingTechnique: (executing: boolean) => void;\n readonly addCombatMessage: (message: string) => void;\n readonly setRoundStarted: (started: boolean) => void;\n readonly setRoundEnded: (ended: boolean) => void;\n readonly setRoundDisplayStatus: (status: \"start\" | \"fight\" | \"ko\" | \"end\" | null) => void;\n readonly setComboCount: (count: number) => void;\n readonly incrementCombo: () => void;\n readonly resetCombo: () => void;\n readonly setLastHitTime: (time: number) => void;\n readonly setScreenShake: (shake: { x: number; y: number }) => void;\n readonly resetScreenShake: () => void;\n readonly resetRoundState: () => void;\n readonly setPlayerLaterality: (laterality: readonly [StanceLaterality, StanceLaterality]) => void;\n readonly setPlayerLateralityIndex: (index: 0 | 1, laterality: StanceLaterality) => void;\n}\n\n/**\n * Custom hook for combat state management\n */\nexport function useCombatState() {\n const [state, dispatch] = useReducer(combatReducer, initialState);\n\n // Memoized action creators to prevent recreation on every render\n const actions: CombatActions = {\n setHitEffects: useCallback(\n (effects: HitEffect[]) => dispatch({ type: \"SET_HIT_EFFECTS\", payload: effects }),\n []\n ),\n addHitEffect: useCallback(\n (effect: HitEffect) => dispatch({ type: \"ADD_HIT_EFFECT\", payload: effect }),\n []\n ),\n removeHitEffect: useCallback(\n (effectId: string) => dispatch({ type: \"REMOVE_HIT_EFFECT\", payload: effectId }),\n []\n ),\n setExecutingTechnique: useCallback(\n (executing: boolean) =>\n dispatch({ type: \"SET_EXECUTING_TECHNIQUE\", payload: executing }),\n []\n ),\n addCombatMessage: useCallback(\n (message: string) => dispatch({ type: \"ADD_COMBAT_MESSAGE\", payload: message }),\n []\n ),\n setRoundStarted: useCallback(\n (started: boolean) => dispatch({ type: \"SET_ROUND_STARTED\", payload: started }),\n []\n ),\n setRoundEnded: useCallback(\n (ended: boolean) => dispatch({ type: \"SET_ROUND_ENDED\", payload: ended }),\n []\n ),\n setRoundDisplayStatus: useCallback(\n (status: \"start\" | \"fight\" | \"ko\" | \"end\" | null) =>\n dispatch({ type: \"SET_ROUND_DISPLAY_STATUS\", payload: status }),\n []\n ),\n setComboCount: useCallback(\n (count: number) => dispatch({ type: \"SET_COMBO_COUNT\", payload: count }),\n []\n ),\n incrementCombo: useCallback(() => dispatch({ type: \"INCREMENT_COMBO\" }), []),\n resetCombo: useCallback(() => dispatch({ type: \"RESET_COMBO\" }), []),\n setLastHitTime: useCallback(\n (time: number) => dispatch({ type: \"SET_LAST_HIT_TIME\", payload: time }),\n []\n ),\n setScreenShake: useCallback(\n (shake: { x: number; y: number }) =>\n dispatch({ type: \"SET_SCREEN_SHAKE\", payload: shake }),\n []\n ),\n resetScreenShake: useCallback(() => dispatch({ type: \"RESET_SCREEN_SHAKE\" }), []),\n resetRoundState: useCallback(() => dispatch({ type: \"RESET_ROUND_STATE\" }), []),\n setPlayerLaterality: useCallback(\n (laterality: readonly [StanceLaterality, StanceLaterality]) =>\n dispatch({ type: \"SET_PLAYER_LATERALITY\", payload: laterality }),\n []\n ),\n setPlayerLateralityIndex: useCallback(\n (index: 0 | 1, laterality: StanceLaterality) =>\n dispatch({ type: \"SET_PLAYER_LATERALITY_INDEX\", payload: { index, laterality } }),\n []\n ),\n };\n\n return { state, actions };\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"useCombatState.js","names":[],"sources":["../../../../../src/components/screens/combat/hooks/useCombatState.ts"],"sourcesContent":["/**\n * useCombatState Hook - Consolidated Combat State Management\n * \n * Custom hook for managing complex combat state using useReducer.\n * Consolidates multiple useState calls into a single reducer for better\n * performance and maintainability.\n *\n * Performance:\n * - Uses useReducer to batch state updates\n * - Reduces number of re-renders\n * - Provides clear action-based state updates\n *\n * @returns Combat state and dispatch actions\n * \n * @example\n * ```typescript\n * const { state, actions } = useCombatState();\n * actions.setHitEffects([effect1, effect2]);\n * actions.incrementCombo();\n * ```\n */\n\nimport { HitEffect } from \"@/systems\";\nimport type { StanceLaterality } from \"@/systems/trigram/types\";\nimport { useCallback, useReducer } from \"react\";\n\n/**\n * Combat screen state managed by the reducer\n * Note: Renamed from CombatState to avoid conflict with CombatState enum in types/common.ts\n */\nexport interface CombatScreenState {\n readonly hitEffects: HitEffect[];\n readonly isExecutingTechnique: boolean;\n readonly combatMessages: string[];\n readonly roundStarted: boolean;\n readonly roundEnded: boolean;\n readonly roundDisplayStatus: \"start\" | \"fight\" | \"ko\" | \"end\" | null;\n readonly comboCount: number;\n readonly lastHitTime: number;\n readonly screenShake: { x: number; y: number };\n readonly playerLaterality: readonly [StanceLaterality, StanceLaterality];\n}\n\n/**\n * Combat state actions\n */\ntype CombatAction =\n | { type: \"SET_HIT_EFFECTS\"; payload: HitEffect[] }\n | { type: \"ADD_HIT_EFFECT\"; payload: HitEffect }\n | { type: \"REMOVE_HIT_EFFECT\"; payload: string }\n | { type: \"SET_EXECUTING_TECHNIQUE\"; payload: boolean }\n | { type: \"ADD_COMBAT_MESSAGE\"; payload: string }\n | { type: \"SET_ROUND_STARTED\"; payload: boolean }\n | { type: \"SET_ROUND_ENDED\"; payload: boolean }\n | { type: \"SET_ROUND_DISPLAY_STATUS\"; payload: \"start\" | \"fight\" | \"ko\" | \"end\" | null }\n | { type: \"SET_COMBO_COUNT\"; payload: number }\n | { type: \"INCREMENT_COMBO\" }\n | { type: \"RESET_COMBO\" }\n | { type: \"SET_LAST_HIT_TIME\"; payload: number }\n | { type: \"SET_SCREEN_SHAKE\"; payload: { x: number; y: number } }\n | { type: \"RESET_SCREEN_SHAKE\" }\n | { type: \"RESET_ROUND_STATE\" }\n | { type: \"SET_PLAYER_LATERALITY\"; payload: readonly [StanceLaterality, StanceLaterality] }\n | { type: \"SET_PLAYER_LATERALITY_INDEX\"; payload: { index: 0 | 1; laterality: StanceLaterality } };\n\n/**\n * Initial combat state\n */\nconst initialState: CombatScreenState = {\n hitEffects: [],\n isExecutingTechnique: false,\n combatMessages: [],\n roundStarted: false,\n roundEnded: false,\n roundDisplayStatus: null,\n comboCount: 0,\n lastHitTime: 0,\n screenShake: { x: 0, y: 0 },\n playerLaterality: [\"right\", \"right\"],\n};\n\n/**\n * Combat state reducer\n * Handles all combat state updates in a centralized, performant way\n */\nfunction combatReducer(state: CombatScreenState, action: CombatAction): CombatScreenState {\n switch (action.type) {\n case \"SET_HIT_EFFECTS\":\n return { ...state, hitEffects: action.payload };\n\n case \"ADD_HIT_EFFECT\":\n return { ...state, hitEffects: [...state.hitEffects, action.payload] };\n\n case \"REMOVE_HIT_EFFECT\":\n return {\n ...state,\n hitEffects: state.hitEffects.filter((effect) => effect.id !== action.payload),\n };\n\n case \"SET_EXECUTING_TECHNIQUE\":\n return { ...state, isExecutingTechnique: action.payload };\n\n case \"ADD_COMBAT_MESSAGE\":\n return {\n ...state,\n combatMessages: [action.payload, ...state.combatMessages.slice(0, 4)],\n };\n\n case \"SET_ROUND_STARTED\":\n return { ...state, roundStarted: action.payload };\n\n case \"SET_ROUND_ENDED\":\n return { ...state, roundEnded: action.payload };\n\n case \"SET_ROUND_DISPLAY_STATUS\":\n return { ...state, roundDisplayStatus: action.payload };\n\n case \"SET_COMBO_COUNT\":\n return { ...state, comboCount: action.payload };\n\n case \"INCREMENT_COMBO\":\n return { ...state, comboCount: state.comboCount + 1 };\n\n case \"RESET_COMBO\":\n return { ...state, comboCount: 0 };\n\n case \"SET_LAST_HIT_TIME\":\n return { ...state, lastHitTime: action.payload };\n\n case \"SET_SCREEN_SHAKE\":\n return { ...state, screenShake: action.payload };\n\n case \"RESET_SCREEN_SHAKE\":\n return { ...state, screenShake: { x: 0, y: 0 } };\n\n case \"RESET_ROUND_STATE\":\n return {\n ...state,\n roundStarted: false,\n roundEnded: false,\n roundDisplayStatus: null,\n comboCount: 0,\n hitEffects: [],\n combatMessages: [],\n };\n\n case \"SET_PLAYER_LATERALITY\":\n return { ...state, playerLaterality: action.payload };\n\n case \"SET_PLAYER_LATERALITY_INDEX\":\n const newLaterality: [StanceLaterality, StanceLaterality] = [...state.playerLaterality] as [StanceLaterality, StanceLaterality];\n newLaterality[action.payload.index] = action.payload.laterality;\n return { ...state, playerLaterality: newLaterality };\n\n default:\n return state;\n }\n}\n\n/**\n * Combat state action creators\n */\nexport interface CombatActions {\n readonly setHitEffects: (effects: HitEffect[]) => void;\n readonly addHitEffect: (effect: HitEffect) => void;\n readonly removeHitEffect: (effectId: string) => void;\n readonly setExecutingTechnique: (executing: boolean) => void;\n readonly addCombatMessage: (message: string) => void;\n readonly setRoundStarted: (started: boolean) => void;\n readonly setRoundEnded: (ended: boolean) => void;\n readonly setRoundDisplayStatus: (status: \"start\" | \"fight\" | \"ko\" | \"end\" | null) => void;\n readonly setComboCount: (count: number) => void;\n readonly incrementCombo: () => void;\n readonly resetCombo: () => void;\n readonly setLastHitTime: (time: number) => void;\n readonly setScreenShake: (shake: { x: number; y: number }) => void;\n readonly resetScreenShake: () => void;\n readonly resetRoundState: () => void;\n readonly setPlayerLaterality: (laterality: readonly [StanceLaterality, StanceLaterality]) => void;\n readonly setPlayerLateralityIndex: (index: 0 | 1, laterality: StanceLaterality) => void;\n}\n\n/**\n * Custom hook for combat state management\n */\nexport function useCombatState() {\n const [state, dispatch] = useReducer(combatReducer, initialState);\n\n // Memoized action creators to prevent recreation on every render\n const actions: CombatActions = {\n setHitEffects: useCallback(\n (effects: HitEffect[]) => dispatch({ type: \"SET_HIT_EFFECTS\", payload: effects }),\n []\n ),\n addHitEffect: useCallback(\n (effect: HitEffect) => dispatch({ type: \"ADD_HIT_EFFECT\", payload: effect }),\n []\n ),\n removeHitEffect: useCallback(\n (effectId: string) => dispatch({ type: \"REMOVE_HIT_EFFECT\", payload: effectId }),\n []\n ),\n setExecutingTechnique: useCallback(\n (executing: boolean) =>\n dispatch({ type: \"SET_EXECUTING_TECHNIQUE\", payload: executing }),\n []\n ),\n addCombatMessage: useCallback(\n (message: string) => dispatch({ type: \"ADD_COMBAT_MESSAGE\", payload: message }),\n []\n ),\n setRoundStarted: useCallback(\n (started: boolean) => dispatch({ type: \"SET_ROUND_STARTED\", payload: started }),\n []\n ),\n setRoundEnded: useCallback(\n (ended: boolean) => dispatch({ type: \"SET_ROUND_ENDED\", payload: ended }),\n []\n ),\n setRoundDisplayStatus: useCallback(\n (status: \"start\" | \"fight\" | \"ko\" | \"end\" | null) =>\n dispatch({ type: \"SET_ROUND_DISPLAY_STATUS\", payload: status }),\n []\n ),\n setComboCount: useCallback(\n (count: number) => dispatch({ type: \"SET_COMBO_COUNT\", payload: count }),\n []\n ),\n incrementCombo: useCallback(() => dispatch({ type: \"INCREMENT_COMBO\" }), []),\n resetCombo: useCallback(() => dispatch({ type: \"RESET_COMBO\" }), []),\n setLastHitTime: useCallback(\n (time: number) => dispatch({ type: \"SET_LAST_HIT_TIME\", payload: time }),\n []\n ),\n setScreenShake: useCallback(\n (shake: { x: number; y: number }) =>\n dispatch({ type: \"SET_SCREEN_SHAKE\", payload: shake }),\n []\n ),\n resetScreenShake: useCallback(() => dispatch({ type: \"RESET_SCREEN_SHAKE\" }), []),\n resetRoundState: useCallback(() => dispatch({ type: \"RESET_ROUND_STATE\" }), []),\n setPlayerLaterality: useCallback(\n (laterality: readonly [StanceLaterality, StanceLaterality]) =>\n dispatch({ type: \"SET_PLAYER_LATERALITY\", payload: laterality }),\n []\n ),\n setPlayerLateralityIndex: useCallback(\n (index: 0 | 1, laterality: StanceLaterality) =>\n dispatch({ type: \"SET_PLAYER_LATERALITY_INDEX\", payload: { index, laterality } }),\n []\n ),\n };\n\n return { state, actions };\n}\n"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ControlsScreen3D.js","names":[],"sources":["../../../../src/components/screens/controls/ControlsScreen3D.tsx"],"sourcesContent":["// UI renders outside Canvas in absolute-positioned div - no Html needed\nimport { Canvas } from \"@react-three/fiber\";\nimport React, { useCallback, useEffect, useMemo } from \"react\";\nimport { useAudio } from \"../../../audio/AudioProvider\";\nimport { useWebGLContextLossHandler } from \"../../../hooks/useWebGLContextLossHandler\";\nimport { useWindowSize } from \"../../../hooks/useWindowSize\";\nimport { COMBAT_CONTROLS } from \"../../../systems\";\nimport { FONT_FAMILY } from \"@/types/constants\";\nimport { Z_INDEX } from \"../../../types/LayoutTypes\";\nimport { hexToRgbaString } from \"../../../utils/colorUtils\";\nimport { shouldUseMobileControls } from \"../../../utils/deviceDetection\";\nimport { getLayoutConstants } from \"../../../utils/responsiveLayoutHelpers\";\nimport { useKoreanTheme } from \"../../shared/base/useKoreanTheme\";\nimport { BackgroundScene3D } from \"../../shared/three\";\nimport { BackButton } from \"../../shared/ui/BackButton\";\nimport { VolumeControl } from \"../../shared/ui/VolumeControl\";\nimport { ControlBindingsOverlayHtml } from \"./components/ControlBindingsOverlayHtml\";\nimport { ControlCategoryTabs } from \"./components/ControlCategoryTabsOverlayHtml\";\nimport { GamepadVisualization3D } from \"./components/GamepadVisualization3D\";\nimport { InteractiveControlDemo } from \"./components/InteractiveControlDemoOverlayHtml\";\nimport { VisualKeyboard3D } from \"./components/VisualKeyboard3D\";\nimport { useControlsState } from \"./hooks/useControlsState\";\n\nexport interface ControlsScreen3DProps {\n readonly onReturnToMenu: () => void;\n readonly width?: number;\n readonly height?: number;\n}\n\n/**\n * Three.js-based ControlsScreen Component\n */\nexport const ControlsScreen3D: React.FC<ControlsScreen3DProps> = ({\n onReturnToMenu,\n width: propWidth,\n height: propHeight,\n}) => {\n // UI now renders outside Canvas - no mount state needed\n\n // Use controls state hook for keyboard/gamepad tracking\n const { pressedKeys, category, selectedTab, setCategory, setSelectedTab } =\n useControlsState();\n\n // Handle WebGL context loss and restoration (for 3D background only)\n useWebGLContextLossHandler({\n onContextLost: () => {\n console.warn(\"⚠️ WebGL context lost in ControlsScreen\");\n },\n onContextRestored: () => {\n console.log(\"✓ WebGL context restored in ControlsScreen\");\n },\n autoRestore: true,\n });\n\n const audio = useAudio();\n const { width, height } = useWindowSize();\n\n // Use prop dimensions if provided, otherwise use window size\n const screenWidth = propWidth ?? width;\n const screenHeight = propHeight ?? height;\n\n // Responsive layout calculations with large desktop support\n // Use device detection instead of width-only breakpoint to correctly identify high-res mobile devices\n const isMobile = shouldUseMobileControls();\n // Only use width for tablet/desktop distinction when NOT mobile\n const isTablet = useMemo(\n () => !isMobile && screenWidth >= 768 && screenWidth < 1024,\n [isMobile, screenWidth],\n );\n const isLargeDesktop = useMemo(\n () => !isMobile && screenWidth >= 1920,\n [isMobile, screenWidth],\n ); // 4K/2K displays\n\n // Use centralized responsive layout helper for consistent scaling\n const layoutConstants = useMemo(\n () => getLayoutConstants(screenWidth),\n [screenWidth],\n );\n\n // Use Korean theme hook for consistent theming\n const theme = useKoreanTheme({\n variant: \"primary\",\n size: \"md\",\n isMobile,\n });\n\n // Memoize scrollbar style to prevent re-creating style tag on every render\n const scrollbarStyle = useMemo(\n () => ({\n __html: `\n .korean-scrollbar::-webkit-scrollbar {\n width: 12px !important;\n display: block !important;\n }\n .korean-scrollbar::-webkit-scrollbar-track {\n background: ${hexToRgbaString(theme.colors.UI_BACKGROUND_DARK, 0.8)};\n border-radius: 6px;\n }\n .korean-scrollbar::-webkit-scrollbar-thumb {\n background: ${hexToRgbaString(theme.colors.ACCENT_GOLD, 1)};\n border-radius: 6px;\n border: 2px solid ${hexToRgbaString(theme.colors.UI_BACKGROUND_DARK, 0.8)};\n }\n .korean-scrollbar::-webkit-scrollbar-thumb:hover {\n background: ${hexToRgbaString(theme.colors.PRIMARY_CYAN, 1)};\n }\n `,\n }),\n [theme],\n );\n\n // Memoize colors from theme for performance\n const colors = useMemo(\n () => ({\n background: hexToRgbaString(theme.colors.UI_BACKGROUND_DARK, 0.95),\n headerBg: hexToRgbaString(theme.colors.UI_BACKGROUND_DARK, 0.9),\n sectionBg: hexToRgbaString(theme.colors.UI_BACKGROUND_DARK, 0.8),\n borderGold: hexToRgbaString(theme.colors.ACCENT_GOLD, 0.6),\n borderCyan: hexToRgbaString(theme.colors.PRIMARY_CYAN, 0.5),\n borderRed: hexToRgbaString(theme.colors.KOREAN_RED, 0.8),\n textPrimary: `#${theme.colors.TEXT_PRIMARY.toString(16).padStart(6, \"0\")}`,\n textSecondary: `#${theme.colors.TEXT_SECONDARY.toString(16).padStart(6, \"0\")}`,\n accentGold: `#${theme.colors.ACCENT_GOLD.toString(16).padStart(6, \"0\")}`,\n accentCyan: `#${theme.colors.PRIMARY_CYAN.toString(16).padStart(6, \"0\")}`,\n koreanBlack: `#${theme.colors.KOREAN_BLACK.toString(16).padStart(6, \"0\")}`,\n }),\n [theme],\n );\n\n // Enhanced keyboard handling for screen-level navigation\n useEffect(() => {\n const handleKeyDown = (event: KeyboardEvent) => {\n if (event.key === \"Escape\" || event.key.toLowerCase() === \"m\") {\n event.preventDefault();\n audio.playSFX(\"menu_back\");\n onReturnToMenu();\n }\n };\n\n window.addEventListener(\"keydown\", handleKeyDown, { passive: false });\n return () => window.removeEventListener(\"keydown\", handleKeyDown);\n }, [onReturnToMenu, audio]);\n\n // Handle back button click\n const handleBackClick = useCallback(() => {\n audio.playSFX(\"menu_back\");\n onReturnToMenu();\n }, [audio, onReturnToMenu]);\n\n // Mode toggle button hover handlers (extracted for DRY)\n const handleModeButtonEnter = useCallback(\n (e: React.MouseEvent<HTMLButtonElement>, isActive: boolean) => {\n if (!isActive) {\n e.currentTarget.style.background = hexToRgbaString(\n theme.colors.PRIMARY_CYAN,\n 0.1,\n );\n e.currentTarget.style.borderColor = hexToRgbaString(\n theme.colors.PRIMARY_CYAN,\n 0.8,\n );\n }\n },\n [theme],\n );\n\n const handleModeButtonLeave = useCallback(\n (e: React.MouseEvent<HTMLButtonElement>, isActive: boolean) => {\n if (!isActive) {\n e.currentTarget.style.background = hexToRgbaString(\n theme.colors.UI_BACKGROUND_DARK,\n 0.8,\n );\n e.currentTarget.style.borderColor = hexToRgbaString(\n theme.colors.ACCENT_GOLD,\n 0.6,\n );\n }\n },\n [theme],\n );\n\n // Stance controls data\n const stanceControls = useMemo(\n () => Object.entries(COMBAT_CONTROLS.stanceControls),\n [],\n );\n\n // Combat controls data\n const combatControls = useMemo(\n () => Object.entries(COMBAT_CONTROLS.combat),\n [],\n );\n\n // Grid layout calculations\n const buttonsPerRow = isMobile ? 2 : isTablet ? 3 : isLargeDesktop ? 5 : 4;\n const buttonHeight = isMobile\n ? 120\n : isTablet\n ? 130\n : isLargeDesktop\n ? 120\n : 140;\n\n return (\n <div\n style={{\n width: screenWidth,\n height: screenHeight,\n position: \"relative\",\n overflow: \"hidden\",\n }}\n data-testid=\"controls-screen\"\n >\n {/* Volume Control - outside Canvas to maintain AudioProvider context */}\n <VolumeControl position=\"top-right\" compact={isMobile} />\n\n {/* Three.js Canvas for 3D background and visualization */}\n <Canvas\n style={{\n position: \"absolute\",\n top: 0,\n left: 0,\n width: \"100%\",\n height: \"100%\",\n zIndex: Z_INDEX.ARENA,\n }}\n gl={{\n antialias: true,\n alpha: false,\n powerPreference: \"high-performance\",\n }}\n dpr={[1, 2]}\n camera={{ position: [0, 5, 10], fov: 75 }}\n onCreated={({ gl }) => {\n gl.setClearColor(theme.colors.UI_BACKGROUND_DARK, 1);\n }}\n >\n {/* 3D Background Scene */}\n <BackgroundScene3D theme=\"controls\" />\n\n {/* Conditional 3D Visualization based on category */}\n {category === \"keyboard\" ? (\n <VisualKeyboard3D\n pressedKeys={pressedKeys}\n selectedTab={selectedTab}\n />\n ) : (\n <GamepadVisualization3D isMobile={isMobile} />\n )}\n </Canvas>\n\n {/* UI Overlay (positioned absolutely over Canvas) - matches CombatScreen pattern */}\n <div\n style={{\n position: \"absolute\",\n top: 0,\n left: 0,\n width: \"100%\",\n height: \"100%\",\n pointerEvents: \"none\",\n zIndex: Z_INDEX.HUD,\n }}\n data-testid=\"controls-hud-overlay\"\n >\n {/* WebKit Scrollbar Styling - Using !important to override global hide */}\n <style dangerouslySetInnerHTML={scrollbarStyle} />\n\n <div\n style={{\n width: \"100%\",\n height: \"100%\",\n display: \"flex\",\n flexDirection: \"column\",\n color: colors.textPrimary,\n fontFamily: theme.koreanTypography.fontFamily,\n lineHeight: theme.koreanTypography.lineHeight,\n pointerEvents: \"auto\",\n }}\n >\n {/* Header */}\n <div\n style={{\n height: `${layoutConstants.headerHeight}px`,\n display: \"flex\",\n flexDirection: \"column\",\n alignItems: \"center\",\n justifyContent: \"center\",\n background: colors.headerBg,\n borderBottom: `3px solid ${colors.borderRed}`,\n padding: `${layoutConstants.padding}px`,\n }}\n data-testid=\"controls-header\"\n >\n <h1\n style={{\n fontSize: isMobile ? \"20px\" : \"24px\",\n fontWeight: \"bold\",\n color: colors.accentGold,\n margin: 0,\n textShadow: `0 0 10px ${hexToRgbaString(\n theme.colors.ACCENT_GOLD,\n 0.5,\n )}`,\n }}\n >\n 조작법 안내 - Controls Guide\n </h1>\n <p\n style={{\n fontSize: isMobile ? \"12px\" : \"16px\",\n color: colors.accentCyan,\n margin: \"8px 0 0 0\",\n fontStyle: \"italic\",\n }}\n >\n ☯ 팔괘 철학과 급소술의 융합 | Eight Trigrams Philosophy & Vital\n Point Arts ☯\n </p>\n </div>\n\n {/* Mode Toggle (Keyboard/Gamepad) */}\n <div\n style={{\n display: \"flex\",\n justifyContent: \"center\",\n gap: isMobile ? \"10px\" : \"15px\",\n padding: `${layoutConstants.padding}px`,\n background: colors.headerBg,\n borderBottom: `2px solid ${colors.borderGold}`,\n }}\n data-testid=\"mode-toggle\"\n >\n <button\n onClick={() => {\n audio.playSFX(\"menu_select\");\n setCategory(\"keyboard\");\n }}\n style={{\n padding: isMobile ? \"10px 20px\" : \"12px 30px\",\n borderRadius: \"8px\",\n border: `2px solid ${category === \"keyboard\" ? colors.accentCyan : colors.borderGold}`,\n background:\n category === \"keyboard\"\n ? hexToRgbaString(theme.colors.PRIMARY_CYAN, 0.3)\n : hexToRgbaString(theme.colors.UI_BACKGROUND_DARK, 0.8),\n color:\n category === \"keyboard\"\n ? colors.accentCyan\n : colors.textSecondary,\n fontFamily: FONT_FAMILY.KOREAN,\n fontSize: isMobile ? \"14px\" : \"16px\",\n fontWeight: \"bold\",\n cursor: \"pointer\",\n transition: \"all 0.2s ease\",\n }}\n data-testid=\"keyboard-mode-button\"\n onMouseEnter={(e) =>\n handleModeButtonEnter(e, category === \"keyboard\")\n }\n onMouseLeave={(e) =>\n handleModeButtonLeave(e, category === \"keyboard\")\n }\n >\n ⌨️ 키보드 | Keyboard\n </button>\n <button\n onClick={() => {\n audio.playSFX(\"menu_select\");\n setCategory(\"gamepad\");\n }}\n style={{\n padding: isMobile ? \"10px 20px\" : \"12px 30px\",\n borderRadius: \"8px\",\n border: `2px solid ${category === \"gamepad\" ? colors.accentCyan : colors.borderGold}`,\n background:\n category === \"gamepad\"\n ? hexToRgbaString(theme.colors.PRIMARY_CYAN, 0.3)\n : hexToRgbaString(theme.colors.UI_BACKGROUND_DARK, 0.8),\n color:\n category === \"gamepad\"\n ? colors.accentCyan\n : colors.textSecondary,\n fontFamily: FONT_FAMILY.KOREAN,\n fontSize: isMobile ? \"14px\" : \"16px\",\n fontWeight: \"bold\",\n cursor: \"pointer\",\n transition: \"all 0.2s ease\",\n }}\n data-testid=\"gamepad-mode-button\"\n onMouseEnter={(e) =>\n handleModeButtonEnter(e, category === \"gamepad\")\n }\n onMouseLeave={(e) =>\n handleModeButtonLeave(e, category === \"gamepad\")\n }\n >\n 🎮 게임패드 | Gamepad\n </button>\n </div>\n\n {/* Control Category Tabs */}\n <div\n style={{\n background: colors.headerBg,\n borderBottom: `2px solid ${colors.borderCyan}`,\n }}\n >\n <ControlCategoryTabs\n selectedTab={selectedTab}\n onTabChange={(tab) => {\n audio.playSFX(\"menu_select\");\n setSelectedTab(tab);\n }}\n isMobile={isMobile}\n />\n </div>\n\n {/* Content Area - Scrollable */}\n <div\n style={{\n flex: 1,\n overflowY: \"auto\",\n overflowX: \"hidden\",\n padding: `${layoutConstants.padding}px`,\n // Custom scrollbar styling for Korean aesthetic (Firefox)\n scrollbarWidth: \"thin\",\n scrollbarColor: `${colors.accentGold} ${colors.sectionBg}`,\n }}\n className=\"korean-scrollbar\"\n data-testid=\"controls-content\"\n >\n {/* Control Bindings Display */}\n <ControlBindingsOverlayHtml\n selectedTab={selectedTab}\n isMobile={isMobile}\n />\n\n {/* Legacy Content - Keeping for backward compatibility and additional info */}\n {/* Trigram Stances Section - Additional context beyond key bindings */}\n <div\n style={{\n marginTop: `${layoutConstants.sectionSpacing}px`,\n marginBottom: `${layoutConstants.sectionSpacing}px`,\n background: colors.sectionBg,\n borderRadius: \"12px\",\n border: `2px solid ${colors.borderGold}`,\n padding: \"20px\",\n }}\n data-testid=\"trigram-controls\"\n >\n <h2\n style={{\n fontSize: isMobile ? \"18px\" : \"22px\",\n fontWeight: \"bold\",\n color: colors.accentGold,\n margin: \"0 0 10px 0\",\n }}\n >\n 팔괘 무술 자세 - Eight Trigram Combat Stances\n </h2>\n <p\n style={{\n fontSize: isMobile ? \"11px\" : \"14px\",\n color: colors.accentCyan,\n fontStyle: \"italic\",\n margin: \"0 0 20px 0\",\n }}\n >\n 🗡️ 전통 한국 무예의 8가지 핵심 자세 | 8 Core Stances of\n Traditional Korean Martial Arts 🗡️\n </p>\n\n {/* Stance Controls Grid */}\n <div\n style={{\n display: \"grid\",\n gridTemplateColumns: `repeat(${buttonsPerRow}, 1fr)`,\n gap: \"15px\",\n }}\n data-testid=\"stance-controls-grid\"\n >\n {stanceControls.map(([key, value]) => (\n <div\n key={key}\n style={{\n background: `linear-gradient(135deg, ${hexToRgbaString(\n theme.colors.UI_BACKGROUND_MEDIUM,\n 0.9,\n )}, ${hexToRgbaString(\n theme.colors.UI_BACKGROUND_LIGHT,\n 0.9,\n )})`,\n borderRadius: \"10px\",\n border: `2px solid ${colors.borderGold}`,\n padding: \"12px\",\n position: \"relative\",\n minHeight: `${buttonHeight}px`,\n }}\n data-testid={`stance-control-${key}`}\n >\n {/* Key Badge */}\n <div\n style={{\n position: \"absolute\",\n top: \"8px\",\n left: \"8px\",\n background: colors.accentGold,\n borderRadius: \"6px\",\n width: \"25px\",\n height: \"25px\",\n display: \"flex\",\n alignItems: \"center\",\n justifyContent: \"center\",\n fontSize: isMobile ? \"14px\" : \"16px\",\n fontWeight: \"bold\",\n color: colors.koreanBlack,\n }}\n >\n {key}\n </div>\n\n {/* Trigram Symbol */}\n <div\n style={{\n position: \"absolute\",\n top: \"8px\",\n right: \"8px\",\n fontSize: isMobile ? \"18px\" : \"22px\",\n color: colors.accentGold,\n fontWeight: \"bold\",\n }}\n >\n {value.symbol}\n </div>\n\n {/* Stance Name */}\n <div style={{ marginTop: \"35px\" }}>\n <div\n style={{\n fontSize: isMobile ? \"12px\" : \"14px\",\n fontWeight: \"bold\",\n color: colors.textPrimary,\n }}\n >\n {value.korean}\n </div>\n <div\n style={{\n fontSize: isMobile ? \"9px\" : \"10px\",\n color: colors.textSecondary,\n fontStyle: \"italic\",\n }}\n >\n {value.english}\n </div>\n </div>\n\n {/* Technique */}\n <div style={{ marginTop: \"8px\" }}>\n <div\n style={{\n fontSize: isMobile ? \"9px\" : \"10px\",\n fontWeight: \"bold\",\n color: colors.accentCyan,\n }}\n >\n 🥋 {value.technique.korean}\n </div>\n <div\n style={{\n fontSize: isMobile ? \"8px\" : \"9px\",\n color: colors.accentCyan,\n fontStyle: \"italic\",\n }}\n >\n {value.technique.english}\n </div>\n </div>\n\n {/* Combat Focus */}\n <div style={{ marginTop: \"6px\" }}>\n <div\n style={{\n fontSize: isMobile ? \"8px\" : \"9px\",\n fontWeight: \"bold\",\n color: `#${theme.colors.SECONDARY_MAGENTA.toString(\n 16,\n ).padStart(6, \"0\")}`,\n }}\n >\n ⚔️ {value.combatFocus.korean}\n </div>\n <div\n style={{\n fontSize: isMobile ? \"7px\" : \"8px\",\n color: `#${theme.colors.SECONDARY_MAGENTA.toString(\n 16,\n ).padStart(6, \"0\")}`,\n fontStyle: \"italic\",\n }}\n >\n {value.combatFocus.english}\n </div>\n </div>\n\n {/* Combat Effects */}\n <div\n style={{\n marginTop: \"6px\",\n fontSize: isMobile ? \"7px\" : \"8px\",\n fontWeight: \"bold\",\n color: `#${theme.colors.NEGATIVE_RED.toString(\n 16,\n ).padStart(6, \"0\")}`,\n }}\n >\n 💥 {value.combatEffects.korean}\n </div>\n\n {/* Effectiveness Indicator */}\n <div\n style={{\n position: \"absolute\",\n bottom: \"8px\",\n right: \"8px\",\n fontSize: isMobile ? \"7px\" : \"8px\",\n fontWeight: \"bold\",\n color: `#${theme.colors.KOREAN_RED.toString(\n 16,\n ).padStart(6, \"0\")}`,\n }}\n >\n 급소술\n </div>\n </div>\n ))}\n </div>\n </div>\n\n {/* Combat Controls Section */}\n <div\n style={{\n marginBottom: `${layoutConstants.sectionSpacing}px`,\n background: colors.sectionBg,\n borderRadius: \"12px\",\n border: `2px solid ${colors.borderCyan}`,\n padding: \"20px\",\n }}\n data-testid=\"combat-controls\"\n >\n <h2\n style={{\n fontSize: isMobile ? \"18px\" : \"22px\",\n fontWeight: \"bold\",\n color: colors.accentCyan,\n margin: \"0 0 20px 0\",\n }}\n >\n 실전 격투 조작 - Combat Actions\n </h2>\n\n {/* Combat Controls List */}\n <div\n style={{\n display: \"flex\",\n flexDirection: \"column\",\n gap: \"10px\",\n }}\n data-testid=\"combat-controls-list\"\n >\n {combatControls.map(([key, description]) => (\n <div\n key={key}\n style={{\n background: `linear-gradient(90deg, ${hexToRgbaString(\n theme.colors.UI_BACKGROUND_LIGHT,\n 0.9,\n )}, ${hexToRgbaString(\n theme.colors.UI_BACKGROUND_MEDIUM,\n 0.9,\n )})`,\n borderRadius: \"8px\",\n border: `1px solid ${colors.borderCyan}`,\n padding: \"12px\",\n display: \"flex\",\n alignItems: \"center\",\n gap: \"15px\",\n }}\n data-testid={`combat-control-${key}`}\n >\n {/* Key Badge */}\n <div\n style={{\n background: hexToRgbaString(\n theme.colors.ACCENT_CYAN,\n 0.3,\n ),\n borderRadius: \"6px\",\n padding: \"4px 12px\",\n fontSize: isMobile ? \"14px\" : \"16px\",\n fontWeight: \"bold\",\n color: colors.accentGold,\n minWidth: \"60px\",\n textAlign: \"center\",\n }}\n >\n {key}\n </div>\n\n {/* Description */}\n <div style={{ flex: 1 }}>\n <div\n style={{\n fontSize: isMobile ? \"11px\" : \"13px\",\n fontWeight: \"bold\",\n color: colors.textPrimary,\n }}\n >\n {description.korean}\n </div>\n <div\n style={{\n fontSize: isMobile ? \"9px\" : \"11px\",\n color: colors.textSecondary,\n fontStyle: \"italic\",\n }}\n >\n {description.english}\n </div>\n </div>\n </div>\n ))}\n </div>\n </div>\n\n {/* Movement Controls Section */}\n <div\n style={{\n marginBottom: `${layoutConstants.sectionSpacing}px`,\n background: colors.sectionBg,\n borderRadius: \"12px\",\n border: `2px solid ${hexToRgbaString(\n theme.colors.SECONDARY_MAGENTA,\n 0.5,\n )}`,\n padding: \"20px\",\n }}\n data-testid=\"movement-controls\"\n >\n <h2\n style={{\n fontSize: isMobile ? \"18px\" : \"22px\",\n fontWeight: \"bold\",\n color: `#${theme.colors.SECONDARY_MAGENTA.toString(\n 16,\n ).padStart(6, \"0\")}`,\n margin: \"0 0 20px 0\",\n }}\n >\n 이동 조작 - Movement Controls\n </h2>\n\n {/* Movement Keys */}\n <div style={{ marginBottom: \"15px\" }}>\n <div\n style={{\n fontSize: isMobile ? \"12px\" : \"14px\",\n fontWeight: \"bold\",\n color: colors.accentGold,\n marginBottom: \"10px\",\n }}\n >\n WASD 또는 방향키 | WASD or Arrow Keys\n </div>\n <div\n style={{\n display: \"grid\",\n gridTemplateColumns: isMobile\n ? \"1fr 1fr\"\n : \"1fr 1fr 1fr 1fr\",\n gap: \"8px\",\n }}\n >\n {[\n { key: \"W/↑\", korean: \"전진\", english: \"Forward\" },\n { key: \"S/↓\", korean: \"후퇴\", english: \"Backward\" },\n { key: \"A/←\", korean: \"좌\", english: \"Left\" },\n { key: \"D/→\", korean: \"우\", english: \"Right\" },\n ].map((move) => (\n <div\n key={move.key}\n style={{\n background: hexToRgbaString(\n theme.colors.UI_BACKGROUND_LIGHT,\n 0.8,\n ),\n borderRadius: \"6px\",\n padding: \"8px\",\n textAlign: \"center\",\n }}\n >\n <div\n style={{\n fontSize: isMobile ? \"11px\" : \"13px\",\n fontWeight: \"bold\",\n color: colors.accentCyan,\n }}\n >\n {move.key}\n </div>\n <div\n style={{\n fontSize: isMobile ? \"8px\" : \"9px\",\n color: colors.textSecondary,\n }}\n >\n {move.korean} | {move.english}\n </div>\n </div>\n ))}\n </div>\n </div>\n </div>\n\n {/* Advanced Footwork Section */}\n <div\n style={{\n marginBottom: `${layoutConstants.sectionSpacing}px`,\n background: colors.sectionBg,\n borderRadius: \"12px\",\n border: `2px solid ${hexToRgbaString(\n theme.colors.PRIMARY_CYAN,\n 0.5,\n )}`,\n padding: \"20px\",\n }}\n data-testid=\"advanced-footwork\"\n >\n <h2\n style={{\n fontSize: isMobile ? \"18px\" : \"22px\",\n fontWeight: \"bold\",\n color: colors.accentCyan,\n margin: \"0 0 10px 0\",\n }}\n >\n 고급 보법 - Advanced Footwork\n </h2>\n <p\n style={{\n fontSize: isMobile ? \"10px\" : \"12px\",\n color: colors.textSecondary,\n fontStyle: \"italic\",\n margin: \"0 0 15px 0\",\n }}\n >\n 🥋 전통 한국 무예 발놀림 기법 | Traditional Korean martial arts\n footwork techniques\n </p>\n\n {/* Tactical Steps */}\n <div style={{ marginBottom: \"20px\" }}>\n <h3\n style={{\n fontSize: isMobile ? \"14px\" : \"16px\",\n fontWeight: \"bold\",\n color: colors.accentGold,\n marginBottom: \"10px\",\n }}\n >\n ✅ 전술보법 (Shift + WASD) | Tactical Steps - IMPLEMENTED\n </h3>\n <p\n style={{\n fontSize: isMobile ? \"9px\" : \"10px\",\n color: colors.textSecondary,\n marginBottom: \"10px\",\n fontStyle: \"italic\",\n }}\n >\n Precise 30cm repositioning • 300ms duration •\n Non-interruptible • 5 stamina cost\n </p>\n <div\n style={{\n display: \"grid\",\n gridTemplateColumns: isMobile\n ? \"1fr 1fr\"\n : \"repeat(4, 1fr)\",\n gap: \"8px\",\n }}\n >\n {[\n {\n key: \"Shift+W\",\n korean: \"전진보법\",\n english: \"Forward Step\",\n },\n {\n key: \"Shift+S\",\n korean: \"후퇴보법\",\n english: \"Retreat Step\",\n },\n {\n key: \"Shift+A\",\n korean: \"좌측면보법\",\n english: \"Left Step\",\n },\n {\n key: \"Shift+D\",\n korean: \"우측면보법\",\n english: \"Right Step\",\n },\n {\n key: \"Shift+W+A\",\n korean: \"전좌측보법\",\n english: \"Forward-Left\",\n },\n {\n key: \"Shift+W+D\",\n korean: \"전우측보법\",\n english: \"Forward-Right\",\n },\n {\n key: \"Shift+S+A\",\n korean: \"후좌측보법\",\n english: \"Back-Left\",\n },\n {\n key: \"Shift+S+D\",\n korean: \"후우측보법\",\n english: \"Back-Right\",\n },\n ].map((step) => (\n <div\n key={step.key}\n style={{\n background: hexToRgbaString(\n theme.colors.UI_BACKGROUND_LIGHT,\n 0.8,\n ),\n borderRadius: \"6px\",\n border: `1px solid ${colors.borderCyan}`,\n padding: \"8px\",\n textAlign: \"center\",\n }}\n >\n <div\n style={{\n fontSize: isMobile ? \"9px\" : \"10px\",\n fontWeight: \"bold\",\n color: colors.accentGold,\n marginBottom: \"4px\",\n }}\n >\n {step.key}\n </div>\n <div\n style={{\n fontSize: isMobile ? \"7px\" : \"8px\",\n color: colors.textPrimary,\n }}\n >\n {step.korean}\n </div>\n <div\n style={{\n fontSize: isMobile ? \"6px\" : \"7px\",\n color: colors.textSecondary,\n fontStyle: \"italic\",\n }}\n >\n {step.english}\n </div>\n </div>\n ))}\n </div>\n </div>\n\n {/* Footwork Patterns */}\n <div>\n <h3\n style={{\n fontSize: isMobile ? \"14px\" : \"16px\",\n fontWeight: \"bold\",\n color: colors.accentGold,\n marginBottom: \"10px\",\n }}\n >\n 보법 패턴 (Ctrl + WASD) | Footwork Patterns\n </h3>\n\n {/* Circular Steps */}\n <div style={{ marginBottom: \"12px\" }}>\n <div\n style={{\n fontSize: isMobile ? \"11px\" : \"12px\",\n fontWeight: \"bold\",\n color: colors.textPrimary,\n marginBottom: \"6px\",\n }}\n >\n ✅ 원형보 (Wonhyeongbo) | Circular Step - IMPLEMENTED\n </div>\n <p\n style={{\n fontSize: isMobile ? \"8px\" : \"9px\",\n color: colors.textSecondary,\n marginBottom: \"8px\",\n fontStyle: \"italic\",\n }}\n >\n Lateral movement while maintaining guard • 30cm distance •\n 300ms\n </p>\n <div style={{ display: \"flex\", gap: \"8px\" }}>\n {[\n {\n key: \"Ctrl+A\",\n korean: \"원형보 좌\",\n english: \"Circular Left\",\n },\n {\n key: \"Ctrl+D\",\n korean: \"원형보 우\",\n english: \"Circular Right\",\n },\n ].map((move) => (\n <div\n key={move.key}\n style={{\n flex: 1,\n background: hexToRgbaString(\n theme.colors.ACCENT_CYAN,\n 0.2,\n ),\n borderRadius: \"6px\",\n border: `1px solid ${colors.borderCyan}`,\n padding: \"8px\",\n textAlign: \"center\",\n }}\n >\n <div\n style={{\n fontSize: isMobile ? \"10px\" : \"11px\",\n fontWeight: \"bold\",\n color: colors.accentGold,\n }}\n >\n {move.key}\n </div>\n <div\n style={{\n fontSize: isMobile ? \"8px\" : \"9px\",\n color: colors.textPrimary,\n }}\n >\n {move.korean}\n </div>\n <div\n style={{\n fontSize: isMobile ? \"7px\" : \"8px\",\n color: colors.textSecondary,\n fontStyle: \"italic\",\n }}\n >\n {move.english}\n </div>\n </div>\n ))}\n </div>\n </div>\n\n {/* Slide Steps */}\n <div style={{ marginBottom: \"12px\" }}>\n <div\n style={{\n fontSize: isMobile ? \"11px\" : \"12px\",\n fontWeight: \"bold\",\n color: colors.textPrimary,\n marginBottom: \"6px\",\n }}\n >\n ✅ 미끄럼보 (Mikkeureombo) | Slide Step - IMPLEMENTED\n </div>\n <p\n style={{\n fontSize: isMobile ? \"8px\" : \"9px\",\n color: colors.textSecondary,\n marginBottom: \"8px\",\n fontStyle: \"italic\",\n }}\n >\n Both feet move together • 30cm distance • 200ms (faster!)\n </p>\n <div style={{ display: \"flex\", gap: \"8px\" }}>\n {[\n {\n key: \"Ctrl+W\",\n korean: \"미끄럼보 전\",\n english: \"Slide Forward\",\n },\n {\n key: \"Ctrl+S\",\n korean: \"미끄럼보 후\",\n english: \"Slide Back\",\n },\n ].map((move) => (\n <div\n key={move.key}\n style={{\n flex: 1,\n background: hexToRgbaString(\n theme.colors.ACCENT_CYAN,\n 0.2,\n ),\n borderRadius: \"6px\",\n border: `1px solid ${colors.borderCyan}`,\n padding: \"8px\",\n textAlign: \"center\",\n }}\n >\n <div\n style={{\n fontSize: isMobile ? \"10px\" : \"11px\",\n fontWeight: \"bold\",\n color: colors.accentGold,\n }}\n >\n {move.key}\n </div>\n <div\n style={{\n fontSize: isMobile ? \"8px\" : \"9px\",\n color: colors.textPrimary,\n }}\n >\n {move.korean}\n </div>\n <div\n style={{\n fontSize: isMobile ? \"7px\" : \"8px\",\n color: colors.textSecondary,\n fontStyle: \"italic\",\n }}\n >\n {move.english}\n </div>\n </div>\n ))}\n </div>\n </div>\n\n {/* Pending Footwork */}\n <div\n style={{\n marginTop: \"15px\",\n padding: \"10px\",\n background: hexToRgbaString(theme.colors.ACCENT_CYAN, 0.2),\n borderRadius: \"6px\",\n border: `1px solid ${colors.borderCyan}`,\n }}\n >\n <div\n style={{\n fontSize: isMobile ? \"10px\" : \"11px\",\n fontWeight: \"bold\",\n color: colors.accentGold,\n marginBottom: \"6px\",\n }}\n >\n ✅ 추가 보법 (Advanced Patterns) - IMPLEMENTED\n </div>\n <p\n style={{\n fontSize: isMobile ? \"8px\" : \"9px\",\n color: colors.textPrimary,\n margin: \"0 0 6px 0\",\n }}\n >\n <strong>축족회전 (Chukjok Hoejeon) | Pivot</strong>:\n Shift+Ctrl+A (left) / Shift+Ctrl+D (right) • 90° rotation on\n planted foot • 250ms\n </p>\n <p\n style={{\n fontSize: isMobile ? \"8px\" : \"9px\",\n color: colors.textPrimary,\n margin: 0,\n }}\n >\n <strong>섞음보 (Seokkeumbo) | Shuffle</strong>: Shift+Ctrl+W\n or Shift+Ctrl+S • 15cm micro-adjustment • 100ms\n </p>\n </div>\n </div>\n </div>\n\n {/* Stance Side Switch Section */}\n <div\n style={{\n marginBottom: `${layoutConstants.sectionSpacing}px`,\n background: colors.sectionBg,\n borderRadius: \"12px\",\n border: `2px solid ${hexToRgbaString(\n theme.colors.ACCENT_GOLD,\n 0.5,\n )}`,\n padding: \"20px\",\n }}\n data-testid=\"stance-side-switch\"\n >\n <h2\n style={{\n fontSize: isMobile ? \"18px\" : \"22px\",\n fontWeight: \"bold\",\n color: colors.accentGold,\n margin: \"0 0 10px 0\",\n }}\n >\n 자세 발 바꿈 - Stance Side Switch\n </h2>\n <p\n style={{\n fontSize: isMobile ? \"10px\" : \"12px\",\n color: colors.textSecondary,\n fontStyle: \"italic\",\n margin: \"0 0 15px 0\",\n }}\n >\n ✅ 전방 발 전환 | Switch front foot position - IMPLEMENTED\n </p>\n\n <div\n style={{\n background: hexToRgbaString(theme.colors.ACCENT_GOLD, 0.2),\n borderRadius: \"6px\",\n border: `1px solid ${colors.borderGold}`,\n padding: \"12px\",\n textAlign: \"center\",\n }}\n >\n <div\n style={{\n fontSize: isMobile ? \"16px\" : \"18px\",\n fontWeight: \"bold\",\n color: colors.accentGold,\n marginBottom: \"8px\",\n }}\n >\n H\n </div>\n <div\n style={{\n fontSize: isMobile ? \"10px\" : \"11px\",\n color: colors.textPrimary,\n marginBottom: \"4px\",\n }}\n >\n 발 바꿈 (Bal Bakkum)\n </div>\n <div\n style={{\n fontSize: isMobile ? \"9px\" : \"10px\",\n color: colors.textSecondary,\n fontStyle: \"italic\",\n }}\n >\n Switch Front Foot\n </div>\n <div\n style={{\n fontSize: isMobile ? \"7px\" : \"8px\",\n color: colors.textSecondary,\n marginTop: \"6px\",\n fontStyle: \"italic\",\n }}\n >\n Mirrors your stance (left ↔ right) • 400ms duration\n </div>\n </div>\n </div>\n\n {/* Technique Controls Section */}\n <div\n style={{\n marginBottom: `${layoutConstants.sectionSpacing}px`,\n background: colors.sectionBg,\n borderRadius: \"12px\",\n border: `2px solid ${hexToRgbaString(\n theme.colors.ACCENT_GOLD,\n 0.6,\n )}`,\n padding: \"20px\",\n }}\n data-testid=\"technique-controls\"\n >\n <h2\n style={{\n fontSize: isMobile ? \"18px\" : \"22px\",\n fontWeight: \"bold\",\n color: colors.accentGold,\n margin: \"0 0 10px 0\",\n }}\n >\n 기술 실행 - Technique Execution\n </h2>\n <p\n style={{\n fontSize: isMobile ? \"10px\" : \"12px\",\n color: colors.textSecondary,\n fontStyle: \"italic\",\n margin: \"0 0 15px 0\",\n }}\n >\n ⚡ 원형별 고유 기술 (최대 10개) | Archetype-specific techniques\n (up to 10)\n </p>\n\n {/* Technique Keys Grid */}\n <div\n style={{\n display: \"grid\",\n gridTemplateColumns: isMobile\n ? \"repeat(5, 1fr)\"\n : \"repeat(10, 1fr)\",\n gap: isMobile ? \"6px\" : \"8px\",\n }}\n >\n {[\"Q\", \"E\", \"R\", \"T\", \"Y\", \"F\", \"G\", \"Z\", \"X\", \"C\"].map(\n (key, index) => (\n <div\n key={key}\n style={{\n background: `linear-gradient(135deg, ${hexToRgbaString(\n theme.colors.ACCENT_GOLD,\n 0.3,\n )}, ${hexToRgbaString(theme.colors.ACCENT_GOLD, 0.1)})`,\n borderRadius: \"6px\",\n border: `2px solid ${colors.borderGold}`,\n padding: isMobile ? \"8px 4px\" : \"10px 6px\",\n textAlign: \"center\",\n }}\n >\n <div\n style={{\n fontSize: isMobile ? \"14px\" : \"16px\",\n fontWeight: \"bold\",\n color: colors.accentGold,\n }}\n >\n {key}\n </div>\n <div\n style={{\n fontSize: isMobile ? \"7px\" : \"8px\",\n color: colors.textSecondary,\n marginTop: \"2px\",\n }}\n >\n 기술 {index + 1}\n </div>\n </div>\n ),\n )}\n </div>\n\n {/* Technique Notes */}\n <div\n style={{\n marginTop: \"15px\",\n padding: \"10px\",\n background: hexToRgbaString(\n theme.colors.UI_BACKGROUND_DARK,\n 0.6,\n ),\n borderRadius: \"6px\",\n fontSize: isMobile ? \"9px\" : \"10px\",\n color: colors.textSecondary,\n fontStyle: \"italic\",\n }}\n >\n 💡 <strong>Tip</strong>: Keys positioned around WASD for easy\n access without interfering with movement. Each archetype has\n unique techniques.\n </div>\n </div>\n\n {/* Special Features Section */}\n <div\n style={{\n marginBottom: `${layoutConstants.sectionSpacing}px`,\n background: colors.sectionBg,\n borderRadius: \"12px\",\n border: `2px solid ${colors.borderRed}`,\n padding: \"20px\",\n }}\n data-testid=\"special-features\"\n >\n <h2\n style={{\n fontSize: isMobile ? \"18px\" : \"22px\",\n fontWeight: \"bold\",\n color: `#${theme.colors.KOREAN_RED.toString(16).padStart(\n 6,\n \"0\",\n )}`,\n margin: \"0 0 20px 0\",\n }}\n >\n 특수 기능 - Special Features\n </h2>\n\n {/* Special Keys List */}\n <div\n style={{\n display: \"flex\",\n flexDirection: \"column\",\n gap: \"10px\",\n }}\n >\n {[\n {\n key: \"V\",\n korean: \"급소 표시 전환\",\n english: \"Toggle vital points overlay (70 points)\",\n },\n {\n key: \"B\",\n korean: \"방어 자세\",\n english: \"Defensive guard position\",\n },\n {\n key: \"F1\",\n korean: \"조작법 힌트\",\n english: \"Show control hints\",\n },\n {\n key: \"ESC / M\",\n korean: \"일시정지 / 메뉴\",\n english: \"Pause menu / Return to menu\",\n },\n ].map((special) => (\n <div\n key={special.key}\n style={{\n background: hexToRgbaString(\n theme.colors.UI_BACKGROUND_LIGHT,\n 0.8,\n ),\n borderRadius: \"8px\",\n border: `1px solid ${colors.borderRed}`,\n padding: \"10px\",\n display: \"flex\",\n alignItems: \"center\",\n gap: \"12px\",\n }}\n >\n <div\n style={{\n background: hexToRgbaString(\n theme.colors.KOREAN_RED,\n 0.3,\n ),\n borderRadius: \"6px\",\n padding: \"4px 10px\",\n fontSize: isMobile ? \"12px\" : \"14px\",\n fontWeight: \"bold\",\n color: colors.accentGold,\n minWidth: \"50px\",\n textAlign: \"center\",\n }}\n >\n {special.key}\n </div>\n <div style={{ flex: 1 }}>\n <div\n style={{\n fontSize: isMobile ? \"10px\" : \"12px\",\n fontWeight: \"bold\",\n color: colors.textPrimary,\n }}\n >\n {special.korean}\n </div>\n <div\n style={{\n fontSize: isMobile ? \"8px\" : \"10px\",\n color: colors.textSecondary,\n fontStyle: \"italic\",\n }}\n >\n {special.english}\n </div>\n </div>\n </div>\n ))}\n </div>\n </div>\n </div>\n\n {/* Interactive Control Demo - Shows recently pressed keys */}\n <InteractiveControlDemo\n pressedKeys={pressedKeys}\n isMobile={isMobile}\n />\n\n {/* Footer */}\n <div\n style={{\n height: `${layoutConstants.footerHeight}px`,\n background: colors.headerBg,\n borderTop: `3px solid ${colors.borderRed}`,\n display: \"flex\",\n alignItems: \"center\",\n justifyContent: \"space-between\",\n padding: `0 ${layoutConstants.padding}px`,\n }}\n data-testid=\"controls-footer\"\n >\n {/* Philosophy Text */}\n <div\n style={{\n fontSize: isMobile ? \"10px\" : \"12px\",\n color: colors.accentGold,\n fontStyle: \"italic\",\n }}\n >\n 🥋 흑괘의 길을 걸어라 | Walk the Path of the Black Trigram 🥋\n </div>\n\n {/* Back Button */}\n <BackButton\n onClick={handleBackClick}\n korean=\"무도장 복귀\"\n english=\"Return to Dojang\"\n isMobile={isMobile}\n testId=\"controls-back-button\"\n />\n\n {/* Keyboard Hint */}\n <div\n style={{\n background: hexToRgbaString(\n theme.colors.UI_BACKGROUND_MEDIUM,\n 0.9,\n ),\n borderRadius: \"6px\",\n padding: \"8px 12px\",\n fontSize: isMobile ? \"11px\" : \"12px\",\n fontWeight: \"bold\",\n color: `#${theme.colors.SECONDARY_MAGENTA.toString(16).padStart(\n 6,\n \"0\",\n )}`,\n border: `1px solid ${colors.borderGold}`,\n }}\n data-testid=\"keyboard-shortcuts\"\n >\n ESC | M\n </div>\n </div>\n\n {/* Footer Instruction */}\n <div\n style={{\n textAlign: \"center\",\n padding: \"10px\",\n fontSize: isMobile ? \"10px\" : \"12px\",\n color: colors.textSecondary,\n fontStyle: \"italic\",\n background: hexToRgbaString(theme.colors.UI_BACKGROUND_DARK, 0.7),\n }}\n >\n ESC 또는 M 키로 메뉴로 돌아가기 - Press ESC or M to return to menu\n </div>\n </div>\n </div>\n </div>\n );\n};\n\nexport default ControlsScreen3D;\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"ControlsScreen3D.js","names":[],"sources":["../../../../src/components/screens/controls/ControlsScreen3D.tsx"],"sourcesContent":["// UI renders outside Canvas in absolute-positioned div - no Html needed\nimport { Canvas } from \"@react-three/fiber\";\nimport React, { useCallback, useEffect, useMemo } from \"react\";\nimport { useAudio } from \"../../../audio/AudioProvider\";\nimport { useWebGLContextLossHandler } from \"../../../hooks/useWebGLContextLossHandler\";\nimport { useWindowSize } from \"../../../hooks/useWindowSize\";\nimport { COMBAT_CONTROLS } from \"../../../systems\";\nimport { FONT_FAMILY } from \"@/types/constants\";\nimport { Z_INDEX } from \"../../../types/LayoutTypes\";\nimport { hexToRgbaString } from \"../../../utils/colorUtils\";\nimport { shouldUseMobileControls } from \"../../../utils/deviceDetection\";\nimport { getLayoutConstants } from \"../../../utils/responsiveLayoutHelpers\";\nimport { useKoreanTheme } from \"../../shared/base/useKoreanTheme\";\nimport { BackgroundScene3D } from \"../../shared/three\";\nimport { BackButton } from \"../../shared/ui/BackButton\";\nimport { VolumeControl } from \"../../shared/ui/VolumeControl\";\nimport { ControlBindingsOverlayHtml } from \"./components/ControlBindingsOverlayHtml\";\nimport { ControlCategoryTabs } from \"./components/ControlCategoryTabsOverlayHtml\";\nimport { GamepadVisualization3D } from \"./components/GamepadVisualization3D\";\nimport { InteractiveControlDemo } from \"./components/InteractiveControlDemoOverlayHtml\";\nimport { VisualKeyboard3D } from \"./components/VisualKeyboard3D\";\nimport { useControlsState } from \"./hooks/useControlsState\";\n\nexport interface ControlsScreen3DProps {\n readonly onReturnToMenu: () => void;\n readonly width?: number;\n readonly height?: number;\n}\n\n/**\n * Three.js-based ControlsScreen Component\n */\nexport const ControlsScreen3D: React.FC<ControlsScreen3DProps> = ({\n onReturnToMenu,\n width: propWidth,\n height: propHeight,\n}) => {\n // UI now renders outside Canvas - no mount state needed\n\n // Use controls state hook for keyboard/gamepad tracking\n const { pressedKeys, category, selectedTab, setCategory, setSelectedTab } =\n useControlsState();\n\n // Handle WebGL context loss and restoration (for 3D background only)\n useWebGLContextLossHandler({\n onContextLost: () => {\n console.warn(\"⚠️ WebGL context lost in ControlsScreen\");\n },\n onContextRestored: () => {\n console.log(\"✓ WebGL context restored in ControlsScreen\");\n },\n autoRestore: true,\n });\n\n const audio = useAudio();\n const { width, height } = useWindowSize();\n\n // Use prop dimensions if provided, otherwise use window size\n const screenWidth = propWidth ?? width;\n const screenHeight = propHeight ?? height;\n\n // Responsive layout calculations with large desktop support\n // Use device detection instead of width-only breakpoint to correctly identify high-res mobile devices\n const isMobile = shouldUseMobileControls();\n // Only use width for tablet/desktop distinction when NOT mobile\n const isTablet = useMemo(\n () => !isMobile && screenWidth >= 768 && screenWidth < 1024,\n [isMobile, screenWidth],\n );\n const isLargeDesktop = useMemo(\n () => !isMobile && screenWidth >= 1920,\n [isMobile, screenWidth],\n ); // 4K/2K displays\n\n // Use centralized responsive layout helper for consistent scaling\n const layoutConstants = useMemo(\n () => getLayoutConstants(screenWidth),\n [screenWidth],\n );\n\n // Use Korean theme hook for consistent theming\n const theme = useKoreanTheme({\n variant: \"primary\",\n size: \"md\",\n isMobile,\n });\n\n // Memoize scrollbar style to prevent re-creating style tag on every render\n const scrollbarStyle = useMemo(\n () => ({\n __html: `\n .korean-scrollbar::-webkit-scrollbar {\n width: 12px !important;\n display: block !important;\n }\n .korean-scrollbar::-webkit-scrollbar-track {\n background: ${hexToRgbaString(theme.colors.UI_BACKGROUND_DARK, 0.8)};\n border-radius: 6px;\n }\n .korean-scrollbar::-webkit-scrollbar-thumb {\n background: ${hexToRgbaString(theme.colors.ACCENT_GOLD, 1)};\n border-radius: 6px;\n border: 2px solid ${hexToRgbaString(theme.colors.UI_BACKGROUND_DARK, 0.8)};\n }\n .korean-scrollbar::-webkit-scrollbar-thumb:hover {\n background: ${hexToRgbaString(theme.colors.PRIMARY_CYAN, 1)};\n }\n `,\n }),\n [theme],\n );\n\n // Memoize colors from theme for performance\n const colors = useMemo(\n () => ({\n background: hexToRgbaString(theme.colors.UI_BACKGROUND_DARK, 0.95),\n headerBg: hexToRgbaString(theme.colors.UI_BACKGROUND_DARK, 0.9),\n sectionBg: hexToRgbaString(theme.colors.UI_BACKGROUND_DARK, 0.8),\n borderGold: hexToRgbaString(theme.colors.ACCENT_GOLD, 0.6),\n borderCyan: hexToRgbaString(theme.colors.PRIMARY_CYAN, 0.5),\n borderRed: hexToRgbaString(theme.colors.KOREAN_RED, 0.8),\n textPrimary: `#${theme.colors.TEXT_PRIMARY.toString(16).padStart(6, \"0\")}`,\n textSecondary: `#${theme.colors.TEXT_SECONDARY.toString(16).padStart(6, \"0\")}`,\n accentGold: `#${theme.colors.ACCENT_GOLD.toString(16).padStart(6, \"0\")}`,\n accentCyan: `#${theme.colors.PRIMARY_CYAN.toString(16).padStart(6, \"0\")}`,\n koreanBlack: `#${theme.colors.KOREAN_BLACK.toString(16).padStart(6, \"0\")}`,\n }),\n [theme],\n );\n\n // Enhanced keyboard handling for screen-level navigation\n useEffect(() => {\n const handleKeyDown = (event: KeyboardEvent) => {\n if (event.key === \"Escape\" || event.key.toLowerCase() === \"m\") {\n event.preventDefault();\n audio.playSFX(\"menu_back\");\n onReturnToMenu();\n }\n };\n\n window.addEventListener(\"keydown\", handleKeyDown, { passive: false });\n return () => window.removeEventListener(\"keydown\", handleKeyDown);\n }, [onReturnToMenu, audio]);\n\n // Handle back button click\n const handleBackClick = useCallback(() => {\n audio.playSFX(\"menu_back\");\n onReturnToMenu();\n }, [audio, onReturnToMenu]);\n\n // Mode toggle button hover handlers (extracted for DRY)\n const handleModeButtonEnter = useCallback(\n (e: React.MouseEvent<HTMLButtonElement>, isActive: boolean) => {\n if (!isActive) {\n e.currentTarget.style.background = hexToRgbaString(\n theme.colors.PRIMARY_CYAN,\n 0.1,\n );\n e.currentTarget.style.borderColor = hexToRgbaString(\n theme.colors.PRIMARY_CYAN,\n 0.8,\n );\n }\n },\n [theme],\n );\n\n const handleModeButtonLeave = useCallback(\n (e: React.MouseEvent<HTMLButtonElement>, isActive: boolean) => {\n if (!isActive) {\n e.currentTarget.style.background = hexToRgbaString(\n theme.colors.UI_BACKGROUND_DARK,\n 0.8,\n );\n e.currentTarget.style.borderColor = hexToRgbaString(\n theme.colors.ACCENT_GOLD,\n 0.6,\n );\n }\n },\n [theme],\n );\n\n // Stance controls data\n const stanceControls = useMemo(\n () => Object.entries(COMBAT_CONTROLS.stanceControls),\n [],\n );\n\n // Combat controls data\n const combatControls = useMemo(\n () => Object.entries(COMBAT_CONTROLS.combat),\n [],\n );\n\n // Grid layout calculations\n const buttonsPerRow = isMobile ? 2 : isTablet ? 3 : isLargeDesktop ? 5 : 4;\n const buttonHeight = isMobile\n ? 120\n : isTablet\n ? 130\n : isLargeDesktop\n ? 120\n : 140;\n\n return (\n <div\n style={{\n width: screenWidth,\n height: screenHeight,\n position: \"relative\",\n overflow: \"hidden\",\n }}\n data-testid=\"controls-screen\"\n >\n {/* Volume Control - outside Canvas to maintain AudioProvider context */}\n <VolumeControl position=\"top-right\" compact={isMobile} />\n\n {/* Three.js Canvas for 3D background and visualization */}\n <Canvas\n style={{\n position: \"absolute\",\n top: 0,\n left: 0,\n width: \"100%\",\n height: \"100%\",\n zIndex: Z_INDEX.ARENA,\n }}\n gl={{\n antialias: true,\n alpha: false,\n powerPreference: \"high-performance\",\n }}\n dpr={[1, 2]}\n camera={{ position: [0, 5, 10], fov: 75 }}\n onCreated={({ gl }) => {\n gl.setClearColor(theme.colors.UI_BACKGROUND_DARK, 1);\n }}\n >\n {/* 3D Background Scene */}\n <BackgroundScene3D theme=\"controls\" />\n\n {/* Conditional 3D Visualization based on category */}\n {category === \"keyboard\" ? (\n <VisualKeyboard3D\n pressedKeys={pressedKeys}\n selectedTab={selectedTab}\n />\n ) : (\n <GamepadVisualization3D isMobile={isMobile} />\n )}\n </Canvas>\n\n {/* UI Overlay (positioned absolutely over Canvas) - matches CombatScreen pattern */}\n <div\n style={{\n position: \"absolute\",\n top: 0,\n left: 0,\n width: \"100%\",\n height: \"100%\",\n pointerEvents: \"none\",\n zIndex: Z_INDEX.HUD,\n }}\n data-testid=\"controls-hud-overlay\"\n >\n {/* WebKit Scrollbar Styling - Using !important to override global hide */}\n <style dangerouslySetInnerHTML={scrollbarStyle} />\n\n <div\n style={{\n width: \"100%\",\n height: \"100%\",\n display: \"flex\",\n flexDirection: \"column\",\n color: colors.textPrimary,\n fontFamily: theme.koreanTypography.fontFamily,\n lineHeight: theme.koreanTypography.lineHeight,\n pointerEvents: \"auto\",\n }}\n >\n {/* Header */}\n <div\n style={{\n height: `${layoutConstants.headerHeight}px`,\n display: \"flex\",\n flexDirection: \"column\",\n alignItems: \"center\",\n justifyContent: \"center\",\n background: colors.headerBg,\n borderBottom: `3px solid ${colors.borderRed}`,\n padding: `${layoutConstants.padding}px`,\n }}\n data-testid=\"controls-header\"\n >\n <h1\n style={{\n fontSize: isMobile ? \"20px\" : \"24px\",\n fontWeight: \"bold\",\n color: colors.accentGold,\n margin: 0,\n textShadow: `0 0 10px ${hexToRgbaString(\n theme.colors.ACCENT_GOLD,\n 0.5,\n )}`,\n }}\n >\n 조작법 안내 - Controls Guide\n </h1>\n <p\n style={{\n fontSize: isMobile ? \"12px\" : \"16px\",\n color: colors.accentCyan,\n margin: \"8px 0 0 0\",\n fontStyle: \"italic\",\n }}\n >\n ☯ 팔괘 철학과 급소술의 융합 | Eight Trigrams Philosophy & Vital\n Point Arts ☯\n </p>\n </div>\n\n {/* Mode Toggle (Keyboard/Gamepad) */}\n <div\n style={{\n display: \"flex\",\n justifyContent: \"center\",\n gap: isMobile ? \"10px\" : \"15px\",\n padding: `${layoutConstants.padding}px`,\n background: colors.headerBg,\n borderBottom: `2px solid ${colors.borderGold}`,\n }}\n data-testid=\"mode-toggle\"\n >\n <button\n onClick={() => {\n audio.playSFX(\"menu_select\");\n setCategory(\"keyboard\");\n }}\n style={{\n padding: isMobile ? \"10px 20px\" : \"12px 30px\",\n borderRadius: \"8px\",\n border: `2px solid ${category === \"keyboard\" ? colors.accentCyan : colors.borderGold}`,\n background:\n category === \"keyboard\"\n ? hexToRgbaString(theme.colors.PRIMARY_CYAN, 0.3)\n : hexToRgbaString(theme.colors.UI_BACKGROUND_DARK, 0.8),\n color:\n category === \"keyboard\"\n ? colors.accentCyan\n : colors.textSecondary,\n fontFamily: FONT_FAMILY.KOREAN,\n fontSize: isMobile ? \"14px\" : \"16px\",\n fontWeight: \"bold\",\n cursor: \"pointer\",\n transition: \"all 0.2s ease\",\n }}\n data-testid=\"keyboard-mode-button\"\n onMouseEnter={(e) =>\n handleModeButtonEnter(e, category === \"keyboard\")\n }\n onMouseLeave={(e) =>\n handleModeButtonLeave(e, category === \"keyboard\")\n }\n >\n ⌨️ 키보드 | Keyboard\n </button>\n <button\n onClick={() => {\n audio.playSFX(\"menu_select\");\n setCategory(\"gamepad\");\n }}\n style={{\n padding: isMobile ? \"10px 20px\" : \"12px 30px\",\n borderRadius: \"8px\",\n border: `2px solid ${category === \"gamepad\" ? colors.accentCyan : colors.borderGold}`,\n background:\n category === \"gamepad\"\n ? hexToRgbaString(theme.colors.PRIMARY_CYAN, 0.3)\n : hexToRgbaString(theme.colors.UI_BACKGROUND_DARK, 0.8),\n color:\n category === \"gamepad\"\n ? colors.accentCyan\n : colors.textSecondary,\n fontFamily: FONT_FAMILY.KOREAN,\n fontSize: isMobile ? \"14px\" : \"16px\",\n fontWeight: \"bold\",\n cursor: \"pointer\",\n transition: \"all 0.2s ease\",\n }}\n data-testid=\"gamepad-mode-button\"\n onMouseEnter={(e) =>\n handleModeButtonEnter(e, category === \"gamepad\")\n }\n onMouseLeave={(e) =>\n handleModeButtonLeave(e, category === \"gamepad\")\n }\n >\n 🎮 게임패드 | Gamepad\n </button>\n </div>\n\n {/* Control Category Tabs */}\n <div\n style={{\n background: colors.headerBg,\n borderBottom: `2px solid ${colors.borderCyan}`,\n }}\n >\n <ControlCategoryTabs\n selectedTab={selectedTab}\n onTabChange={(tab) => {\n audio.playSFX(\"menu_select\");\n setSelectedTab(tab);\n }}\n isMobile={isMobile}\n />\n </div>\n\n {/* Content Area - Scrollable */}\n <div\n style={{\n flex: 1,\n overflowY: \"auto\",\n overflowX: \"hidden\",\n padding: `${layoutConstants.padding}px`,\n // Custom scrollbar styling for Korean aesthetic (Firefox)\n scrollbarWidth: \"thin\",\n scrollbarColor: `${colors.accentGold} ${colors.sectionBg}`,\n }}\n className=\"korean-scrollbar\"\n data-testid=\"controls-content\"\n >\n {/* Control Bindings Display */}\n <ControlBindingsOverlayHtml\n selectedTab={selectedTab}\n isMobile={isMobile}\n />\n\n {/* Legacy Content - Keeping for backward compatibility and additional info */}\n {/* Trigram Stances Section - Additional context beyond key bindings */}\n <div\n style={{\n marginTop: `${layoutConstants.sectionSpacing}px`,\n marginBottom: `${layoutConstants.sectionSpacing}px`,\n background: colors.sectionBg,\n borderRadius: \"12px\",\n border: `2px solid ${colors.borderGold}`,\n padding: \"20px\",\n }}\n data-testid=\"trigram-controls\"\n >\n <h2\n style={{\n fontSize: isMobile ? \"18px\" : \"22px\",\n fontWeight: \"bold\",\n color: colors.accentGold,\n margin: \"0 0 10px 0\",\n }}\n >\n 팔괘 무술 자세 - Eight Trigram Combat Stances\n </h2>\n <p\n style={{\n fontSize: isMobile ? \"11px\" : \"14px\",\n color: colors.accentCyan,\n fontStyle: \"italic\",\n margin: \"0 0 20px 0\",\n }}\n >\n 🗡️ 전통 한국 무예의 8가지 핵심 자세 | 8 Core Stances of\n Traditional Korean Martial Arts 🗡️\n </p>\n\n {/* Stance Controls Grid */}\n <div\n style={{\n display: \"grid\",\n gridTemplateColumns: `repeat(${buttonsPerRow}, 1fr)`,\n gap: \"15px\",\n }}\n data-testid=\"stance-controls-grid\"\n >\n {stanceControls.map(([key, value]) => (\n <div\n key={key}\n style={{\n background: `linear-gradient(135deg, ${hexToRgbaString(\n theme.colors.UI_BACKGROUND_MEDIUM,\n 0.9,\n )}, ${hexToRgbaString(\n theme.colors.UI_BACKGROUND_LIGHT,\n 0.9,\n )})`,\n borderRadius: \"10px\",\n border: `2px solid ${colors.borderGold}`,\n padding: \"12px\",\n position: \"relative\",\n minHeight: `${buttonHeight}px`,\n }}\n data-testid={`stance-control-${key}`}\n >\n {/* Key Badge */}\n <div\n style={{\n position: \"absolute\",\n top: \"8px\",\n left: \"8px\",\n background: colors.accentGold,\n borderRadius: \"6px\",\n width: \"25px\",\n height: \"25px\",\n display: \"flex\",\n alignItems: \"center\",\n justifyContent: \"center\",\n fontSize: isMobile ? \"14px\" : \"16px\",\n fontWeight: \"bold\",\n color: colors.koreanBlack,\n }}\n >\n {key}\n </div>\n\n {/* Trigram Symbol */}\n <div\n style={{\n position: \"absolute\",\n top: \"8px\",\n right: \"8px\",\n fontSize: isMobile ? \"18px\" : \"22px\",\n color: colors.accentGold,\n fontWeight: \"bold\",\n }}\n >\n {value.symbol}\n </div>\n\n {/* Stance Name */}\n <div style={{ marginTop: \"35px\" }}>\n <div\n style={{\n fontSize: isMobile ? \"12px\" : \"14px\",\n fontWeight: \"bold\",\n color: colors.textPrimary,\n }}\n >\n {value.korean}\n </div>\n <div\n style={{\n fontSize: isMobile ? \"9px\" : \"10px\",\n color: colors.textSecondary,\n fontStyle: \"italic\",\n }}\n >\n {value.english}\n </div>\n </div>\n\n {/* Technique */}\n <div style={{ marginTop: \"8px\" }}>\n <div\n style={{\n fontSize: isMobile ? \"9px\" : \"10px\",\n fontWeight: \"bold\",\n color: colors.accentCyan,\n }}\n >\n 🥋 {value.technique.korean}\n </div>\n <div\n style={{\n fontSize: isMobile ? \"8px\" : \"9px\",\n color: colors.accentCyan,\n fontStyle: \"italic\",\n }}\n >\n {value.technique.english}\n </div>\n </div>\n\n {/* Combat Focus */}\n <div style={{ marginTop: \"6px\" }}>\n <div\n style={{\n fontSize: isMobile ? \"8px\" : \"9px\",\n fontWeight: \"bold\",\n color: `#${theme.colors.SECONDARY_MAGENTA.toString(\n 16,\n ).padStart(6, \"0\")}`,\n }}\n >\n ⚔️ {value.combatFocus.korean}\n </div>\n <div\n style={{\n fontSize: isMobile ? \"7px\" : \"8px\",\n color: `#${theme.colors.SECONDARY_MAGENTA.toString(\n 16,\n ).padStart(6, \"0\")}`,\n fontStyle: \"italic\",\n }}\n >\n {value.combatFocus.english}\n </div>\n </div>\n\n {/* Combat Effects */}\n <div\n style={{\n marginTop: \"6px\",\n fontSize: isMobile ? \"7px\" : \"8px\",\n fontWeight: \"bold\",\n color: `#${theme.colors.NEGATIVE_RED.toString(\n 16,\n ).padStart(6, \"0\")}`,\n }}\n >\n 💥 {value.combatEffects.korean}\n </div>\n\n {/* Effectiveness Indicator */}\n <div\n style={{\n position: \"absolute\",\n bottom: \"8px\",\n right: \"8px\",\n fontSize: isMobile ? \"7px\" : \"8px\",\n fontWeight: \"bold\",\n color: `#${theme.colors.KOREAN_RED.toString(\n 16,\n ).padStart(6, \"0\")}`,\n }}\n >\n 급소술\n </div>\n </div>\n ))}\n </div>\n </div>\n\n {/* Combat Controls Section */}\n <div\n style={{\n marginBottom: `${layoutConstants.sectionSpacing}px`,\n background: colors.sectionBg,\n borderRadius: \"12px\",\n border: `2px solid ${colors.borderCyan}`,\n padding: \"20px\",\n }}\n data-testid=\"combat-controls\"\n >\n <h2\n style={{\n fontSize: isMobile ? \"18px\" : \"22px\",\n fontWeight: \"bold\",\n color: colors.accentCyan,\n margin: \"0 0 20px 0\",\n }}\n >\n 실전 격투 조작 - Combat Actions\n </h2>\n\n {/* Combat Controls List */}\n <div\n style={{\n display: \"flex\",\n flexDirection: \"column\",\n gap: \"10px\",\n }}\n data-testid=\"combat-controls-list\"\n >\n {combatControls.map(([key, description]) => (\n <div\n key={key}\n style={{\n background: `linear-gradient(90deg, ${hexToRgbaString(\n theme.colors.UI_BACKGROUND_LIGHT,\n 0.9,\n )}, ${hexToRgbaString(\n theme.colors.UI_BACKGROUND_MEDIUM,\n 0.9,\n )})`,\n borderRadius: \"8px\",\n border: `1px solid ${colors.borderCyan}`,\n padding: \"12px\",\n display: \"flex\",\n alignItems: \"center\",\n gap: \"15px\",\n }}\n data-testid={`combat-control-${key}`}\n >\n {/* Key Badge */}\n <div\n style={{\n background: hexToRgbaString(\n theme.colors.ACCENT_CYAN,\n 0.3,\n ),\n borderRadius: \"6px\",\n padding: \"4px 12px\",\n fontSize: isMobile ? \"14px\" : \"16px\",\n fontWeight: \"bold\",\n color: colors.accentGold,\n minWidth: \"60px\",\n textAlign: \"center\",\n }}\n >\n {key}\n </div>\n\n {/* Description */}\n <div style={{ flex: 1 }}>\n <div\n style={{\n fontSize: isMobile ? \"11px\" : \"13px\",\n fontWeight: \"bold\",\n color: colors.textPrimary,\n }}\n >\n {description.korean}\n </div>\n <div\n style={{\n fontSize: isMobile ? \"9px\" : \"11px\",\n color: colors.textSecondary,\n fontStyle: \"italic\",\n }}\n >\n {description.english}\n </div>\n </div>\n </div>\n ))}\n </div>\n </div>\n\n {/* Movement Controls Section */}\n <div\n style={{\n marginBottom: `${layoutConstants.sectionSpacing}px`,\n background: colors.sectionBg,\n borderRadius: \"12px\",\n border: `2px solid ${hexToRgbaString(\n theme.colors.SECONDARY_MAGENTA,\n 0.5,\n )}`,\n padding: \"20px\",\n }}\n data-testid=\"movement-controls\"\n >\n <h2\n style={{\n fontSize: isMobile ? \"18px\" : \"22px\",\n fontWeight: \"bold\",\n color: `#${theme.colors.SECONDARY_MAGENTA.toString(\n 16,\n ).padStart(6, \"0\")}`,\n margin: \"0 0 20px 0\",\n }}\n >\n 이동 조작 - Movement Controls\n </h2>\n\n {/* Movement Keys */}\n <div style={{ marginBottom: \"15px\" }}>\n <div\n style={{\n fontSize: isMobile ? \"12px\" : \"14px\",\n fontWeight: \"bold\",\n color: colors.accentGold,\n marginBottom: \"10px\",\n }}\n >\n WASD 또는 방향키 | WASD or Arrow Keys\n </div>\n <div\n style={{\n display: \"grid\",\n gridTemplateColumns: isMobile\n ? \"1fr 1fr\"\n : \"1fr 1fr 1fr 1fr\",\n gap: \"8px\",\n }}\n >\n {[\n { key: \"W/↑\", korean: \"전진\", english: \"Forward\" },\n { key: \"S/↓\", korean: \"후퇴\", english: \"Backward\" },\n { key: \"A/←\", korean: \"좌\", english: \"Left\" },\n { key: \"D/→\", korean: \"우\", english: \"Right\" },\n ].map((move) => (\n <div\n key={move.key}\n style={{\n background: hexToRgbaString(\n theme.colors.UI_BACKGROUND_LIGHT,\n 0.8,\n ),\n borderRadius: \"6px\",\n padding: \"8px\",\n textAlign: \"center\",\n }}\n >\n <div\n style={{\n fontSize: isMobile ? \"11px\" : \"13px\",\n fontWeight: \"bold\",\n color: colors.accentCyan,\n }}\n >\n {move.key}\n </div>\n <div\n style={{\n fontSize: isMobile ? \"8px\" : \"9px\",\n color: colors.textSecondary,\n }}\n >\n {move.korean} | {move.english}\n </div>\n </div>\n ))}\n </div>\n </div>\n </div>\n\n {/* Advanced Footwork Section */}\n <div\n style={{\n marginBottom: `${layoutConstants.sectionSpacing}px`,\n background: colors.sectionBg,\n borderRadius: \"12px\",\n border: `2px solid ${hexToRgbaString(\n theme.colors.PRIMARY_CYAN,\n 0.5,\n )}`,\n padding: \"20px\",\n }}\n data-testid=\"advanced-footwork\"\n >\n <h2\n style={{\n fontSize: isMobile ? \"18px\" : \"22px\",\n fontWeight: \"bold\",\n color: colors.accentCyan,\n margin: \"0 0 10px 0\",\n }}\n >\n 고급 보법 - Advanced Footwork\n </h2>\n <p\n style={{\n fontSize: isMobile ? \"10px\" : \"12px\",\n color: colors.textSecondary,\n fontStyle: \"italic\",\n margin: \"0 0 15px 0\",\n }}\n >\n 🥋 전통 한국 무예 발놀림 기법 | Traditional Korean martial arts\n footwork techniques\n </p>\n\n {/* Tactical Steps */}\n <div style={{ marginBottom: \"20px\" }}>\n <h3\n style={{\n fontSize: isMobile ? \"14px\" : \"16px\",\n fontWeight: \"bold\",\n color: colors.accentGold,\n marginBottom: \"10px\",\n }}\n >\n ✅ 전술보법 (Shift + WASD) | Tactical Steps - IMPLEMENTED\n </h3>\n <p\n style={{\n fontSize: isMobile ? \"9px\" : \"10px\",\n color: colors.textSecondary,\n marginBottom: \"10px\",\n fontStyle: \"italic\",\n }}\n >\n Precise 30cm repositioning • 300ms duration •\n Non-interruptible • 5 stamina cost\n </p>\n <div\n style={{\n display: \"grid\",\n gridTemplateColumns: isMobile\n ? \"1fr 1fr\"\n : \"repeat(4, 1fr)\",\n gap: \"8px\",\n }}\n >\n {[\n {\n key: \"Shift+W\",\n korean: \"전진보법\",\n english: \"Forward Step\",\n },\n {\n key: \"Shift+S\",\n korean: \"후퇴보법\",\n english: \"Retreat Step\",\n },\n {\n key: \"Shift+A\",\n korean: \"좌측면보법\",\n english: \"Left Step\",\n },\n {\n key: \"Shift+D\",\n korean: \"우측면보법\",\n english: \"Right Step\",\n },\n {\n key: \"Shift+W+A\",\n korean: \"전좌측보법\",\n english: \"Forward-Left\",\n },\n {\n key: \"Shift+W+D\",\n korean: \"전우측보법\",\n english: \"Forward-Right\",\n },\n {\n key: \"Shift+S+A\",\n korean: \"후좌측보법\",\n english: \"Back-Left\",\n },\n {\n key: \"Shift+S+D\",\n korean: \"후우측보법\",\n english: \"Back-Right\",\n },\n ].map((step) => (\n <div\n key={step.key}\n style={{\n background: hexToRgbaString(\n theme.colors.UI_BACKGROUND_LIGHT,\n 0.8,\n ),\n borderRadius: \"6px\",\n border: `1px solid ${colors.borderCyan}`,\n padding: \"8px\",\n textAlign: \"center\",\n }}\n >\n <div\n style={{\n fontSize: isMobile ? \"9px\" : \"10px\",\n fontWeight: \"bold\",\n color: colors.accentGold,\n marginBottom: \"4px\",\n }}\n >\n {step.key}\n </div>\n <div\n style={{\n fontSize: isMobile ? \"7px\" : \"8px\",\n color: colors.textPrimary,\n }}\n >\n {step.korean}\n </div>\n <div\n style={{\n fontSize: isMobile ? \"6px\" : \"7px\",\n color: colors.textSecondary,\n fontStyle: \"italic\",\n }}\n >\n {step.english}\n </div>\n </div>\n ))}\n </div>\n </div>\n\n {/* Footwork Patterns */}\n <div>\n <h3\n style={{\n fontSize: isMobile ? \"14px\" : \"16px\",\n fontWeight: \"bold\",\n color: colors.accentGold,\n marginBottom: \"10px\",\n }}\n >\n 보법 패턴 (Ctrl + WASD) | Footwork Patterns\n </h3>\n\n {/* Circular Steps */}\n <div style={{ marginBottom: \"12px\" }}>\n <div\n style={{\n fontSize: isMobile ? \"11px\" : \"12px\",\n fontWeight: \"bold\",\n color: colors.textPrimary,\n marginBottom: \"6px\",\n }}\n >\n ✅ 원형보 (Wonhyeongbo) | Circular Step - IMPLEMENTED\n </div>\n <p\n style={{\n fontSize: isMobile ? \"8px\" : \"9px\",\n color: colors.textSecondary,\n marginBottom: \"8px\",\n fontStyle: \"italic\",\n }}\n >\n Lateral movement while maintaining guard • 30cm distance •\n 300ms\n </p>\n <div style={{ display: \"flex\", gap: \"8px\" }}>\n {[\n {\n key: \"Ctrl+A\",\n korean: \"원형보 좌\",\n english: \"Circular Left\",\n },\n {\n key: \"Ctrl+D\",\n korean: \"원형보 우\",\n english: \"Circular Right\",\n },\n ].map((move) => (\n <div\n key={move.key}\n style={{\n flex: 1,\n background: hexToRgbaString(\n theme.colors.ACCENT_CYAN,\n 0.2,\n ),\n borderRadius: \"6px\",\n border: `1px solid ${colors.borderCyan}`,\n padding: \"8px\",\n textAlign: \"center\",\n }}\n >\n <div\n style={{\n fontSize: isMobile ? \"10px\" : \"11px\",\n fontWeight: \"bold\",\n color: colors.accentGold,\n }}\n >\n {move.key}\n </div>\n <div\n style={{\n fontSize: isMobile ? \"8px\" : \"9px\",\n color: colors.textPrimary,\n }}\n >\n {move.korean}\n </div>\n <div\n style={{\n fontSize: isMobile ? \"7px\" : \"8px\",\n color: colors.textSecondary,\n fontStyle: \"italic\",\n }}\n >\n {move.english}\n </div>\n </div>\n ))}\n </div>\n </div>\n\n {/* Slide Steps */}\n <div style={{ marginBottom: \"12px\" }}>\n <div\n style={{\n fontSize: isMobile ? \"11px\" : \"12px\",\n fontWeight: \"bold\",\n color: colors.textPrimary,\n marginBottom: \"6px\",\n }}\n >\n ✅ 미끄럼보 (Mikkeureombo) | Slide Step - IMPLEMENTED\n </div>\n <p\n style={{\n fontSize: isMobile ? \"8px\" : \"9px\",\n color: colors.textSecondary,\n marginBottom: \"8px\",\n fontStyle: \"italic\",\n }}\n >\n Both feet move together • 30cm distance • 200ms (faster!)\n </p>\n <div style={{ display: \"flex\", gap: \"8px\" }}>\n {[\n {\n key: \"Ctrl+W\",\n korean: \"미끄럼보 전\",\n english: \"Slide Forward\",\n },\n {\n key: \"Ctrl+S\",\n korean: \"미끄럼보 후\",\n english: \"Slide Back\",\n },\n ].map((move) => (\n <div\n key={move.key}\n style={{\n flex: 1,\n background: hexToRgbaString(\n theme.colors.ACCENT_CYAN,\n 0.2,\n ),\n borderRadius: \"6px\",\n border: `1px solid ${colors.borderCyan}`,\n padding: \"8px\",\n textAlign: \"center\",\n }}\n >\n <div\n style={{\n fontSize: isMobile ? \"10px\" : \"11px\",\n fontWeight: \"bold\",\n color: colors.accentGold,\n }}\n >\n {move.key}\n </div>\n <div\n style={{\n fontSize: isMobile ? \"8px\" : \"9px\",\n color: colors.textPrimary,\n }}\n >\n {move.korean}\n </div>\n <div\n style={{\n fontSize: isMobile ? \"7px\" : \"8px\",\n color: colors.textSecondary,\n fontStyle: \"italic\",\n }}\n >\n {move.english}\n </div>\n </div>\n ))}\n </div>\n </div>\n\n {/* Pending Footwork */}\n <div\n style={{\n marginTop: \"15px\",\n padding: \"10px\",\n background: hexToRgbaString(theme.colors.ACCENT_CYAN, 0.2),\n borderRadius: \"6px\",\n border: `1px solid ${colors.borderCyan}`,\n }}\n >\n <div\n style={{\n fontSize: isMobile ? \"10px\" : \"11px\",\n fontWeight: \"bold\",\n color: colors.accentGold,\n marginBottom: \"6px\",\n }}\n >\n ✅ 추가 보법 (Advanced Patterns) - IMPLEMENTED\n </div>\n <p\n style={{\n fontSize: isMobile ? \"8px\" : \"9px\",\n color: colors.textPrimary,\n margin: \"0 0 6px 0\",\n }}\n >\n <strong>축족회전 (Chukjok Hoejeon) | Pivot</strong>:\n Shift+Ctrl+A (left) / Shift+Ctrl+D (right) • 90° rotation on\n planted foot • 250ms\n </p>\n <p\n style={{\n fontSize: isMobile ? \"8px\" : \"9px\",\n color: colors.textPrimary,\n margin: 0,\n }}\n >\n <strong>섞음보 (Seokkeumbo) | Shuffle</strong>: Shift+Ctrl+W\n or Shift+Ctrl+S • 15cm micro-adjustment • 100ms\n </p>\n </div>\n </div>\n </div>\n\n {/* Stance Side Switch Section */}\n <div\n style={{\n marginBottom: `${layoutConstants.sectionSpacing}px`,\n background: colors.sectionBg,\n borderRadius: \"12px\",\n border: `2px solid ${hexToRgbaString(\n theme.colors.ACCENT_GOLD,\n 0.5,\n )}`,\n padding: \"20px\",\n }}\n data-testid=\"stance-side-switch\"\n >\n <h2\n style={{\n fontSize: isMobile ? \"18px\" : \"22px\",\n fontWeight: \"bold\",\n color: colors.accentGold,\n margin: \"0 0 10px 0\",\n }}\n >\n 자세 발 바꿈 - Stance Side Switch\n </h2>\n <p\n style={{\n fontSize: isMobile ? \"10px\" : \"12px\",\n color: colors.textSecondary,\n fontStyle: \"italic\",\n margin: \"0 0 15px 0\",\n }}\n >\n ✅ 전방 발 전환 | Switch front foot position - IMPLEMENTED\n </p>\n\n <div\n style={{\n background: hexToRgbaString(theme.colors.ACCENT_GOLD, 0.2),\n borderRadius: \"6px\",\n border: `1px solid ${colors.borderGold}`,\n padding: \"12px\",\n textAlign: \"center\",\n }}\n >\n <div\n style={{\n fontSize: isMobile ? \"16px\" : \"18px\",\n fontWeight: \"bold\",\n color: colors.accentGold,\n marginBottom: \"8px\",\n }}\n >\n H\n </div>\n <div\n style={{\n fontSize: isMobile ? \"10px\" : \"11px\",\n color: colors.textPrimary,\n marginBottom: \"4px\",\n }}\n >\n 발 바꿈 (Bal Bakkum)\n </div>\n <div\n style={{\n fontSize: isMobile ? \"9px\" : \"10px\",\n color: colors.textSecondary,\n fontStyle: \"italic\",\n }}\n >\n Switch Front Foot\n </div>\n <div\n style={{\n fontSize: isMobile ? \"7px\" : \"8px\",\n color: colors.textSecondary,\n marginTop: \"6px\",\n fontStyle: \"italic\",\n }}\n >\n Mirrors your stance (left ↔ right) • 400ms duration\n </div>\n </div>\n </div>\n\n {/* Technique Controls Section */}\n <div\n style={{\n marginBottom: `${layoutConstants.sectionSpacing}px`,\n background: colors.sectionBg,\n borderRadius: \"12px\",\n border: `2px solid ${hexToRgbaString(\n theme.colors.ACCENT_GOLD,\n 0.6,\n )}`,\n padding: \"20px\",\n }}\n data-testid=\"technique-controls\"\n >\n <h2\n style={{\n fontSize: isMobile ? \"18px\" : \"22px\",\n fontWeight: \"bold\",\n color: colors.accentGold,\n margin: \"0 0 10px 0\",\n }}\n >\n 기술 실행 - Technique Execution\n </h2>\n <p\n style={{\n fontSize: isMobile ? \"10px\" : \"12px\",\n color: colors.textSecondary,\n fontStyle: \"italic\",\n margin: \"0 0 15px 0\",\n }}\n >\n ⚡ 원형별 고유 기술 (최대 10개) | Archetype-specific techniques\n (up to 10)\n </p>\n\n {/* Technique Keys Grid */}\n <div\n style={{\n display: \"grid\",\n gridTemplateColumns: isMobile\n ? \"repeat(5, 1fr)\"\n : \"repeat(10, 1fr)\",\n gap: isMobile ? \"6px\" : \"8px\",\n }}\n >\n {[\"Q\", \"E\", \"R\", \"T\", \"Y\", \"F\", \"G\", \"Z\", \"X\", \"C\"].map(\n (key, index) => (\n <div\n key={key}\n style={{\n background: `linear-gradient(135deg, ${hexToRgbaString(\n theme.colors.ACCENT_GOLD,\n 0.3,\n )}, ${hexToRgbaString(theme.colors.ACCENT_GOLD, 0.1)})`,\n borderRadius: \"6px\",\n border: `2px solid ${colors.borderGold}`,\n padding: isMobile ? \"8px 4px\" : \"10px 6px\",\n textAlign: \"center\",\n }}\n >\n <div\n style={{\n fontSize: isMobile ? \"14px\" : \"16px\",\n fontWeight: \"bold\",\n color: colors.accentGold,\n }}\n >\n {key}\n </div>\n <div\n style={{\n fontSize: isMobile ? \"7px\" : \"8px\",\n color: colors.textSecondary,\n marginTop: \"2px\",\n }}\n >\n 기술 {index + 1}\n </div>\n </div>\n ),\n )}\n </div>\n\n {/* Technique Notes */}\n <div\n style={{\n marginTop: \"15px\",\n padding: \"10px\",\n background: hexToRgbaString(\n theme.colors.UI_BACKGROUND_DARK,\n 0.6,\n ),\n borderRadius: \"6px\",\n fontSize: isMobile ? \"9px\" : \"10px\",\n color: colors.textSecondary,\n fontStyle: \"italic\",\n }}\n >\n 💡 <strong>Tip</strong>: Keys positioned around WASD for easy\n access without interfering with movement. Each archetype has\n unique techniques.\n </div>\n </div>\n\n {/* Special Features Section */}\n <div\n style={{\n marginBottom: `${layoutConstants.sectionSpacing}px`,\n background: colors.sectionBg,\n borderRadius: \"12px\",\n border: `2px solid ${colors.borderRed}`,\n padding: \"20px\",\n }}\n data-testid=\"special-features\"\n >\n <h2\n style={{\n fontSize: isMobile ? \"18px\" : \"22px\",\n fontWeight: \"bold\",\n color: `#${theme.colors.KOREAN_RED.toString(16).padStart(\n 6,\n \"0\",\n )}`,\n margin: \"0 0 20px 0\",\n }}\n >\n 특수 기능 - Special Features\n </h2>\n\n {/* Special Keys List */}\n <div\n style={{\n display: \"flex\",\n flexDirection: \"column\",\n gap: \"10px\",\n }}\n >\n {[\n {\n key: \"V\",\n korean: \"급소 표시 전환\",\n english: \"Toggle vital points overlay (70 points)\",\n },\n {\n key: \"B\",\n korean: \"방어 자세\",\n english: \"Defensive guard position\",\n },\n {\n key: \"F1\",\n korean: \"조작법 힌트\",\n english: \"Show control hints\",\n },\n {\n key: \"ESC / M\",\n korean: \"일시정지 / 메뉴\",\n english: \"Pause menu / Return to menu\",\n },\n ].map((special) => (\n <div\n key={special.key}\n style={{\n background: hexToRgbaString(\n theme.colors.UI_BACKGROUND_LIGHT,\n 0.8,\n ),\n borderRadius: \"8px\",\n border: `1px solid ${colors.borderRed}`,\n padding: \"10px\",\n display: \"flex\",\n alignItems: \"center\",\n gap: \"12px\",\n }}\n >\n <div\n style={{\n background: hexToRgbaString(\n theme.colors.KOREAN_RED,\n 0.3,\n ),\n borderRadius: \"6px\",\n padding: \"4px 10px\",\n fontSize: isMobile ? \"12px\" : \"14px\",\n fontWeight: \"bold\",\n color: colors.accentGold,\n minWidth: \"50px\",\n textAlign: \"center\",\n }}\n >\n {special.key}\n </div>\n <div style={{ flex: 1 }}>\n <div\n style={{\n fontSize: isMobile ? \"10px\" : \"12px\",\n fontWeight: \"bold\",\n color: colors.textPrimary,\n }}\n >\n {special.korean}\n </div>\n <div\n style={{\n fontSize: isMobile ? \"8px\" : \"10px\",\n color: colors.textSecondary,\n fontStyle: \"italic\",\n }}\n >\n {special.english}\n </div>\n </div>\n </div>\n ))}\n </div>\n </div>\n </div>\n\n {/* Interactive Control Demo - Shows recently pressed keys */}\n <InteractiveControlDemo\n pressedKeys={pressedKeys}\n isMobile={isMobile}\n />\n\n {/* Footer */}\n <div\n style={{\n height: `${layoutConstants.footerHeight}px`,\n background: colors.headerBg,\n borderTop: `3px solid ${colors.borderRed}`,\n display: \"flex\",\n alignItems: \"center\",\n justifyContent: \"space-between\",\n padding: `0 ${layoutConstants.padding}px`,\n }}\n data-testid=\"controls-footer\"\n >\n {/* Philosophy Text */}\n <div\n style={{\n fontSize: isMobile ? \"10px\" : \"12px\",\n color: colors.accentGold,\n fontStyle: \"italic\",\n }}\n >\n 🥋 흑괘의 길을 걸어라 | Walk the Path of the Black Trigram 🥋\n </div>\n\n {/* Back Button */}\n <BackButton\n onClick={handleBackClick}\n korean=\"무도장 복귀\"\n english=\"Return to Dojang\"\n isMobile={isMobile}\n testId=\"controls-back-button\"\n />\n\n {/* Keyboard Hint */}\n <div\n style={{\n background: hexToRgbaString(\n theme.colors.UI_BACKGROUND_MEDIUM,\n 0.9,\n ),\n borderRadius: \"6px\",\n padding: \"8px 12px\",\n fontSize: isMobile ? \"11px\" : \"12px\",\n fontWeight: \"bold\",\n color: `#${theme.colors.SECONDARY_MAGENTA.toString(16).padStart(\n 6,\n \"0\",\n )}`,\n border: `1px solid ${colors.borderGold}`,\n }}\n data-testid=\"keyboard-shortcuts\"\n >\n ESC | M\n </div>\n </div>\n\n {/* Footer Instruction */}\n <div\n style={{\n textAlign: \"center\",\n padding: \"10px\",\n fontSize: isMobile ? \"10px\" : \"12px\",\n color: colors.textSecondary,\n fontStyle: \"italic\",\n background: hexToRgbaString(theme.colors.UI_BACKGROUND_DARK, 0.7),\n }}\n >\n ESC 또는 M 키로 메뉴로 돌아가기 - Press ESC or M to return to menu\n </div>\n </div>\n </div>\n </div>\n );\n};\n\nexport default ControlsScreen3D;\n"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ControlBindingsOverlayHtml.js","names":[],"sources":["../../../../../src/components/screens/controls/components/ControlBindingsOverlayHtml.tsx"],"sourcesContent":["/**\n * ControlBindingsOverlayHtml - Display control bindings filtered by selected category\n * \n * Shows key bindings in a clean, organized list with Korean-English bilingual labels.\n * Uses responsive grid/list layout based on device type.\n * \n * @module components/screens/controls/components\n */\n\nimport React, { useMemo } from \"react\";\nimport { FONT_FAMILY } from \"../../../../types/constants\";\nimport { hexToRgbaString } from \"../../../../utils/colorUtils\";\nimport { useKoreanTheme } from \"../../../shared/base/useKoreanTheme\";\nimport {\n filterKeysByCategory,\n getKeyCategoryColor,\n KEYBOARD_LAYOUT,\n type KeyData,\n} from \"../constants/ControlsConstants\";\n\n/**\n * Props for ControlBindingsOverlayHtml component\n */\nexport interface ControlBindingsOverlayHtmlProps {\n /** Selected control category to filter and display */\n readonly selectedTab: 'combat' | 'movement' | 'system';\n /** Whether on mobile device (list layout vs grid) */\n readonly isMobile: boolean;\n}\n\n/**\n * ControlBindingsOverlayHtml Component\n * \n * Displays control bindings filtered by selected category.\n * Uses grid layout on desktop and list layout on mobile.\n * \n * @example\n * ```tsx\n * <ControlBindingsOverlayHtml\n * selectedTab=\"combat\"\n * isMobile={false}\n * />\n * ```\n */\nexport const ControlBindingsOverlayHtml: React.FC<ControlBindingsOverlayHtmlProps> = ({\n selectedTab,\n isMobile,\n}) => {\n const theme = useKoreanTheme({ variant: 'primary', size: 'md', isMobile });\n\n // Filter keys by selected category\n const filteredKeys = useMemo<readonly KeyData[]>(() => {\n return filterKeysByCategory(KEYBOARD_LAYOUT, selectedTab);\n }, [selectedTab]);\n\n // Container styles - grid for desktop, list for mobile\n const containerStyle = useMemo(() => ({\n display: 'grid',\n gridTemplateColumns: isMobile ? '1fr' : 'repeat(auto-fill, minmax(280px, 1fr))',\n gap: isMobile ? '12px' : '16px',\n padding: isMobile ? '15px' : '20px',\n maxHeight: isMobile ? '60vh' : '70vh',\n overflowY: 'auto' as const,\n overflowX: 'hidden' as const,\n background: hexToRgbaString(theme.colors.UI_BACKGROUND_DARK, 0.9),\n borderRadius: '12px',\n border: `2px solid ${hexToRgbaString(theme.colors.UI_BORDER, 0.6)}`,\n boxShadow: `0 4px 15px ${hexToRgbaString(theme.colors.BLACK_SOLID, 0.5)}`,\n }), [isMobile, theme]);\n\n // Individual binding card style\n const getBindingCardStyle = (keyData: KeyData) => ({\n display: 'flex',\n flexDirection: 'column' as const,\n gap: '8px',\n padding: isMobile ? '12px' : '14px',\n background: hexToRgbaString(theme.colors.UI_BACKGROUND_MEDIUM, 0.8),\n border: `2px solid ${hexToRgbaString(getKeyCategoryColor(keyData.category), 0.6)}`,\n borderRadius: '8px',\n transition: 'all 0.2s ease',\n cursor: 'default',\n });\n\n // Key label style (the actual key)\n const keyLabelStyle = (keyData: KeyData) => ({\n fontFamily: FONT_FAMILY.KOREAN,\n fontSize: isMobile ? '16px' : '18px',\n fontWeight: 'bold' as const,\n color: hexToRgbaString(getKeyCategoryColor(keyData.category)),\n textShadow: `0 0 10px ${hexToRgbaString(getKeyCategoryColor(keyData.category), 0.5)}`,\n display: 'flex',\n alignItems: 'center',\n gap: '8px',\n });\n\n // Korean label style\n const koreanLabelStyle = {\n fontFamily: FONT_FAMILY.KOREAN,\n fontSize: isMobile ? '13px' : '14px',\n fontWeight: 'bold' as const,\n color: hexToRgbaString(theme.colors.ACCENT_GOLD),\n };\n\n // Description style\n const descriptionStyle = {\n fontFamily: FONT_FAMILY.KOREAN,\n fontSize: isMobile ? '11px' : '12px',\n color: hexToRgbaString(theme.colors.TEXT_SECONDARY),\n lineHeight: 1.4,\n };\n\n return (\n <div style={containerStyle} data-testid=\"control-bindings\">\n {filteredKeys.map((keyData) => (\n <div\n key={keyData.code}\n style={getBindingCardStyle(keyData)}\n data-testid={`binding-${keyData.code}`}\n onMouseEnter={(e) => {\n e.currentTarget.style.background = hexToRgbaString(theme.colors.UI_BACKGROUND_MEDIUM, 1);\n e.currentTarget.style.borderColor = hexToRgbaString(getKeyCategoryColor(keyData.category), 1);\n e.currentTarget.style.transform = 'translateY(-2px)';\n e.currentTarget.style.boxShadow = `0 4px 15px ${hexToRgbaString(getKeyCategoryColor(keyData.category), 0.4)}`;\n }}\n onMouseLeave={(e) => {\n e.currentTarget.style.background = hexToRgbaString(theme.colors.UI_BACKGROUND_MEDIUM, 0.8);\n e.currentTarget.style.borderColor = hexToRgbaString(getKeyCategoryColor(keyData.category), 0.6);\n e.currentTarget.style.transform = 'translateY(0)';\n e.currentTarget.style.boxShadow = 'none';\n }}\n >\n {/* Key label */}\n <div style={keyLabelStyle(keyData)}>\n <span style={{ \n padding: '4px 8px', \n background: hexToRgbaString(getKeyCategoryColor(keyData.category), 0.2),\n borderRadius: '4px',\n minWidth: isMobile ? '40px' : '50px',\n textAlign: 'center' as const,\n }}>\n {keyData.label}\n </span>\n {keyData.labelKorean && (\n <span style={koreanLabelStyle}>\n {keyData.labelKorean}\n </span>\n )}\n </div>\n\n {/* Description (Korean | English) */}\n {keyData.description && keyData.descriptionKorean && (\n <div style={descriptionStyle}>\n {keyData.descriptionKorean} | {keyData.description}\n </div>\n )}\n\n {/* Category badge */}\n <div style={{\n fontSize: isMobile ? '9px' : '10px',\n color: hexToRgbaString(theme.colors.TEXT_TERTIARY),\n textTransform: 'uppercase' as const,\n letterSpacing: '0.5px',\n }}>\n {keyData.category}\n </div>\n </div>\n ))}\n\n {filteredKeys.length === 0 && (\n <div style={{\n gridColumn: '1 / -1',\n textAlign: 'center' as const,\n padding: '40px 20px',\n fontFamily: FONT_FAMILY.KOREAN,\n fontSize: isMobile ? '14px' : '16px',\n color: hexToRgbaString(theme.colors.TEXT_SECONDARY),\n }}>\n 선택한 카테고리에 컨트롤이 없습니다 | No controls in selected category\n </div>\n )}\n </div>\n );\n};\n\nexport default ControlBindingsOverlayHtml;\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA4CA,IAAa,8BAAyE,EACpF,aACA,eACI;CACJ,MAAM,QAAQ,eAAe;EAAE,SAAS;EAAW,MAAM;EAAM;EAAU,CAAC;CAG1E,MAAM,eAAe,cAAkC;
|
|
1
|
+
{"version":3,"file":"ControlBindingsOverlayHtml.js","names":[],"sources":["../../../../../src/components/screens/controls/components/ControlBindingsOverlayHtml.tsx"],"sourcesContent":["/**\n * ControlBindingsOverlayHtml - Display control bindings filtered by selected category\n * \n * Shows key bindings in a clean, organized list with Korean-English bilingual labels.\n * Uses responsive grid/list layout based on device type.\n * \n * @module components/screens/controls/components\n */\n\nimport React, { useMemo } from \"react\";\nimport { FONT_FAMILY } from \"../../../../types/constants\";\nimport { hexToRgbaString } from \"../../../../utils/colorUtils\";\nimport { useKoreanTheme } from \"../../../shared/base/useKoreanTheme\";\nimport {\n filterKeysByCategory,\n getKeyCategoryColor,\n KEYBOARD_LAYOUT,\n type KeyData,\n} from \"../constants/ControlsConstants\";\n\n/**\n * Props for ControlBindingsOverlayHtml component\n */\nexport interface ControlBindingsOverlayHtmlProps {\n /** Selected control category to filter and display */\n readonly selectedTab: 'combat' | 'movement' | 'system';\n /** Whether on mobile device (list layout vs grid) */\n readonly isMobile: boolean;\n}\n\n/**\n * ControlBindingsOverlayHtml Component\n * \n * Displays control bindings filtered by selected category.\n * Uses grid layout on desktop and list layout on mobile.\n * \n * @example\n * ```tsx\n * <ControlBindingsOverlayHtml\n * selectedTab=\"combat\"\n * isMobile={false}\n * />\n * ```\n */\nexport const ControlBindingsOverlayHtml: React.FC<ControlBindingsOverlayHtmlProps> = ({\n selectedTab,\n isMobile,\n}) => {\n const theme = useKoreanTheme({ variant: 'primary', size: 'md', isMobile });\n\n // Filter keys by selected category\n const filteredKeys = useMemo<readonly KeyData[]>(() => {\n return filterKeysByCategory(KEYBOARD_LAYOUT, selectedTab);\n }, [selectedTab]);\n\n // Container styles - grid for desktop, list for mobile\n const containerStyle = useMemo(() => ({\n display: 'grid',\n gridTemplateColumns: isMobile ? '1fr' : 'repeat(auto-fill, minmax(280px, 1fr))',\n gap: isMobile ? '12px' : '16px',\n padding: isMobile ? '15px' : '20px',\n maxHeight: isMobile ? '60vh' : '70vh',\n overflowY: 'auto' as const,\n overflowX: 'hidden' as const,\n background: hexToRgbaString(theme.colors.UI_BACKGROUND_DARK, 0.9),\n borderRadius: '12px',\n border: `2px solid ${hexToRgbaString(theme.colors.UI_BORDER, 0.6)}`,\n boxShadow: `0 4px 15px ${hexToRgbaString(theme.colors.BLACK_SOLID, 0.5)}`,\n }), [isMobile, theme]);\n\n // Individual binding card style\n const getBindingCardStyle = (keyData: KeyData) => ({\n display: 'flex',\n flexDirection: 'column' as const,\n gap: '8px',\n padding: isMobile ? '12px' : '14px',\n background: hexToRgbaString(theme.colors.UI_BACKGROUND_MEDIUM, 0.8),\n border: `2px solid ${hexToRgbaString(getKeyCategoryColor(keyData.category), 0.6)}`,\n borderRadius: '8px',\n transition: 'all 0.2s ease',\n cursor: 'default',\n });\n\n // Key label style (the actual key)\n const keyLabelStyle = (keyData: KeyData) => ({\n fontFamily: FONT_FAMILY.KOREAN,\n fontSize: isMobile ? '16px' : '18px',\n fontWeight: 'bold' as const,\n color: hexToRgbaString(getKeyCategoryColor(keyData.category)),\n textShadow: `0 0 10px ${hexToRgbaString(getKeyCategoryColor(keyData.category), 0.5)}`,\n display: 'flex',\n alignItems: 'center',\n gap: '8px',\n });\n\n // Korean label style\n const koreanLabelStyle = {\n fontFamily: FONT_FAMILY.KOREAN,\n fontSize: isMobile ? '13px' : '14px',\n fontWeight: 'bold' as const,\n color: hexToRgbaString(theme.colors.ACCENT_GOLD),\n };\n\n // Description style\n const descriptionStyle = {\n fontFamily: FONT_FAMILY.KOREAN,\n fontSize: isMobile ? '11px' : '12px',\n color: hexToRgbaString(theme.colors.TEXT_SECONDARY),\n lineHeight: 1.4,\n };\n\n return (\n <div style={containerStyle} data-testid=\"control-bindings\">\n {filteredKeys.map((keyData) => (\n <div\n key={keyData.code}\n style={getBindingCardStyle(keyData)}\n data-testid={`binding-${keyData.code}`}\n onMouseEnter={(e) => {\n e.currentTarget.style.background = hexToRgbaString(theme.colors.UI_BACKGROUND_MEDIUM, 1);\n e.currentTarget.style.borderColor = hexToRgbaString(getKeyCategoryColor(keyData.category), 1);\n e.currentTarget.style.transform = 'translateY(-2px)';\n e.currentTarget.style.boxShadow = `0 4px 15px ${hexToRgbaString(getKeyCategoryColor(keyData.category), 0.4)}`;\n }}\n onMouseLeave={(e) => {\n e.currentTarget.style.background = hexToRgbaString(theme.colors.UI_BACKGROUND_MEDIUM, 0.8);\n e.currentTarget.style.borderColor = hexToRgbaString(getKeyCategoryColor(keyData.category), 0.6);\n e.currentTarget.style.transform = 'translateY(0)';\n e.currentTarget.style.boxShadow = 'none';\n }}\n >\n {/* Key label */}\n <div style={keyLabelStyle(keyData)}>\n <span style={{ \n padding: '4px 8px', \n background: hexToRgbaString(getKeyCategoryColor(keyData.category), 0.2),\n borderRadius: '4px',\n minWidth: isMobile ? '40px' : '50px',\n textAlign: 'center' as const,\n }}>\n {keyData.label}\n </span>\n {keyData.labelKorean && (\n <span style={koreanLabelStyle}>\n {keyData.labelKorean}\n </span>\n )}\n </div>\n\n {/* Description (Korean | English) */}\n {keyData.description && keyData.descriptionKorean && (\n <div style={descriptionStyle}>\n {keyData.descriptionKorean} | {keyData.description}\n </div>\n )}\n\n {/* Category badge */}\n <div style={{\n fontSize: isMobile ? '9px' : '10px',\n color: hexToRgbaString(theme.colors.TEXT_TERTIARY),\n textTransform: 'uppercase' as const,\n letterSpacing: '0.5px',\n }}>\n {keyData.category}\n </div>\n </div>\n ))}\n\n {filteredKeys.length === 0 && (\n <div style={{\n gridColumn: '1 / -1',\n textAlign: 'center' as const,\n padding: '40px 20px',\n fontFamily: FONT_FAMILY.KOREAN,\n fontSize: isMobile ? '14px' : '16px',\n color: hexToRgbaString(theme.colors.TEXT_SECONDARY),\n }}>\n 선택한 카테고리에 컨트롤이 없습니다 | No controls in selected category\n </div>\n )}\n </div>\n );\n};\n\nexport default ControlBindingsOverlayHtml;\n"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ControlCategoryTabsOverlayHtml.js","names":[],"sources":["../../../../../src/components/screens/controls/components/ControlCategoryTabsOverlayHtml.tsx"],"sourcesContent":["/**\n * ControlCategoryTabs - Tab navigation for control categories\n * \n * Allows switching between Combat, Movement, and System control views\n * Uses BaseButtonOverlayHtml for consistent styling\n * \n * @module components/screens/controls/components\n */\n\nimport React, { useMemo } from \"react\";\nimport { hexToRgbaString } from \"../../../../utils/colorUtils\";\nimport { useKoreanTheme } from \"../../../shared/base/useKoreanTheme\";\nimport { CONTROL_CATEGORIES } from \"../constants/ControlsConstants\";\n\nexport interface ControlCategoryTabsProps {\n readonly selectedTab: 'combat' | 'movement' | 'system';\n readonly onTabChange: (tab: 'combat' | 'movement' | 'system') => void;\n readonly isMobile: boolean;\n}\n\n/**\n * ControlCategoryTabs Component\n * \n * Displays tabs for switching between control categories.\n * Uses Korean theming and responsive sizing.\n * \n * @example\n * ```tsx\n * <ControlCategoryTabs\n * selectedTab=\"combat\"\n * onTabChange={(tab) => setSelectedTab(tab)}\n * isMobile={false}\n * />\n * ```\n */\nexport const ControlCategoryTabs: React.FC<ControlCategoryTabsProps> = ({\n selectedTab,\n onTabChange,\n isMobile,\n}) => {\n const theme = useKoreanTheme({ variant: 'primary', size: 'md', isMobile });\n\n const tabStyle = useMemo(() => ({\n display: 'flex',\n flexDirection: 'row' as const,\n gap: isMobile ? '8px' : '12px',\n justifyContent: 'center',\n alignItems: 'center',\n padding: isMobile ? '10px' : '15px',\n flexWrap: 'wrap' as const,\n }), [isMobile]);\n\n const buttonBaseStyle = useMemo(() => ({\n padding: isMobile ? '8px 16px' : '12px 24px',\n borderRadius: '8px',\n border: `2px solid ${hexToRgbaString(theme.colors.UI_BORDER, 0.6)}`,\n cursor: 'pointer',\n transition: 'all 0.2s ease',\n fontFamily: 'inherit',\n fontSize: isMobile ? '14px' : '16px',\n fontWeight: 'bold' as const,\n display: 'flex',\n alignItems: 'center',\n gap: '8px',\n }), [isMobile, theme]);\n\n return (\n <div style={tabStyle} data-testid=\"control-category-tabs\">\n {CONTROL_CATEGORIES.map((category) => {\n const isSelected = selectedTab === category.id;\n \n const buttonStyle = {\n ...buttonBaseStyle,\n background: isSelected\n ? hexToRgbaString(category.color, 0.3)\n : hexToRgbaString(theme.colors.UI_BACKGROUND_DARK, 0.8),\n borderColor: isSelected\n ? hexToRgbaString(category.color, 1)\n : hexToRgbaString(theme.colors.UI_BORDER, 0.6),\n color: isSelected\n ? `#${category.color.toString(16).padStart(6, '0')}`\n : `#${theme.colors.TEXT_SECONDARY.toString(16).padStart(6, '0')}`,\n };\n\n return (\n <button\n key={category.id}\n style={buttonStyle}\n onClick={() => onTabChange(category.id as 'combat' | 'movement' | 'system')}\n data-testid={`tab-${category.id}`}\n onMouseEnter={(e) => {\n if (!isSelected) {\n e.currentTarget.style.background = hexToRgbaString(category.color, 0.1);\n e.currentTarget.style.borderColor = hexToRgbaString(category.color, 0.8);\n }\n }}\n onMouseLeave={(e) => {\n if (!isSelected) {\n e.currentTarget.style.background = hexToRgbaString(theme.colors.UI_BACKGROUND_DARK, 0.8);\n e.currentTarget.style.borderColor = hexToRgbaString(theme.colors.UI_BORDER, 0.6);\n }\n }}\n >\n <span role=\"img\" aria-label={category.english}>{category.icon}</span>\n <span>{category.korean} | {category.english}</span>\n </button>\n );\n })}\n </div>\n );\n};\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAmCA,IAAa,uBAA2D,EACtE,aACA,aACA,eACI;CACJ,MAAM,QAAQ,eAAe;EAAE,SAAS;EAAW,MAAM;EAAM;EAAU,CAAC;CAE1E,MAAM,WAAW,eAAe;EAC9B,SAAS;EACT,eAAe;EACf,KAAK,WAAW,QAAQ;EACxB,gBAAgB;EAChB,YAAY;EACZ,SAAS,WAAW,SAAS;EAC7B,UAAU;EACX,GAAG,CAAC,SAAS,CAAC;CAEf,MAAM,kBAAkB,eAAe;EACrC,SAAS,WAAW,aAAa;EACjC,cAAc;EACd,QAAQ,aAAa,gBAAgB,MAAM,OAAO,WAAW,GAAI;EACjE,QAAQ;EACR,YAAY;EACZ,YAAY;EACZ,UAAU,WAAW,SAAS;EAC9B,YAAY;EACZ,SAAS;EACT,YAAY;EACZ,KAAK;EACN,GAAG,CAAC,UAAU,MAAM,CAAC;
|
|
1
|
+
{"version":3,"file":"ControlCategoryTabsOverlayHtml.js","names":[],"sources":["../../../../../src/components/screens/controls/components/ControlCategoryTabsOverlayHtml.tsx"],"sourcesContent":["/**\n * ControlCategoryTabs - Tab navigation for control categories\n * \n * Allows switching between Combat, Movement, and System control views\n * Uses BaseButtonOverlayHtml for consistent styling\n * \n * @module components/screens/controls/components\n */\n\nimport React, { useMemo } from \"react\";\nimport { hexToRgbaString } from \"../../../../utils/colorUtils\";\nimport { useKoreanTheme } from \"../../../shared/base/useKoreanTheme\";\nimport { CONTROL_CATEGORIES } from \"../constants/ControlsConstants\";\n\nexport interface ControlCategoryTabsProps {\n readonly selectedTab: 'combat' | 'movement' | 'system';\n readonly onTabChange: (tab: 'combat' | 'movement' | 'system') => void;\n readonly isMobile: boolean;\n}\n\n/**\n * ControlCategoryTabs Component\n * \n * Displays tabs for switching between control categories.\n * Uses Korean theming and responsive sizing.\n * \n * @example\n * ```tsx\n * <ControlCategoryTabs\n * selectedTab=\"combat\"\n * onTabChange={(tab) => setSelectedTab(tab)}\n * isMobile={false}\n * />\n * ```\n */\nexport const ControlCategoryTabs: React.FC<ControlCategoryTabsProps> = ({\n selectedTab,\n onTabChange,\n isMobile,\n}) => {\n const theme = useKoreanTheme({ variant: 'primary', size: 'md', isMobile });\n\n const tabStyle = useMemo(() => ({\n display: 'flex',\n flexDirection: 'row' as const,\n gap: isMobile ? '8px' : '12px',\n justifyContent: 'center',\n alignItems: 'center',\n padding: isMobile ? '10px' : '15px',\n flexWrap: 'wrap' as const,\n }), [isMobile]);\n\n const buttonBaseStyle = useMemo(() => ({\n padding: isMobile ? '8px 16px' : '12px 24px',\n borderRadius: '8px',\n border: `2px solid ${hexToRgbaString(theme.colors.UI_BORDER, 0.6)}`,\n cursor: 'pointer',\n transition: 'all 0.2s ease',\n fontFamily: 'inherit',\n fontSize: isMobile ? '14px' : '16px',\n fontWeight: 'bold' as const,\n display: 'flex',\n alignItems: 'center',\n gap: '8px',\n }), [isMobile, theme]);\n\n return (\n <div style={tabStyle} data-testid=\"control-category-tabs\">\n {CONTROL_CATEGORIES.map((category) => {\n const isSelected = selectedTab === category.id;\n \n const buttonStyle = {\n ...buttonBaseStyle,\n background: isSelected\n ? hexToRgbaString(category.color, 0.3)\n : hexToRgbaString(theme.colors.UI_BACKGROUND_DARK, 0.8),\n borderColor: isSelected\n ? hexToRgbaString(category.color, 1)\n : hexToRgbaString(theme.colors.UI_BORDER, 0.6),\n color: isSelected\n ? `#${category.color.toString(16).padStart(6, '0')}`\n : `#${theme.colors.TEXT_SECONDARY.toString(16).padStart(6, '0')}`,\n };\n\n return (\n <button\n key={category.id}\n style={buttonStyle}\n onClick={() => onTabChange(category.id as 'combat' | 'movement' | 'system')}\n data-testid={`tab-${category.id}`}\n onMouseEnter={(e) => {\n if (!isSelected) {\n e.currentTarget.style.background = hexToRgbaString(category.color, 0.1);\n e.currentTarget.style.borderColor = hexToRgbaString(category.color, 0.8);\n }\n }}\n onMouseLeave={(e) => {\n if (!isSelected) {\n e.currentTarget.style.background = hexToRgbaString(theme.colors.UI_BACKGROUND_DARK, 0.8);\n e.currentTarget.style.borderColor = hexToRgbaString(theme.colors.UI_BORDER, 0.6);\n }\n }}\n >\n <span role=\"img\" aria-label={category.english}>{category.icon}</span>\n <span>{category.korean} | {category.english}</span>\n </button>\n );\n })}\n </div>\n );\n};\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAmCA,IAAa,uBAA2D,EACtE,aACA,aACA,eACI;CACJ,MAAM,QAAQ,eAAe;EAAE,SAAS;EAAW,MAAM;EAAM;EAAU,CAAC;CAE1E,MAAM,WAAW,eAAe;EAC9B,SAAS;EACT,eAAe;EACf,KAAK,WAAW,QAAQ;EACxB,gBAAgB;EAChB,YAAY;EACZ,SAAS,WAAW,SAAS;EAC7B,UAAU;EACX,GAAG,CAAC,SAAS,CAAC;CAEf,MAAM,kBAAkB,eAAe;EACrC,SAAS,WAAW,aAAa;EACjC,cAAc;EACd,QAAQ,aAAa,gBAAgB,MAAM,OAAO,WAAW,GAAI;EACjE,QAAQ;EACR,YAAY;EACZ,YAAY;EACZ,UAAU,WAAW,SAAS;EAC9B,YAAY;EACZ,SAAS;EACT,YAAY;EACZ,KAAK;EACN,GAAG,CAAC,UAAU,MAAM,CAAC;CAEtB,OACE,oBAAC,OAAD;EAAK,OAAO;EAAU,eAAY;YAC/B,mBAAmB,KAAK,aAAa;GACpC,MAAM,aAAa,gBAAgB,SAAS;GAe5C,OACE,qBAAC,UAAD;IAEE,OAAO;KAfT,GAAG;KACH,YAAY,aACR,gBAAgB,SAAS,OAAO,GAAI,GACpC,gBAAgB,MAAM,OAAO,oBAAoB,GAAI;KACzD,aAAa,aACT,gBAAgB,SAAS,OAAO,EAAE,GAClC,gBAAgB,MAAM,OAAO,WAAW,GAAI;KAChD,OAAO,aACH,IAAI,SAAS,MAAM,SAAS,GAAG,CAAC,SAAS,GAAG,IAAI,KAChD,IAAI,MAAM,OAAO,eAAe,SAAS,GAAG,CAAC,SAAS,GAAG,IAAI;KAMxD;IACP,eAAe,YAAY,SAAS,GAAuC;IAC3E,eAAa,OAAO,SAAS;IAC7B,eAAe,MAAM;KACnB,IAAI,CAAC,YAAY;MACf,EAAE,cAAc,MAAM,aAAa,gBAAgB,SAAS,OAAO,GAAI;MACvE,EAAE,cAAc,MAAM,cAAc,gBAAgB,SAAS,OAAO,GAAI;;;IAG5E,eAAe,MAAM;KACnB,IAAI,CAAC,YAAY;MACf,EAAE,cAAc,MAAM,aAAa,gBAAgB,MAAM,OAAO,oBAAoB,GAAI;MACxF,EAAE,cAAc,MAAM,cAAc,gBAAgB,MAAM,OAAO,WAAW,GAAI;;;cAdtF,CAkBE,oBAAC,QAAD;KAAM,MAAK;KAAM,cAAY,SAAS;eAAU,SAAS;KAAY,CAAA,EACrE,qBAAC,QAAD,EAAA,UAAA;KAAO,SAAS;KAAO;KAAI,SAAS;KAAe,EAAA,CAAA,CAC5C;MAnBF,SAAS,GAmBP;IAEX;EACE,CAAA"}
|
|
@@ -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 // Gamepad body material\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 // Cleanup material on unmount to prevent memory leaks\n useEffect(() => {\n return () => {\n bodyMaterial.dispose();\n };\n }, [bodyMaterial]);\n\n // Button positions (simplified layout)\n const buttonPositions = useMemo(\n () => ({\n // Face buttons (right side)\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 // Shoulder buttons (top)\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 // Menu buttons (center)\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 // Stick buttons (lower)\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;CAEJ,MAAM,eAAe,cAEjB,IAAI,MAAM,qBAAqB;EAC7B,OAAO,cAAc;EACrB,WAAW;EACX,WAAW;EACX,aAAa;EACb,SAAS;EACV,CAAC,EACJ,EAAE,CACH;
|
|
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 // Gamepad body material\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 // Cleanup material on unmount to prevent memory leaks\n useEffect(() => {\n return () => {\n bodyMaterial.dispose();\n };\n }, [bodyMaterial]);\n\n // Button positions (simplified layout)\n const buttonPositions = useMemo(\n () => ({\n // Face buttons (right side)\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 // Shoulder buttons (top)\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 // Menu buttons (center)\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 // Stick buttons (lower)\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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"}
|