blacktrigram 0.7.47 → 0.7.49
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/App2.js.map +1 -1
- package/lib/audio/AudioAssetLoader.js.map +1 -1
- package/lib/audio/AudioAssetRegistry.js.map +1 -1
- package/lib/audio/AudioCache.js.map +1 -1
- package/lib/audio/AudioManager.js.map +1 -1
- package/lib/audio/AudioMonitor.js.map +1 -1
- package/lib/audio/AudioPool.js.map +1 -1
- package/lib/audio/AudioProvider.js.map +1 -1
- package/lib/audio/AudioUtils.js.map +1 -1
- package/lib/audio/BoneImpactAudioMap.js.map +1 -1
- package/lib/audio/VariantSelector.js.map +1 -1
- package/lib/audio/types.js.map +1 -1
- package/lib/components/screens/combat/CombatScreen3D.d.ts.map +1 -1
- package/lib/components/screens/combat/CombatScreen3D.js +29 -25
- package/lib/components/screens/combat/CombatScreen3D.js.map +1 -1
- package/lib/components/screens/combat/components/controls/CombatButtons.js.map +1 -1
- package/lib/components/screens/combat/components/controls/CombatControlsPanel.js.map +1 -1
- package/lib/components/screens/combat/components/controls/ControlsGuide.js.map +1 -1
- package/lib/components/screens/combat/components/controls/KeyboardHints.js.map +1 -1
- package/lib/components/screens/combat/components/controls/PauseMenu.js.map +1 -1
- package/lib/components/screens/combat/components/controls/PauseMenuButton.js.map +1 -1
- package/lib/components/screens/combat/components/controls/QuickSettings.js.map +1 -1
- package/lib/components/screens/combat/components/effects/BloodDecals3D.js.map +1 -1
- package/lib/components/screens/combat/components/effects/BloodLossOverlayHtml.js.map +1 -1
- package/lib/components/screens/combat/components/effects/BloodParticles3D.js.map +1 -1
- package/lib/components/screens/combat/components/effects/BloodViscosity3D.js.map +1 -1
- package/lib/components/screens/combat/components/effects/CombatParticleEffects3D.js.map +1 -1
- package/lib/components/screens/combat/components/effects/ConsciousnessBlur.js.map +1 -1
- package/lib/components/screens/combat/components/effects/InternalDamage3D.js.map +1 -1
- package/lib/components/screens/combat/components/effects/PainVignette.js.map +1 -1
- package/lib/components/screens/combat/components/effects/ParticleAudio3D.js.map +1 -1
- package/lib/components/screens/combat/components/effects/TraumaOverlay3D.js.map +1 -1
- package/lib/components/screens/combat/components/feedback/MatchCountdown.js.map +1 -1
- package/lib/components/screens/combat/components/feedback/RoundAnnouncementOverlayHtml.js.map +1 -1
- package/lib/components/screens/combat/components/feedback/RoundDisplayStatus.js.map +1 -1
- package/lib/components/screens/combat/components/feedback/RoundStartAnnouncementOverlayHtml.js.map +1 -1
- package/lib/components/screens/combat/components/hud/CombatBottomHUD.d.ts.map +1 -1
- package/lib/components/screens/combat/components/hud/CombatBottomHUD.js +2 -2
- package/lib/components/screens/combat/components/hud/CombatBottomHUD.js.map +1 -1
- package/lib/components/screens/combat/components/hud/CombatLeftHUD.js.map +1 -1
- package/lib/components/screens/combat/components/hud/CombatPortraitStatusStrip.js +1 -1
- package/lib/components/screens/combat/components/hud/CombatPortraitStatusStrip.js.map +1 -1
- package/lib/components/screens/combat/components/hud/CombatRightHUD.js.map +1 -1
- package/lib/components/screens/combat/components/hud/CombatTopHUD.d.ts.map +1 -1
- package/lib/components/screens/combat/components/hud/CombatTopHUD.js +2 -1
- package/lib/components/screens/combat/components/hud/CombatTopHUD.js.map +1 -1
- package/lib/components/screens/combat/components/hud/DifficultyIndicator.js.map +1 -1
- package/lib/components/screens/combat/components/hud/FPSMonitor.js.map +1 -1
- package/lib/components/screens/combat/components/hud/MobileControlsWrapper.js.map +1 -1
- package/lib/components/screens/combat/components/hud/PlayerStateOverlayHtml.js.map +1 -1
- package/lib/components/screens/combat/components/indicators/BalanceIndicator.js.map +1 -1
- package/lib/components/screens/combat/components/indicators/InputBufferDisplay.js.map +1 -1
- package/lib/components/screens/combat/components/indicators/StaminaWarning.js.map +1 -1
- package/lib/components/screens/combat/components/indicators/TechniqueNameDisplay.js.map +1 -1
- package/lib/components/screens/combat/helpers/AnimationUpdater.d.ts.map +1 -1
- package/lib/components/screens/combat/helpers/AnimationUpdater.js +4 -2
- package/lib/components/screens/combat/helpers/AnimationUpdater.js.map +1 -1
- package/lib/components/screens/combat/helpers/combatHelpers.js.map +1 -1
- package/lib/components/screens/combat/hooks/useAICombat.js.map +1 -1
- package/lib/components/screens/combat/hooks/useCombatActions.js.map +1 -1
- package/lib/components/screens/combat/hooks/useCombatAttackMovement.js.map +1 -1
- package/lib/components/screens/combat/hooks/useCombatAudio.js.map +1 -1
- package/lib/components/screens/combat/hooks/useCombatLayout.d.ts.map +1 -1
- package/lib/components/screens/combat/hooks/useCombatLayout.js +11 -5
- package/lib/components/screens/combat/hooks/useCombatLayout.js.map +1 -1
- package/lib/components/screens/combat/hooks/useCombatState.js.map +1 -1
- package/lib/components/screens/controls/ControlsScreen3D.js +1 -1
- package/lib/components/screens/controls/ControlsScreen3D.js.map +1 -1
- package/lib/components/screens/controls/components/ControlBindingsOverlayHtml.js.map +1 -1
- package/lib/components/screens/controls/components/ControlCategoryTabsOverlayHtml.js.map +1 -1
- package/lib/components/screens/controls/components/GamepadVisualization3D.js.map +1 -1
- package/lib/components/screens/controls/components/InteractiveControlDemoOverlayHtml.js.map +1 -1
- package/lib/components/screens/controls/components/Key3D.js.map +1 -1
- package/lib/components/screens/controls/components/VisualKeyboard3D.js.map +1 -1
- package/lib/components/screens/controls/constants/ControlsConstants.js.map +1 -1
- package/lib/components/screens/controls/hooks/useControlsState.js.map +1 -1
- package/lib/components/screens/endscreen/EndScreen3D.js.map +1 -1
- package/lib/components/screens/endscreen/components/DefeatAnimation3D.js.map +1 -1
- package/lib/components/screens/endscreen/components/MatchStatisticsDisplayOverlayHtml.js.map +1 -1
- package/lib/components/screens/endscreen/components/NavigationButtonsOverlayHtml.js.map +1 -1
- package/lib/components/screens/endscreen/components/PerformanceBreakdownOverlayHtml.js.map +1 -1
- package/lib/components/screens/endscreen/components/PerformanceRatingOverlayHtml.js.map +1 -1
- package/lib/components/screens/endscreen/components/VictoryAnimation3D.js.map +1 -1
- package/lib/components/screens/endscreen/components/WinnerDisplayOverlayHtml.js.map +1 -1
- package/lib/components/screens/intro/IntroScreen3D.js +1 -1
- package/lib/components/screens/intro/IntroScreen3D.js.map +1 -1
- package/lib/components/screens/intro/components/AbilityListOverlayHtml.js.map +1 -1
- package/lib/components/screens/intro/components/ArchetypeCardGridOverlayHtml.js.map +1 -1
- package/lib/components/screens/intro/components/ArchetypeCardOverlayHtml.js.map +1 -1
- package/lib/components/screens/intro/components/ArchetypeDisplayOverlayHtml.js.map +1 -1
- package/lib/components/screens/intro/components/EnhancedArchetypeDisplayOverlayHtml.js.map +1 -1
- package/lib/components/screens/intro/components/MenuButtonsOverlayHtml.js.map +1 -1
- package/lib/components/screens/intro/components/MenuSectionOverlayHtml.js.map +1 -1
- package/lib/components/screens/intro/components/StatBarOverlayHtml.js.map +1 -1
- package/lib/components/screens/philosophy/PhilosophyScreen3D.js +1 -1
- package/lib/components/screens/philosophy/PhilosophyScreen3D.js.map +1 -1
- package/lib/components/screens/training/TrainingScreen3D.d.ts.map +1 -1
- package/lib/components/screens/training/TrainingScreen3D.js +3 -11
- package/lib/components/screens/training/TrainingScreen3D.js.map +1 -1
- package/lib/components/screens/training/components/AnatomyControlsOverlayHtml.js.map +1 -1
- package/lib/components/screens/training/components/AnatomyOverlay3D.js.map +1 -1
- package/lib/components/screens/training/components/FootPlacementMarkers3D.js.map +1 -1
- package/lib/components/screens/training/components/FootworkDrillsOverlayHtml.js.map +1 -1
- package/lib/components/screens/training/components/HitFeedbackEffect3D.js.map +1 -1
- package/lib/components/screens/training/components/TrainingButtonsOverlayHtml.js.map +1 -1
- package/lib/components/screens/training/components/TrainingControlsOverlayHtml.js.map +1 -1
- package/lib/components/screens/training/components/TrainingDummy3D.js.map +1 -1
- package/lib/components/screens/training/components/TrainingFeedbackOverlayHtml.js.map +1 -1
- package/lib/components/screens/training/components/TrainingModeSelectorOverlayHtml.js.map +1 -1
- package/lib/components/screens/training/components/TrainingStatsOverlayHtml.js.map +1 -1
- package/lib/components/screens/training/components/VitalPointMarker3D.js.map +1 -1
- package/lib/components/screens/training/components/VitalPointTrainingOverlayHtml.js.map +1 -1
- package/lib/components/screens/training/components/hud/TrainingBottomHUD.d.ts.map +1 -1
- package/lib/components/screens/training/components/hud/TrainingBottomHUD.js +2 -2
- package/lib/components/screens/training/components/hud/TrainingBottomHUD.js.map +1 -1
- package/lib/components/screens/training/components/hud/TrainingLeftHUD.js.map +1 -1
- package/lib/components/screens/training/components/hud/TrainingRightHUD.js.map +1 -1
- package/lib/components/screens/training/components/hud/TrainingTopHUD.js.map +1 -1
- package/lib/components/screens/training/hooks/useAttackMovement.js.map +1 -1
- package/lib/components/screens/training/hooks/useTrainingActions.d.ts +1 -0
- package/lib/components/screens/training/hooks/useTrainingActions.d.ts.map +1 -1
- package/lib/components/screens/training/hooks/useTrainingActions.js +6 -4
- package/lib/components/screens/training/hooks/useTrainingActions.js.map +1 -1
- package/lib/components/screens/training/hooks/useTrainingLayout.d.ts.map +1 -1
- package/lib/components/screens/training/hooks/useTrainingLayout.js +11 -5
- package/lib/components/screens/training/hooks/useTrainingLayout.js.map +1 -1
- package/lib/components/screens/training/hooks/useTrainingState.js.map +1 -1
- package/lib/components/shared/base/BaseButton.js.map +1 -1
- package/lib/components/shared/base/BaseButtonOverlayHtml.js.map +1 -1
- package/lib/components/shared/base/BasePanel.js.map +1 -1
- package/lib/components/shared/base/BaseText.js.map +1 -1
- package/lib/components/shared/base/useKoreanTheme.js.map +1 -1
- package/lib/components/shared/debug/PerformanceDebugOverlayHtml.js.map +1 -1
- package/lib/components/shared/mobile/ActionButtons.js.map +1 -1
- package/lib/components/shared/mobile/GestureRecognizerPure.js.map +1 -1
- package/lib/components/shared/mobile/HapticController.js.map +1 -1
- package/lib/components/shared/mobile/MobileControlsPure.js.map +1 -1
- package/lib/components/shared/mobile/StanceWheelPure.js.map +1 -1
- package/lib/components/shared/mobile/TouchOptimizer.js.map +1 -1
- package/lib/components/shared/mobile/VirtualDPad.js.map +1 -1
- package/lib/components/shared/three/anatomy/BodySurface.js.map +1 -1
- package/lib/components/shared/three/anatomy/BoneAttachedMuscles.js.map +1 -1
- package/lib/components/shared/three/anatomy/BoneClothing.js.map +1 -1
- package/lib/components/shared/three/anatomy/BoneRenderer.js.map +1 -1
- package/lib/components/shared/three/anatomy/Face3D.js.map +1 -1
- package/lib/components/shared/three/anatomy/Foot3D.js.map +1 -1
- package/lib/components/shared/three/anatomy/Hand3D.js.map +1 -1
- package/lib/components/shared/three/effects/ActionFeedback.js.map +1 -1
- package/lib/components/shared/three/effects/DamageNumbers.js.map +1 -1
- package/lib/components/shared/three/effects/HitEffects3D.js.map +1 -1
- package/lib/components/shared/three/effects/PlayerStateIndicators.js.map +1 -1
- package/lib/components/shared/three/effects/StanceSymbol3D.js.map +1 -1
- package/lib/components/shared/three/effects/StanceTransitionEffect.js.map +1 -1
- package/lib/components/shared/three/effects/VitalPointMarkers3D.js.map +1 -1
- package/lib/components/shared/three/indicators/ElementalColorSystem.js.map +1 -1
- package/lib/components/shared/three/indicators/GuardIndicator.js.map +1 -1
- package/lib/components/shared/three/indicators/HapticFeedback.js.map +1 -1
- package/lib/components/shared/three/indicators/StanceChangeIndicator.js.map +1 -1
- package/lib/components/shared/three/models/Player3DWithTransitions.js.map +1 -1
- package/lib/components/shared/three/models/SkeletalPlayer3D.d.ts.map +1 -1
- package/lib/components/shared/three/models/SkeletalPlayer3D.js +7 -5
- package/lib/components/shared/three/models/SkeletalPlayer3D.js.map +1 -1
- package/lib/components/shared/three/optimization/AdaptiveQuality.js.map +1 -1
- package/lib/components/shared/three/scene/AtmosphericParticles3D.js.map +1 -1
- package/lib/components/shared/three/scene/BackgroundScene3D.js.map +1 -1
- package/lib/components/shared/three/scene/CombatArena3D.js.map +1 -1
- package/lib/components/shared/three/scene/KoreanSignage3D.js.map +1 -1
- package/lib/components/shared/three/ui/ArchetypeCard.js.map +1 -1
- package/lib/components/shared/three/ui/BodyPartHealthDisplay.js.map +1 -1
- package/lib/components/shared/three/ui/BreathingIndicator2.js.map +1 -1
- package/lib/components/shared/three/ui/CombatReadinessBar.js.map +1 -1
- package/lib/components/shared/three/ui/ComboCounter.js.map +1 -1
- package/lib/components/shared/three/ui/HealthBar.js.map +1 -1
- package/lib/components/shared/three/ui/KoreanButton.js.map +1 -1
- package/lib/components/shared/three/ui/KoreanPanel.js.map +1 -1
- package/lib/components/shared/three/ui/KoreanText.js.map +1 -1
- package/lib/components/shared/three/ui/MenuList.js.map +1 -1
- package/lib/components/shared/three/ui/PlayerHUD.js.map +1 -1
- package/lib/components/shared/three/ui/ProgressBar.js.map +1 -1
- package/lib/components/shared/three/ui/SpeedIndicatorHUD.js.map +1 -1
- package/lib/components/shared/three/ui/StaminaBar.js.map +1 -1
- package/lib/components/shared/three/ui/TechniqueBar.js.map +1 -1
- package/lib/components/shared/three/ui/TechniqueCard.js.map +1 -1
- package/lib/components/shared/three/ui/VitalPointOverlayControlsHtml.js.map +1 -1
- package/lib/components/shared/ui/BackButton.js.map +1 -1
- package/lib/components/shared/ui/BaseHUDContainer.js.map +1 -1
- package/lib/components/shared/ui/CombatTimer.js.map +1 -1
- package/lib/components/shared/ui/ErrorModal.js.map +1 -1
- package/lib/components/shared/ui/LoadingState.js.map +1 -1
- package/lib/components/shared/ui/SplashScreen.js +2 -2
- package/lib/components/shared/ui/SplashScreen.js.map +1 -1
- package/lib/components/shared/ui/VitalPointOverlayControlsPure.js.map +1 -1
- package/lib/components/shared/ui/VolumeControl.js.map +1 -1
- package/lib/components/shared/ui/shared/ConfirmDialog.js.map +1 -1
- package/lib/components/ui/combat/BalanceIndicatorOverlayHtml.js.map +1 -1
- package/lib/constants/bodyDimensions.js.map +1 -1
- package/lib/constants/bodyRenderingConstants.js.map +1 -1
- package/lib/data/archetypeClothing.js.map +1 -1
- package/lib/data/archetypePhysicalAttributes.js.map +1 -1
- package/lib/data/techniqueMappings.js.map +1 -1
- package/lib/data/techniques.js.map +1 -1
- package/lib/hooks/useActionFeedback.js.map +1 -1
- package/lib/hooks/useBalanceAnimations.js.map +1 -1
- package/lib/hooks/useCombatTimer.js.map +1 -1
- package/lib/hooks/useDebounce.js.map +1 -1
- package/lib/hooks/useHUDLayout.d.ts.map +1 -1
- package/lib/hooks/useHUDLayout.js +3 -2
- package/lib/hooks/useHUDLayout.js.map +1 -1
- package/lib/hooks/useHandPoseTransitions.js.map +1 -1
- package/lib/hooks/useKeyboardControls.js.map +1 -1
- package/lib/hooks/useMatchCountdown.js.map +1 -1
- package/lib/hooks/useMuscleActivation.js.map +1 -1
- package/lib/hooks/usePauseMenu.js.map +1 -1
- package/lib/hooks/usePlayerAnimation.js.map +1 -1
- package/lib/hooks/useResponsiveLayout.js.map +1 -1
- package/lib/hooks/useRoundTransition.js.map +1 -1
- package/lib/hooks/useSkeletalAnimation.d.ts.map +1 -1
- package/lib/hooks/useSkeletalAnimation.js +1 -1
- package/lib/hooks/useSkeletalAnimation.js.map +1 -1
- package/lib/hooks/useTechniqueSelection.js.map +1 -1
- package/lib/hooks/useThrottle.js.map +1 -1
- package/lib/hooks/useTouchControls.js.map +1 -1
- package/lib/hooks/useWebGLContextLossHandler.js.map +1 -1
- package/lib/hooks/useWindowSize.js.map +1 -1
- package/lib/systems/CombatSystem.js.map +1 -1
- package/lib/systems/EffectCalculator.js.map +1 -1
- package/lib/systems/LayoutSystem.js.map +1 -1
- package/lib/systems/PlayerEffectManager.js.map +1 -1
- package/lib/systems/ResponsiveScaling.js.map +1 -1
- package/lib/systems/TrigramSystem.js.map +1 -1
- package/lib/systems/VitalPointSystem.js.map +1 -1
- package/lib/systems/ai/AIPersonality.js.map +1 -1
- package/lib/systems/ai/AdaptiveDifficulty.js +16 -16
- package/lib/systems/ai/AdaptiveDifficulty.js.map +1 -1
- package/lib/systems/ai/ArchetypeEnforcer.js.map +1 -1
- package/lib/systems/ai/ComboSystem.js.map +1 -1
- package/lib/systems/ai/DecisionTree.js.map +1 -1
- package/lib/systems/ai/TrainingAI.js.map +1 -1
- package/lib/systems/ai/types.js.map +1 -1
- package/lib/systems/animation/builders/AnimationBuilder.js.map +1 -1
- package/lib/systems/animation/builders/HandPoseApplicator.js.map +1 -1
- package/lib/systems/animation/builders/HandPoses.js.map +1 -1
- package/lib/systems/animation/builders/KeyframeConfig.js.map +1 -1
- package/lib/systems/animation/builders/KeyframeInterpolation.js.map +1 -1
- package/lib/systems/animation/builders/KickPhaseApplicator.d.ts +6 -0
- package/lib/systems/animation/builders/KickPhaseApplicator.d.ts.map +1 -1
- package/lib/systems/animation/builders/KickPhaseApplicator.js +16 -9
- package/lib/systems/animation/builders/KickPhaseApplicator.js.map +1 -1
- package/lib/systems/animation/builders/KoreanGuardPositions.d.ts +4 -4
- package/lib/systems/animation/builders/KoreanGuardPositions.js.map +1 -1
- package/lib/systems/animation/builders/MartialArtsAnimationBuilder.d.ts +1 -1
- package/lib/systems/animation/builders/MartialArtsAnimationBuilder.d.ts.map +1 -1
- package/lib/systems/animation/builders/MartialArtsAnimationBuilder.js +5 -5
- package/lib/systems/animation/builders/MartialArtsAnimationBuilder.js.map +1 -1
- package/lib/systems/animation/builders/MartialArtsConstants.d.ts +112 -71
- package/lib/systems/animation/builders/MartialArtsConstants.d.ts.map +1 -1
- package/lib/systems/animation/builders/MartialArtsConstants.js +113 -72
- package/lib/systems/animation/builders/MartialArtsConstants.js.map +1 -1
- package/lib/systems/animation/builders/MartialPoseApplicator.js.map +1 -1
- package/lib/systems/animation/builders/PunchPhaseApplicator.js.map +1 -1
- package/lib/systems/animation/builders/SkeletonRig.js.map +1 -1
- package/lib/systems/animation/builders/TrigramGuardApplicator.js.map +1 -1
- package/lib/systems/animation/catalogs/AttackAnimations.js.map +1 -1
- package/lib/systems/animation/catalogs/BasicAnimations.js.map +1 -1
- package/lib/systems/animation/catalogs/ComboAnimations.js.map +1 -1
- package/lib/systems/animation/catalogs/DarkOpsAnimations.js.map +1 -1
- package/lib/systems/animation/catalogs/DefensiveAnimations.js.map +1 -1
- package/lib/systems/animation/catalogs/ElbowKneeAnimations.js.map +1 -1
- package/lib/systems/animation/catalogs/EnhancedAttackAnimations.js.map +1 -1
- package/lib/systems/animation/catalogs/EnhancedElbowKneeAnimations.js.map +1 -1
- package/lib/systems/animation/catalogs/FootworkSkeletalAnimations.js.map +1 -1
- package/lib/systems/animation/catalogs/GamRedirectionAnimations.js.map +1 -1
- package/lib/systems/animation/catalogs/GamStanceAnimations.js +21 -0
- package/lib/systems/animation/catalogs/GamStanceAnimations.js.map +1 -0
- package/lib/systems/animation/catalogs/GamTechniqueAnimations.js +34 -2
- package/lib/systems/animation/catalogs/GamTechniqueAnimations.js.map +1 -1
- package/lib/systems/animation/catalogs/GanStanceAnimations.js.map +1 -1
- package/lib/systems/animation/catalogs/GanTechniqueAnimations.js.map +1 -1
- package/lib/systems/animation/catalogs/GeonStanceAnimations.js.map +1 -1
- package/lib/systems/animation/catalogs/GonTechniqueAnimations.d.ts +9 -0
- package/lib/systems/animation/catalogs/GonTechniqueAnimations.d.ts.map +1 -1
- package/lib/systems/animation/catalogs/GonTechniqueAnimations.js +288 -0
- package/lib/systems/animation/catalogs/GonTechniqueAnimations.js.map +1 -0
- package/lib/systems/animation/catalogs/GrapplingAnimations.js.map +1 -1
- package/lib/systems/animation/catalogs/JinStanceAnimations.js.map +1 -1
- package/lib/systems/animation/catalogs/JinTechniqueAnimations.js.map +1 -1
- package/lib/systems/animation/catalogs/KickAnimations.d.ts +2 -2
- package/lib/systems/animation/catalogs/KickAnimations.js +2 -2
- package/lib/systems/animation/catalogs/KickAnimations.js.map +1 -1
- package/lib/systems/animation/catalogs/LiStanceAnimations.js +14 -1
- package/lib/systems/animation/catalogs/LiStanceAnimations.js.map +1 -1
- package/lib/systems/animation/catalogs/LiTechniqueAnimations.js.map +1 -1
- package/lib/systems/animation/catalogs/MovementAnimations.js.map +1 -1
- package/lib/systems/animation/catalogs/PunchAnimations.d.ts +1 -1
- package/lib/systems/animation/catalogs/PunchAnimations.js +1 -1
- package/lib/systems/animation/catalogs/PunchAnimations.js.map +1 -1
- package/lib/systems/animation/catalogs/RecoveryAnimations.js.map +1 -1
- package/lib/systems/animation/catalogs/SonStanceAnimations.js.map +1 -1
- package/lib/systems/animation/catalogs/SonTechniqueAnimations.js.map +1 -1
- package/lib/systems/animation/catalogs/SpecializedPunchAnimations.js.map +1 -1
- package/lib/systems/animation/catalogs/StanceAnimations.js.map +1 -1
- package/lib/systems/animation/catalogs/StanceAttackAnimations.js.map +1 -1
- package/lib/systems/animation/catalogs/StanceGuardPoses.d.ts +6 -6
- package/lib/systems/animation/catalogs/StanceGuardPoses.js +36 -36
- package/lib/systems/animation/catalogs/StanceGuardPoses.js.map +1 -1
- package/lib/systems/animation/catalogs/StanceIdleAnimations.js.map +1 -1
- package/lib/systems/animation/catalogs/StanceLocomotionAnimations.js.map +1 -1
- package/lib/systems/animation/catalogs/StepSkeletalAnimations.js.map +1 -1
- package/lib/systems/animation/catalogs/TaeJointLockAnimations.js.map +1 -1
- package/lib/systems/animation/catalogs/TaeStanceAnimations.js.map +1 -1
- package/lib/systems/animation/constants/AnatomicalLimits.js.map +1 -1
- package/lib/systems/animation/core/AnimationHitTiming.js.map +1 -1
- package/lib/systems/animation/core/AnimationOptimizations.js.map +1 -1
- package/lib/systems/animation/core/AnimationPriority.js +15 -15
- package/lib/systems/animation/core/AnimationPriority.js.map +1 -1
- package/lib/systems/animation/core/AnimationRegistry.d.ts +30 -0
- package/lib/systems/animation/core/AnimationRegistry.d.ts.map +1 -1
- package/lib/systems/animation/core/AnimationRegistry.js +74 -12
- package/lib/systems/animation/core/AnimationRegistry.js.map +1 -1
- package/lib/systems/animation/core/AnimationStateMachine.js +16 -16
- package/lib/systems/animation/core/AnimationStateMachine.js.map +1 -1
- package/lib/systems/animation/core/AnimationTransitions.d.ts.map +1 -1
- package/lib/systems/animation/core/AnimationTransitions.js +34 -0
- package/lib/systems/animation/core/AnimationTransitions.js.map +1 -1
- package/lib/systems/animation/core/LateralityTransform.js.map +1 -1
- package/lib/systems/animation/core/RecoveryPhaseEnhancer.js.map +1 -1
- package/lib/systems/animation/core/TechniqueAnimationMapper.js.map +1 -1
- package/lib/systems/animation/core/TechniqueAnimationMapping.js.map +1 -1
- package/lib/systems/animation/core/index.d.ts +1 -1
- package/lib/systems/animation/core/index.d.ts.map +1 -1
- package/lib/systems/animation/core/types.d.ts +24 -0
- package/lib/systems/animation/core/types.d.ts.map +1 -1
- package/lib/systems/animation/core/types.js +27 -11
- package/lib/systems/animation/core/types.js.map +1 -1
- package/lib/systems/animation/systems/AdvancedJointMovements.js.map +1 -1
- package/lib/systems/animation/systems/BodyFacingSystem.js.map +1 -1
- package/lib/systems/animation/systems/FacialExpressions.js.map +1 -1
- package/lib/systems/animation/systems/FallAnimations.js.map +1 -1
- package/lib/systems/animation/systems/MuscleActivation.js.map +1 -1
- package/lib/systems/bodypart/BodyPartDamageIntegration.js.map +1 -1
- package/lib/systems/bodypart/BodyPartHealthSystem.js.map +1 -1
- package/lib/systems/bodypart/BodyPartPositionMapping.js.map +1 -1
- package/lib/systems/bodypart/CombatInjuryIntegration.js.map +1 -1
- package/lib/systems/bodypart/InjuryIntegration.js.map +1 -1
- package/lib/systems/bodypart/InjuryTracker.js.map +1 -1
- package/lib/systems/bodypart/MovementPenaltySystem.js.map +1 -1
- package/lib/systems/bodypart/PlayerInjuryTrackingManager.js.map +1 -1
- package/lib/systems/bodypart/types.js.map +1 -1
- package/lib/systems/breathing/BreathingDisruptionSystem.js +19 -19
- package/lib/systems/breathing/BreathingDisruptionSystem.js.map +1 -1
- package/lib/systems/breathing/feedback.js.map +1 -1
- package/lib/systems/breathing/integration.js.map +1 -1
- package/lib/systems/combat/BalanceSystem.js +19 -19
- package/lib/systems/combat/BalanceSystem.js.map +1 -1
- package/lib/systems/combat/BreakingStatusEffects.js.map +1 -1
- package/lib/systems/combat/CombatStateSystem.js +17 -17
- package/lib/systems/combat/CombatStateSystem.js.map +1 -1
- package/lib/systems/combat/ConsciousnessSystem.js +24 -24
- package/lib/systems/combat/ConsciousnessSystem.js.map +1 -1
- package/lib/systems/combat/FallIntegration.js.map +1 -1
- package/lib/systems/combat/GrappleSystem.js.map +1 -1
- package/lib/systems/combat/LimbExposureSystem.js.map +1 -1
- package/lib/systems/combat/PainResponseSystem.js +21 -21
- package/lib/systems/combat/PainResponseSystem.js.map +1 -1
- package/lib/systems/combat/TrainingCombatSystem.js.map +1 -1
- package/lib/systems/combat/painConsciousnessUtils.js.map +1 -1
- package/lib/systems/combat/typeGuards.js.map +1 -1
- package/lib/systems/effects.js.map +1 -1
- package/lib/systems/game.js.map +1 -1
- package/lib/systems/movement/InjuryMovementModifier.js.map +1 -1
- package/lib/systems/movement/helpers/AccelerationUpdater.js.map +1 -1
- package/lib/systems/movement/helpers/accelerationUtils.js.map +1 -1
- package/lib/systems/movement/integration.js.map +1 -1
- package/lib/systems/physics/AttackMovementPhysics.js.map +1 -1
- package/lib/systems/physics/CollisionDetection.js.map +1 -1
- package/lib/systems/physics/CoordinateMapper.js.map +1 -1
- package/lib/systems/physics/KnockbackPhysics.js.map +1 -1
- package/lib/systems/physics/MovementPhysics.js.map +1 -1
- package/lib/systems/physics/PhysicalReachCalculator.js.map +1 -1
- package/lib/systems/physics/SpeedModifierSystem.js +6 -6
- package/lib/systems/physics/SpeedModifierSystem.js.map +1 -1
- package/lib/systems/trigram/KoreanCulture.js.map +1 -1
- package/lib/systems/trigram/KoreanTechniques.js.map +1 -1
- package/lib/systems/trigram/StanceManager.js.map +1 -1
- package/lib/systems/trigram/TransitionCalculator.js.map +1 -1
- package/lib/systems/trigram/TrigramCalculator.js.map +1 -1
- package/lib/systems/trigram/techniques/DarkOpsTechniques.js.map +1 -1
- package/lib/systems/trigram/techniques/GamTechniques.js.map +1 -1
- package/lib/systems/trigram/techniques/GanTechniques.js.map +1 -1
- package/lib/systems/trigram/techniques/GeonTechniques.js.map +1 -1
- package/lib/systems/trigram/techniques/GonTechniques.js.map +1 -1
- package/lib/systems/trigram/techniques/JinTechniques.js.map +1 -1
- package/lib/systems/trigram/techniques/LiTechniques.js.map +1 -1
- package/lib/systems/trigram/techniques/SonTechniques.js.map +1 -1
- package/lib/systems/trigram/techniques/TaeTechniques.js.map +1 -1
- package/lib/systems/trigram/techniques/TechniqueConfig.js.map +1 -1
- package/lib/systems/trigram/techniques/index.js.map +1 -1
- package/lib/systems/trigram/types/GonTechniqueExtensions.js.map +1 -1
- package/lib/systems/trigram/types.js.map +1 -1
- package/lib/systems/types.js.map +1 -1
- package/lib/systems/vitalpoint/DamageCalculator.js.map +1 -1
- package/lib/systems/vitalpoint/HitDetection.js.map +1 -1
- package/lib/systems/vitalpoint/KoreanAnatomy.js.map +1 -1
- package/lib/systems/vitalpoint/KoreanVitalPoints.js.map +1 -1
- package/lib/systems/vitalpoint/MeridianVitalPointMapping.js.map +1 -1
- package/lib/systems/vitalpoint/VitalPointsData.js.map +1 -1
- package/lib/types/AccessibilityTypes.js.map +1 -1
- package/lib/types/LayoutTypes.js.map +1 -1
- package/lib/types/PhysicsTypes.js.map +1 -1
- package/lib/types/common.js.map +1 -1
- package/lib/types/constants/animations.js.map +1 -1
- package/lib/types/constants/colors.js.map +1 -1
- package/lib/types/constants/designSystem.js.map +1 -1
- package/lib/types/constants/index.js.map +1 -1
- package/lib/types/constants/layout.d.ts +21 -0
- package/lib/types/constants/layout.d.ts.map +1 -1
- package/lib/types/constants/layout.js +22 -1
- package/lib/types/constants/layout.js.map +1 -1
- package/lib/types/constants/performance.js.map +1 -1
- package/lib/types/constants/typography.js.map +1 -1
- package/lib/types/constants/ui.js.map +1 -1
- package/lib/types/facial.js +19 -19
- package/lib/types/facial.js.map +1 -1
- package/lib/types/hand-animation.js.map +1 -1
- package/lib/types/injury.js.map +1 -1
- package/lib/types/muscle.js.map +1 -1
- package/lib/types/physics.js.map +1 -1
- package/lib/types/physicsConstants.js.map +1 -1
- package/lib/types/player-visual.d.ts +1 -1
- package/lib/types/player-visual.d.ts.map +1 -1
- package/lib/types/skeletal.js.map +1 -1
- package/lib/types/techniqueId.js.map +1 -1
- package/lib/utils/accessibility.js.map +1 -1
- package/lib/utils/arenaWorldDimensions.js.map +1 -1
- package/lib/utils/assetConfig.js.map +1 -1
- package/lib/utils/characterScaling.js.map +1 -1
- package/lib/utils/colorHelpers.js.map +1 -1
- package/lib/utils/colorUtils.js.map +1 -1
- package/lib/utils/combatReadiness.js.map +1 -1
- package/lib/utils/controlMapping.js.map +1 -1
- package/lib/utils/deviceDetection.js +6 -7
- package/lib/utils/deviceDetection.js.map +1 -1
- package/lib/utils/effectUtils.js.map +1 -1
- package/lib/utils/fabricTextures.js.map +1 -1
- package/lib/utils/hapticFeedback.js.map +1 -1
- package/lib/utils/haptics.js.map +1 -1
- package/lib/utils/htmlOverlayHelpers.js.map +1 -1
- package/lib/utils/inputSystem.js.map +1 -1
- package/lib/utils/koreanThemeHelpers.js.map +1 -1
- package/lib/utils/math.js.map +1 -1
- package/lib/utils/mobileLayoutHelpers.js.map +1 -1
- package/lib/utils/mobileUIUtils.js.map +1 -1
- package/lib/utils/performance/PerformanceMonitor.js.map +1 -1
- package/lib/utils/performance/PerformanceOverlay3D.js.map +1 -1
- package/lib/utils/performance/usePerformanceMonitor.js.map +1 -1
- package/lib/utils/performanceOptimization.js.map +1 -1
- package/lib/utils/player3DHelpers.js.map +1 -1
- package/lib/utils/playerUtils.js.map +1 -1
- package/lib/utils/responsiveLayout.js.map +1 -1
- package/lib/utils/responsiveLayoutHelpers.d.ts +7 -0
- package/lib/utils/responsiveLayoutHelpers.d.ts.map +1 -1
- package/lib/utils/responsiveLayoutHelpers.js +16 -2
- package/lib/utils/responsiveLayoutHelpers.js.map +1 -1
- package/lib/utils/responsiveOrientationConstants.js.map +1 -1
- package/lib/utils/safeAreaUtils.js.map +1 -1
- package/lib/utils/sharedPhysicsConfig.js.map +1 -1
- package/lib/utils/skeletonScaling.js.map +1 -1
- package/lib/utils/stanceHelpers.js.map +1 -1
- package/lib/utils/threeObjectPool.js.map +1 -1
- package/lib/utils/visualEffects.js.map +1 -1
- package/package.json +7 -7
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"PerformanceBreakdownOverlayHtml.js","names":[],"sources":["../../../../../src/components/screens/endscreen/components/PerformanceBreakdownOverlayHtml.tsx"],"sourcesContent":["import React, { useMemo } from \"react\";\nimport { MatchStatistics } from \"../../../../systems/combat\";\nimport { PlayerMatchStats } from \"../../../../systems/player\";\nimport { FONT_FAMILY, KOREAN_COLORS } from \"../../../../types/constants\";\nimport { hexToRgbaString } from \"../../../../utils/colorUtils\";\n\nexport interface PerformanceBreakdownProps {\n readonly matchStats: MatchStatistics;\n readonly isMobile: boolean;\n readonly isTablet: boolean;\n}\n\n/**\n * Helper to convert hex color to CSS string\n */\nconst toCssColor = (hex: number): string => hexToRgbaString(hex, 1);\n\n/**\n * Category rating component\n */\ninterface CategoryRatingProps {\n readonly category: string;\n readonly korean: string;\n readonly value: number;\n readonly maxValue: number;\n readonly color: number;\n readonly fontSize: number;\n}\n\nconst CategoryRating: React.FC<CategoryRatingProps> = ({\n category,\n korean,\n value,\n maxValue,\n color,\n fontSize,\n}) => {\n const percentage = Math.round((value / maxValue) * 100);\n const grade = percentage >= 90 ? \"S\" : percentage >= 75 ? \"A\" : percentage >= 60 ? \"B\" : \"C\";\n\n return (\n <div\n style={{\n display: \"flex\",\n flexDirection: \"column\",\n gap: \"6px\",\n }}\n data-testid={`category-${category.toLowerCase()}`}\n >\n <div\n style={{\n display: \"flex\",\n justifyContent: \"space-between\",\n alignItems: \"center\",\n fontSize: fontSize - 2,\n color: toCssColor(KOREAN_COLORS.TEXT_SECONDARY),\n }}\n >\n <span>{korean} | {category}</span>\n <span\n style={{\n fontWeight: \"bold\",\n color: toCssColor(color),\n fontSize: fontSize + 2,\n }}\n >\n {grade}\n </span>\n </div>\n <div\n style={{\n width: \"100%\",\n height: \"8px\",\n background: hexToRgbaString(KOREAN_COLORS.UI_BACKGROUND_MEDIUM, 0.5),\n borderRadius: \"4px\",\n overflow: \"hidden\",\n }}\n >\n <div\n style={{\n width: `${percentage}%`,\n height: \"100%\",\n background: toCssColor(color),\n transition: \"width 0.5s ease-out\",\n }}\n data-testid={`progress-${category.toLowerCase()}`}\n />\n </div>\n </div>\n );\n};\n\n/**\n * Calculate category scores from match statistics\n */\nfunction calculateCategoryScores(stats: PlayerMatchStats) {\n const offense = Math.min(\n (stats.totalDamageDealt / 100) * 50 + (stats.hitsLanded / 10) * 50,\n 100\n );\n\n const defenseRaw = Math.max(0, 100 - stats.totalDamageReceived / 2);\n const defense = Math.min(defenseRaw, 100);\n\n const technique = Math.min(\n stats.perfectStrikes * 15 + stats.vitalPointHits * 10,\n 100\n );\n\n const damageRatio =\n stats.totalDamageReceived > 0\n ? stats.totalDamageDealt / stats.totalDamageReceived\n : stats.totalDamageDealt > 0\n ? 2\n : 0;\n const efficiency = Math.min(damageRatio * 40, 100);\n\n return { offense, defense, technique, efficiency };\n}\n\n/**\n * Performance Breakdown Component\n * Provides detailed analysis of combat performance by category\n */\nexport const PerformanceBreakdown: React.FC<PerformanceBreakdownProps> = ({\n matchStats,\n isMobile,\n isTablet,\n}) => {\n const fontSize = isMobile ? 12 : isTablet ? 13 : 14;\n const labelFontSize = isMobile ? 14 : isTablet ? 16 : 18;\n const padding = isMobile ? 12 : isTablet ? 15 : 18;\n\n const winnerStats = useMemo(\n () => (matchStats.winner === 0 ? matchStats.player1 : matchStats.player2),\n [matchStats]\n );\n\n const scores = useMemo(\n () => calculateCategoryScores(winnerStats),\n [winnerStats]\n );\n\n const techniqueCount = winnerStats.techniques?.length ?? 0;\n const uniqueTechniques = useMemo(() => {\n if (!winnerStats.techniques || winnerStats.techniques.length === 0) {\n return 0;\n }\n return new Set(winnerStats.techniques).size;\n }, [winnerStats.techniques]);\n\n return (\n <div\n data-testid=\"performance-breakdown\"\n style={{\n width: isMobile ? \"95%\" : isTablet ? \"80%\" : \"70%\",\n maxWidth: \"900px\",\n background: hexToRgbaString(KOREAN_COLORS.UI_BACKGROUND_DARK, 0.9),\n border: `2px solid ${hexToRgbaString(KOREAN_COLORS.PRIMARY_CYAN, 0.6)}`,\n borderRadius: \"12px\",\n padding: padding * 1.5,\n marginBottom: padding,\n fontFamily: FONT_FAMILY.KOREAN,\n }}\n >\n {/* Title */}\n <div\n style={{\n fontSize: labelFontSize + 2,\n fontWeight: \"bold\",\n color: toCssColor(KOREAN_COLORS.PRIMARY_CYAN),\n textAlign: \"center\",\n marginBottom: padding * 1.5,\n }}\n data-testid=\"breakdown-title\"\n >\n 전투 분석 | Performance Breakdown\n </div>\n\n {/* Category Ratings */}\n <div\n style={{\n display: \"flex\",\n flexDirection: \"column\",\n gap: padding,\n marginBottom: padding * 1.5,\n }}\n data-testid=\"category-ratings\"\n >\n <CategoryRating\n category=\"Offense\"\n korean=\"공격\"\n value={scores.offense}\n maxValue={100}\n color={KOREAN_COLORS.ACCENT_RED}\n fontSize={fontSize}\n />\n <CategoryRating\n category=\"Defense\"\n korean=\"방어\"\n value={scores.defense}\n maxValue={100}\n color={KOREAN_COLORS.ACCENT_BLUE}\n fontSize={fontSize}\n />\n <CategoryRating\n category=\"Technique\"\n korean=\"기술\"\n value={scores.technique}\n maxValue={100}\n color={KOREAN_COLORS.ACCENT_GOLD}\n fontSize={fontSize}\n />\n <CategoryRating\n category=\"Efficiency\"\n korean=\"효율\"\n value={scores.efficiency}\n maxValue={100}\n color={KOREAN_COLORS.PRIMARY_CYAN}\n fontSize={fontSize}\n />\n </div>\n\n {/* Technique Analysis */}\n <div\n style={{\n padding,\n background: hexToRgbaString(KOREAN_COLORS.UI_BACKGROUND_MEDIUM, 0.4),\n borderRadius: \"8px\",\n }}\n data-testid=\"technique-analysis\"\n >\n <div\n style={{\n fontSize: labelFontSize,\n fontWeight: \"bold\",\n color: toCssColor(KOREAN_COLORS.ACCENT_GOLD),\n marginBottom: padding / 2,\n textAlign: \"center\",\n }}\n >\n 기술 사용 분석 | Technique Analysis\n </div>\n\n <div\n style={{\n display: \"grid\",\n gridTemplateColumns: isMobile ? \"1fr\" : \"1fr 1fr 1fr\",\n gap: padding,\n marginTop: padding,\n }}\n >\n <div style={{ textAlign: \"center\" }}>\n <div\n style={{\n fontSize: fontSize + 6,\n fontWeight: \"bold\",\n color: toCssColor(KOREAN_COLORS.PRIMARY_CYAN),\n }}\n >\n {techniqueCount}\n </div>\n <div\n style={{\n fontSize: fontSize - 2,\n color: toCssColor(KOREAN_COLORS.TEXT_TERTIARY),\n }}\n >\n 총 기술 | Total Uses\n </div>\n </div>\n\n <div style={{ textAlign: \"center\" }}>\n <div\n style={{\n fontSize: fontSize + 6,\n fontWeight: \"bold\",\n color: toCssColor(KOREAN_COLORS.ACCENT_GOLD),\n }}\n >\n {uniqueTechniques}\n </div>\n <div\n style={{\n fontSize: fontSize - 2,\n color: toCssColor(KOREAN_COLORS.TEXT_TERTIARY),\n }}\n >\n 고유 기술 | Unique\n </div>\n </div>\n\n <div style={{ textAlign: \"center\" }}>\n <div\n style={{\n fontSize: fontSize + 6,\n fontWeight: \"bold\",\n color: toCssColor(KOREAN_COLORS.VITAL_POINT_HIT),\n }}\n >\n {winnerStats.vitalPointHits ?? 0}\n </div>\n <div\n style={{\n fontSize: fontSize - 2,\n color: toCssColor(KOREAN_COLORS.TEXT_TERTIARY),\n }}\n >\n 급소 타격 | Vital Hits\n </div>\n </div>\n </div>\n\n {/* Most Used Techniques */}\n {winnerStats.techniques && winnerStats.techniques.length > 0 && (\n <div\n style={{\n marginTop: padding,\n paddingTop: padding,\n borderTop: `1px solid ${hexToRgbaString(\n KOREAN_COLORS.UI_BORDER,\n 0.3\n )}`,\n }}\n >\n <div\n style={{\n fontSize: fontSize - 2,\n color: toCssColor(KOREAN_COLORS.TEXT_TERTIARY),\n marginBottom: padding / 2,\n }}\n >\n 주요 기술 | Primary Techniques:\n </div>\n <div\n style={{\n fontSize: fontSize,\n color: toCssColor(KOREAN_COLORS.TEXT_SECONDARY),\n lineHeight: 1.6,\n }}\n >\n {winnerStats.techniques.slice(0, 5).join(\", \")}\n {winnerStats.techniques.length > 5 && \"...\"}\n </div>\n </div>\n )}\n </div>\n\n {/* Combat Effectiveness Summary */}\n <div\n style={{\n marginTop: padding,\n padding,\n background: hexToRgbaString(KOREAN_COLORS.UI_BACKGROUND_MEDIUM, 0.4),\n borderRadius: \"8px\",\n textAlign: \"center\",\n }}\n data-testid=\"effectiveness-summary\"\n >\n <div\n style={{\n fontSize: fontSize - 2,\n color: toCssColor(KOREAN_COLORS.TEXT_TERTIARY),\n marginBottom: padding / 4,\n }}\n >\n 전투 효율성 | Combat Effectiveness\n </div>\n <div\n style={{\n fontSize: labelFontSize + 2,\n fontWeight: \"bold\",\n color: toCssColor(KOREAN_COLORS.ACCENT_GOLD),\n }}\n >\n {Math.round((scores.offense + scores.defense + scores.technique + scores.efficiency) / 4)}%\n </div>\n </div>\n </div>\n );\n};\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"PerformanceBreakdownOverlayHtml.js","names":[],"sources":["../../../../../src/components/screens/endscreen/components/PerformanceBreakdownOverlayHtml.tsx"],"sourcesContent":["import React, { useMemo } from \"react\";\nimport { MatchStatistics } from \"../../../../systems/combat\";\nimport { PlayerMatchStats } from \"../../../../systems/player\";\nimport { FONT_FAMILY, KOREAN_COLORS } from \"../../../../types/constants\";\nimport { hexToRgbaString } from \"../../../../utils/colorUtils\";\n\nexport interface PerformanceBreakdownProps {\n readonly matchStats: MatchStatistics;\n readonly isMobile: boolean;\n readonly isTablet: boolean;\n}\n\n/**\n * Helper to convert hex color to CSS string\n */\nconst toCssColor = (hex: number): string => hexToRgbaString(hex, 1);\n\n/**\n * Category rating component\n */\ninterface CategoryRatingProps {\n readonly category: string;\n readonly korean: string;\n readonly value: number;\n readonly maxValue: number;\n readonly color: number;\n readonly fontSize: number;\n}\n\nconst CategoryRating: React.FC<CategoryRatingProps> = ({\n category,\n korean,\n value,\n maxValue,\n color,\n fontSize,\n}) => {\n const percentage = Math.round((value / maxValue) * 100);\n const grade = percentage >= 90 ? \"S\" : percentage >= 75 ? \"A\" : percentage >= 60 ? \"B\" : \"C\";\n\n return (\n <div\n style={{\n display: \"flex\",\n flexDirection: \"column\",\n gap: \"6px\",\n }}\n data-testid={`category-${category.toLowerCase()}`}\n >\n <div\n style={{\n display: \"flex\",\n justifyContent: \"space-between\",\n alignItems: \"center\",\n fontSize: fontSize - 2,\n color: toCssColor(KOREAN_COLORS.TEXT_SECONDARY),\n }}\n >\n <span>{korean} | {category}</span>\n <span\n style={{\n fontWeight: \"bold\",\n color: toCssColor(color),\n fontSize: fontSize + 2,\n }}\n >\n {grade}\n </span>\n </div>\n <div\n style={{\n width: \"100%\",\n height: \"8px\",\n background: hexToRgbaString(KOREAN_COLORS.UI_BACKGROUND_MEDIUM, 0.5),\n borderRadius: \"4px\",\n overflow: \"hidden\",\n }}\n >\n <div\n style={{\n width: `${percentage}%`,\n height: \"100%\",\n background: toCssColor(color),\n transition: \"width 0.5s ease-out\",\n }}\n data-testid={`progress-${category.toLowerCase()}`}\n />\n </div>\n </div>\n );\n};\n\n/**\n * Calculate category scores from match statistics\n */\nfunction calculateCategoryScores(stats: PlayerMatchStats) {\n const offense = Math.min(\n (stats.totalDamageDealt / 100) * 50 + (stats.hitsLanded / 10) * 50,\n 100\n );\n\n const defenseRaw = Math.max(0, 100 - stats.totalDamageReceived / 2);\n const defense = Math.min(defenseRaw, 100);\n\n const technique = Math.min(\n stats.perfectStrikes * 15 + stats.vitalPointHits * 10,\n 100\n );\n\n const damageRatio =\n stats.totalDamageReceived > 0\n ? stats.totalDamageDealt / stats.totalDamageReceived\n : stats.totalDamageDealt > 0\n ? 2\n : 0;\n const efficiency = Math.min(damageRatio * 40, 100);\n\n return { offense, defense, technique, efficiency };\n}\n\n/**\n * Performance Breakdown Component\n * Provides detailed analysis of combat performance by category\n */\nexport const PerformanceBreakdown: React.FC<PerformanceBreakdownProps> = ({\n matchStats,\n isMobile,\n isTablet,\n}) => {\n const fontSize = isMobile ? 12 : isTablet ? 13 : 14;\n const labelFontSize = isMobile ? 14 : isTablet ? 16 : 18;\n const padding = isMobile ? 12 : isTablet ? 15 : 18;\n\n const winnerStats = useMemo(\n () => (matchStats.winner === 0 ? matchStats.player1 : matchStats.player2),\n [matchStats]\n );\n\n const scores = useMemo(\n () => calculateCategoryScores(winnerStats),\n [winnerStats]\n );\n\n const techniqueCount = winnerStats.techniques?.length ?? 0;\n const uniqueTechniques = useMemo(() => {\n if (!winnerStats.techniques || winnerStats.techniques.length === 0) {\n return 0;\n }\n return new Set(winnerStats.techniques).size;\n }, [winnerStats.techniques]);\n\n return (\n <div\n data-testid=\"performance-breakdown\"\n style={{\n width: isMobile ? \"95%\" : isTablet ? \"80%\" : \"70%\",\n maxWidth: \"900px\",\n background: hexToRgbaString(KOREAN_COLORS.UI_BACKGROUND_DARK, 0.9),\n border: `2px solid ${hexToRgbaString(KOREAN_COLORS.PRIMARY_CYAN, 0.6)}`,\n borderRadius: \"12px\",\n padding: padding * 1.5,\n marginBottom: padding,\n fontFamily: FONT_FAMILY.KOREAN,\n }}\n >\n {/* Title */}\n <div\n style={{\n fontSize: labelFontSize + 2,\n fontWeight: \"bold\",\n color: toCssColor(KOREAN_COLORS.PRIMARY_CYAN),\n textAlign: \"center\",\n marginBottom: padding * 1.5,\n }}\n data-testid=\"breakdown-title\"\n >\n 전투 분석 | Performance Breakdown\n </div>\n\n {/* Category Ratings */}\n <div\n style={{\n display: \"flex\",\n flexDirection: \"column\",\n gap: padding,\n marginBottom: padding * 1.5,\n }}\n data-testid=\"category-ratings\"\n >\n <CategoryRating\n category=\"Offense\"\n korean=\"공격\"\n value={scores.offense}\n maxValue={100}\n color={KOREAN_COLORS.ACCENT_RED}\n fontSize={fontSize}\n />\n <CategoryRating\n category=\"Defense\"\n korean=\"방어\"\n value={scores.defense}\n maxValue={100}\n color={KOREAN_COLORS.ACCENT_BLUE}\n fontSize={fontSize}\n />\n <CategoryRating\n category=\"Technique\"\n korean=\"기술\"\n value={scores.technique}\n maxValue={100}\n color={KOREAN_COLORS.ACCENT_GOLD}\n fontSize={fontSize}\n />\n <CategoryRating\n category=\"Efficiency\"\n korean=\"효율\"\n value={scores.efficiency}\n maxValue={100}\n color={KOREAN_COLORS.PRIMARY_CYAN}\n fontSize={fontSize}\n />\n </div>\n\n {/* Technique Analysis */}\n <div\n style={{\n padding,\n background: hexToRgbaString(KOREAN_COLORS.UI_BACKGROUND_MEDIUM, 0.4),\n borderRadius: \"8px\",\n }}\n data-testid=\"technique-analysis\"\n >\n <div\n style={{\n fontSize: labelFontSize,\n fontWeight: \"bold\",\n color: toCssColor(KOREAN_COLORS.ACCENT_GOLD),\n marginBottom: padding / 2,\n textAlign: \"center\",\n }}\n >\n 기술 사용 분석 | Technique Analysis\n </div>\n\n <div\n style={{\n display: \"grid\",\n gridTemplateColumns: isMobile ? \"1fr\" : \"1fr 1fr 1fr\",\n gap: padding,\n marginTop: padding,\n }}\n >\n <div style={{ textAlign: \"center\" }}>\n <div\n style={{\n fontSize: fontSize + 6,\n fontWeight: \"bold\",\n color: toCssColor(KOREAN_COLORS.PRIMARY_CYAN),\n }}\n >\n {techniqueCount}\n </div>\n <div\n style={{\n fontSize: fontSize - 2,\n color: toCssColor(KOREAN_COLORS.TEXT_TERTIARY),\n }}\n >\n 총 기술 | Total Uses\n </div>\n </div>\n\n <div style={{ textAlign: \"center\" }}>\n <div\n style={{\n fontSize: fontSize + 6,\n fontWeight: \"bold\",\n color: toCssColor(KOREAN_COLORS.ACCENT_GOLD),\n }}\n >\n {uniqueTechniques}\n </div>\n <div\n style={{\n fontSize: fontSize - 2,\n color: toCssColor(KOREAN_COLORS.TEXT_TERTIARY),\n }}\n >\n 고유 기술 | Unique\n </div>\n </div>\n\n <div style={{ textAlign: \"center\" }}>\n <div\n style={{\n fontSize: fontSize + 6,\n fontWeight: \"bold\",\n color: toCssColor(KOREAN_COLORS.VITAL_POINT_HIT),\n }}\n >\n {winnerStats.vitalPointHits ?? 0}\n </div>\n <div\n style={{\n fontSize: fontSize - 2,\n color: toCssColor(KOREAN_COLORS.TEXT_TERTIARY),\n }}\n >\n 급소 타격 | Vital Hits\n </div>\n </div>\n </div>\n\n {/* Most Used Techniques */}\n {winnerStats.techniques && winnerStats.techniques.length > 0 && (\n <div\n style={{\n marginTop: padding,\n paddingTop: padding,\n borderTop: `1px solid ${hexToRgbaString(\n KOREAN_COLORS.UI_BORDER,\n 0.3\n )}`,\n }}\n >\n <div\n style={{\n fontSize: fontSize - 2,\n color: toCssColor(KOREAN_COLORS.TEXT_TERTIARY),\n marginBottom: padding / 2,\n }}\n >\n 주요 기술 | Primary Techniques:\n </div>\n <div\n style={{\n fontSize: fontSize,\n color: toCssColor(KOREAN_COLORS.TEXT_SECONDARY),\n lineHeight: 1.6,\n }}\n >\n {winnerStats.techniques.slice(0, 5).join(\", \")}\n {winnerStats.techniques.length > 5 && \"...\"}\n </div>\n </div>\n )}\n </div>\n\n {/* Combat Effectiveness Summary */}\n <div\n style={{\n marginTop: padding,\n padding,\n background: hexToRgbaString(KOREAN_COLORS.UI_BACKGROUND_MEDIUM, 0.4),\n borderRadius: \"8px\",\n textAlign: \"center\",\n }}\n data-testid=\"effectiveness-summary\"\n >\n <div\n style={{\n fontSize: fontSize - 2,\n color: toCssColor(KOREAN_COLORS.TEXT_TERTIARY),\n marginBottom: padding / 4,\n }}\n >\n 전투 효율성 | Combat Effectiveness\n </div>\n <div\n style={{\n fontSize: labelFontSize + 2,\n fontWeight: \"bold\",\n color: toCssColor(KOREAN_COLORS.ACCENT_GOLD),\n }}\n >\n {Math.round((scores.offense + scores.defense + scores.technique + scores.efficiency) / 4)}%\n </div>\n </div>\n </div>\n );\n};\n"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"PerformanceRatingOverlayHtml.js","names":[],"sources":["../../../../../src/components/screens/endscreen/components/PerformanceRatingOverlayHtml.tsx"],"sourcesContent":["import React, { useMemo } from \"react\";\nimport { MatchStatistics } from \"../../../../systems/combat\";\nimport {\n FONT_FAMILY,\n KOREAN_COLORS,\n PERFORMANCE_RATING_THRESHOLDS,\n} from \"../../../../types/constants\";\nimport { hexToRgbaString } from \"../../../../utils/colorUtils\";\nimport { pulseAnimation } from \"./animations\";\n\nexport interface PerformanceRatingProps {\n readonly matchStats: MatchStatistics;\n readonly isMobile: boolean;\n readonly isTablet: boolean;\n}\n\n/**\n * Helper to convert hex color to CSS string\n */\nconst toCssColor = (hex: number): string => hexToRgbaString(hex, 1);\n\n/**\n * Calculate performance score based on match statistics\n * Score ranges from 0-100 based on combat effectiveness\n */\nfunction calculatePerformanceScore(stats: MatchStatistics): number {\n const winnerStats = stats.winner === 0 ? stats.player1 : stats.player2;\n\n const accuracy = Math.min((winnerStats.hitsLanded / 10) * 100, 100);\n\n const damageRatio =\n winnerStats.totalDamageReceived > 0\n ? winnerStats.totalDamageDealt / winnerStats.totalDamageReceived\n : winnerStats.totalDamageDealt > 0\n ? 2\n : 0;\n const damageScore = Math.min(damageRatio * 30, 30); // Max 30 points\n\n const precisionBonus =\n winnerStats.perfectStrikes * 5 + winnerStats.vitalPointHits * 3;\n const precisionScore = Math.min(precisionBonus, 25); // Max 25 points\n\n const speedScore =\n stats.matchDuration < 60 ? 15 : stats.matchDuration < 120 ? 10 : 5;\n\n const totalScore = accuracy * 0.3 + damageScore + precisionScore + speedScore;\n\n return Math.min(Math.round(totalScore), 100);\n}\n\n/**\n * Get performance rating based on score\n */\nfunction getPerformanceRating(\n score: number\n): keyof typeof PERFORMANCE_RATING_THRESHOLDS {\n if (score >= PERFORMANCE_RATING_THRESHOLDS.S.minScore) return \"S\";\n if (score >= PERFORMANCE_RATING_THRESHOLDS.A.minScore) return \"A\";\n if (score >= PERFORMANCE_RATING_THRESHOLDS.B.minScore) return \"B\";\n return \"C\";\n}\n\n/**\n * Performance Rating Component\n * Displays S/A/B/C ranking based on combat performance\n */\nexport const PerformanceRating: React.FC<PerformanceRatingProps> = ({\n matchStats,\n isMobile,\n isTablet,\n}) => {\n const ratingFontSize = isMobile ? 36 : isTablet ? 48 : 56;\n const labelFontSize = isMobile ? 12 : isTablet ? 14 : 15;\n const scoreFontSize = isMobile ? 18 : isTablet ? 20 : 24;\n const padding = isMobile ? 10 : isTablet ? 12 : 15;\n\n const performanceScore = useMemo(\n () => calculatePerformanceScore(matchStats),\n [matchStats]\n );\n\n const rating = useMemo(\n () => getPerformanceRating(performanceScore),\n [performanceScore]\n );\n\n const ratingInfo = PERFORMANCE_RATING_THRESHOLDS[rating];\n\n const winnerStats = useMemo(\n () => (matchStats.winner === 0 ? matchStats.player1 : matchStats.player2),\n [matchStats]\n );\n\n return (\n <div\n data-testid=\"performance-rating\"\n style={{\n display: \"flex\",\n flexDirection: \"column\",\n alignItems: \"center\",\n background: hexToRgbaString(KOREAN_COLORS.UI_BACKGROUND_DARK, 0.9),\n border: `3px solid ${hexToRgbaString(ratingInfo.color, 0.8)}`,\n borderRadius: \"16px\",\n padding: padding,\n marginBottom: padding / 2,\n minWidth: isMobile ? \"260px\" : \"300px\",\n boxShadow: `0 0 30px ${hexToRgbaString(ratingInfo.color, 0.3)}`,\n animation: \"ratingPulse 2s ease-in-out infinite\",\n }}\n >\n {/* Title */}\n <div\n style={{\n fontSize: labelFontSize,\n color: toCssColor(KOREAN_COLORS.TEXT_SECONDARY),\n fontFamily: FONT_FAMILY.KOREAN,\n marginBottom: padding / 2,\n textTransform: \"uppercase\",\n letterSpacing: \"0.1em\",\n }}\n data-testid=\"rating-label\"\n >\n 전투 등급 | Performance Rating\n </div>\n\n {/* Rating Letter */}\n <div\n style={{\n fontSize: ratingFontSize,\n fontWeight: \"bold\",\n color: toCssColor(ratingInfo.color),\n fontFamily: FONT_FAMILY.KOREAN,\n textShadow: `0 0 20px ${hexToRgbaString(ratingInfo.color, 0.6)}`,\n marginBottom: padding / 2,\n animation: \"ratingGlow 1.5s ease-in-out infinite\",\n }}\n data-testid=\"rating-letter\"\n >\n {rating}\n </div>\n\n {/* Rating Description */}\n <div\n style={{\n fontSize: labelFontSize,\n color: toCssColor(KOREAN_COLORS.PRIMARY_CYAN),\n fontFamily: FONT_FAMILY.KOREAN,\n marginBottom: padding,\n textAlign: \"center\",\n }}\n data-testid=\"rating-description\"\n >\n {ratingInfo.description.korean} | {ratingInfo.description.english}\n </div>\n\n {/* Performance Score */}\n <div\n style={{\n display: \"flex\",\n flexDirection: \"column\",\n alignItems: \"center\",\n width: \"100%\",\n padding: padding,\n background: hexToRgbaString(KOREAN_COLORS.UI_BACKGROUND_MEDIUM, 0.4),\n borderRadius: \"8px\",\n }}\n >\n <div\n style={{\n fontSize: scoreFontSize,\n fontWeight: \"bold\",\n color: toCssColor(ratingInfo.color),\n fontFamily: FONT_FAMILY.KOREAN,\n marginBottom: padding / 4,\n }}\n data-testid=\"performance-score\"\n >\n {performanceScore}\n </div>\n <div\n style={{\n fontSize: labelFontSize - 2,\n color: toCssColor(KOREAN_COLORS.TEXT_TERTIARY),\n fontFamily: FONT_FAMILY.KOREAN,\n }}\n >\n 전투 점수 | Combat Score\n </div>\n\n {/* Score Breakdown */}\n <div\n style={{\n display: \"grid\",\n gridTemplateColumns: \"1fr 1fr\",\n gap: padding / 2,\n marginTop: padding,\n width: \"100%\",\n fontSize: labelFontSize - 4,\n color: toCssColor(KOREAN_COLORS.TEXT_SECONDARY),\n }}\n data-testid=\"score-breakdown\"\n >\n <div style={{ textAlign: \"center\" }}>\n <div\n style={{\n color: toCssColor(KOREAN_COLORS.ACCENT_GOLD),\n fontWeight: \"bold\",\n }}\n >\n {winnerStats.perfectStrikes ?? 0}\n </div>\n <div>완벽 | Perfect</div>\n </div>\n <div style={{ textAlign: \"center\" }}>\n <div\n style={{\n color: toCssColor(KOREAN_COLORS.VITAL_POINT_HIT),\n fontWeight: \"bold\",\n }}\n >\n {winnerStats.vitalPointHits ?? 0}\n </div>\n <div>급소 | Vital Hits</div>\n </div>\n <div style={{ textAlign: \"center\" }}>\n <div\n style={{\n color: toCssColor(KOREAN_COLORS.PRIMARY_CYAN),\n fontWeight: \"bold\",\n }}\n >\n {winnerStats.techniques?.length ?? 0}\n </div>\n <div>기술 | Techniques</div>\n </div>\n <div style={{ textAlign: \"center\" }}>\n <div\n style={{\n color: toCssColor(KOREAN_COLORS.CRITICAL_HIT),\n fontWeight: \"bold\",\n }}\n >\n {matchStats.criticalHits}\n </div>\n <div>치명타 | Criticals</div>\n </div>\n </div>\n </div>\n\n {/* CSS Animations */}\n <style>{`\n ${pulseAnimation}\n\n @keyframes ratingGlow {\n 0%, 100% {\n text-shadow: 0 0 20px ${hexToRgbaString(ratingInfo.color, 0.6)};\n }\n 50% {\n text-shadow: 0 0 30px ${hexToRgbaString(\n ratingInfo.color,\n 0.9\n )}, 0 0 50px ${hexToRgbaString(ratingInfo.color, 0.5)};\n }\n }\n `}</style>\n </div>\n );\n};\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"PerformanceRatingOverlayHtml.js","names":[],"sources":["../../../../../src/components/screens/endscreen/components/PerformanceRatingOverlayHtml.tsx"],"sourcesContent":["import React, { useMemo } from \"react\";\nimport { MatchStatistics } from \"../../../../systems/combat\";\nimport {\n FONT_FAMILY,\n KOREAN_COLORS,\n PERFORMANCE_RATING_THRESHOLDS,\n} from \"../../../../types/constants\";\nimport { hexToRgbaString } from \"../../../../utils/colorUtils\";\nimport { pulseAnimation } from \"./animations\";\n\nexport interface PerformanceRatingProps {\n readonly matchStats: MatchStatistics;\n readonly isMobile: boolean;\n readonly isTablet: boolean;\n}\n\n/**\n * Helper to convert hex color to CSS string\n */\nconst toCssColor = (hex: number): string => hexToRgbaString(hex, 1);\n\n/**\n * Calculate performance score based on match statistics\n * Score ranges from 0-100 based on combat effectiveness\n */\nfunction calculatePerformanceScore(stats: MatchStatistics): number {\n const winnerStats = stats.winner === 0 ? stats.player1 : stats.player2;\n\n const accuracy = Math.min((winnerStats.hitsLanded / 10) * 100, 100);\n\n const damageRatio =\n winnerStats.totalDamageReceived > 0\n ? winnerStats.totalDamageDealt / winnerStats.totalDamageReceived\n : winnerStats.totalDamageDealt > 0\n ? 2\n : 0;\n const damageScore = Math.min(damageRatio * 30, 30); // Max 30 points\n\n const precisionBonus =\n winnerStats.perfectStrikes * 5 + winnerStats.vitalPointHits * 3;\n const precisionScore = Math.min(precisionBonus, 25); // Max 25 points\n\n const speedScore =\n stats.matchDuration < 60 ? 15 : stats.matchDuration < 120 ? 10 : 5;\n\n const totalScore = accuracy * 0.3 + damageScore + precisionScore + speedScore;\n\n return Math.min(Math.round(totalScore), 100);\n}\n\n/**\n * Get performance rating based on score\n */\nfunction getPerformanceRating(\n score: number\n): keyof typeof PERFORMANCE_RATING_THRESHOLDS {\n if (score >= PERFORMANCE_RATING_THRESHOLDS.S.minScore) return \"S\";\n if (score >= PERFORMANCE_RATING_THRESHOLDS.A.minScore) return \"A\";\n if (score >= PERFORMANCE_RATING_THRESHOLDS.B.minScore) return \"B\";\n return \"C\";\n}\n\n/**\n * Performance Rating Component\n * Displays S/A/B/C ranking based on combat performance\n */\nexport const PerformanceRating: React.FC<PerformanceRatingProps> = ({\n matchStats,\n isMobile,\n isTablet,\n}) => {\n const ratingFontSize = isMobile ? 36 : isTablet ? 48 : 56;\n const labelFontSize = isMobile ? 12 : isTablet ? 14 : 15;\n const scoreFontSize = isMobile ? 18 : isTablet ? 20 : 24;\n const padding = isMobile ? 10 : isTablet ? 12 : 15;\n\n const performanceScore = useMemo(\n () => calculatePerformanceScore(matchStats),\n [matchStats]\n );\n\n const rating = useMemo(\n () => getPerformanceRating(performanceScore),\n [performanceScore]\n );\n\n const ratingInfo = PERFORMANCE_RATING_THRESHOLDS[rating];\n\n const winnerStats = useMemo(\n () => (matchStats.winner === 0 ? matchStats.player1 : matchStats.player2),\n [matchStats]\n );\n\n return (\n <div\n data-testid=\"performance-rating\"\n style={{\n display: \"flex\",\n flexDirection: \"column\",\n alignItems: \"center\",\n background: hexToRgbaString(KOREAN_COLORS.UI_BACKGROUND_DARK, 0.9),\n border: `3px solid ${hexToRgbaString(ratingInfo.color, 0.8)}`,\n borderRadius: \"16px\",\n padding: padding,\n marginBottom: padding / 2,\n minWidth: isMobile ? \"260px\" : \"300px\",\n boxShadow: `0 0 30px ${hexToRgbaString(ratingInfo.color, 0.3)}`,\n animation: \"ratingPulse 2s ease-in-out infinite\",\n }}\n >\n {/* Title */}\n <div\n style={{\n fontSize: labelFontSize,\n color: toCssColor(KOREAN_COLORS.TEXT_SECONDARY),\n fontFamily: FONT_FAMILY.KOREAN,\n marginBottom: padding / 2,\n textTransform: \"uppercase\",\n letterSpacing: \"0.1em\",\n }}\n data-testid=\"rating-label\"\n >\n 전투 등급 | Performance Rating\n </div>\n\n {/* Rating Letter */}\n <div\n style={{\n fontSize: ratingFontSize,\n fontWeight: \"bold\",\n color: toCssColor(ratingInfo.color),\n fontFamily: FONT_FAMILY.KOREAN,\n textShadow: `0 0 20px ${hexToRgbaString(ratingInfo.color, 0.6)}`,\n marginBottom: padding / 2,\n animation: \"ratingGlow 1.5s ease-in-out infinite\",\n }}\n data-testid=\"rating-letter\"\n >\n {rating}\n </div>\n\n {/* Rating Description */}\n <div\n style={{\n fontSize: labelFontSize,\n color: toCssColor(KOREAN_COLORS.PRIMARY_CYAN),\n fontFamily: FONT_FAMILY.KOREAN,\n marginBottom: padding,\n textAlign: \"center\",\n }}\n data-testid=\"rating-description\"\n >\n {ratingInfo.description.korean} | {ratingInfo.description.english}\n </div>\n\n {/* Performance Score */}\n <div\n style={{\n display: \"flex\",\n flexDirection: \"column\",\n alignItems: \"center\",\n width: \"100%\",\n padding: padding,\n background: hexToRgbaString(KOREAN_COLORS.UI_BACKGROUND_MEDIUM, 0.4),\n borderRadius: \"8px\",\n }}\n >\n <div\n style={{\n fontSize: scoreFontSize,\n fontWeight: \"bold\",\n color: toCssColor(ratingInfo.color),\n fontFamily: FONT_FAMILY.KOREAN,\n marginBottom: padding / 4,\n }}\n data-testid=\"performance-score\"\n >\n {performanceScore}\n </div>\n <div\n style={{\n fontSize: labelFontSize - 2,\n color: toCssColor(KOREAN_COLORS.TEXT_TERTIARY),\n fontFamily: FONT_FAMILY.KOREAN,\n }}\n >\n 전투 점수 | Combat Score\n </div>\n\n {/* Score Breakdown */}\n <div\n style={{\n display: \"grid\",\n gridTemplateColumns: \"1fr 1fr\",\n gap: padding / 2,\n marginTop: padding,\n width: \"100%\",\n fontSize: labelFontSize - 4,\n color: toCssColor(KOREAN_COLORS.TEXT_SECONDARY),\n }}\n data-testid=\"score-breakdown\"\n >\n <div style={{ textAlign: \"center\" }}>\n <div\n style={{\n color: toCssColor(KOREAN_COLORS.ACCENT_GOLD),\n fontWeight: \"bold\",\n }}\n >\n {winnerStats.perfectStrikes ?? 0}\n </div>\n <div>완벽 | Perfect</div>\n </div>\n <div style={{ textAlign: \"center\" }}>\n <div\n style={{\n color: toCssColor(KOREAN_COLORS.VITAL_POINT_HIT),\n fontWeight: \"bold\",\n }}\n >\n {winnerStats.vitalPointHits ?? 0}\n </div>\n <div>급소 | Vital Hits</div>\n </div>\n <div style={{ textAlign: \"center\" }}>\n <div\n style={{\n color: toCssColor(KOREAN_COLORS.PRIMARY_CYAN),\n fontWeight: \"bold\",\n }}\n >\n {winnerStats.techniques?.length ?? 0}\n </div>\n <div>기술 | Techniques</div>\n </div>\n <div style={{ textAlign: \"center\" }}>\n <div\n style={{\n color: toCssColor(KOREAN_COLORS.CRITICAL_HIT),\n fontWeight: \"bold\",\n }}\n >\n {matchStats.criticalHits}\n </div>\n <div>치명타 | Criticals</div>\n </div>\n </div>\n </div>\n\n {/* CSS Animations */}\n <style>{`\n ${pulseAnimation}\n\n @keyframes ratingGlow {\n 0%, 100% {\n text-shadow: 0 0 20px ${hexToRgbaString(ratingInfo.color, 0.6)};\n }\n 50% {\n text-shadow: 0 0 30px ${hexToRgbaString(\n ratingInfo.color,\n 0.9\n )}, 0 0 50px ${hexToRgbaString(ratingInfo.color, 0.5)};\n }\n }\n `}</style>\n </div>\n );\n};\n"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"VictoryAnimation3D.js","names":[],"sources":["../../../../../src/components/screens/endscreen/components/VictoryAnimation3D.tsx"],"sourcesContent":["import { useFrame } from \"@react-three/fiber\";\nimport React, { useEffect, useRef, useState } from \"react\";\nimport * as THREE from \"three\";\nimport { KOREAN_COLORS } from \"../../../../types/constants\";\n\n/**\n * Victory Animation 3D Component\n * Displays celebratory 3D particle effects for victory screen\n * Enhanced with additional Korean symbolism and dynamic effects\n * Optimized for 60fps performance with object reuse\n */\nexport const VictoryAnimation3D: React.FC = () => {\n const groupRef = useRef<THREE.Group>(null);\n const particlesRef = useRef<THREE.Points>(null);\n const ringsRef = useRef<THREE.Group>(null);\n const symbolsRef = useRef<THREE.Group>(null);\n\n const [reusableScale] = useState(() => new THREE.Vector3());\n const [reusablePosition] = useState(() => new THREE.Vector3());\n\n const [particlePositions] = useState(() => {\n const count = 200; // Increased from 150 for more dramatic effect\n const positions = new Float32Array(count * 3);\n\n for (let i = 0; i < count; i++) {\n const i3 = i * 3;\n const radius = 3 + Math.random() * 2;\n const theta = Math.random() * Math.PI * 2;\n const phi = Math.random() * Math.PI;\n\n positions[i3] = radius * Math.sin(phi) * Math.cos(theta);\n positions[i3 + 1] = radius * Math.cos(phi);\n positions[i3 + 2] = radius * Math.sin(phi) * Math.sin(theta);\n }\n\n return positions;\n });\n\n const [secondaryParticles] = useState(() => {\n const count = 50;\n const positions = new Float32Array(count * 3);\n\n for (let i = 0; i < count; i++) {\n const i3 = i * 3;\n const radius = 5 + Math.random() * 3;\n const theta = Math.random() * Math.PI * 2;\n\n positions[i3] = radius * Math.cos(theta);\n positions[i3 + 1] = Math.random() * 4 - 2;\n positions[i3 + 2] = radius * Math.sin(theta);\n }\n\n return positions;\n });\n\n useFrame((state) => {\n const time = state.clock.elapsedTime;\n\n if (groupRef.current) {\n groupRef.current.rotation.y = time * 0.3;\n }\n\n if (particlesRef.current) {\n const scale = 1 + Math.sin(time * 2) * 0.2;\n reusableScale.setScalar(scale);\n particlesRef.current.scale.copy(reusableScale);\n \n reusablePosition.set(0, Math.sin(time * 0.8) * 0.5, 0);\n particlesRef.current.position.copy(reusablePosition);\n }\n\n if (ringsRef.current) {\n ringsRef.current.rotation.x = time * 0.5;\n ringsRef.current.rotation.z = time * 0.3;\n }\n\n if (symbolsRef.current) {\n symbolsRef.current.rotation.y = -time * 0.4;\n symbolsRef.current.rotation.x = Math.sin(time * 0.5) * 0.1;\n }\n });\n\n useEffect(() => {\n const group = groupRef.current;\n const particles = particlesRef.current;\n const rings = ringsRef.current;\n const symbols = symbolsRef.current;\n\n return () => {\n if (particles) {\n particles.geometry?.dispose();\n if (particles.material) {\n (particles.material as THREE.Material).dispose();\n }\n }\n if (rings?.children && Array.isArray(rings.children)) {\n rings.children.forEach((child) => {\n if (child instanceof THREE.Mesh) {\n child.geometry?.dispose();\n if (child.material) {\n (child.material as THREE.Material).dispose();\n }\n }\n });\n }\n if (symbols?.children && Array.isArray(symbols.children)) {\n symbols.children.forEach((child) => {\n if (child instanceof THREE.Mesh) {\n child.geometry?.dispose();\n if (child.material) {\n (child.material as THREE.Material).dispose();\n }\n }\n });\n }\n if (group?.children && Array.isArray(group.children)) {\n group.children.forEach((child) => {\n if (\n child === particles ||\n child === rings ||\n child === symbols\n ) {\n return;\n }\n\n if (child instanceof THREE.Mesh) {\n child.geometry?.dispose();\n if (child.material) {\n (child.material as THREE.Material).dispose();\n }\n } else if (child instanceof THREE.Points) {\n child.geometry?.dispose();\n if (child.material) {\n (child.material as THREE.Material).dispose();\n }\n }\n });\n }\n };\n }, []);\n\n return (\n <group\n ref={groupRef}\n position={[0, 2, 0]}\n data-testid=\"victory-animation-3d\"\n >\n {/* Primary victory particles */}\n <points ref={particlesRef}>\n <bufferGeometry>\n <bufferAttribute\n attach=\"attributes-position\"\n count={200}\n itemSize={3}\n args={[particlePositions, 3]}\n />\n </bufferGeometry>\n <pointsMaterial\n size={0.2}\n color={new THREE.Color(KOREAN_COLORS.ACCENT_GOLD)}\n transparent\n opacity={0.8}\n sizeAttenuation\n depthWrite={false}\n />\n </points>\n\n {/* Secondary particle layer */}\n <points position={[0, 1, 0]}>\n <bufferGeometry>\n <bufferAttribute\n attach=\"attributes-position\"\n count={50}\n itemSize={3}\n args={[secondaryParticles, 3]}\n />\n </bufferGeometry>\n <pointsMaterial\n size={0.15}\n color={new THREE.Color(KOREAN_COLORS.PRIMARY_CYAN)}\n transparent\n opacity={0.6}\n sizeAttenuation\n depthWrite={false}\n />\n </points>\n\n {/* Rotating rings */}\n <group ref={ringsRef}>\n <mesh rotation={[Math.PI / 2, 0, 0]}>\n <torusGeometry args={[2, 0.05, 16, 100]} />\n <meshBasicMaterial\n color={KOREAN_COLORS.PRIMARY_CYAN}\n transparent\n opacity={0.6}\n />\n </mesh>\n\n <mesh rotation={[Math.PI / 2, Math.PI / 4, 0]}>\n <torusGeometry args={[2.5, 0.05, 16, 100]} />\n <meshBasicMaterial\n color={KOREAN_COLORS.ACCENT_GOLD}\n transparent\n opacity={0.4}\n />\n </mesh>\n\n <mesh rotation={[Math.PI / 2, Math.PI / 2, 0]}>\n <torusGeometry args={[3, 0.05, 16, 100]} />\n <meshBasicMaterial\n color={KOREAN_COLORS.PRIMARY_CYAN}\n transparent\n opacity={0.3}\n />\n </mesh>\n </group>\n\n {/* Korean symbol elements - octagonal shape representing 팔괘 (eight trigrams) */}\n <group ref={symbolsRef}>\n {Array.from({ length: 8 }).map((_, i) => {\n const angle = (i / 8) * Math.PI * 2;\n const radius = 4;\n return (\n <mesh\n key={i}\n position={[\n Math.cos(angle) * radius,\n 0,\n Math.sin(angle) * radius,\n ]}\n rotation={[0, angle + Math.PI / 2, 0]}\n >\n <boxGeometry args={[0.8, 0.1, 0.1]} />\n <meshStandardMaterial\n color={KOREAN_COLORS.ACCENT_GOLD}\n emissive={KOREAN_COLORS.ACCENT_GOLD}\n emissiveIntensity={0.5}\n transparent\n opacity={0.7}\n />\n </mesh>\n );\n })}\n </group>\n\n {/* Central glow sphere */}\n <mesh>\n <sphereGeometry args={[0.5, 32, 32]} />\n <meshStandardMaterial\n color={KOREAN_COLORS.ACCENT_GOLD}\n emissive={KOREAN_COLORS.ACCENT_GOLD}\n emissiveIntensity={1.5}\n transparent\n opacity={0.8}\n />\n </mesh>\n\n {/* Outer glow */}\n <mesh>\n <sphereGeometry args={[0.8, 32, 32]} />\n <meshBasicMaterial\n color={KOREAN_COLORS.PRIMARY_CYAN}\n transparent\n opacity={0.2}\n side={THREE.BackSide}\n />\n </mesh>\n\n {/* Additional inner glow layer */}\n <mesh>\n <sphereGeometry args={[0.6, 32, 32]} />\n <meshBasicMaterial\n color={KOREAN_COLORS.ACCENT_GOLD}\n transparent\n opacity={0.3}\n side={THREE.BackSide}\n />\n </mesh>\n\n {/* Point light for glow effect */}\n <pointLight\n position={[0, 0, 0]}\n intensity={3}\n distance={10}\n color={KOREAN_COLORS.ACCENT_GOLD}\n />\n\n {/* Secondary accent lights */}\n <pointLight\n position={[2, 2, 0]}\n intensity={1.5}\n distance={6}\n color={KOREAN_COLORS.PRIMARY_CYAN}\n />\n <pointLight\n position={[-2, 2, 0]}\n intensity={1.5}\n distance={6}\n color={KOREAN_COLORS.PRIMARY_CYAN}\n />\n </group>\n );\n};\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"VictoryAnimation3D.js","names":[],"sources":["../../../../../src/components/screens/endscreen/components/VictoryAnimation3D.tsx"],"sourcesContent":["import { useFrame } from \"@react-three/fiber\";\nimport React, { useEffect, useRef, useState } from \"react\";\nimport * as THREE from \"three\";\nimport { KOREAN_COLORS } from \"../../../../types/constants\";\n\n/**\n * Victory Animation 3D Component\n * Displays celebratory 3D particle effects for victory screen\n * Enhanced with additional Korean symbolism and dynamic effects\n * Optimized for 60fps performance with object reuse\n */\nexport const VictoryAnimation3D: React.FC = () => {\n const groupRef = useRef<THREE.Group>(null);\n const particlesRef = useRef<THREE.Points>(null);\n const ringsRef = useRef<THREE.Group>(null);\n const symbolsRef = useRef<THREE.Group>(null);\n\n const [reusableScale] = useState(() => new THREE.Vector3());\n const [reusablePosition] = useState(() => new THREE.Vector3());\n\n const [particlePositions] = useState(() => {\n const count = 200; // Increased from 150 for more dramatic effect\n const positions = new Float32Array(count * 3);\n\n for (let i = 0; i < count; i++) {\n const i3 = i * 3;\n const radius = 3 + Math.random() * 2;\n const theta = Math.random() * Math.PI * 2;\n const phi = Math.random() * Math.PI;\n\n positions[i3] = radius * Math.sin(phi) * Math.cos(theta);\n positions[i3 + 1] = radius * Math.cos(phi);\n positions[i3 + 2] = radius * Math.sin(phi) * Math.sin(theta);\n }\n\n return positions;\n });\n\n const [secondaryParticles] = useState(() => {\n const count = 50;\n const positions = new Float32Array(count * 3);\n\n for (let i = 0; i < count; i++) {\n const i3 = i * 3;\n const radius = 5 + Math.random() * 3;\n const theta = Math.random() * Math.PI * 2;\n\n positions[i3] = radius * Math.cos(theta);\n positions[i3 + 1] = Math.random() * 4 - 2;\n positions[i3 + 2] = radius * Math.sin(theta);\n }\n\n return positions;\n });\n\n useFrame((state) => {\n const time = state.clock.elapsedTime;\n\n if (groupRef.current) {\n groupRef.current.rotation.y = time * 0.3;\n }\n\n if (particlesRef.current) {\n const scale = 1 + Math.sin(time * 2) * 0.2;\n reusableScale.setScalar(scale);\n particlesRef.current.scale.copy(reusableScale);\n \n reusablePosition.set(0, Math.sin(time * 0.8) * 0.5, 0);\n particlesRef.current.position.copy(reusablePosition);\n }\n\n if (ringsRef.current) {\n ringsRef.current.rotation.x = time * 0.5;\n ringsRef.current.rotation.z = time * 0.3;\n }\n\n if (symbolsRef.current) {\n symbolsRef.current.rotation.y = -time * 0.4;\n symbolsRef.current.rotation.x = Math.sin(time * 0.5) * 0.1;\n }\n });\n\n useEffect(() => {\n const group = groupRef.current;\n const particles = particlesRef.current;\n const rings = ringsRef.current;\n const symbols = symbolsRef.current;\n\n return () => {\n if (particles) {\n particles.geometry?.dispose();\n if (particles.material) {\n (particles.material as THREE.Material).dispose();\n }\n }\n if (rings?.children && Array.isArray(rings.children)) {\n rings.children.forEach((child) => {\n if (child instanceof THREE.Mesh) {\n child.geometry?.dispose();\n if (child.material) {\n (child.material as THREE.Material).dispose();\n }\n }\n });\n }\n if (symbols?.children && Array.isArray(symbols.children)) {\n symbols.children.forEach((child) => {\n if (child instanceof THREE.Mesh) {\n child.geometry?.dispose();\n if (child.material) {\n (child.material as THREE.Material).dispose();\n }\n }\n });\n }\n if (group?.children && Array.isArray(group.children)) {\n group.children.forEach((child) => {\n if (\n child === particles ||\n child === rings ||\n child === symbols\n ) {\n return;\n }\n\n if (child instanceof THREE.Mesh) {\n child.geometry?.dispose();\n if (child.material) {\n (child.material as THREE.Material).dispose();\n }\n } else if (child instanceof THREE.Points) {\n child.geometry?.dispose();\n if (child.material) {\n (child.material as THREE.Material).dispose();\n }\n }\n });\n }\n };\n }, []);\n\n return (\n <group\n ref={groupRef}\n position={[0, 2, 0]}\n data-testid=\"victory-animation-3d\"\n >\n {/* Primary victory particles */}\n <points ref={particlesRef}>\n <bufferGeometry>\n <bufferAttribute\n attach=\"attributes-position\"\n count={200}\n itemSize={3}\n args={[particlePositions, 3]}\n />\n </bufferGeometry>\n <pointsMaterial\n size={0.2}\n color={new THREE.Color(KOREAN_COLORS.ACCENT_GOLD)}\n transparent\n opacity={0.8}\n sizeAttenuation\n depthWrite={false}\n />\n </points>\n\n {/* Secondary particle layer */}\n <points position={[0, 1, 0]}>\n <bufferGeometry>\n <bufferAttribute\n attach=\"attributes-position\"\n count={50}\n itemSize={3}\n args={[secondaryParticles, 3]}\n />\n </bufferGeometry>\n <pointsMaterial\n size={0.15}\n color={new THREE.Color(KOREAN_COLORS.PRIMARY_CYAN)}\n transparent\n opacity={0.6}\n sizeAttenuation\n depthWrite={false}\n />\n </points>\n\n {/* Rotating rings */}\n <group ref={ringsRef}>\n <mesh rotation={[Math.PI / 2, 0, 0]}>\n <torusGeometry args={[2, 0.05, 16, 100]} />\n <meshBasicMaterial\n color={KOREAN_COLORS.PRIMARY_CYAN}\n transparent\n opacity={0.6}\n />\n </mesh>\n\n <mesh rotation={[Math.PI / 2, Math.PI / 4, 0]}>\n <torusGeometry args={[2.5, 0.05, 16, 100]} />\n <meshBasicMaterial\n color={KOREAN_COLORS.ACCENT_GOLD}\n transparent\n opacity={0.4}\n />\n </mesh>\n\n <mesh rotation={[Math.PI / 2, Math.PI / 2, 0]}>\n <torusGeometry args={[3, 0.05, 16, 100]} />\n <meshBasicMaterial\n color={KOREAN_COLORS.PRIMARY_CYAN}\n transparent\n opacity={0.3}\n />\n </mesh>\n </group>\n\n {/* Korean symbol elements - octagonal shape representing 팔괘 (eight trigrams) */}\n <group ref={symbolsRef}>\n {Array.from({ length: 8 }).map((_, i) => {\n const angle = (i / 8) * Math.PI * 2;\n const radius = 4;\n return (\n <mesh\n key={i}\n position={[\n Math.cos(angle) * radius,\n 0,\n Math.sin(angle) * radius,\n ]}\n rotation={[0, angle + Math.PI / 2, 0]}\n >\n <boxGeometry args={[0.8, 0.1, 0.1]} />\n <meshStandardMaterial\n color={KOREAN_COLORS.ACCENT_GOLD}\n emissive={KOREAN_COLORS.ACCENT_GOLD}\n emissiveIntensity={0.5}\n transparent\n opacity={0.7}\n />\n </mesh>\n );\n })}\n </group>\n\n {/* Central glow sphere */}\n <mesh>\n <sphereGeometry args={[0.5, 32, 32]} />\n <meshStandardMaterial\n color={KOREAN_COLORS.ACCENT_GOLD}\n emissive={KOREAN_COLORS.ACCENT_GOLD}\n emissiveIntensity={1.5}\n transparent\n opacity={0.8}\n />\n </mesh>\n\n {/* Outer glow */}\n <mesh>\n <sphereGeometry args={[0.8, 32, 32]} />\n <meshBasicMaterial\n color={KOREAN_COLORS.PRIMARY_CYAN}\n transparent\n opacity={0.2}\n side={THREE.BackSide}\n />\n </mesh>\n\n {/* Additional inner glow layer */}\n <mesh>\n <sphereGeometry args={[0.6, 32, 32]} />\n <meshBasicMaterial\n color={KOREAN_COLORS.ACCENT_GOLD}\n transparent\n opacity={0.3}\n side={THREE.BackSide}\n />\n </mesh>\n\n {/* Point light for glow effect */}\n <pointLight\n position={[0, 0, 0]}\n intensity={3}\n distance={10}\n color={KOREAN_COLORS.ACCENT_GOLD}\n />\n\n {/* Secondary accent lights */}\n <pointLight\n position={[2, 2, 0]}\n intensity={1.5}\n distance={6}\n color={KOREAN_COLORS.PRIMARY_CYAN}\n />\n <pointLight\n position={[-2, 2, 0]}\n intensity={1.5}\n distance={6}\n color={KOREAN_COLORS.PRIMARY_CYAN}\n />\n </group>\n );\n};\n"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"WinnerDisplayOverlayHtml.js","names":[],"sources":["../../../../../src/components/screens/endscreen/components/WinnerDisplayOverlayHtml.tsx"],"sourcesContent":["import React from \"react\";\nimport { PlayerState } from \"../../../../systems\";\nimport {\n ARCHETYPE_ASSETS,\n FONT_FAMILY,\n KOREAN_COLORS,\n} from \"../../../../types/constants\";\nimport { hexToRgbaString } from \"../../../../utils/colorUtils\";\nimport { fadeInAnimation, scaleInAnimation } from \"./animations\";\n\nexport interface WinnerDisplayProps {\n readonly winner: PlayerState;\n readonly isVictory: boolean;\n readonly isMobile: boolean;\n readonly isTablet: boolean;\n}\n\n/**\n * Helper to convert hex color to CSS string\n */\nconst toCssColor = (hex: number): string => hexToRgbaString(hex, 1);\n\n/**\n * Winner Display Component\n * Shows winner announcement with archetype details\n */\nexport const WinnerDisplay: React.FC<WinnerDisplayProps> = ({\n winner,\n isVictory,\n isMobile,\n isTablet,\n}) => {\n const titleFontSize = isMobile ? 28 : isTablet ? 36 : 44;\n const subtitleFontSize = isMobile ? 16 : isTablet ? 18 : 22;\n const detailFontSize = isMobile ? 12 : 14;\n const spacing = isMobile ? 10 : isTablet ? 12 : 15;\n\n const primaryColor = isVictory\n ? KOREAN_COLORS.ACCENT_GOLD\n : KOREAN_COLORS.ACCENT_RED;\n\n const resultText = isVictory\n ? { korean: \"승리!\", english: \"Victory!\" }\n : { korean: \"패배\", english: \"Defeat\" };\n\n const archetypeKey =\n winner.archetype.toLowerCase() as keyof typeof ARCHETYPE_ASSETS;\n const archetypeAsset =\n ARCHETYPE_ASSETS[archetypeKey] || ARCHETYPE_ASSETS.musa;\n\n return (\n <div\n data-testid=\"winner-display\"\n style={{\n display: \"flex\",\n flexDirection: \"column\",\n alignItems: \"center\",\n marginBottom: spacing,\n animation: \"fadeIn 0.8s ease-in\",\n }}\n >\n {/* Result Title with glow effect */}\n <div\n style={{\n fontSize: titleFontSize,\n fontWeight: \"bold\",\n color: toCssColor(primaryColor),\n textShadow: `0 0 20px ${hexToRgbaString(\n primaryColor,\n 0.8\n )}, 0 0 40px ${hexToRgbaString(primaryColor, 0.4)}`,\n marginBottom: spacing,\n textAlign: \"center\",\n fontFamily: FONT_FAMILY.KOREAN,\n animation: \"scaleIn 0.5s ease-out\",\n }}\n data-testid=\"result-title\"\n >\n {resultText.korean} | {resultText.english}\n </div>\n\n {/* Winner Name */}\n <div\n style={{\n fontSize: subtitleFontSize,\n color: toCssColor(KOREAN_COLORS.PRIMARY_CYAN),\n marginBottom: spacing / 2,\n textAlign: \"center\",\n fontFamily: FONT_FAMILY.KOREAN,\n fontWeight: \"bold\",\n }}\n data-testid=\"winner-name\"\n >\n {winner.name.korean} | {winner.name.english}\n </div>\n\n {/* Archetype Display */}\n <div\n style={{\n display: \"flex\",\n flexDirection: \"column\",\n alignItems: \"center\",\n background: hexToRgbaString(KOREAN_COLORS.UI_BACKGROUND_MEDIUM, 0.6),\n border: `2px solid ${hexToRgbaString(primaryColor, 0.8)}`,\n borderRadius: \"12px\",\n padding: spacing,\n marginTop: spacing / 2,\n minWidth: isMobile ? \"280px\" : \"320px\",\n }}\n data-testid=\"winner-archetype-display\"\n >\n {/* Archetype Name */}\n <div\n style={{\n fontSize: detailFontSize + 2,\n color: toCssColor(KOREAN_COLORS.ACCENT_GOLD),\n fontFamily: FONT_FAMILY.KOREAN,\n fontWeight: \"bold\",\n marginBottom: spacing / 2,\n }}\n data-testid=\"winner-archetype\"\n >\n {archetypeAsset.name_korean} | {archetypeAsset.name_english}\n </div>\n\n {/* Archetype Code */}\n <div\n style={{\n fontSize: detailFontSize - 2,\n color: toCssColor(KOREAN_COLORS.TEXT_SECONDARY),\n fontFamily: FONT_FAMILY.KOREAN,\n textTransform: \"uppercase\",\n letterSpacing: \"0.1em\",\n }}\n data-testid=\"archetype-code\"\n >\n {winner.archetype.toUpperCase()}\n </div>\n\n {/* Combat Stats Summary */}\n <div\n style={{\n display: \"flex\",\n flexDirection: \"row\",\n gap: spacing,\n marginTop: spacing,\n fontSize: detailFontSize - 2,\n color: toCssColor(KOREAN_COLORS.TEXT_TERTIARY),\n }}\n data-testid=\"combat-stats-summary\"\n >\n <div style={{ textAlign: \"center\" }}>\n <div\n style={{\n color: toCssColor(KOREAN_COLORS.HEALTH_FULL),\n fontWeight: \"bold\",\n }}\n >\n {Math.round(winner.health)}\n </div>\n <div>체력 | HP</div>\n </div>\n <div style={{ textAlign: \"center\" }}>\n <div\n style={{\n color: toCssColor(KOREAN_COLORS.KI_FULL),\n fontWeight: \"bold\",\n }}\n >\n {Math.round(winner.ki)}\n </div>\n <div>기력 | Ki</div>\n </div>\n <div style={{ textAlign: \"center\" }}>\n <div\n style={{\n color: toCssColor(KOREAN_COLORS.STAMINA_FULL),\n fontWeight: \"bold\",\n }}\n >\n {Math.round(winner.stamina)}\n </div>\n <div>스태미나 | Stamina</div>\n </div>\n </div>\n </div>\n\n {/* CSS Animations */}\n <style>{`\n ${fadeInAnimation}\n ${scaleInAnimation}\n `}</style>\n </div>\n );\n};\n"],"mappings":";;;;;;;;;;;AAoBA,IAAM,cAAc,QAAwB,gBAAgB,KAAK,
|
|
1
|
+
{"version":3,"file":"WinnerDisplayOverlayHtml.js","names":[],"sources":["../../../../../src/components/screens/endscreen/components/WinnerDisplayOverlayHtml.tsx"],"sourcesContent":["import React from \"react\";\nimport { PlayerState } from \"../../../../systems\";\nimport {\n ARCHETYPE_ASSETS,\n FONT_FAMILY,\n KOREAN_COLORS,\n} from \"../../../../types/constants\";\nimport { hexToRgbaString } from \"../../../../utils/colorUtils\";\nimport { fadeInAnimation, scaleInAnimation } from \"./animations\";\n\nexport interface WinnerDisplayProps {\n readonly winner: PlayerState;\n readonly isVictory: boolean;\n readonly isMobile: boolean;\n readonly isTablet: boolean;\n}\n\n/**\n * Helper to convert hex color to CSS string\n */\nconst toCssColor = (hex: number): string => hexToRgbaString(hex, 1);\n\n/**\n * Winner Display Component\n * Shows winner announcement with archetype details\n */\nexport const WinnerDisplay: React.FC<WinnerDisplayProps> = ({\n winner,\n isVictory,\n isMobile,\n isTablet,\n}) => {\n const titleFontSize = isMobile ? 28 : isTablet ? 36 : 44;\n const subtitleFontSize = isMobile ? 16 : isTablet ? 18 : 22;\n const detailFontSize = isMobile ? 12 : 14;\n const spacing = isMobile ? 10 : isTablet ? 12 : 15;\n\n const primaryColor = isVictory\n ? KOREAN_COLORS.ACCENT_GOLD\n : KOREAN_COLORS.ACCENT_RED;\n\n const resultText = isVictory\n ? { korean: \"승리!\", english: \"Victory!\" }\n : { korean: \"패배\", english: \"Defeat\" };\n\n const archetypeKey =\n winner.archetype.toLowerCase() as keyof typeof ARCHETYPE_ASSETS;\n const archetypeAsset =\n ARCHETYPE_ASSETS[archetypeKey] || ARCHETYPE_ASSETS.musa;\n\n return (\n <div\n data-testid=\"winner-display\"\n style={{\n display: \"flex\",\n flexDirection: \"column\",\n alignItems: \"center\",\n marginBottom: spacing,\n animation: \"fadeIn 0.8s ease-in\",\n }}\n >\n {/* Result Title with glow effect */}\n <div\n style={{\n fontSize: titleFontSize,\n fontWeight: \"bold\",\n color: toCssColor(primaryColor),\n textShadow: `0 0 20px ${hexToRgbaString(\n primaryColor,\n 0.8\n )}, 0 0 40px ${hexToRgbaString(primaryColor, 0.4)}`,\n marginBottom: spacing,\n textAlign: \"center\",\n fontFamily: FONT_FAMILY.KOREAN,\n animation: \"scaleIn 0.5s ease-out\",\n }}\n data-testid=\"result-title\"\n >\n {resultText.korean} | {resultText.english}\n </div>\n\n {/* Winner Name */}\n <div\n style={{\n fontSize: subtitleFontSize,\n color: toCssColor(KOREAN_COLORS.PRIMARY_CYAN),\n marginBottom: spacing / 2,\n textAlign: \"center\",\n fontFamily: FONT_FAMILY.KOREAN,\n fontWeight: \"bold\",\n }}\n data-testid=\"winner-name\"\n >\n {winner.name.korean} | {winner.name.english}\n </div>\n\n {/* Archetype Display */}\n <div\n style={{\n display: \"flex\",\n flexDirection: \"column\",\n alignItems: \"center\",\n background: hexToRgbaString(KOREAN_COLORS.UI_BACKGROUND_MEDIUM, 0.6),\n border: `2px solid ${hexToRgbaString(primaryColor, 0.8)}`,\n borderRadius: \"12px\",\n padding: spacing,\n marginTop: spacing / 2,\n minWidth: isMobile ? \"280px\" : \"320px\",\n }}\n data-testid=\"winner-archetype-display\"\n >\n {/* Archetype Name */}\n <div\n style={{\n fontSize: detailFontSize + 2,\n color: toCssColor(KOREAN_COLORS.ACCENT_GOLD),\n fontFamily: FONT_FAMILY.KOREAN,\n fontWeight: \"bold\",\n marginBottom: spacing / 2,\n }}\n data-testid=\"winner-archetype\"\n >\n {archetypeAsset.name_korean} | {archetypeAsset.name_english}\n </div>\n\n {/* Archetype Code */}\n <div\n style={{\n fontSize: detailFontSize - 2,\n color: toCssColor(KOREAN_COLORS.TEXT_SECONDARY),\n fontFamily: FONT_FAMILY.KOREAN,\n textTransform: \"uppercase\",\n letterSpacing: \"0.1em\",\n }}\n data-testid=\"archetype-code\"\n >\n {winner.archetype.toUpperCase()}\n </div>\n\n {/* Combat Stats Summary */}\n <div\n style={{\n display: \"flex\",\n flexDirection: \"row\",\n gap: spacing,\n marginTop: spacing,\n fontSize: detailFontSize - 2,\n color: toCssColor(KOREAN_COLORS.TEXT_TERTIARY),\n }}\n data-testid=\"combat-stats-summary\"\n >\n <div style={{ textAlign: \"center\" }}>\n <div\n style={{\n color: toCssColor(KOREAN_COLORS.HEALTH_FULL),\n fontWeight: \"bold\",\n }}\n >\n {Math.round(winner.health)}\n </div>\n <div>체력 | HP</div>\n </div>\n <div style={{ textAlign: \"center\" }}>\n <div\n style={{\n color: toCssColor(KOREAN_COLORS.KI_FULL),\n fontWeight: \"bold\",\n }}\n >\n {Math.round(winner.ki)}\n </div>\n <div>기력 | Ki</div>\n </div>\n <div style={{ textAlign: \"center\" }}>\n <div\n style={{\n color: toCssColor(KOREAN_COLORS.STAMINA_FULL),\n fontWeight: \"bold\",\n }}\n >\n {Math.round(winner.stamina)}\n </div>\n <div>스태미나 | Stamina</div>\n </div>\n </div>\n </div>\n\n {/* CSS Animations */}\n <style>{`\n ${fadeInAnimation}\n ${scaleInAnimation}\n `}</style>\n </div>\n );\n};\n"],"mappings":"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"}
|
|
@@ -21,7 +21,7 @@ import { useCallback, useEffect, useMemo, useRef, useState } from "react";
|
|
|
21
21
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
22
22
|
import { Canvas } from "@react-three/fiber";
|
|
23
23
|
//#region src/components/screens/intro/IntroScreen3D.tsx
|
|
24
|
-
var APP_VERSION = "0.7.
|
|
24
|
+
var APP_VERSION = "0.7.49";
|
|
25
25
|
var MENU_ITEMS = [
|
|
26
26
|
{
|
|
27
27
|
mode: GameMode.VERSUS,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"IntroScreen3D.js","names":[],"sources":["../../../../src/components/screens/intro/IntroScreen3D.tsx"],"sourcesContent":["import { Canvas } from \"@react-three/fiber\";\nimport React, {\n useCallback,\n useEffect,\n useMemo,\n useRef,\n useState,\n} from \"react\";\nimport { useAudio } from \"../../../audio/AudioProvider\";\nimport { useWebGLContextLossHandler } from \"../../../hooks/useWebGLContextLossHandler\";\nimport { useWindowSize } from \"../../../hooks/useWindowSize\";\nimport { getScreenSize } from \"../../../systems/ResponsiveScaling\";\nimport { PLAYER_ARCHETYPES_DATA } from \"../../../systems/types\";\nimport { GameMode, PlayerArchetype } from \"../../../types/common\";\nimport {\n ARCHETYPE_BACKGROUNDS,\n getKoreanFontSize,\n getPerformanceSettings,\n} from \"../../../types/constants\";\nimport { Z_INDEX } from \"../../../types/LayoutTypes\";\nimport { hexToRgbaString } from \"../../../utils/colorUtils\";\nimport { shouldUseMobileControls } from \"../../../utils/deviceDetection\";\nimport { getArchetypeAssets } from \"../../../utils/playerUtils\";\nimport { useKoreanTheme } from \"../../shared/base/useKoreanTheme\";\nimport { BackgroundScene3D } from \"../../shared/three\";\nimport { VolumeControl } from \"../../shared/ui/VolumeControl\";\nimport { ArchetypeDisplayOverlayHtml } from \"./components/ArchetypeDisplayOverlayHtml\";\nimport { EnhancedArchetypeDisplay } from \"./components/EnhancedArchetypeDisplayOverlayHtml\";\nimport { MenuSectionOverlayHtml } from \"./components/MenuSectionOverlayHtml\";\n\nconst APP_VERSION = import.meta.env.APP_VERSION;\n\nexport interface IntroScreen3DProps {\n readonly onMenuSelect: (mode: GameMode, archetype?: PlayerArchetype) => void;\n readonly onArchetypeSelect?: (archetype: PlayerArchetype) => void;\n readonly selectedArchetype?: PlayerArchetype;\n readonly width?: number;\n readonly height?: number;\n readonly useEnhancedArchetypeDisplay?: boolean; // Use enhanced card display\n}\n\nconst MENU_ITEMS: { mode: GameMode; korean: string; english: string }[] = [\n { mode: GameMode.VERSUS, korean: \"대전\", english: \"Combat\" },\n { mode: GameMode.TRAINING, korean: \"훈련\", english: \"Training\" },\n { mode: GameMode.CONTROLS, korean: \"조작\", english: \"Controls\" },\n { mode: GameMode.PHILOSOPHY, korean: \"철학\", english: \"Philosophy\" },\n];\n\nconst ARCHETYPE_TEXTURE_MAPPING: Record<PlayerArchetype, string> = {\n [PlayerArchetype.MUSA]: \"musa\",\n [PlayerArchetype.AMSALJA]: \"amsalja\",\n [PlayerArchetype.HACKER]: \"hacker\",\n [PlayerArchetype.JEONGBO_YOWON]: \"jeongbo_yowon\",\n [PlayerArchetype.JOJIK_POKRYEOKBAE]: \"jojik_pokryeokbae\",\n};\n\nconst getArchetypeIndex = (archetype: PlayerArchetype): number => {\n const archetypeKeys = Object.keys(\n PLAYER_ARCHETYPES_DATA,\n ) as PlayerArchetype[];\n return archetypeKeys.indexOf(archetype);\n};\n\nconst getArchetypeFromIndex = (index: number): PlayerArchetype => {\n const archetypeKeys = Object.keys(\n PLAYER_ARCHETYPES_DATA,\n ) as PlayerArchetype[];\n return archetypeKeys[index] ?? PlayerArchetype.MUSA;\n};\n\n/**\n * Three.js-based IntroScreen Component\n */\nexport const IntroScreen3D: React.FC<IntroScreen3DProps> = ({\n onMenuSelect,\n onArchetypeSelect,\n selectedArchetype = PlayerArchetype.MUSA,\n width: propWidth,\n height: propHeight,\n useEnhancedArchetypeDisplay = true, // Default to enhanced display\n}) => {\n const audio = useAudio();\n const introMusicStarted = useRef(false);\n const [selectedMenuIndex, setSelectedMenuIndex] = useState(0);\n\n useWebGLContextLossHandler({\n onContextLost: () => {\n console.warn(\"⚠️ WebGL context lost in IntroScreen\");\n },\n onContextRestored: () => {\n console.log(\"✓ WebGL context restored in IntroScreen\");\n },\n autoRestore: true,\n });\n\n const [currentArchetype, setCurrentArchetype] =\n useState<PlayerArchetype>(selectedArchetype);\n const [selectedArchetypeIndex, setSelectedArchetypeIndex] = useState<number>(\n getArchetypeIndex(selectedArchetype),\n );\n\n const { width, height } = useWindowSize();\n\n const screenWidth = propWidth ?? (width || 1200);\n const screenHeight = propHeight ?? (height || 800);\n\n const archetypeData = useMemo(() => {\n return Object.entries(PLAYER_ARCHETYPES_DATA).map(([key, data]) => {\n const archetypeEnum = key as PlayerArchetype;\n return {\n id: key.toLowerCase(),\n korean: data.name.korean,\n english: data.name.english,\n description: data.description.korean,\n color: data.colors.primary,\n textureKey: ARCHETYPE_TEXTURE_MAPPING[archetypeEnum],\n stats: data.stats,\n philosophy: data.philosophy,\n specialAbilities: data.specialAbilities, // Include special abilities\n };\n });\n }, []);\n\n useEffect(() => {\n setCurrentArchetype(selectedArchetype);\n setSelectedArchetypeIndex(getArchetypeIndex(selectedArchetype));\n }, [selectedArchetype]);\n\n const handleMenuItemSelect = useCallback(\n (mode: GameMode) => {\n onMenuSelect(mode, currentArchetype);\n },\n [onMenuSelect, currentArchetype],\n );\n\n const handleArchetypeIndexChange = useCallback(\n (index: number) => {\n const newArchetype = getArchetypeFromIndex(index);\n setSelectedArchetypeIndex(index);\n setCurrentArchetype(newArchetype);\n onArchetypeSelect?.(newArchetype);\n\n if (audio.isAudioReady) {\n audio.playSFX(\"menu_hover\");\n\n const archetypeAssets = getArchetypeAssets(newArchetype);\n audio.stopMusic();\n audio.playMusic(archetypeAssets.themeId);\n }\n },\n [onArchetypeSelect, audio],\n );\n\n useEffect(() => {\n const startMusic = () => {\n if (audio.isAudioReady && !introMusicStarted.current) {\n introMusicStarted.current = true;\n audio.playMusic(\"intro_theme\");\n }\n };\n window.addEventListener(\"keydown\", startMusic, { once: true });\n window.addEventListener(\"mousedown\", startMusic, { once: true });\n window.addEventListener(\"touchstart\", startMusic, { once: true, passive: true });\n\n return () => {\n if (audio.isInitialized) {\n audio.stopMusic();\n }\n };\n }, [audio]);\n\n useEffect(() => {\n const handleKeyDown = (event: KeyboardEvent) => {\n if (event.key === \"ArrowLeft\") {\n const newIndex =\n selectedArchetypeIndex === 0\n ? archetypeData.length - 1\n : selectedArchetypeIndex - 1;\n handleArchetypeIndexChange(newIndex);\n } else if (event.key === \"ArrowRight\") {\n const newIndex = (selectedArchetypeIndex + 1) % archetypeData.length;\n handleArchetypeIndexChange(newIndex);\n } else {\n switch (event.key.toLowerCase()) {\n case \"c\":\n handleMenuItemSelect(GameMode.CONTROLS);\n break;\n case \"p\":\n handleMenuItemSelect(GameMode.PHILOSOPHY);\n break;\n case \"t\":\n handleMenuItemSelect(GameMode.TRAINING);\n break;\n case \"v\":\n handleMenuItemSelect(GameMode.VERSUS);\n break;\n }\n }\n };\n\n window.addEventListener(\"keydown\", handleKeyDown);\n return () => window.removeEventListener(\"keydown\", handleKeyDown);\n }, [\n audio,\n archetypeData.length,\n selectedArchetypeIndex,\n handleArchetypeIndexChange,\n handleMenuItemSelect,\n ]);\n\n const isMobile = useMemo(() => shouldUseMobileControls(), []);\n\n const theme = useKoreanTheme({\n variant: \"primary\",\n size: \"md\",\n isMobile,\n });\n\n const colors = useMemo(\n () => ({\n trigramTextShadow: `0 0 10px ${hexToRgbaString(\n theme.colors.PRIMARY_CYAN,\n 0.8,\n )}`,\n footerBackground: hexToRgbaString(theme.colors.UI_BACKGROUND_DARK, 0.9),\n footerBorder: hexToRgbaString(theme.colors.ACCENT_GOLD, 0.3),\n }),\n [theme],\n );\n\n const performanceSettings = useMemo(() => {\n return getPerformanceSettings(screenWidth, isMobile);\n }, [screenWidth, isMobile]);\n\n const screenSize = useMemo(() => getScreenSize(screenWidth), [screenWidth]);\n\n const logoSize = useMemo(() => {\n const minDim = Math.min(screenWidth, screenHeight);\n const logoScale = {\n mobile: 0.28,\n tablet: 0.22,\n desktop: 0.18,\n large: 0.15,\n xlarge: 0.12,\n }[screenSize];\n return Math.min(minDim * logoScale, screenSize === \"xlarge\" ? 250 : 300);\n }, [screenWidth, screenHeight, screenSize]);\n\n const layoutHeights = useMemo(() => {\n const availableHeight = screenHeight;\n\n const titleHeight = screenWidth < 768 ? 32 : 38;\n\n const trigramHeight = screenWidth < 768 ? 16 : 22;\n const logoAreaHeight = logoSize + trigramHeight;\n\n const footerHeight = Math.max(availableHeight * 0.05, 48);\n\n const contentHeight =\n availableHeight - titleHeight - logoAreaHeight - footerHeight;\n\n const menuMinHeight = screenWidth < 768 ? 180 : 120;\n const menuPercent = screenWidth < 768 ? 0.38 : 0.25;\n const menuHeight = Math.max(contentHeight * menuPercent, menuMinHeight);\n const archetypeHeight = contentHeight - menuHeight - 8; // 8px gap\n\n const gap = Math.max(screenHeight * 0.002, 2);\n\n return {\n titleHeight,\n logoAreaHeight,\n menuHeight,\n archetypeHeight,\n footerHeight,\n gap,\n };\n }, [screenHeight, screenWidth, logoSize]);\n return (\n <div\n style={{\n width: screenWidth,\n height: screenHeight,\n position: \"relative\",\n overflow: \"hidden\",\n }}\n data-testid=\"intro-screen\"\n >\n {/* Archetype background image (very subtle, behind 3D scene) */}\n <div\n style={{\n position: \"absolute\",\n top: 0,\n left: 0,\n width: \"100%\",\n height: \"100%\",\n backgroundImage: `url(${ARCHETYPE_BACKGROUNDS.overview})`,\n backgroundSize: \"cover\",\n backgroundPosition: \"center\",\n backgroundRepeat: \"no-repeat\",\n opacity: 0.08,\n filter: \"blur(4px)\",\n zIndex: Z_INDEX.BACKGROUND,\n }}\n data-testid=\"archetype-background\"\n />\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 */}\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: performanceSettings.antialias,\n alpha: true,\n powerPreference: \"high-performance\",\n }}\n dpr={performanceSettings.dpr}\n camera={{ position: [0, 5, 10], fov: 75 }}\n onCreated={({ gl }) => {\n gl.setClearColor(theme.colors.UI_BACKGROUND_DARK, 0.95);\n }}\n >\n {/* 3D Background Scene */}\n <BackgroundScene3D theme=\"intro\" />\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=\"intro-hud-overlay\"\n >\n <div\n style={{\n width: \"100vw\",\n height: \"100vh\",\n display: \"flex\",\n flexDirection: \"column\",\n alignItems: \"center\",\n justifyContent: \"space-between\",\n padding: 0,\n pointerEvents: \"none\",\n zIndex: Z_INDEX.HUD,\n overflow: \"hidden\",\n }}\n >\n {/* Title - Small, above logo */}\n <div\n style={{\n display: \"flex\",\n flexDirection: \"column\",\n alignItems: \"center\",\n gap: \"2px\",\n pointerEvents: \"none\",\n marginTop: \"4px\",\n flexShrink: 0,\n }}\n data-testid=\"main-title-container\"\n >\n <div\n style={{\n fontSize: screenWidth < 768 ? \"14px\" : \"16px\",\n fontWeight: \"bold\",\n fontFamily: theme.koreanTypography.fontFamily,\n lineHeight: theme.koreanTypography.lineHeight,\n letterSpacing: theme.koreanTypography.letterSpacing,\n wordBreak: theme.koreanTypography.wordBreak,\n color: `#${theme.colors.ACCENT_GOLD.toString(16).padStart(6, \"0\")}`,\n textShadow: \"0 0 10px rgba(255, 170, 0, 0.5)\",\n }}\n >\n 흑괘 | Black Trigram\n </div>\n <div\n style={{\n fontSize: screenWidth < 768 ? \"10px\" : \"11px\",\n fontFamily: theme.koreanTypography.fontFamily,\n color: `#${theme.colors.TEXT_SECONDARY.toString(16).padStart(6, \"0\")}`,\n }}\n >\n 한국 무술 시뮬레이터 | Korean Martial Arts Simulator\n </div>\n </div>\n\n {/* Logo Section - Primary branding */}\n <div\n style={{\n flexShrink: 0,\n display: \"flex\",\n flexDirection: \"column\",\n alignItems: \"center\",\n justifyContent: \"center\",\n height: `${layoutHeights.logoAreaHeight}px`,\n pointerEvents: \"none\",\n }}\n data-testid=\"logo-section\"\n >\n {/* Logo Image - Prominent */}\n <img\n src=\"/assets/visual/logo/black-trigram.png\"\n alt=\"Black Trigram Logo\"\n style={{\n width: `${logoSize}px`,\n height: `${logoSize}px`,\n objectFit: \"contain\",\n filter: \"drop-shadow(0 0 30px rgba(0, 255, 255, 0.6))\",\n }}\n data-testid=\"main-logo\"\n onError={(e) => {\n e.currentTarget.style.display = \"none\";\n }}\n />\n\n {/* Trigram Symbols */}\n <div\n style={{\n fontSize: screenWidth < 768 ? \"16px\" : \"18px\",\n color: `#${theme.colors.PRIMARY_CYAN.toString(16).padStart(\n 6,\n \"0\",\n )}`,\n letterSpacing: screenWidth < 768 ? \"6px\" : \"8px\",\n textAlign: \"center\",\n marginTop: screenWidth < 768 ? \"8px\" : \"10px\",\n textShadow: colors.trigramTextShadow,\n }}\n data-testid=\"trigram-symbols\"\n >\n ☰ ☱ ☲ ☳ ☴ ☵ ☶ ☷\n </div>\n </div>\n\n {/* Main Content Area */}\n <div\n style={{\n width: \"100%\",\n flex: 1,\n display: \"flex\",\n flexDirection: \"column\",\n alignItems: \"center\",\n justifyContent: \"flex-start\",\n gap: \"4px\",\n paddingLeft: screenWidth < 768 ? \"8px\" : \"16px\",\n paddingRight: screenWidth < 768 ? \"8px\" : \"16px\",\n overflow: \"hidden\",\n pointerEvents: \"auto\",\n minHeight: 0,\n }}\n data-testid=\"main-content\"\n >\n {/* Menu Section - Compact */}\n <div\n style={{\n width: screenWidth < 768 ? \"95%\" : \"70%\",\n maxWidth: screenWidth < 768 ? \"100%\" : \"600px\",\n }}\n data-testid=\"menu-section-container\"\n >\n <MenuSectionOverlayHtml\n menuItems={MENU_ITEMS}\n selectedIndex={selectedMenuIndex}\n onModeSelect={handleMenuItemSelect}\n onSelectedIndexChange={setSelectedMenuIndex}\n onPlaySFX={audio.playSFX}\n width={\n screenWidth < 768\n ? screenWidth * 0.9\n : screenWidth < 1024\n ? Math.min(500, screenWidth * 0.6)\n : Math.min(550, screenWidth * 0.4)\n }\n height={layoutHeights.menuHeight}\n isMobile={isMobile}\n />\n </div>\n\n {/* Archetype Selection - Scrollable container */}\n <div\n style={{\n width: screenWidth < 768 ? \"100%\" : \"85%\",\n maxWidth: screenWidth < 768 ? \"100%\" : \"900px\",\n flex: 1,\n minHeight: 0,\n overflowY: \"auto\",\n overflowX: \"hidden\",\n }}\n data-testid=\"archetype-section-container\"\n >\n {useEnhancedArchetypeDisplay ? (\n <EnhancedArchetypeDisplay\n archetypes={archetypeData}\n selectedIndex={selectedArchetypeIndex}\n onArchetypeChange={handleArchetypeIndexChange}\n onPlaySFX={audio.playSFX}\n width={\n screenWidth < 768\n ? screenWidth * 0.9\n : screenWidth < 1024\n ? Math.min(700, screenWidth * 0.7)\n : Math.min(850, screenWidth * 0.55)\n }\n height={Math.max(layoutHeights.archetypeHeight - 40, 200)}\n isMobile={isMobile}\n allowDetailedView={screenWidth >= 768}\n />\n ) : (\n <ArchetypeDisplayOverlayHtml\n archetypes={archetypeData}\n selectedIndex={selectedArchetypeIndex}\n onArchetypeChange={handleArchetypeIndexChange}\n onPlaySFX={audio.playSFX}\n width={\n screenWidth < 768\n ? screenWidth * 0.9\n : screenWidth < 1024\n ? Math.min(700, screenWidth * 0.7)\n : Math.min(850, screenWidth * 0.55)\n }\n height={Math.max(layoutHeights.archetypeHeight - 40, 200)}\n isMobile={isMobile}\n />\n )}\n </div>\n </div>\n\n {/* Footer - Compact with all info */}\n <div\n style={{\n width: \"100%\",\n minHeight: `${layoutHeights.footerHeight}px`,\n display: \"flex\",\n flexDirection: \"column\",\n alignItems: \"center\",\n justifyContent: \"center\",\n gap: \"1px\",\n background: `linear-gradient(to bottom, rgba(0, 0, 0, 0), ${colors.footerBackground})`,\n borderTop: `1px solid ${colors.footerBorder}`,\n pointerEvents: \"auto\",\n paddingBottom: \"2px\",\n flexShrink: 0,\n }}\n data-testid=\"intro-footer\"\n >\n {/* Motto */}\n <div\n style={{\n fontSize: `${Math.max(getKoreanFontSize(\"SMALL\", screenWidth) - 3, 10)}px`,\n color: `#${theme.colors.ACCENT_CYAN.toString(16).padStart(6, \"0\")}`,\n fontFamily: theme.koreanTypography.fontFamily,\n fontStyle: \"italic\",\n textAlign: \"center\",\n }}\n data-testid=\"footer-motto\"\n >\n 흑괘의 길을 걸어라 - Walk the Path of the Black Trigram\n </div>\n {/* Open Source Link */}\n <div\n style={{\n fontSize: `${Math.max(getKoreanFontSize(\"SMALL\", screenWidth) - 4, 9)}px`,\n color: `#${theme.colors.ACCENT_BLUE.toString(16).padStart(6, \"0\")}`,\n textAlign: \"center\",\n cursor: \"pointer\",\n }}\n onClick={() =>\n window.open(\"https://github.com/Hack23/blacktrigram\", \"_blank\")\n }\n data-testid=\"footer-link\"\n >\n Open Source Korean Martial Arts Game by Hack23\n </div>\n {/* Version */}\n <div\n style={{\n fontSize: `${Math.max(getKoreanFontSize(\"SMALL\", screenWidth) - 5, 8)}px`,\n color: `#${theme.colors.ACCENT_BLUE.toString(16).padStart(6, \"0\")}`,\n textAlign: \"center\",\n cursor: \"pointer\",\n }}\n onClick={() =>\n window.open(\n `https://github.com/Hack23/blacktrigram/releases/tag/v${APP_VERSION}`,\n \"_blank\",\n )\n }\n data-testid=\"footer-version\"\n >\n v{APP_VERSION}\n </div>\n </div>\n </div>\n </div>\n </div>\n );\n};\n\nexport default IntroScreen3D;\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"IntroScreen3D.js","names":[],"sources":["../../../../src/components/screens/intro/IntroScreen3D.tsx"],"sourcesContent":["import { Canvas } from \"@react-three/fiber\";\nimport React, {\n useCallback,\n useEffect,\n useMemo,\n useRef,\n useState,\n} from \"react\";\nimport { useAudio } from \"../../../audio/AudioProvider\";\nimport { useWebGLContextLossHandler } from \"../../../hooks/useWebGLContextLossHandler\";\nimport { useWindowSize } from \"../../../hooks/useWindowSize\";\nimport { getScreenSize } from \"../../../systems/ResponsiveScaling\";\nimport { PLAYER_ARCHETYPES_DATA } from \"../../../systems/types\";\nimport { GameMode, PlayerArchetype } from \"../../../types/common\";\nimport {\n ARCHETYPE_BACKGROUNDS,\n getKoreanFontSize,\n getPerformanceSettings,\n} from \"../../../types/constants\";\nimport { Z_INDEX } from \"../../../types/LayoutTypes\";\nimport { hexToRgbaString } from \"../../../utils/colorUtils\";\nimport { shouldUseMobileControls } from \"../../../utils/deviceDetection\";\nimport { getArchetypeAssets } from \"../../../utils/playerUtils\";\nimport { useKoreanTheme } from \"../../shared/base/useKoreanTheme\";\nimport { BackgroundScene3D } from \"../../shared/three\";\nimport { VolumeControl } from \"../../shared/ui/VolumeControl\";\nimport { ArchetypeDisplayOverlayHtml } from \"./components/ArchetypeDisplayOverlayHtml\";\nimport { EnhancedArchetypeDisplay } from \"./components/EnhancedArchetypeDisplayOverlayHtml\";\nimport { MenuSectionOverlayHtml } from \"./components/MenuSectionOverlayHtml\";\n\nconst APP_VERSION = import.meta.env.APP_VERSION;\n\nexport interface IntroScreen3DProps {\n readonly onMenuSelect: (mode: GameMode, archetype?: PlayerArchetype) => void;\n readonly onArchetypeSelect?: (archetype: PlayerArchetype) => void;\n readonly selectedArchetype?: PlayerArchetype;\n readonly width?: number;\n readonly height?: number;\n readonly useEnhancedArchetypeDisplay?: boolean; // Use enhanced card display\n}\n\nconst MENU_ITEMS: { mode: GameMode; korean: string; english: string }[] = [\n { mode: GameMode.VERSUS, korean: \"대전\", english: \"Combat\" },\n { mode: GameMode.TRAINING, korean: \"훈련\", english: \"Training\" },\n { mode: GameMode.CONTROLS, korean: \"조작\", english: \"Controls\" },\n { mode: GameMode.PHILOSOPHY, korean: \"철학\", english: \"Philosophy\" },\n];\n\nconst ARCHETYPE_TEXTURE_MAPPING: Record<PlayerArchetype, string> = {\n [PlayerArchetype.MUSA]: \"musa\",\n [PlayerArchetype.AMSALJA]: \"amsalja\",\n [PlayerArchetype.HACKER]: \"hacker\",\n [PlayerArchetype.JEONGBO_YOWON]: \"jeongbo_yowon\",\n [PlayerArchetype.JOJIK_POKRYEOKBAE]: \"jojik_pokryeokbae\",\n};\n\nconst getArchetypeIndex = (archetype: PlayerArchetype): number => {\n const archetypeKeys = Object.keys(\n PLAYER_ARCHETYPES_DATA,\n ) as PlayerArchetype[];\n return archetypeKeys.indexOf(archetype);\n};\n\nconst getArchetypeFromIndex = (index: number): PlayerArchetype => {\n const archetypeKeys = Object.keys(\n PLAYER_ARCHETYPES_DATA,\n ) as PlayerArchetype[];\n return archetypeKeys[index] ?? PlayerArchetype.MUSA;\n};\n\n/**\n * Three.js-based IntroScreen Component\n */\nexport const IntroScreen3D: React.FC<IntroScreen3DProps> = ({\n onMenuSelect,\n onArchetypeSelect,\n selectedArchetype = PlayerArchetype.MUSA,\n width: propWidth,\n height: propHeight,\n useEnhancedArchetypeDisplay = true, // Default to enhanced display\n}) => {\n const audio = useAudio();\n const introMusicStarted = useRef(false);\n const [selectedMenuIndex, setSelectedMenuIndex] = useState(0);\n\n useWebGLContextLossHandler({\n onContextLost: () => {\n console.warn(\"⚠️ WebGL context lost in IntroScreen\");\n },\n onContextRestored: () => {\n console.log(\"✓ WebGL context restored in IntroScreen\");\n },\n autoRestore: true,\n });\n\n const [currentArchetype, setCurrentArchetype] =\n useState<PlayerArchetype>(selectedArchetype);\n const [selectedArchetypeIndex, setSelectedArchetypeIndex] = useState<number>(\n getArchetypeIndex(selectedArchetype),\n );\n\n const { width, height } = useWindowSize();\n\n const screenWidth = propWidth ?? (width || 1200);\n const screenHeight = propHeight ?? (height || 800);\n\n const archetypeData = useMemo(() => {\n return Object.entries(PLAYER_ARCHETYPES_DATA).map(([key, data]) => {\n const archetypeEnum = key as PlayerArchetype;\n return {\n id: key.toLowerCase(),\n korean: data.name.korean,\n english: data.name.english,\n description: data.description.korean,\n color: data.colors.primary,\n textureKey: ARCHETYPE_TEXTURE_MAPPING[archetypeEnum],\n stats: data.stats,\n philosophy: data.philosophy,\n specialAbilities: data.specialAbilities, // Include special abilities\n };\n });\n }, []);\n\n useEffect(() => {\n setCurrentArchetype(selectedArchetype);\n setSelectedArchetypeIndex(getArchetypeIndex(selectedArchetype));\n }, [selectedArchetype]);\n\n const handleMenuItemSelect = useCallback(\n (mode: GameMode) => {\n onMenuSelect(mode, currentArchetype);\n },\n [onMenuSelect, currentArchetype],\n );\n\n const handleArchetypeIndexChange = useCallback(\n (index: number) => {\n const newArchetype = getArchetypeFromIndex(index);\n setSelectedArchetypeIndex(index);\n setCurrentArchetype(newArchetype);\n onArchetypeSelect?.(newArchetype);\n\n if (audio.isAudioReady) {\n audio.playSFX(\"menu_hover\");\n\n const archetypeAssets = getArchetypeAssets(newArchetype);\n audio.stopMusic();\n audio.playMusic(archetypeAssets.themeId);\n }\n },\n [onArchetypeSelect, audio],\n );\n\n useEffect(() => {\n const startMusic = () => {\n if (audio.isAudioReady && !introMusicStarted.current) {\n introMusicStarted.current = true;\n audio.playMusic(\"intro_theme\");\n }\n };\n window.addEventListener(\"keydown\", startMusic, { once: true });\n window.addEventListener(\"mousedown\", startMusic, { once: true });\n window.addEventListener(\"touchstart\", startMusic, { once: true, passive: true });\n\n return () => {\n if (audio.isInitialized) {\n audio.stopMusic();\n }\n };\n }, [audio]);\n\n useEffect(() => {\n const handleKeyDown = (event: KeyboardEvent) => {\n if (event.key === \"ArrowLeft\") {\n const newIndex =\n selectedArchetypeIndex === 0\n ? archetypeData.length - 1\n : selectedArchetypeIndex - 1;\n handleArchetypeIndexChange(newIndex);\n } else if (event.key === \"ArrowRight\") {\n const newIndex = (selectedArchetypeIndex + 1) % archetypeData.length;\n handleArchetypeIndexChange(newIndex);\n } else {\n switch (event.key.toLowerCase()) {\n case \"c\":\n handleMenuItemSelect(GameMode.CONTROLS);\n break;\n case \"p\":\n handleMenuItemSelect(GameMode.PHILOSOPHY);\n break;\n case \"t\":\n handleMenuItemSelect(GameMode.TRAINING);\n break;\n case \"v\":\n handleMenuItemSelect(GameMode.VERSUS);\n break;\n }\n }\n };\n\n window.addEventListener(\"keydown\", handleKeyDown);\n return () => window.removeEventListener(\"keydown\", handleKeyDown);\n }, [\n audio,\n archetypeData.length,\n selectedArchetypeIndex,\n handleArchetypeIndexChange,\n handleMenuItemSelect,\n ]);\n\n const isMobile = useMemo(() => shouldUseMobileControls(), []);\n\n const theme = useKoreanTheme({\n variant: \"primary\",\n size: \"md\",\n isMobile,\n });\n\n const colors = useMemo(\n () => ({\n trigramTextShadow: `0 0 10px ${hexToRgbaString(\n theme.colors.PRIMARY_CYAN,\n 0.8,\n )}`,\n footerBackground: hexToRgbaString(theme.colors.UI_BACKGROUND_DARK, 0.9),\n footerBorder: hexToRgbaString(theme.colors.ACCENT_GOLD, 0.3),\n }),\n [theme],\n );\n\n const performanceSettings = useMemo(() => {\n return getPerformanceSettings(screenWidth, isMobile);\n }, [screenWidth, isMobile]);\n\n const screenSize = useMemo(() => getScreenSize(screenWidth), [screenWidth]);\n\n const logoSize = useMemo(() => {\n const minDim = Math.min(screenWidth, screenHeight);\n const logoScale = {\n mobile: 0.28,\n tablet: 0.22,\n desktop: 0.18,\n large: 0.15,\n xlarge: 0.12,\n }[screenSize];\n return Math.min(minDim * logoScale, screenSize === \"xlarge\" ? 250 : 300);\n }, [screenWidth, screenHeight, screenSize]);\n\n const layoutHeights = useMemo(() => {\n const availableHeight = screenHeight;\n\n const titleHeight = screenWidth < 768 ? 32 : 38;\n\n const trigramHeight = screenWidth < 768 ? 16 : 22;\n const logoAreaHeight = logoSize + trigramHeight;\n\n const footerHeight = Math.max(availableHeight * 0.05, 48);\n\n const contentHeight =\n availableHeight - titleHeight - logoAreaHeight - footerHeight;\n\n const menuMinHeight = screenWidth < 768 ? 180 : 120;\n const menuPercent = screenWidth < 768 ? 0.38 : 0.25;\n const menuHeight = Math.max(contentHeight * menuPercent, menuMinHeight);\n const archetypeHeight = contentHeight - menuHeight - 8; // 8px gap\n\n const gap = Math.max(screenHeight * 0.002, 2);\n\n return {\n titleHeight,\n logoAreaHeight,\n menuHeight,\n archetypeHeight,\n footerHeight,\n gap,\n };\n }, [screenHeight, screenWidth, logoSize]);\n return (\n <div\n style={{\n width: screenWidth,\n height: screenHeight,\n position: \"relative\",\n overflow: \"hidden\",\n }}\n data-testid=\"intro-screen\"\n >\n {/* Archetype background image (very subtle, behind 3D scene) */}\n <div\n style={{\n position: \"absolute\",\n top: 0,\n left: 0,\n width: \"100%\",\n height: \"100%\",\n backgroundImage: `url(${ARCHETYPE_BACKGROUNDS.overview})`,\n backgroundSize: \"cover\",\n backgroundPosition: \"center\",\n backgroundRepeat: \"no-repeat\",\n opacity: 0.08,\n filter: \"blur(4px)\",\n zIndex: Z_INDEX.BACKGROUND,\n }}\n data-testid=\"archetype-background\"\n />\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 */}\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: performanceSettings.antialias,\n alpha: true,\n powerPreference: \"high-performance\",\n }}\n dpr={performanceSettings.dpr}\n camera={{ position: [0, 5, 10], fov: 75 }}\n onCreated={({ gl }) => {\n gl.setClearColor(theme.colors.UI_BACKGROUND_DARK, 0.95);\n }}\n >\n {/* 3D Background Scene */}\n <BackgroundScene3D theme=\"intro\" />\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=\"intro-hud-overlay\"\n >\n <div\n style={{\n width: \"100vw\",\n height: \"100vh\",\n display: \"flex\",\n flexDirection: \"column\",\n alignItems: \"center\",\n justifyContent: \"space-between\",\n padding: 0,\n pointerEvents: \"none\",\n zIndex: Z_INDEX.HUD,\n overflow: \"hidden\",\n }}\n >\n {/* Title - Small, above logo */}\n <div\n style={{\n display: \"flex\",\n flexDirection: \"column\",\n alignItems: \"center\",\n gap: \"2px\",\n pointerEvents: \"none\",\n marginTop: \"4px\",\n flexShrink: 0,\n }}\n data-testid=\"main-title-container\"\n >\n <div\n style={{\n fontSize: screenWidth < 768 ? \"14px\" : \"16px\",\n fontWeight: \"bold\",\n fontFamily: theme.koreanTypography.fontFamily,\n lineHeight: theme.koreanTypography.lineHeight,\n letterSpacing: theme.koreanTypography.letterSpacing,\n wordBreak: theme.koreanTypography.wordBreak,\n color: `#${theme.colors.ACCENT_GOLD.toString(16).padStart(6, \"0\")}`,\n textShadow: \"0 0 10px rgba(255, 170, 0, 0.5)\",\n }}\n >\n 흑괘 | Black Trigram\n </div>\n <div\n style={{\n fontSize: screenWidth < 768 ? \"10px\" : \"11px\",\n fontFamily: theme.koreanTypography.fontFamily,\n color: `#${theme.colors.TEXT_SECONDARY.toString(16).padStart(6, \"0\")}`,\n }}\n >\n 한국 무술 시뮬레이터 | Korean Martial Arts Simulator\n </div>\n </div>\n\n {/* Logo Section - Primary branding */}\n <div\n style={{\n flexShrink: 0,\n display: \"flex\",\n flexDirection: \"column\",\n alignItems: \"center\",\n justifyContent: \"center\",\n height: `${layoutHeights.logoAreaHeight}px`,\n pointerEvents: \"none\",\n }}\n data-testid=\"logo-section\"\n >\n {/* Logo Image - Prominent */}\n <img\n src=\"/assets/visual/logo/black-trigram.png\"\n alt=\"Black Trigram Logo\"\n style={{\n width: `${logoSize}px`,\n height: `${logoSize}px`,\n objectFit: \"contain\",\n filter: \"drop-shadow(0 0 30px rgba(0, 255, 255, 0.6))\",\n }}\n data-testid=\"main-logo\"\n onError={(e) => {\n e.currentTarget.style.display = \"none\";\n }}\n />\n\n {/* Trigram Symbols */}\n <div\n style={{\n fontSize: screenWidth < 768 ? \"16px\" : \"18px\",\n color: `#${theme.colors.PRIMARY_CYAN.toString(16).padStart(\n 6,\n \"0\",\n )}`,\n letterSpacing: screenWidth < 768 ? \"6px\" : \"8px\",\n textAlign: \"center\",\n marginTop: screenWidth < 768 ? \"8px\" : \"10px\",\n textShadow: colors.trigramTextShadow,\n }}\n data-testid=\"trigram-symbols\"\n >\n ☰ ☱ ☲ ☳ ☴ ☵ ☶ ☷\n </div>\n </div>\n\n {/* Main Content Area */}\n <div\n style={{\n width: \"100%\",\n flex: 1,\n display: \"flex\",\n flexDirection: \"column\",\n alignItems: \"center\",\n justifyContent: \"flex-start\",\n gap: \"4px\",\n paddingLeft: screenWidth < 768 ? \"8px\" : \"16px\",\n paddingRight: screenWidth < 768 ? \"8px\" : \"16px\",\n overflow: \"hidden\",\n pointerEvents: \"auto\",\n minHeight: 0,\n }}\n data-testid=\"main-content\"\n >\n {/* Menu Section - Compact */}\n <div\n style={{\n width: screenWidth < 768 ? \"95%\" : \"70%\",\n maxWidth: screenWidth < 768 ? \"100%\" : \"600px\",\n }}\n data-testid=\"menu-section-container\"\n >\n <MenuSectionOverlayHtml\n menuItems={MENU_ITEMS}\n selectedIndex={selectedMenuIndex}\n onModeSelect={handleMenuItemSelect}\n onSelectedIndexChange={setSelectedMenuIndex}\n onPlaySFX={audio.playSFX}\n width={\n screenWidth < 768\n ? screenWidth * 0.9\n : screenWidth < 1024\n ? Math.min(500, screenWidth * 0.6)\n : Math.min(550, screenWidth * 0.4)\n }\n height={layoutHeights.menuHeight}\n isMobile={isMobile}\n />\n </div>\n\n {/* Archetype Selection - Scrollable container */}\n <div\n style={{\n width: screenWidth < 768 ? \"100%\" : \"85%\",\n maxWidth: screenWidth < 768 ? \"100%\" : \"900px\",\n flex: 1,\n minHeight: 0,\n overflowY: \"auto\",\n overflowX: \"hidden\",\n }}\n data-testid=\"archetype-section-container\"\n >\n {useEnhancedArchetypeDisplay ? (\n <EnhancedArchetypeDisplay\n archetypes={archetypeData}\n selectedIndex={selectedArchetypeIndex}\n onArchetypeChange={handleArchetypeIndexChange}\n onPlaySFX={audio.playSFX}\n width={\n screenWidth < 768\n ? screenWidth * 0.9\n : screenWidth < 1024\n ? Math.min(700, screenWidth * 0.7)\n : Math.min(850, screenWidth * 0.55)\n }\n height={Math.max(layoutHeights.archetypeHeight - 40, 200)}\n isMobile={isMobile}\n allowDetailedView={screenWidth >= 768}\n />\n ) : (\n <ArchetypeDisplayOverlayHtml\n archetypes={archetypeData}\n selectedIndex={selectedArchetypeIndex}\n onArchetypeChange={handleArchetypeIndexChange}\n onPlaySFX={audio.playSFX}\n width={\n screenWidth < 768\n ? screenWidth * 0.9\n : screenWidth < 1024\n ? Math.min(700, screenWidth * 0.7)\n : Math.min(850, screenWidth * 0.55)\n }\n height={Math.max(layoutHeights.archetypeHeight - 40, 200)}\n isMobile={isMobile}\n />\n )}\n </div>\n </div>\n\n {/* Footer - Compact with all info */}\n <div\n style={{\n width: \"100%\",\n minHeight: `${layoutHeights.footerHeight}px`,\n display: \"flex\",\n flexDirection: \"column\",\n alignItems: \"center\",\n justifyContent: \"center\",\n gap: \"1px\",\n background: `linear-gradient(to bottom, rgba(0, 0, 0, 0), ${colors.footerBackground})`,\n borderTop: `1px solid ${colors.footerBorder}`,\n pointerEvents: \"auto\",\n paddingBottom: \"2px\",\n flexShrink: 0,\n }}\n data-testid=\"intro-footer\"\n >\n {/* Motto */}\n <div\n style={{\n fontSize: `${Math.max(getKoreanFontSize(\"SMALL\", screenWidth) - 3, 10)}px`,\n color: `#${theme.colors.ACCENT_CYAN.toString(16).padStart(6, \"0\")}`,\n fontFamily: theme.koreanTypography.fontFamily,\n fontStyle: \"italic\",\n textAlign: \"center\",\n }}\n data-testid=\"footer-motto\"\n >\n 흑괘의 길을 걸어라 - Walk the Path of the Black Trigram\n </div>\n {/* Open Source Link */}\n <div\n style={{\n fontSize: `${Math.max(getKoreanFontSize(\"SMALL\", screenWidth) - 4, 9)}px`,\n color: `#${theme.colors.ACCENT_BLUE.toString(16).padStart(6, \"0\")}`,\n textAlign: \"center\",\n cursor: \"pointer\",\n }}\n onClick={() =>\n window.open(\"https://github.com/Hack23/blacktrigram\", \"_blank\")\n }\n data-testid=\"footer-link\"\n >\n Open Source Korean Martial Arts Game by Hack23\n </div>\n {/* Version */}\n <div\n style={{\n fontSize: `${Math.max(getKoreanFontSize(\"SMALL\", screenWidth) - 5, 8)}px`,\n color: `#${theme.colors.ACCENT_BLUE.toString(16).padStart(6, \"0\")}`,\n textAlign: \"center\",\n cursor: \"pointer\",\n }}\n onClick={() =>\n window.open(\n `https://github.com/Hack23/blacktrigram/releases/tag/v${APP_VERSION}`,\n \"_blank\",\n )\n }\n data-testid=\"footer-version\"\n >\n v{APP_VERSION}\n </div>\n </div>\n </div>\n </div>\n </div>\n );\n};\n\nexport default IntroScreen3D;\n"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"AbilityListOverlayHtml.js","names":[],"sources":["../../../../../src/components/screens/intro/components/AbilityListOverlayHtml.tsx"],"sourcesContent":["/**\n * AbilityList - Enhanced ability list component with Korean theming\n * \n * Refactored to use useKoreanTheme hook for consistent styling\n * Displays a list of special abilities with bilingual support\n * \n * Performance optimized with React.memo and useMemo\n * \n * @module components/screens/intro\n * @category Intro UI\n * @korean 능력리스트\n */\n\nimport React, { useMemo } from \"react\";\nimport { useKoreanTheme } from \"../../../shared/base/useKoreanTheme\";\nimport { KOREAN_COLORS } from \"../../../../types/constants\";\nimport { hexToRgbaString, hexColorToCSS } from \"../../../../utils/colorUtils\";\n\nexport interface Ability {\n readonly korean: string;\n readonly english: string;\n readonly description?: {\n readonly korean: string;\n readonly english: string;\n };\n}\n\nexport interface AbilityListProps {\n readonly abilities: readonly string[] | readonly Ability[];\n readonly maxAbilities?: number; // Maximum abilities to show\n readonly color?: number; // Accent color for abilities\n readonly isMobile?: boolean;\n}\n\n/**\n * AbilityList component - Displays a list of special abilities\n * \n * Refactored to use useKoreanTheme for consistent Korean theming:\n * - Uses Korean typography configuration\n * - Applies Korean color palette\n * - Responsive sizing based on device type\n * - Memoized for optimal performance\n * \n * Used in archetype cards to show key techniques and skills\n * \n * @example\n * ```tsx\n * <AbilityList\n * abilities={[\n * { korean: \"급소격\", english: \"Vital Strike\" },\n * { korean: \"연격\", english: \"Chain Attack\" }\n * ]}\n * color={KOREAN_COLORS.PRIMARY_CYAN}\n * isMobile={false}\n * />\n * ```\n */\nexport const AbilityList: React.FC<AbilityListProps> = React.memo(\n ({ abilities, maxAbilities = 3, color = KOREAN_COLORS.ACCENT_GOLD, isMobile = false }) => {\n const { koreanTypography, calculateResponsiveSize, fontFamily } = useKoreanTheme({\n size: \"small\",\n isMobile,\n });\n\n const normalizedAbilities = useMemo(() => {\n return abilities.slice(0, maxAbilities).map((ability, index) => {\n if (typeof ability === \"string\") {\n return {\n id: `ability-${index}`,\n english: ability,\n korean: ability, // TEMPORARY: English used as Korean fallback\n };\n } else {\n return {\n id: `ability-${index}`,\n english: ability.english,\n korean: ability.korean,\n description: ability.description,\n };\n }\n });\n }, [abilities, maxAbilities]);\n\n const colors = useMemo(\n () => ({\n abilityBorder: hexToRgbaString(color, 0.5),\n abilityBackground: hexToRgbaString(\n KOREAN_COLORS.UI_BACKGROUND_MEDIUM,\n 0.7\n ),\n abilityText: hexColorToCSS(color),\n descriptionText: hexColorToCSS(KOREAN_COLORS.TEXT_SECONDARY),\n }),\n [color]\n );\n\n const fontSize = calculateResponsiveSize(isMobile ? 10 : 12);\n const descFontSize = calculateResponsiveSize(isMobile ? 8 : 10);\n const padding = isMobile ? `${calculateResponsiveSize(6)}px ${calculateResponsiveSize(10)}px` : `${calculateResponsiveSize(8)}px ${calculateResponsiveSize(12)}px`;\n\n if (normalizedAbilities.length === 0) {\n return null;\n }\n\n return (\n <div\n style={{\n width: \"100%\",\n display: \"flex\",\n flexDirection: \"column\",\n gap: `${calculateResponsiveSize(8)}px`,\n }}\n data-testid=\"ability-list\"\n >\n {/* Header with Korean typography */}\n <div\n style={{\n fontSize: `${calculateResponsiveSize(isMobile ? 12 : 14)}px`,\n fontWeight: \"bold\",\n fontFamily: fontFamily.KOREAN,\n color: colors.abilityText,\n lineHeight: koreanTypography.lineHeight,\n letterSpacing: koreanTypography.letterSpacing,\n }}\n data-testid=\"ability-list-header\"\n >\n 특수 능력 | Special Abilities\n </div>\n\n {/* Ability items */}\n <div\n style={{\n display: \"flex\",\n flexDirection: \"column\",\n gap: `${calculateResponsiveSize(6)}px`,\n }}\n >\n {normalizedAbilities.map((ability) => (\n <div\n key={ability.id}\n style={{\n padding,\n background: colors.abilityBackground,\n border: `1px solid ${colors.abilityBorder}`,\n borderRadius: \"4px\",\n display: \"flex\",\n flexDirection: \"column\",\n gap: `${calculateResponsiveSize(4)}px`,\n }}\n data-testid={ability.id}\n >\n {/* Ability name with Korean typography */}\n <div\n style={{\n fontSize: `${fontSize}px`,\n fontWeight: \"bold\",\n fontFamily: fontFamily.KOREAN,\n color: colors.abilityText,\n lineHeight: koreanTypography.lineHeight,\n letterSpacing: koreanTypography.letterSpacing,\n wordBreak: koreanTypography.wordBreak,\n }}\n data-testid={`${ability.id}-name`}\n >\n {ability.korean} | {ability.english}\n </div>\n\n {/* Ability description (if available) */}\n {ability.description && (\n <div\n style={{\n fontSize: `${descFontSize}px`,\n fontStyle: \"italic\",\n fontFamily: fontFamily.KOREAN,\n color: colors.descriptionText,\n lineHeight: koreanTypography.lineHeight,\n letterSpacing: koreanTypography.letterSpacing,\n wordBreak: koreanTypography.wordBreak,\n }}\n data-testid={`${ability.id}-description`}\n >\n {ability.description.korean}\n {ability.description.english && (\n <>\n <br />\n {ability.description.english}\n </>\n )}\n </div>\n )}\n </div>\n ))}\n </div>\n </div>\n );\n }\n);\n\nAbilityList.displayName = \"AbilityList\";\n\nexport default AbilityList;\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAyDA,IAAa,cAA0C,MAAM,MAC1D,EAAE,WAAW,eAAe,GAAG,QAAQ,cAAc,aAAa,WAAW,YAAY;CACxF,MAAM,EAAE,kBAAkB,yBAAyB,eAAe,eAAe;EAC/E,MAAM;EACN;
|
|
1
|
+
{"version":3,"file":"AbilityListOverlayHtml.js","names":[],"sources":["../../../../../src/components/screens/intro/components/AbilityListOverlayHtml.tsx"],"sourcesContent":["/**\n * AbilityList - Enhanced ability list component with Korean theming\n * \n * Refactored to use useKoreanTheme hook for consistent styling\n * Displays a list of special abilities with bilingual support\n * \n * Performance optimized with React.memo and useMemo\n * \n * @module components/screens/intro\n * @category Intro UI\n * @korean 능력리스트\n */\n\nimport React, { useMemo } from \"react\";\nimport { useKoreanTheme } from \"../../../shared/base/useKoreanTheme\";\nimport { KOREAN_COLORS } from \"../../../../types/constants\";\nimport { hexToRgbaString, hexColorToCSS } from \"../../../../utils/colorUtils\";\n\nexport interface Ability {\n readonly korean: string;\n readonly english: string;\n readonly description?: {\n readonly korean: string;\n readonly english: string;\n };\n}\n\nexport interface AbilityListProps {\n readonly abilities: readonly string[] | readonly Ability[];\n readonly maxAbilities?: number; // Maximum abilities to show\n readonly color?: number; // Accent color for abilities\n readonly isMobile?: boolean;\n}\n\n/**\n * AbilityList component - Displays a list of special abilities\n * \n * Refactored to use useKoreanTheme for consistent Korean theming:\n * - Uses Korean typography configuration\n * - Applies Korean color palette\n * - Responsive sizing based on device type\n * - Memoized for optimal performance\n * \n * Used in archetype cards to show key techniques and skills\n * \n * @example\n * ```tsx\n * <AbilityList\n * abilities={[\n * { korean: \"급소격\", english: \"Vital Strike\" },\n * { korean: \"연격\", english: \"Chain Attack\" }\n * ]}\n * color={KOREAN_COLORS.PRIMARY_CYAN}\n * isMobile={false}\n * />\n * ```\n */\nexport const AbilityList: React.FC<AbilityListProps> = React.memo(\n ({ abilities, maxAbilities = 3, color = KOREAN_COLORS.ACCENT_GOLD, isMobile = false }) => {\n const { koreanTypography, calculateResponsiveSize, fontFamily } = useKoreanTheme({\n size: \"small\",\n isMobile,\n });\n\n const normalizedAbilities = useMemo(() => {\n return abilities.slice(0, maxAbilities).map((ability, index) => {\n if (typeof ability === \"string\") {\n return {\n id: `ability-${index}`,\n english: ability,\n korean: ability, // TEMPORARY: English used as Korean fallback\n };\n } else {\n return {\n id: `ability-${index}`,\n english: ability.english,\n korean: ability.korean,\n description: ability.description,\n };\n }\n });\n }, [abilities, maxAbilities]);\n\n const colors = useMemo(\n () => ({\n abilityBorder: hexToRgbaString(color, 0.5),\n abilityBackground: hexToRgbaString(\n KOREAN_COLORS.UI_BACKGROUND_MEDIUM,\n 0.7\n ),\n abilityText: hexColorToCSS(color),\n descriptionText: hexColorToCSS(KOREAN_COLORS.TEXT_SECONDARY),\n }),\n [color]\n );\n\n const fontSize = calculateResponsiveSize(isMobile ? 10 : 12);\n const descFontSize = calculateResponsiveSize(isMobile ? 8 : 10);\n const padding = isMobile ? `${calculateResponsiveSize(6)}px ${calculateResponsiveSize(10)}px` : `${calculateResponsiveSize(8)}px ${calculateResponsiveSize(12)}px`;\n\n if (normalizedAbilities.length === 0) {\n return null;\n }\n\n return (\n <div\n style={{\n width: \"100%\",\n display: \"flex\",\n flexDirection: \"column\",\n gap: `${calculateResponsiveSize(8)}px`,\n }}\n data-testid=\"ability-list\"\n >\n {/* Header with Korean typography */}\n <div\n style={{\n fontSize: `${calculateResponsiveSize(isMobile ? 12 : 14)}px`,\n fontWeight: \"bold\",\n fontFamily: fontFamily.KOREAN,\n color: colors.abilityText,\n lineHeight: koreanTypography.lineHeight,\n letterSpacing: koreanTypography.letterSpacing,\n }}\n data-testid=\"ability-list-header\"\n >\n 특수 능력 | Special Abilities\n </div>\n\n {/* Ability items */}\n <div\n style={{\n display: \"flex\",\n flexDirection: \"column\",\n gap: `${calculateResponsiveSize(6)}px`,\n }}\n >\n {normalizedAbilities.map((ability) => (\n <div\n key={ability.id}\n style={{\n padding,\n background: colors.abilityBackground,\n border: `1px solid ${colors.abilityBorder}`,\n borderRadius: \"4px\",\n display: \"flex\",\n flexDirection: \"column\",\n gap: `${calculateResponsiveSize(4)}px`,\n }}\n data-testid={ability.id}\n >\n {/* Ability name with Korean typography */}\n <div\n style={{\n fontSize: `${fontSize}px`,\n fontWeight: \"bold\",\n fontFamily: fontFamily.KOREAN,\n color: colors.abilityText,\n lineHeight: koreanTypography.lineHeight,\n letterSpacing: koreanTypography.letterSpacing,\n wordBreak: koreanTypography.wordBreak,\n }}\n data-testid={`${ability.id}-name`}\n >\n {ability.korean} | {ability.english}\n </div>\n\n {/* Ability description (if available) */}\n {ability.description && (\n <div\n style={{\n fontSize: `${descFontSize}px`,\n fontStyle: \"italic\",\n fontFamily: fontFamily.KOREAN,\n color: colors.descriptionText,\n lineHeight: koreanTypography.lineHeight,\n letterSpacing: koreanTypography.letterSpacing,\n wordBreak: koreanTypography.wordBreak,\n }}\n data-testid={`${ability.id}-description`}\n >\n {ability.description.korean}\n {ability.description.english && (\n <>\n <br />\n {ability.description.english}\n </>\n )}\n </div>\n )}\n </div>\n ))}\n </div>\n </div>\n );\n }\n);\n\nAbilityList.displayName = \"AbilityList\";\n\nexport default AbilityList;\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAyDA,IAAa,cAA0C,MAAM,MAC1D,EAAE,WAAW,eAAe,GAAG,QAAQ,cAAc,aAAa,WAAW,YAAY;CACxF,MAAM,EAAE,kBAAkB,yBAAyB,eAAe,eAAe;EAC/E,MAAM;EACN;CACF,CAAC;CAED,MAAM,sBAAsB,cAAc;EACxC,OAAO,UAAU,MAAM,GAAG,YAAY,EAAE,KAAK,SAAS,UAAU;GAC9D,IAAI,OAAO,YAAY,UACrB,OAAO;IACL,IAAI,WAAW;IACf,SAAS;IACT,QAAQ;GACV;QAEA,OAAO;IACL,IAAI,WAAW;IACf,SAAS,QAAQ;IACjB,QAAQ,QAAQ;IAChB,aAAa,QAAQ;GACvB;EAEJ,CAAC;CACH,GAAG,CAAC,WAAW,YAAY,CAAC;CAE5B,MAAM,SAAS,eACN;EACL,eAAe,gBAAgB,OAAO,EAAG;EACzC,mBAAmB,gBACjB,cAAc,sBACd,EACF;EACA,aAAa,cAAc,KAAK;EAChC,iBAAiB,cAAc,cAAc,cAAc;CAC7D,IACA,CAAC,KAAK,CACR;CAEA,MAAM,WAAW,wBAAwB,WAAW,KAAK,EAAE;CAC3D,MAAM,eAAe,wBAAwB,WAAW,IAAI,EAAE;CAC9D,MAAM,UAAU,WAAW,GAAG,wBAAwB,CAAC,EAAE,KAAK,wBAAwB,EAAE,EAAE,MAAM,GAAG,wBAAwB,CAAC,EAAE,KAAK,wBAAwB,EAAE,EAAE;CAE/J,IAAI,oBAAoB,WAAW,GACjC,OAAO;CAGT,OACE,qBAAC,OAAD;EACE,OAAO;GACL,OAAO;GACP,SAAS;GACT,eAAe;GACf,KAAK,GAAG,wBAAwB,CAAC,EAAE;EACrC;EACA,eAAY;YAPd,CAUE,oBAAC,OAAD;GACE,OAAO;IACL,UAAU,GAAG,wBAAwB,WAAW,KAAK,EAAE,EAAE;IACzD,YAAY;IACZ,YAAY,WAAW;IACvB,OAAO,OAAO;IACd,YAAY,iBAAiB;IAC7B,eAAe,iBAAiB;GAClC;GACA,eAAY;aACb;EAEI,CAAA,GAGL,oBAAC,OAAD;GACE,OAAO;IACL,SAAS;IACT,eAAe;IACf,KAAK,GAAG,wBAAwB,CAAC,EAAE;GACrC;aAEC,oBAAoB,KAAK,YACxB,qBAAC,OAAD;IAEE,OAAO;KACL;KACA,YAAY,OAAO;KACnB,QAAQ,aAAa,OAAO;KAC5B,cAAc;KACd,SAAS;KACT,eAAe;KACf,KAAK,GAAG,wBAAwB,CAAC,EAAE;IACrC;IACA,eAAa,QAAQ;cAXvB,CAcE,qBAAC,OAAD;KACE,OAAO;MACL,UAAU,GAAG,SAAS;MACtB,YAAY;MACZ,YAAY,WAAW;MACvB,OAAO,OAAO;MACd,YAAY,iBAAiB;MAC7B,eAAe,iBAAiB;MAChC,WAAW,iBAAiB;KAC9B;KACA,eAAa,GAAG,QAAQ,GAAG;eAV7B;MAYG,QAAQ;MAAO;MAAI,QAAQ;KACzB;QAGJ,QAAQ,eACP,qBAAC,OAAD;KACE,OAAO;MACL,UAAU,GAAG,aAAa;MAC1B,WAAW;MACX,YAAY,WAAW;MACvB,OAAO,OAAO;MACd,YAAY,iBAAiB;MAC7B,eAAe,iBAAiB;MAChC,WAAW,iBAAiB;KAC9B;KACA,eAAa,GAAG,QAAQ,GAAG;eAV7B,CAYG,QAAQ,YAAY,QACpB,QAAQ,YAAY,WACnB,qBAAA,UAAA,EAAA,UAAA,CACE,oBAAC,MAAD,CAAK,CAAA,GACJ,QAAQ,YAAY,OACrB,EAAA,CAAA,CAED;MAEJ;MAnDE,QAAQ,EAmDV,CACN;EACE,CAAA,CACF;;AAET,CACF;AAEA,YAAY,cAAc"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ArchetypeCardGridOverlayHtml.js","names":[],"sources":["../../../../../src/components/screens/intro/components/ArchetypeCardGridOverlayHtml.tsx"],"sourcesContent":["import React, { useCallback, useMemo } from \"react\";\nimport { PlayerArchetype } from \"../../../../types/common\";\nimport { FONT_FAMILY, KOREAN_COLORS } from \"../../../../types/constants\";\nimport { hexColorToCSS, hexToRgbaString } from \"../../../../utils/colorUtils\";\nimport { ArchetypeCard, ArchetypeCardData } from \"./ArchetypeCardOverlayHtml\";\n\nexport interface ArchetypeCardGridProps {\n readonly archetypes: readonly ArchetypeCardData[];\n readonly selectedArchetype: PlayerArchetype;\n readonly onArchetypeChange: (archetype: PlayerArchetype) => void;\n readonly onArchetypeConfirm?: (archetype: PlayerArchetype) => void;\n readonly onPlaySFX: (sound: string) => void;\n readonly width?: number;\n readonly height?: number;\n readonly isMobile?: boolean;\n}\n\n/**\n * ArchetypeCardGrid - Grid layout for displaying multiple archetype cards\n * Provides an enhanced selection interface with detailed preview cards\n */\nexport const ArchetypeCardGrid: React.FC<ArchetypeCardGridProps> = React.memo(\n ({\n archetypes,\n selectedArchetype,\n onArchetypeChange,\n onArchetypeConfirm,\n onPlaySFX,\n width = 900,\n height = 600,\n isMobile: _isMobile = false, // Kept for interface compatibility, layout uses width\n }) => {\n void _isMobile;\n\n const isSmallScreen = width < 768;\n\n const selectedIndex = useMemo(() => {\n return archetypes.findIndex((a) => a.archetype === selectedArchetype);\n }, [archetypes, selectedArchetype]);\n\n const cardWidth = useMemo(() => {\n if (isSmallScreen) return Math.min(280, width - 40);\n const isLargeContainer = width >= 1100;\n return isLargeContainer ? 340 : 380;\n }, [isSmallScreen, width]);\n\n const colors = useMemo(\n () => ({\n background: hexToRgbaString(KOREAN_COLORS.UI_BACKGROUND_DARK, 0.95),\n border: hexToRgbaString(KOREAN_COLORS.PRIMARY_CYAN, 0.7),\n headerColor: hexColorToCSS(KOREAN_COLORS.ACCENT_GOLD),\n }),\n [],\n );\n\n const handleCardSelect = useCallback(\n (archetype: PlayerArchetype) => {\n onArchetypeChange(archetype);\n onPlaySFX(\"menu_hover\");\n },\n [onArchetypeChange, onPlaySFX],\n );\n\n const handleCardConfirm = useCallback(\n (archetype: PlayerArchetype) => {\n onArchetypeConfirm?.(archetype);\n onPlaySFX(\"menu_select\");\n },\n [onArchetypeConfirm, onPlaySFX],\n );\n\n const handleKeyDown = useCallback(\n (event: React.KeyboardEvent<HTMLDivElement>) => {\n if (event.key === \"ArrowLeft\" || event.key === \"ArrowUp\") {\n event.preventDefault();\n const newIndex =\n selectedIndex === 0 ? archetypes.length - 1 : selectedIndex - 1;\n const newArchetype = archetypes[newIndex].archetype;\n handleCardSelect(newArchetype);\n } else if (event.key === \"ArrowRight\" || event.key === \"ArrowDown\") {\n event.preventDefault();\n const newIndex = (selectedIndex + 1) % archetypes.length;\n const newArchetype = archetypes[newIndex].archetype;\n handleCardSelect(newArchetype);\n } else if (event.key === \"Enter\") {\n event.preventDefault();\n if (onArchetypeConfirm) {\n handleCardConfirm(selectedArchetype);\n }\n }\n },\n [\n selectedIndex,\n archetypes,\n selectedArchetype,\n handleCardSelect,\n handleCardConfirm,\n onArchetypeConfirm,\n ],\n );\n\n const columnsCount = isSmallScreen ? 1 : width >= 1400 ? 3 : 2;\n const gap = isSmallScreen ? 16 : 20;\n\n const [isFocused, setIsFocused] = React.useState(false);\n\n return (\n <div\n tabIndex={0}\n onKeyDown={handleKeyDown}\n onFocus={() => setIsFocused(true)}\n onBlur={() => setIsFocused(false)}\n style={{\n width: `${width}px`,\n minHeight: `${height}px`,\n display: \"flex\",\n flexDirection: \"column\",\n alignItems: \"center\",\n gap: `${gap}px`,\n background: colors.background,\n borderRadius: \"12px\",\n border: `2px solid ${colors.border}`,\n padding: isSmallScreen ? \"16px\" : \"24px\",\n overflow: \"auto\",\n maxHeight: `${height}px`,\n outline: isFocused ? `3px solid ${colors.headerColor}` : \"none\",\n outlineOffset: \"2px\",\n }}\n data-testid=\"archetype-card-grid\"\n role=\"region\"\n aria-label=\"Archetype selection grid\"\n >\n {/* Header */}\n <div\n style={{\n width: \"100%\",\n display: \"flex\",\n flexDirection: \"column\",\n alignItems: \"center\",\n gap: \"8px\",\n marginBottom: `${gap / 2}px`,\n }}\n >\n <h2\n style={{\n fontSize: isSmallScreen ? \"20px\" : \"28px\",\n fontWeight: \"bold\",\n fontFamily: FONT_FAMILY.KOREAN,\n color: colors.headerColor,\n margin: 0,\n textAlign: \"center\",\n }}\n data-testid=\"grid-header\"\n >\n 원형 선택 | Select Archetype\n </h2>\n\n <div\n style={{\n fontSize: isSmallScreen ? \"12px\" : \"14px\",\n fontFamily: FONT_FAMILY.KOREAN,\n color: hexColorToCSS(KOREAN_COLORS.TEXT_SECONDARY),\n textAlign: \"center\",\n fontStyle: \"italic\",\n }}\n data-testid=\"grid-hint\"\n >\n {isSmallScreen\n ? \"카드를 탭하여 선택\"\n : \"화살표 키로 탐색, 엔터로 확인 | Arrow keys to navigate, Enter to confirm\"}\n </div>\n </div>\n\n {/* Card Grid */}\n <div\n style={{\n display: \"grid\",\n gridTemplateColumns: `repeat(${columnsCount}, 1fr)`,\n gap: `${gap}px`,\n width: \"100%\",\n justifyItems: \"center\",\n }}\n data-testid=\"card-grid-container\"\n >\n {archetypes.map((archetype) => (\n <ArchetypeCard\n key={archetype.id}\n data={archetype}\n isSelected={archetype.archetype === selectedArchetype}\n onSelect={() => handleCardSelect(archetype.archetype)}\n onConfirm={\n onArchetypeConfirm\n ? () => handleCardConfirm(archetype.archetype)\n : undefined\n }\n isMobile={isSmallScreen}\n width={cardWidth}\n showSelectButton={!!onArchetypeConfirm}\n />\n ))}\n </div>\n\n {/* Footer navigation hint */}\n {!isSmallScreen && (\n <div\n style={{\n marginTop: `${gap}px`,\n fontSize: \"12px\",\n fontFamily: FONT_FAMILY.KOREAN,\n color: hexColorToCSS(KOREAN_COLORS.TEXT_SECONDARY),\n textAlign: \"center\",\n fontStyle: \"italic\",\n }}\n data-testid=\"grid-footer\"\n >\n ← → 또는 ↑ ↓ 키로 원형 변경 | Use ← → or ↑ ↓ keys to change\n archetype\n </div>\n )}\n </div>\n );\n },\n);\n\nArchetypeCardGrid.displayName = \"ArchetypeCardGrid\";\n\nexport default ArchetypeCardGrid;\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"ArchetypeCardGridOverlayHtml.js","names":[],"sources":["../../../../../src/components/screens/intro/components/ArchetypeCardGridOverlayHtml.tsx"],"sourcesContent":["import React, { useCallback, useMemo } from \"react\";\nimport { PlayerArchetype } from \"../../../../types/common\";\nimport { FONT_FAMILY, KOREAN_COLORS } from \"../../../../types/constants\";\nimport { hexColorToCSS, hexToRgbaString } from \"../../../../utils/colorUtils\";\nimport { ArchetypeCard, ArchetypeCardData } from \"./ArchetypeCardOverlayHtml\";\n\nexport interface ArchetypeCardGridProps {\n readonly archetypes: readonly ArchetypeCardData[];\n readonly selectedArchetype: PlayerArchetype;\n readonly onArchetypeChange: (archetype: PlayerArchetype) => void;\n readonly onArchetypeConfirm?: (archetype: PlayerArchetype) => void;\n readonly onPlaySFX: (sound: string) => void;\n readonly width?: number;\n readonly height?: number;\n readonly isMobile?: boolean;\n}\n\n/**\n * ArchetypeCardGrid - Grid layout for displaying multiple archetype cards\n * Provides an enhanced selection interface with detailed preview cards\n */\nexport const ArchetypeCardGrid: React.FC<ArchetypeCardGridProps> = React.memo(\n ({\n archetypes,\n selectedArchetype,\n onArchetypeChange,\n onArchetypeConfirm,\n onPlaySFX,\n width = 900,\n height = 600,\n isMobile: _isMobile = false, // Kept for interface compatibility, layout uses width\n }) => {\n void _isMobile;\n\n const isSmallScreen = width < 768;\n\n const selectedIndex = useMemo(() => {\n return archetypes.findIndex((a) => a.archetype === selectedArchetype);\n }, [archetypes, selectedArchetype]);\n\n const cardWidth = useMemo(() => {\n if (isSmallScreen) return Math.min(280, width - 40);\n const isLargeContainer = width >= 1100;\n return isLargeContainer ? 340 : 380;\n }, [isSmallScreen, width]);\n\n const colors = useMemo(\n () => ({\n background: hexToRgbaString(KOREAN_COLORS.UI_BACKGROUND_DARK, 0.95),\n border: hexToRgbaString(KOREAN_COLORS.PRIMARY_CYAN, 0.7),\n headerColor: hexColorToCSS(KOREAN_COLORS.ACCENT_GOLD),\n }),\n [],\n );\n\n const handleCardSelect = useCallback(\n (archetype: PlayerArchetype) => {\n onArchetypeChange(archetype);\n onPlaySFX(\"menu_hover\");\n },\n [onArchetypeChange, onPlaySFX],\n );\n\n const handleCardConfirm = useCallback(\n (archetype: PlayerArchetype) => {\n onArchetypeConfirm?.(archetype);\n onPlaySFX(\"menu_select\");\n },\n [onArchetypeConfirm, onPlaySFX],\n );\n\n const handleKeyDown = useCallback(\n (event: React.KeyboardEvent<HTMLDivElement>) => {\n if (event.key === \"ArrowLeft\" || event.key === \"ArrowUp\") {\n event.preventDefault();\n const newIndex =\n selectedIndex === 0 ? archetypes.length - 1 : selectedIndex - 1;\n const newArchetype = archetypes[newIndex].archetype;\n handleCardSelect(newArchetype);\n } else if (event.key === \"ArrowRight\" || event.key === \"ArrowDown\") {\n event.preventDefault();\n const newIndex = (selectedIndex + 1) % archetypes.length;\n const newArchetype = archetypes[newIndex].archetype;\n handleCardSelect(newArchetype);\n } else if (event.key === \"Enter\") {\n event.preventDefault();\n if (onArchetypeConfirm) {\n handleCardConfirm(selectedArchetype);\n }\n }\n },\n [\n selectedIndex,\n archetypes,\n selectedArchetype,\n handleCardSelect,\n handleCardConfirm,\n onArchetypeConfirm,\n ],\n );\n\n const columnsCount = isSmallScreen ? 1 : width >= 1400 ? 3 : 2;\n const gap = isSmallScreen ? 16 : 20;\n\n const [isFocused, setIsFocused] = React.useState(false);\n\n return (\n <div\n tabIndex={0}\n onKeyDown={handleKeyDown}\n onFocus={() => setIsFocused(true)}\n onBlur={() => setIsFocused(false)}\n style={{\n width: `${width}px`,\n minHeight: `${height}px`,\n display: \"flex\",\n flexDirection: \"column\",\n alignItems: \"center\",\n gap: `${gap}px`,\n background: colors.background,\n borderRadius: \"12px\",\n border: `2px solid ${colors.border}`,\n padding: isSmallScreen ? \"16px\" : \"24px\",\n overflow: \"auto\",\n maxHeight: `${height}px`,\n outline: isFocused ? `3px solid ${colors.headerColor}` : \"none\",\n outlineOffset: \"2px\",\n }}\n data-testid=\"archetype-card-grid\"\n role=\"region\"\n aria-label=\"Archetype selection grid\"\n >\n {/* Header */}\n <div\n style={{\n width: \"100%\",\n display: \"flex\",\n flexDirection: \"column\",\n alignItems: \"center\",\n gap: \"8px\",\n marginBottom: `${gap / 2}px`,\n }}\n >\n <h2\n style={{\n fontSize: isSmallScreen ? \"20px\" : \"28px\",\n fontWeight: \"bold\",\n fontFamily: FONT_FAMILY.KOREAN,\n color: colors.headerColor,\n margin: 0,\n textAlign: \"center\",\n }}\n data-testid=\"grid-header\"\n >\n 원형 선택 | Select Archetype\n </h2>\n\n <div\n style={{\n fontSize: isSmallScreen ? \"12px\" : \"14px\",\n fontFamily: FONT_FAMILY.KOREAN,\n color: hexColorToCSS(KOREAN_COLORS.TEXT_SECONDARY),\n textAlign: \"center\",\n fontStyle: \"italic\",\n }}\n data-testid=\"grid-hint\"\n >\n {isSmallScreen\n ? \"카드를 탭하여 선택\"\n : \"화살표 키로 탐색, 엔터로 확인 | Arrow keys to navigate, Enter to confirm\"}\n </div>\n </div>\n\n {/* Card Grid */}\n <div\n style={{\n display: \"grid\",\n gridTemplateColumns: `repeat(${columnsCount}, 1fr)`,\n gap: `${gap}px`,\n width: \"100%\",\n justifyItems: \"center\",\n }}\n data-testid=\"card-grid-container\"\n >\n {archetypes.map((archetype) => (\n <ArchetypeCard\n key={archetype.id}\n data={archetype}\n isSelected={archetype.archetype === selectedArchetype}\n onSelect={() => handleCardSelect(archetype.archetype)}\n onConfirm={\n onArchetypeConfirm\n ? () => handleCardConfirm(archetype.archetype)\n : undefined\n }\n isMobile={isSmallScreen}\n width={cardWidth}\n showSelectButton={!!onArchetypeConfirm}\n />\n ))}\n </div>\n\n {/* Footer navigation hint */}\n {!isSmallScreen && (\n <div\n style={{\n marginTop: `${gap}px`,\n fontSize: \"12px\",\n fontFamily: FONT_FAMILY.KOREAN,\n color: hexColorToCSS(KOREAN_COLORS.TEXT_SECONDARY),\n textAlign: \"center\",\n fontStyle: \"italic\",\n }}\n data-testid=\"grid-footer\"\n >\n ← → 또는 ↑ ↓ 키로 원형 변경 | Use ← → or ↑ ↓ keys to change\n archetype\n </div>\n )}\n </div>\n );\n },\n);\n\nArchetypeCardGrid.displayName = \"ArchetypeCardGrid\";\n\nexport default ArchetypeCardGrid;\n"],"mappings":"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"}
|