blacktrigram 0.7.24 → 0.7.27
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/ARCHITECTURE.md +51 -47
- package/CRA-ASSESSMENT.md +13 -13
- package/DATA_MODEL.md +6 -2
- package/SECURITY_ARCHITECTURE.md +6 -6
- package/THREAT_MODEL.md +6 -6
- package/lib/App2.js.map +1 -1
- package/lib/audio/AudioAssetRegistry.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/components/screens/combat/CombatScreen3D.js.map +1 -1
- package/lib/components/screens/combat/components/effects/BloodDecals3D.js +5 -0
- 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 +19 -0
- package/lib/components/screens/combat/components/effects/BloodParticles3D.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/TraumaOverlay3D.js.map +1 -1
- package/lib/components/screens/combat/components/feedback/RoundAnnouncementOverlayHtml.js.map +1 -1
- package/lib/components/screens/combat/components/hud/CombatBottomHUD.js.map +1 -1
- package/lib/components/screens/combat/components/hud/CombatPortraitStatusStrip.js.map +1 -1
- package/lib/components/screens/combat/components/hud/CombatTopHUD.js.map +1 -1
- package/lib/components/screens/combat/components/indicators/BalanceIndicator.js.map +1 -1
- package/lib/components/screens/combat/components/indicators/StaminaWarning.js.map +1 -1
- package/lib/components/screens/combat/hooks/useAICombat.js.map +1 -1
- package/lib/components/screens/combat/hooks/useCombatActions.js.map +1 -1
- package/lib/components/screens/combat/hooks/useCombatAttackMovement.js.map +1 -1
- package/lib/components/screens/combat/hooks/useCombatAudio.js.map +1 -1
- package/lib/components/screens/combat/hooks/useCombatLayout.js.map +1 -1
- package/lib/components/screens/combat/hooks/useCombatState.js.map +1 -1
- package/lib/components/screens/controls/components/ControlCategoryTabsOverlayHtml.js.map +1 -1
- package/lib/components/screens/controls/components/InteractiveControlDemoOverlayHtml.js.map +1 -1
- package/lib/components/screens/controls/hooks/useControlsState.js.map +1 -1
- package/lib/components/screens/endscreen/components/PerformanceBreakdownOverlayHtml.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/training/TrainingScreen3D.js.map +1 -1
- package/lib/components/screens/training/components/AnatomyControlsOverlayHtml.js.map +1 -1
- package/lib/components/screens/training/components/TrainingDummy3D.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/hooks/useAttackMovement.js.map +1 -1
- package/lib/components/screens/training/hooks/useTrainingActions.js.map +1 -1
- package/lib/components/screens/training/hooks/useTrainingLayout.js.map +1 -1
- package/lib/components/screens/training/hooks/useTrainingState.js.map +1 -1
- package/lib/components/shared/base/BaseButtonOverlayHtml.js.map +1 -1
- package/lib/components/shared/base/BasePanel.js.map +1 -1
- package/lib/components/shared/base/useKoreanTheme.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/StanceWheelPure.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/Face3D.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/VitalPointMarkers3D.js.map +1 -1
- package/lib/components/shared/three/indicators/ElementalColorSystem.js +5 -0
- 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/models/SkeletalPlayer3D.js.map +1 -1
- package/lib/components/shared/three/scene/CombatArena3D.js.map +1 -1
- package/lib/components/shared/three/ui/BreathingIndicator2.js.map +1 -1
- package/lib/components/shared/three/ui/ComboCounter.js.map +1 -1
- package/lib/components/shared/three/ui/PlayerHUD.js.map +1 -1
- package/lib/components/shared/three/ui/TechniqueCard.js.map +1 -1
- package/lib/components/shared/ui/BaseHUDContainer.js.map +1 -1
- package/lib/components/shared/ui/SplashScreen.js +2 -2
- package/lib/data/archetypePhysicalAttributes.js +420 -0
- package/lib/data/archetypePhysicalAttributes.js.map +1 -1
- package/lib/data/techniques.js.map +1 -1
- package/lib/hooks/useCombatTimer.js.map +1 -1
- package/lib/hooks/useHUDLayout.js.map +1 -1
- package/lib/hooks/useKeyboardControls.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 +4 -0
- package/lib/hooks/useResponsiveLayout.js.map +1 -1
- package/lib/hooks/useRoundTransition.js.map +1 -1
- package/lib/hooks/useSkeletalAnimation.js.map +1 -1
- package/lib/hooks/useTechniqueSelection.js.map +1 -1
- package/lib/hooks/useWebGLContextLossHandler.js.map +1 -1
- package/lib/systems/CombatSystem.js.map +1 -1
- package/lib/systems/EffectCalculator.js.map +1 -1
- package/lib/systems/ResponsiveScaling.js +3 -0
- 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 +47 -0
- package/lib/systems/ai/AIPersonality.js.map +1 -1
- package/lib/systems/ai/AdaptiveDifficulty.js.map +1 -1
- package/lib/systems/ai/ArchetypeEnforcer.js +30 -0
- package/lib/systems/ai/ArchetypeEnforcer.js.map +1 -1
- package/lib/systems/ai/ComboSystem.js.map +1 -1
- package/lib/systems/ai/DecisionTree.js +4 -0
- package/lib/systems/ai/DecisionTree.js.map +1 -1
- package/lib/systems/animation/builders/HandPoses.js +7 -0
- package/lib/systems/animation/builders/HandPoses.js.map +1 -1
- package/lib/systems/animation/builders/KeyframeInterpolation.js +25 -0
- package/lib/systems/animation/builders/KeyframeInterpolation.js.map +1 -1
- package/lib/systems/animation/builders/KoreanGuardPositions.js.map +1 -1
- package/lib/systems/animation/builders/MartialArtsAnimationBuilder.js +60 -0
- package/lib/systems/animation/builders/MartialArtsAnimationBuilder.js.map +1 -1
- package/lib/systems/animation/builders/MartialArtsConstants.js +288 -0
- package/lib/systems/animation/builders/MartialArtsConstants.js.map +1 -1
- package/lib/systems/animation/builders/SkeletonRig.js +24 -0
- package/lib/systems/animation/builders/SkeletonRig.js.map +1 -1
- package/lib/systems/animation/catalogs/StanceIdleAnimations.js.map +1 -1
- package/lib/systems/animation/constants/AnatomicalLimits.js +133 -1
- package/lib/systems/animation/constants/AnatomicalLimits.js.map +1 -1
- package/lib/systems/animation/core/AnimationOptimizations.js.map +1 -1
- package/lib/systems/animation/core/AnimationPriority.js.map +1 -1
- package/lib/systems/animation/core/AnimationStateMachine.js.map +1 -1
- package/lib/systems/animation/core/TechniqueAnimationMapper.js.map +1 -1
- package/lib/systems/animation/systems/AdvancedJointMovements.js +24 -0
- 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/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 +6 -0
- package/lib/systems/bodypart/BodyPartPositionMapping.js.map +1 -1
- package/lib/systems/bodypart/CombatInjuryIntegration.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 +11 -0
- package/lib/systems/bodypart/types.js.map +1 -1
- package/lib/systems/breathing/BreathingDisruptionSystem.js.map +1 -1
- package/lib/systems/combat/BalanceSystem.js.map +1 -1
- package/lib/systems/combat/BreakingStatusEffects.js +35 -0
- package/lib/systems/combat/BreakingStatusEffects.js.map +1 -1
- package/lib/systems/combat/CombatStateSystem.js.map +1 -1
- package/lib/systems/combat/ConsciousnessSystem.js.map +1 -1
- package/lib/systems/combat/FallIntegration.js.map +1 -1
- package/lib/systems/combat/GrappleSystem.js.map +1 -1
- package/lib/systems/combat/LimbExposureSystem.js.map +1 -1
- package/lib/systems/combat/PainResponseSystem.js.map +1 -1
- package/lib/systems/combat/TrainingCombatSystem.js.map +1 -1
- package/lib/systems/movement/InjuryMovementModifier.js.map +1 -1
- package/lib/systems/movement/helpers/AccelerationUpdater.js.map +1 -1
- package/lib/systems/movement/helpers/accelerationUtils.js +14 -0
- package/lib/systems/movement/helpers/accelerationUtils.js.map +1 -1
- package/lib/systems/movement/integration.js.map +1 -1
- package/lib/systems/physics/AttackMovementPhysics.js.map +1 -1
- package/lib/systems/physics/CollisionDetection.js.map +1 -1
- package/lib/systems/physics/CoordinateMapper.js.map +1 -1
- package/lib/systems/physics/KnockbackPhysics.js.map +1 -1
- package/lib/systems/physics/MovementPhysics.js.map +1 -1
- package/lib/systems/physics/PhysicalReachCalculator.js.map +1 -1
- package/lib/systems/physics/SpeedModifierSystem.js.map +1 -1
- package/lib/systems/trigram/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/TechniqueConfig.js.map +1 -1
- package/lib/systems/types.js.map +1 -1
- package/lib/systems/vitalpoint/DamageCalculator.js.map +1 -1
- package/lib/systems/vitalpoint/HitDetection.js.map +1 -1
- package/lib/systems/vitalpoint/KoreanAnatomy.js.map +1 -1
- package/lib/systems/vitalpoint/KoreanVitalPoints.js.map +1 -1
- package/lib/systems/vitalpoint/MeridianVitalPointMapping.js.map +1 -1
- package/lib/types/LayoutTypes.js +32 -0
- package/lib/types/LayoutTypes.js.map +1 -1
- package/lib/types/constants/colors.js +9 -0
- package/lib/types/constants/colors.js.map +1 -1
- package/lib/types/constants/designSystem.js +84 -1
- package/lib/types/constants/designSystem.js.map +1 -1
- package/lib/types/constants/index.js +5 -0
- package/lib/types/constants/index.js.map +1 -1
- package/lib/types/constants/layout.js +3 -0
- package/lib/types/constants/layout.js.map +1 -1
- package/lib/types/constants/performance.js +4 -0
- package/lib/types/constants/performance.js.map +1 -1
- package/lib/types/physicsConstants.js +24 -0
- package/lib/types/physicsConstants.js.map +1 -1
- package/lib/utils/arenaWorldDimensions.js +9 -0
- package/lib/utils/arenaWorldDimensions.js.map +1 -1
- package/lib/utils/characterScaling.js +13 -0
- package/lib/utils/characterScaling.js.map +1 -1
- package/lib/utils/colorUtils.js.map +1 -1
- package/lib/utils/combatReadiness.js.map +1 -1
- package/lib/utils/controlMapping.js.map +1 -1
- package/lib/utils/deviceDetection.js.map +1 -1
- package/lib/utils/effectUtils.js.map +1 -1
- package/lib/utils/fabricTextures.js +4 -0
- package/lib/utils/fabricTextures.js.map +1 -1
- package/lib/utils/hapticFeedback.js +36 -0
- package/lib/utils/hapticFeedback.js.map +1 -1
- package/lib/utils/htmlOverlayHelpers.js.map +1 -1
- package/lib/utils/koreanThemeHelpers.js.map +1 -1
- package/lib/utils/mobileLayoutHelpers.js.map +1 -1
- package/lib/utils/performance/PerformanceMonitor.js.map +1 -1
- package/lib/utils/performanceOptimization.js +9 -0
- package/lib/utils/performanceOptimization.js.map +1 -1
- package/lib/utils/playerUtils.js.map +1 -1
- package/lib/utils/responsiveOrientationConstants.js +4 -0
- package/lib/utils/responsiveOrientationConstants.js.map +1 -1
- package/lib/utils/sharedPhysicsConfig.js.map +1 -1
- package/lib/utils/stanceHelpers.js.map +1 -1
- package/lib/utils/threeObjectPool.js +23 -0
- package/lib/utils/threeObjectPool.js.map +1 -1
- package/lib/utils/visualEffects.js.map +1 -1
- package/package.json +9 -9
package/ARCHITECTURE.md
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
|
-
# 🎮 Black Trigram (흑괘) – Technical Architecture (
|
|
1
|
+
# 🎮 Black Trigram (흑괘) – Technical Architecture (Q2 2026)
|
|
2
2
|
|
|
3
|
-
**Last Updated**:
|
|
4
|
-
**Architecture Version**: 2.
|
|
5
|
-
**
|
|
3
|
+
**Last Updated**: 2026-04-21
|
|
4
|
+
**Architecture Version**: 2.1 (Production-Ready)
|
|
5
|
+
**Product Version**: 0.7.24
|
|
6
|
+
**Status**: Production-Ready (9.4/10) - Combat Realism 100% Complete (13/13 Systems)
|
|
6
7
|
|
|
7
8
|
---
|
|
8
9
|
|
|
@@ -13,12 +14,12 @@
|
|
|
13
14
|
| **[🌐 System Context](#-system-context)** | C4 Model | High-level view showing actors (Player, CDNs) and the entirely front-end application |
|
|
14
15
|
| **[🏢 Container View](#-container-view)** | C4 Model | Frontend-only architecture: UI Layer, Game Logic, Three.js Renderer, Animation System, State Management |
|
|
15
16
|
| **[🧩 Component View](#-component-view)** | C4 Model | Detailed breakdown: Combat System, Trigram System (8 stances), Vital Point System (70 points), Skeletal Animation (28 bones) |
|
|
16
|
-
| **[🔧 File Structure](#-file-structure-
|
|
17
|
+
| **[🔧 File Structure](#-file-structure-q2-2026)** | Organization | Q2 2026 project structure with systems/, components/, data/, types/ layout |
|
|
17
18
|
| **[🔄 Combat Flow Sequence](#-combat-flow-sequence)** | Sequence Diagram | Input → Trigram → Vital Point → Damage → Three.js rendering with skeletal animation |
|
|
18
19
|
| **[🎬 Skeletal Animation](#-skeletal-animation-architecture)** | Animation System | 28-bone hierarchy, 7 hand poses, muscle tension visualization |
|
|
19
20
|
| **[⚡ Performance Architecture](#-performance-architecture-q1-2026)** | Performance | Three.js optimization, 60fps targets, instancing, LOD, benchmarks |
|
|
20
|
-
| **[📊 SWOT Analysis](#-swot-analysis)** | Strategy |
|
|
21
|
-
| **[📈 Game Status Report](game-status.md)** | Current Progress | Comprehensive status (
|
|
21
|
+
| **[📊 SWOT Analysis](#-swot-analysis)** | Strategy | Q2 2026 status: Strengths (70/70 vital points, 100% combat realism), Weaknesses (niche market), Opportunities, Threats |
|
|
22
|
+
| **[📈 Game Status Report](game-status.md)** | Current Progress | Comprehensive status (72.34% line coverage per docs/coverage/coverage-summary.json, 13/13 combat realism systems, 8/8 trigram stances) |
|
|
22
23
|
| **[🔮 Future Architecture](FUTURE_ARCHITECTURE.md)** | Roadmap | Q2 2026+ evolution: Combat realism completion, VR/AR integration, advanced features |
|
|
23
24
|
| **[🎯 Core Game Concepts](#-core-game-concepts)** | Game Design | Player archetypes (5), trigram system (8), resources & mechanics |
|
|
24
25
|
| **[🏗️ Architecture Concepts](#-architecture-concepts)** | Technical Design | Mindmap of system architecture layers and components |
|
|
@@ -32,7 +33,7 @@
|
|
|
32
33
|
```mermaid
|
|
33
34
|
%%{init: {'theme':'base', 'themeVariables': {'primaryColor':'#2979FF','primaryTextColor':'#fff','primaryBorderColor':'#0D47A1','lineColor':'#455A64','secondaryColor':'#4CAF50','tertiaryColor':'#FF9800','personBkg':'#1565C0','containerBkg':'#2979FF','componentBkg':'#4CAF50','person_bg':'#1565C0','container_bg':'#2979FF','component_bg':'#4CAF50'}}}%%
|
|
34
35
|
C4Context
|
|
35
|
-
title System Context - Black Trigram (흑괘) Web Application (
|
|
36
|
+
title System Context - Black Trigram (흑괘) Web Application (v0.7.24)
|
|
36
37
|
|
|
37
38
|
Person(player, "🧑🤝🧑 Martial Arts Student", "Learns Korean vital point targeting with 70 anatomical points and 28-bone skeletal animation")
|
|
38
39
|
Person(instructor, "🥋 Martial Arts Instructor", "Uses for teaching traditional Korean techniques and I Ching philosophy")
|
|
@@ -224,22 +225,24 @@ C4Component
|
|
|
224
225
|
UpdateLayoutConfig($c4ShapeInRow="4", $c4BoundaryInRow="1")
|
|
225
226
|
```
|
|
226
227
|
|
|
227
|
-
### 🧩 Component Implementation Status (
|
|
228
|
+
### 🧩 Component Implementation Status (Q2 2026)
|
|
228
229
|
|
|
229
|
-
#### Combat System (
|
|
230
|
-
- **Combat Realism**:
|
|
230
|
+
#### Combat System (9.6/10 - Production-Ready)
|
|
231
|
+
- **Combat Realism**: 13/13 systems complete (100%)
|
|
231
232
|
- ✅ **Body Part Health**: 100% (8 parts tracked with Korean/English labels)
|
|
232
233
|
- ✅ **Vital Point Targeting**: 100% (70/70 points with TCM meridian mapping)
|
|
233
234
|
- ✅ **Enhanced Anatomy**: 95% (polygon-based detection, 13 zones, <0.01ms)
|
|
234
|
-
- ✅ **Visual Feedback**:
|
|
235
|
-
- ✅ **Pain Response**:
|
|
236
|
-
- ✅ **Consciousness Levels**:
|
|
237
|
-
- ✅ **Breathing Disruption**:
|
|
238
|
-
-
|
|
239
|
-
-
|
|
240
|
-
-
|
|
241
|
-
-
|
|
242
|
-
-
|
|
235
|
+
- ✅ **Visual Feedback**: 97% (damage numbers, hit effects, combo counter, limb exposure)
|
|
236
|
+
- ✅ **Pain Response**: 100% (production-ready with comprehensive tests)
|
|
237
|
+
- ✅ **Consciousness Levels**: 100% (production-ready, 4-level gradation)
|
|
238
|
+
- ✅ **Breathing Disruption**: 95% (full CombatSystem integration with UI)
|
|
239
|
+
- ✅ **Trauma Visualization**: 90% (per-player injury tracking)
|
|
240
|
+
- ✅ **Balance/Vulnerability**: 70% (transitions refined)
|
|
241
|
+
- ✅ **Combat Readiness HUD**: 85% (EndScreen integration complete)
|
|
242
|
+
- ✅ **Injury-Based Movement**: 60% (dynamic speed modifiers)
|
|
243
|
+
- ✅ **Bone Impact Audio**: 80% (anatomical region detection)
|
|
244
|
+
- ✅ **Grappling System**: 95% (4 animation states, visual feedback, audio hooks)
|
|
245
|
+
- ✅ **Limb Exposure/Counter-Attacks**: 100% (AI integration, visual indicators, 132 tests)
|
|
243
246
|
|
|
244
247
|
#### Vital Point System (9.5/10 - Excellent)
|
|
245
248
|
- 70/70 vital points with Korean names (100% complete)
|
|
@@ -347,10 +350,10 @@ Black Trigram uses Three.js for 3D rendering, achieving enhanced visual capabili
|
|
|
347
350
|
|
|
348
351
|
The following Three.js packages are now installed and configured:
|
|
349
352
|
|
|
350
|
-
- **three@0.
|
|
351
|
-
- **@react-three/fiber@9.
|
|
353
|
+
- **three@0.184.0** - Core Three.js 3D engine with WebGL rendering
|
|
354
|
+
- **@react-three/fiber@9.6.0** - React renderer for Three.js (declarative 3D in React)
|
|
352
355
|
- **@react-three/drei@10.7.7** - Useful helpers and abstractions (@react-three/fiber)
|
|
353
|
-
- **@types/three@0.
|
|
356
|
+
- **@types/three@0.184.0** - TypeScript type definitions for Three.js
|
|
354
357
|
|
|
355
358
|
### 🏗️ Architecture Integration
|
|
356
359
|
|
|
@@ -906,15 +909,15 @@ PELVIS (root)
|
|
|
906
909
|
- 2 characters (player + opponent): ~1220 bytes animation overhead
|
|
907
910
|
- Negligible impact on 60fps target (<1ms per frame)
|
|
908
911
|
|
|
909
|
-
### 🎯 Performance Targets (
|
|
912
|
+
### 🎯 Performance Targets (Q2 2026)
|
|
910
913
|
|
|
911
914
|
| Platform | Resolution | Target FPS | Achieved FPS | Particles | Memory | Status |
|
|
912
915
|
|----------|-----------|------------|--------------|-----------|--------|--------|
|
|
913
916
|
| **Desktop** | 1200x800 | 60fps | 60fps | 1000+ | 180MB | ✅ Met |
|
|
914
|
-
| **Tablet** | 1024x768 | 30fps |
|
|
915
|
-
| **Mobile** | 720p | 30fps |
|
|
917
|
+
| **Tablet** | 1024x768 | 30fps | 55fps+ | 750+ | 150MB | ✅ Optimized |
|
|
918
|
+
| **Mobile** | 720p | 30fps | 55fps+ | 500+ | 150MB | ✅ Optimized |
|
|
916
919
|
|
|
917
|
-
**Overall Performance Rating**: 8.
|
|
920
|
+
**Overall Performance Rating**: 8.5/10 (60fps desktop maintained, mobile optimization complete 55fps+)
|
|
918
921
|
|
|
919
922
|
### 🚀 Three.js Optimization Techniques
|
|
920
923
|
|
|
@@ -955,16 +958,16 @@ Static environment objects merged into single mesh for reduced draw calls.
|
|
|
955
958
|
- **Custom performance metrics** tracked in React/Three.js state and surfaced via `PerformanceOverlay3D`
|
|
956
959
|
- **Performance tests** in the Vitest suite validating `PerformanceOverlay3D` frame timing, overlay stability, and animation timing
|
|
957
960
|
|
|
958
|
-
### 📱 Mobile Performance Optimization (Q2 2026
|
|
961
|
+
### 📱 Mobile Performance Optimization (Q2 2026 Complete)
|
|
959
962
|
|
|
960
|
-
**Current Status:**
|
|
963
|
+
**Current Status:** 55fps+ on mobile (exceeded 30fps baseline target)
|
|
961
964
|
|
|
962
|
-
**Optimization
|
|
965
|
+
**Optimization Completed:**
|
|
963
966
|
1. Adaptive quality settings based on device capabilities
|
|
964
967
|
2. Simplified shaders for mobile-optimized materials
|
|
965
968
|
3. Reduced particle count (500 cap vs 1000 desktop)
|
|
966
969
|
4. LOD aggressive tuning for earlier low-detail transitions
|
|
967
|
-
5.
|
|
970
|
+
5. Mobile R3F fixes and coordinate system corrections
|
|
968
971
|
|
|
969
972
|
### 📚 Documentation & Resources
|
|
970
973
|
|
|
@@ -1016,7 +1019,7 @@ Static environment objects merged into single mesh for reduced draw calls.
|
|
|
1016
1019
|
|
|
1017
1020
|
---
|
|
1018
1021
|
|
|
1019
|
-
## 🔧 File Structure (
|
|
1022
|
+
## 🔧 File Structure (Q2 2026)
|
|
1020
1023
|
|
|
1021
1024
|
### Current Implementation Structure
|
|
1022
1025
|
|
|
@@ -1096,17 +1099,17 @@ src/
|
|
|
1096
1099
|
└── useCombat.ts # Combat-related hooks
|
|
1097
1100
|
```
|
|
1098
1101
|
|
|
1099
|
-
### Key Implementation Files (
|
|
1102
|
+
### Key Implementation Files (Q2 2026)
|
|
1100
1103
|
|
|
1101
1104
|
**Combat Systems:**
|
|
1102
|
-
- `src/systems/CombatSystem.ts` (36,888 bytes ~36KB) - Core combat logic with
|
|
1105
|
+
- `src/systems/CombatSystem.ts` (36,888 bytes ~36KB) - Core combat logic with 13/13 realism systems
|
|
1103
1106
|
- `src/systems/VitalPointSystem.ts` (19,583 bytes ~20KB) - 70-point vital targeting (100% complete)
|
|
1104
1107
|
- `src/systems/TrigramSystem.ts` (12,843 bytes ~13KB) - 8-stance management with I Ching philosophy
|
|
1105
|
-
- `src/systems/combat/PainResponseSystem.ts` - Pain response system (
|
|
1106
|
-
- `src/systems/combat/ConsciousnessSystem.ts` - Consciousness levels (
|
|
1107
|
-
- `src/systems/breathing/BreathingDisruptionSystem.ts` - Breathing disruption (
|
|
1108
|
+
- `src/systems/combat/PainResponseSystem.ts` - Pain response system (100% complete, production-ready)
|
|
1109
|
+
- `src/systems/combat/ConsciousnessSystem.ts` - Consciousness levels (100% complete, 4-level gradation)
|
|
1110
|
+
- `src/systems/breathing/BreathingDisruptionSystem.ts` - Breathing disruption (95% complete)
|
|
1108
1111
|
|
|
1109
|
-
**Skeletal Animation (
|
|
1112
|
+
**Skeletal Animation (Q2 2026):**
|
|
1110
1113
|
- `src/types/skeletal.ts` (857 lines) - 28-bone hierarchy definitions, BoneName enum, SkeletalRig interface
|
|
1111
1114
|
- `src/types/muscle.ts` - Muscle tension visualization system (0.0-1.0 intensity mapping)
|
|
1112
1115
|
- `src/types/hand-animation.ts` - Hand pose system (7 primary poses: fist_vertical, fist_horizontal, open_hand_knife, spear_hand, grasping, open_palm, relaxed)
|
|
@@ -1119,22 +1122,23 @@ src/
|
|
|
1119
1122
|
|
|
1120
1123
|
**Three.js Components:**
|
|
1121
1124
|
- `src/components/shared/three/` - 3D rendering components (Player3DUnified, StanceAura, SkeletalRig)
|
|
1122
|
-
- `src/components/screens/combat/` - CombatScreen3D implementation (production-ready,
|
|
1125
|
+
- `src/components/screens/combat/` - CombatScreen3D implementation (production-ready, 41.71% line coverage per docs/coverage/coverage-summary.json)
|
|
1123
1126
|
- `src/utils/player3DHelpers.ts` - PlayerState to Three.js conversion utilities
|
|
1124
1127
|
|
|
1125
|
-
**Audio System (
|
|
1128
|
+
**Audio System (88.08% Line Coverage):**
|
|
1126
1129
|
- `src/audio/AudioProvider.tsx` - React context provider and useAudio hook
|
|
1127
1130
|
- `src/audio/AudioAssetRegistry.ts` - Sound library with damage-scaled audio
|
|
1128
1131
|
- `src/audio/AudioManager.ts` - Audio playback with Web Audio API
|
|
1129
1132
|
- `src/audio/BoneImpactAudioMap.ts` - Bone impact audio system (60% complete)
|
|
1130
|
-
-
|
|
1133
|
+
- 88.08% line coverage across audio systems (843/957 lines, per docs/coverage/audio/index.html)
|
|
1131
1134
|
|
|
1132
|
-
**Test Coverage (
|
|
1133
|
-
- **Overall**:
|
|
1135
|
+
**Test Coverage (Q2 2026):**
|
|
1136
|
+
- **Overall**: 72.34% line coverage (17,451 / 24,121 lines, 518 tests - Vitest unit + Cypress E2E, per docs/coverage/coverage-summary.json)
|
|
1134
1137
|
- **New Components**: 95% test coverage
|
|
1135
1138
|
- **Core Systems**: >85% test coverage
|
|
1136
|
-
- **Audio System**:
|
|
1137
|
-
- **
|
|
1139
|
+
- **Audio System**: 88.08% line coverage (843/957 lines)
|
|
1140
|
+
- **Combat Realism**: 518 tests passing (372 + 146 new)
|
|
1141
|
+
- **Grappling/Limb Exposure**: 132 comprehensive tests
|
|
1138
1142
|
|
|
1139
1143
|
### Component Naming Conventions
|
|
1140
1144
|
|
|
@@ -2421,6 +2425,6 @@ The Q1 2026 architecture successfully demonstrates the feasibility of authentic
|
|
|
2421
2425
|
**✅ Approved by:** James Pether Sörling, CEO
|
|
2422
2426
|
**📤 Distribution:** Public
|
|
2423
2427
|
**🏷️ Classification:** [](https://github.com/Hack23/ISMS-PUBLIC/blob/main/CLASSIFICATION.md#confidentiality-levels) [](https://github.com/Hack23/ISMS-PUBLIC/blob/main/CLASSIFICATION.md#integrity-levels) [](https://github.com/Hack23/ISMS-PUBLIC/blob/main/CLASSIFICATION.md#availability-levels)
|
|
2424
|
-
**📅 Effective Date:** 2026-
|
|
2425
|
-
**⏰ Next Review:** 2026-
|
|
2428
|
+
**📅 Effective Date:** 2026-04-21
|
|
2429
|
+
**⏰ Next Review:** 2026-10-21
|
|
2426
2430
|
**🎯 Framework Compliance:** [](https://github.com/Hack23/ISMS-PUBLIC/blob/main/CLASSIFICATION.md) [](https://github.com/Hack23/ISMS-PUBLIC/blob/main/CLASSIFICATION.md) [](https://github.com/Hack23/ISMS-PUBLIC/blob/main/CLASSIFICATION.md)
|
package/CRA-ASSESSMENT.md
CHANGED
|
@@ -13,13 +13,13 @@
|
|
|
13
13
|
|
|
14
14
|
<p align="center">
|
|
15
15
|
<a href="#"><img src="https://img.shields.io/badge/Owner-CEO-0A66C2?style=for-the-badge" alt="Owner"/></a>
|
|
16
|
-
<a href="#"><img src="https://img.shields.io/badge/Version-1.
|
|
17
|
-
<a href="#"><img src="https://img.shields.io/badge/Effective-2026--
|
|
16
|
+
<a href="#"><img src="https://img.shields.io/badge/Version-1.3-555?style=for-the-badge" alt="Version"/></a>
|
|
17
|
+
<a href="#"><img src="https://img.shields.io/badge/Effective-2026--04--21-success?style=for-the-badge" alt="Effective Date"/></a>
|
|
18
18
|
<a href="#"><img src="https://img.shields.io/badge/Review-Quarterly-orange?style=for-the-badge" alt="Review Cycle"/></a>
|
|
19
19
|
</p>
|
|
20
20
|
|
|
21
|
-
**Document Owner:** CEO | **Version:** 1.
|
|
22
|
-
**Review Cycle:** Quarterly | **Next Review:** 2026-
|
|
21
|
+
**Document Owner:** CEO | **Version:** 1.3 | **Last Updated:** 2026-04-21
|
|
22
|
+
**Review Cycle:** Quarterly | **Next Review:** 2026-07-21
|
|
23
23
|
|
|
24
24
|
---
|
|
25
25
|
|
|
@@ -91,7 +91,7 @@ _Supports CRA Annex V § 1 - Product Description Requirements_
|
|
|
91
91
|
| Field | Value |
|
|
92
92
|
| ---------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
93
93
|
| 📦 Product | Black Trigram (흑괘) - Korean Martial Arts Combat Simulator |
|
|
94
|
-
| 🏷️ Version Tag | 0.
|
|
94
|
+
| 🏷️ Version Tag | 0.7.24 (reflects current project state) |
|
|
95
95
|
| 🔗 Repository | https://github.com/Hack23/blacktrigram |
|
|
96
96
|
| 📧 Security Contact | security@hack23.org |
|
|
97
97
|
| 🎯 Purpose (1–2 lines) | Educational 3D combat game teaching authentic Korean martial arts through realistic anatomical targeting and traditional Eight Trigram philosophy |
|
|
@@ -270,10 +270,10 @@ GitHub Attestations: `https://github.com/Hack23/blacktrigram/attestations`
|
|
|
270
270
|
**🎯 Release Assets Structure:**
|
|
271
271
|
|
|
272
272
|
```
|
|
273
|
-
blacktrigram-0.
|
|
274
|
-
blacktrigram-0.
|
|
275
|
-
blacktrigram-0.
|
|
276
|
-
blacktrigram-0.
|
|
273
|
+
blacktrigram-0.7.24.zip # Main application bundle
|
|
274
|
+
blacktrigram-0.7.24.zip.intoto.jsonl # SLSA provenance attestation
|
|
275
|
+
blacktrigram-0.7.24.spdx.json # SPDX SBOM
|
|
276
|
+
blacktrigram-0.7.24.spdx.json.intoto.jsonl # SBOM attestation
|
|
277
277
|
```
|
|
278
278
|
|
|
279
279
|
**📋 Release Notes Format:**
|
|
@@ -304,7 +304,7 @@ blacktrigram-0.6.58.spdx.json.intoto.jsonl # SBOM attestation
|
|
|
304
304
|
|
|
305
305
|
Thanks to @dependabot[bot] for automated security updates!
|
|
306
306
|
|
|
307
|
-
**Full Changelog**: https://github.com/Hack23/blacktrigram/compare/v0.
|
|
307
|
+
**Full Changelog**: https://github.com/Hack23/blacktrigram/compare/v0.7.23...v0.7.24
|
|
308
308
|
```
|
|
309
309
|
|
|
310
310
|
**🔍 Evidence Validation Commands:**
|
|
@@ -357,7 +357,7 @@ _Supports CRA Article 28 - EU Declaration of Conformity_
|
|
|
357
357
|
> **📝 Complete when placing product on EU market**
|
|
358
358
|
|
|
359
359
|
**🏢 Manufacturer:** Hack23 AB, Stockholm, Sweden
|
|
360
|
-
**📦 Product:** Black Trigram (흑괘) 0.
|
|
360
|
+
**📦 Product:** Black Trigram (흑괘) 0.7.24
|
|
361
361
|
**📋 CRA Compliance:** Self-assessment documentation supporting CRA essential cybersecurity requirements evaluation
|
|
362
362
|
**🔍 Assessment:** Self-assessment documentation per Article 24 - Standard product classification
|
|
363
363
|
**📊 Standards:** ETSI EN 303 645 (IoT Security), ISO/IEC 27001 (ISMS), OWASP ASVS (Application Security), NIST SSDF (Secure Development)
|
|
@@ -422,7 +422,7 @@ CRA assessment updated only when changes constitute "substantial modification" u
|
|
|
422
422
|
```markdown
|
|
423
423
|
## Current CRA Self-Assessment Evidence
|
|
424
424
|
|
|
425
|
-
**🏷️ Product Version:** 0.
|
|
425
|
+
**🏷️ Product Version:** 0.7.24
|
|
426
426
|
**📦 CRA Technical Documentation:** This assessment + [Latest Release](https://github.com/Hack23/blacktrigram/releases/latest)
|
|
427
427
|
**🛡️ Security Attestations:** [GitHub Attestations](https://github.com/Hack23/blacktrigram/attestations)
|
|
428
428
|
**📊 Assessment Status:** 
|
|
@@ -503,6 +503,6 @@ CRA assessment updated only when changes constitute "substantial modification" u
|
|
|
503
503
|
**Approved by:** James Pether Sörling, CEO
|
|
504
504
|
**Distribution:** Public
|
|
505
505
|
**Classification:** [](https://github.com/Hack23/ISMS-PUBLIC/blob/main/CLASSIFICATION.md#confidentiality-levels)
|
|
506
|
-
**Effective Date:** 2026-
|
|
506
|
+
**Effective Date:** 2026-04-21
|
|
507
507
|
**CRA Alignment:** Template supports CRA Annex V technical documentation and self-assessment requirements
|
|
508
508
|
**ISMS Integration:** Comprehensive alignment with public ISMS framework for operational excellence
|
package/DATA_MODEL.md
CHANGED
|
@@ -1,5 +1,9 @@
|
|
|
1
1
|
# 📊 Black Trigram (흑괘) Data Model
|
|
2
2
|
|
|
3
|
+
**Last Updated:** 2026-04-21
|
|
4
|
+
**Product Version:** 0.7.24
|
|
5
|
+
**Status:** Production-Ready
|
|
6
|
+
|
|
3
7
|
## 📚 Related Documentation
|
|
4
8
|
|
|
5
9
|
| Document | Focus | Description |
|
|
@@ -1020,6 +1024,6 @@ This data model documentation ensures type-safe, performant, and culturally auth
|
|
|
1020
1024
|
**✅ Approved by:** James Pether Sörling, CEO
|
|
1021
1025
|
**📤 Distribution:** Public
|
|
1022
1026
|
**🏷️ Classification:** [](https://github.com/Hack23/ISMS-PUBLIC/blob/main/CLASSIFICATION.md#confidentiality-levels) [](https://github.com/Hack23/ISMS-PUBLIC/blob/main/CLASSIFICATION.md#integrity-levels) [](https://github.com/Hack23/ISMS-PUBLIC/blob/main/CLASSIFICATION.md#availability-levels)
|
|
1023
|
-
**📅 Effective Date:** 2026-
|
|
1024
|
-
**⏰ Next Review:** 2026-
|
|
1027
|
+
**📅 Effective Date:** 2026-04-21
|
|
1028
|
+
**⏰ Next Review:** 2026-10-21
|
|
1025
1029
|
**🎯 Framework Compliance:** [](https://github.com/Hack23/ISMS-PUBLIC/blob/main/CLASSIFICATION.md) [](https://github.com/Hack23/ISMS-PUBLIC/blob/main/CLASSIFICATION.md)
|
package/SECURITY_ARCHITECTURE.md
CHANGED
|
@@ -11,13 +11,13 @@
|
|
|
11
11
|
|
|
12
12
|
<p align="center">
|
|
13
13
|
<a><img src="https://img.shields.io/badge/Owner-CEO-0A66C2?style=for-the-badge" alt="Owner"/></a>
|
|
14
|
-
<a><img src="https://img.shields.io/badge/Version-2.
|
|
15
|
-
<a><img src="https://img.shields.io/badge/Effective-2026--
|
|
14
|
+
<a><img src="https://img.shields.io/badge/Version-2.1-555?style=for-the-badge" alt="Version"/></a>
|
|
15
|
+
<a><img src="https://img.shields.io/badge/Effective-2026--04--21-success?style=for-the-badge" alt="Effective Date"/></a>
|
|
16
16
|
<a><img src="https://img.shields.io/badge/Review-Annual-orange?style=for-the-badge" alt="Review Cycle"/></a>
|
|
17
17
|
</p>
|
|
18
18
|
|
|
19
|
-
**📋 Document Owner:** CEO | **📄 Version:** 2.
|
|
20
|
-
**🔄 Review Cycle:** Annual | **⏰ Next Review:** 2027-
|
|
19
|
+
**📋 Document Owner:** CEO | **📄 Version:** 2.1 | **📅 Last Updated:** 2026-04-21 (UTC)
|
|
20
|
+
**🔄 Review Cycle:** Annual | **⏰ Next Review:** 2027-04-21
|
|
21
21
|
**🏷️ Classification:** Public (Open Source Educational Gaming Platform)
|
|
22
22
|
|
|
23
23
|
---
|
|
@@ -1174,8 +1174,8 @@ As documented in the [End-of-Life Strategy](End-of-Life-Strategy.md), any future
|
|
|
1174
1174
|
**✅ Approved by:** James Pether Sörling, CEO
|
|
1175
1175
|
**📤 Distribution:** Public
|
|
1176
1176
|
**🏷️ Classification:** [](https://github.com/Hack23/ISMS-PUBLIC/blob/main/CLASSIFICATION.md#confidentiality-levels) [](https://github.com/Hack23/ISMS-PUBLIC/blob/main/CLASSIFICATION.md#integrity-levels) [](https://github.com/Hack23/ISMS-PUBLIC/blob/main/CLASSIFICATION.md#availability-levels)
|
|
1177
|
-
**📅 Effective Date:** 2026-
|
|
1178
|
-
**⏰ Next Review:** 2027-
|
|
1177
|
+
**📅 Effective Date:** 2026-04-21
|
|
1178
|
+
**⏰ Next Review:** 2027-04-21
|
|
1179
1179
|
**🎯 Framework Compliance:** [](https://github.com/Hack23/ISMS-PUBLIC/blob/main/CLASSIFICATION.md) [](https://github.com/Hack23/ISMS-PUBLIC/blob/main/CLASSIFICATION.md) [](https://github.com/Hack23/ISMS-PUBLIC/blob/main/CLASSIFICATION.md) [](https://github.com/Hack23/ISMS-PUBLIC/blob/main/CLASSIFICATION.md)
|
|
1180
1180
|
|
|
1181
1181
|
**흑괘의 길을 걸어라** - _Walk the Path of the Black Trigram with Security_
|
package/THREAT_MODEL.md
CHANGED
|
@@ -11,13 +11,13 @@
|
|
|
11
11
|
|
|
12
12
|
<p align="center">
|
|
13
13
|
<a><img src="https://img.shields.io/badge/Owner-CEO-0A66C2?style=for-the-badge" alt="Owner"/></a>
|
|
14
|
-
<a><img src="https://img.shields.io/badge/Version-2.
|
|
15
|
-
<a><img src="https://img.shields.io/badge/Effective-2026--
|
|
14
|
+
<a><img src="https://img.shields.io/badge/Version-2.1-555?style=for-the-badge" alt="Version"/></a>
|
|
15
|
+
<a><img src="https://img.shields.io/badge/Effective-2026--04--21-success?style=for-the-badge" alt="Effective Date"/></a>
|
|
16
16
|
<a><img src="https://img.shields.io/badge/Review-Annual-orange?style=for-the-badge" alt="Review Cycle"/></a>
|
|
17
17
|
</p>
|
|
18
18
|
|
|
19
|
-
**📋 Document Owner:** CEO | **📄 Version:** 2.
|
|
20
|
-
**🔄 Review Cycle:** Annual | **⏰ Next Review:** 2027-
|
|
19
|
+
**📋 Document Owner:** CEO | **📄 Version:** 2.1 | **📅 Last Updated:** 2026-04-21 (UTC)
|
|
20
|
+
**🔄 Review Cycle:** Annual | **⏰ Next Review:** 2027-04-21
|
|
21
21
|
**🏷️ Classification:** Public (Open Source Educational Gaming Platform)
|
|
22
22
|
|
|
23
23
|
---
|
|
@@ -1165,6 +1165,6 @@ Following [Hack23 AB Threat Modeling Policy §2.1](https://github.com/Hack23/ISM
|
|
|
1165
1165
|
**✅ Approved by:** James Pether Sörling, CEO
|
|
1166
1166
|
**📤 Distribution:** Public
|
|
1167
1167
|
**🏷️ Classification:** [](https://github.com/Hack23/ISMS-PUBLIC/blob/main/CLASSIFICATION.md)
|
|
1168
|
-
**📅 Effective Date:** 2026-
|
|
1169
|
-
**⏰ Next Review:** 2027-
|
|
1168
|
+
**📅 Effective Date:** 2026-04-21
|
|
1169
|
+
**⏰ Next Review:** 2027-04-21
|
|
1170
1170
|
**🎯 Framework Compliance:** [](https://github.com/Hack23/ISMS-PUBLIC/blob/main/CLASSIFICATION.md) [](https://github.com/Hack23/ISMS-PUBLIC/blob/main/CLASSIFICATION.md) [](https://github.com/Hack23/ISMS-PUBLIC/blob/main/CLASSIFICATION.md) [](https://github.com/Hack23/ISMS-PUBLIC/blob/main/CLASSIFICATION.md) [](https://github.com/Hack23/ISMS-PUBLIC/blob/main/Threat_Modeling.md)
|
package/lib/App2.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"App2.js","names":[],"sources":["../src/App.tsx"],"sourcesContent":["import {\n lazy,\n Suspense,\n useCallback,\n useEffect,\n useRef,\n useState,\n} from \"react\";\nimport \"./App.css\";\n// Main application component - manages game state and screen navigation\nimport { useAudio } from \"./audio/AudioProvider\";\nimport { CombatScreen3D as CombatScreen } from \"./components/screens/combat/CombatScreen3D\";\nimport { ControlsScreen3D as ControlsScreen } from \"./components/screens/controls/ControlsScreen3D\";\nimport { EndScreen3D } from \"./components/screens/endscreen\";\nimport { IntroScreen3D as IntroScreen } from \"./components/screens/intro/IntroScreen3D\";\nimport { PhilosophyScreen3D as PhilosophyScreen } from \"./components/screens/philosophy/PhilosophyScreen3D\";\nimport { PerformanceDebugOverlayHtml } from \"./components/shared/debug/PerformanceDebugOverlayHtml\";\nimport { ErrorModal } from \"./components/shared/ui/ErrorModal\";\nimport { LoadingState } from \"./components/shared/ui/LoadingState\";\nimport { SplashScreen } from \"./components/shared/ui/SplashScreen\";\nimport { PlayerState } from \"./systems\";\nimport { MatchStatistics } from \"./systems/combat\";\nimport { GameMode, PlayerArchetype } from \"./types/common\";\nimport { clearPlatformCache, detectPlatform } from \"./utils/deviceDetection\";\nimport { createPlayerFromArchetype } from \"./utils/playerUtils\";\n\n// Lazy load heavy screens\nconst TrainingScreen = lazy(() =>\n import(\"./components/screens/training/TrainingScreen3D\").then((m) => ({\n default: m.TrainingScreen3D,\n })),\n);\n\n// 150ms delay to allow WebGL context cleanup between full-screen 3D scene transitions\nconst SCREEN_TRANSITION_DELAY_MS = 150;\n// 100ms delay for lighter menu/UI transitions where WebGL teardown/re-init cost is lower\nconst MENU_TRANSITION_DELAY_MS = 100;\n\nfunction App() {\n const [gameMode, setGameMode] = useState<GameMode | null>(null);\n const [selectedArchetype, setSelectedArchetype] = useState<PlayerArchetype>(\n PlayerArchetype.MUSA,\n );\n const [isGameActive, setIsGameActive] = useState(false);\n const [gameWinner, setGameWinner] = useState<PlayerState | null>(null);\n const [matchStats, setMatchStats] = useState<MatchStatistics | null>(null);\n const [appReady, setAppReady] = useState(false);\n const [showSplash, setShowSplash] = useState(true);\n const [showAudioError, setShowAudioError] = useState(false);\n // Performance debug overlay toggle (P key in dev mode)\n const [showPerformanceDebug, setShowPerformanceDebug] = useState(false);\n // Transition state to allow WebGL cleanup between screens\n const [isTransitioning, setIsTransitioning] = useState(false);\n const pendingModeRef = useRef<{\n mode: GameMode;\n archetype?: PlayerArchetype;\n } | null>(null);\n\n // Combat players state - managed here so updates persist\n const [combatPlayers, setCombatPlayers] = useState<PlayerState[]>([]);\n\n const audio = useAudio();\n\n // Add responsive screen size detection with proper device detection\n // Uses user-agent detection first for high-res mobile devices\n const [screenSize, setScreenSize] = useState(() => {\n const platform = detectPlatform();\n return {\n width: window.innerWidth,\n height: window.innerHeight,\n isMobile: platform.isMobile,\n isTablet: platform.isTablet,\n isDesktop: platform.isDesktop,\n };\n });\n\n useEffect(() => {\n // Define handlers outside async function for proper cleanup\n const handleGlobalError = (e: ErrorEvent) => {\n console.error(\"Global error:\", e.error);\n };\n\n const handleUnhandledRejection = (e: PromiseRejectionEvent) => {\n console.error(\"Unhandled promise rejection:\", e.reason);\n if (\n e.reason?.message?.includes(\"Failed to load\") ||\n e.reason?.message?.includes(\"no supported source\")\n ) {\n e.preventDefault();\n }\n };\n\n const initializeApp = async () => {\n try {\n window.focus();\n\n window.addEventListener(\"error\", handleGlobalError);\n window.addEventListener(\"unhandledrejection\", handleUnhandledRejection);\n\n // PHASE 2: Performance optimization initialization\n console.log(\"🔧 Initializing animation performance optimizations...\");\n\n // 1. Prewarm object pools for animation optimization\n // This eliminates GC pressure from ~1,344 allocations per frame\n const { ThreeObjectPools } = await import(\"./utils/threeObjectPool\");\n ThreeObjectPools.prewarmAll();\n const poolStatus = ThreeObjectPools.getStatus();\n console.log(\" ✓ Object pools prewarmed:\", poolStatus);\n\n // 2. Precompute all animations for 90%+ cache hit rate\n const { precomputeAnimation } =\n await import(\"./systems/animation/core/AnimationOptimizations\");\n const { ALL_ANIMATIONS } =\n await import(\"./systems/animation/core/AnimationRegistry\");\n\n let precomputedCount = 0;\n ALL_ANIMATIONS.forEach((animation) => {\n // Precompute at 60fps for smooth playback\n // Use animation.name as the unique identifier\n precomputeAnimation(animation.name, animation, 60);\n precomputedCount++;\n });\n console.log(` ✓ Precomputed ${precomputedCount} animations at 60fps`);\n\n console.log(\n \"✅ Animation optimizations ready (expect <5ms frame time, 90%+ cache hit)\",\n );\n\n setAppReady(true);\n console.log(\"🎯 Black Trigram app initialized\");\n } catch (error) {\n console.error(\"Failed to initialize app:\", error);\n setAppReady(true);\n }\n };\n\n initializeApp();\n\n // Cleanup global event handlers to prevent memory leaks\n return () => {\n window.removeEventListener(\"error\", handleGlobalError);\n window.removeEventListener(\n \"unhandledrejection\",\n handleUnhandledRejection,\n );\n };\n }, []);\n\n // Shared audio initialization logic for splash and retry\n const initializeAudioWithRetry = useCallback(async () => {\n if (!appReady) {\n console.warn(\"App not ready yet, please wait...\");\n return false;\n }\n try {\n await audio.initializeAudio();\n console.log(\"🎵 Audio initialized\");\n return true;\n } catch (error) {\n console.error(\"Failed to initialize audio:\", error);\n return false;\n }\n }, [audio, appReady]);\n\n // Handle splash screen start - initialize audio on user gesture\n const handleSplashStart = useCallback(async () => {\n setShowAudioError(false);\n const success = await initializeAudioWithRetry();\n if (success) {\n setShowSplash(false);\n } else {\n setShowAudioError(true);\n }\n }, [initializeAudioWithRetry]);\n\n const handleAudioErrorRetry = useCallback(async () => {\n setShowAudioError(false);\n const success = await initializeAudioWithRetry();\n if (success) {\n setShowSplash(false);\n } else {\n setShowAudioError(true);\n }\n }, [initializeAudioWithRetry]);\n\n const handleAudioErrorContinue = useCallback(() => {\n // Continue without sound\n setShowAudioError(false);\n setShowSplash(false);\n console.log(\"Continuing without audio (silent mode)\");\n }, []);\n\n // ✅ SIMPLIFIED: Handle game mode selection directly\n const handleGameStart = useCallback(\n (mode: GameMode, archetype?: PlayerArchetype) => {\n console.log(\"🎮 Starting game mode:\", mode, \"with archetype:\", archetype);\n\n // Store pending mode and start transition to allow WebGL cleanup\n pendingModeRef.current = { mode, archetype };\n setIsTransitioning(true);\n\n // Clear current mode first (unmounts Canvas)\n setGameMode(null);\n setIsGameActive(false);\n\n // After brief delay, mount new screen\n // Increased delay to allow proper WebGL context cleanup\n setTimeout(() => {\n const pending = pendingModeRef.current;\n if (!pending) return;\n\n // ✅ NEW: Handle controls and philosophy as separate modes\n if (\n pending.mode === GameMode.CONTROLS ||\n pending.mode === GameMode.PHILOSOPHY\n ) {\n setGameMode(pending.mode);\n setIsGameActive(false); // These are not game modes, just screens\n } else {\n setGameMode(pending.mode);\n setIsGameActive(true);\n }\n\n setGameWinner(null);\n setMatchStats(null);\n if (pending.archetype) {\n setSelectedArchetype(pending.archetype);\n }\n\n setIsTransitioning(false);\n pendingModeRef.current = null;\n }, SCREEN_TRANSITION_DELAY_MS); // Delay for WebGL cleanup\n },\n [],\n );\n\n const handleGameEnd = useCallback(\n (winner: number) => {\n setIsGameActive(false);\n setGameWinner(createPlayerFromArchetype(selectedArchetype, winner));\n // Reset combat players for next match\n setCombatPlayers([]);\n\n setMatchStats({\n totalDamageDealt: 150,\n totalDamageTaken: 100,\n criticalHits: 3,\n vitalPointHits: 2,\n techniquesUsed: 8,\n perfectStrikes: 1,\n consecutiveWins: 1,\n matchDuration: 120,\n totalMatches: 1,\n maxRounds: 3,\n winner: winner,\n totalRounds: 2,\n currentRound: 2,\n timeRemaining: 0,\n combatEvents: [],\n finalScore: {\n player1: winner === 0 ? 2 : 0,\n player2: winner === 1 ? 2 : 0,\n },\n roundsWon: {\n player1: winner === 0 ? 2 : 0,\n player2: winner === 1 ? 2 : 0,\n },\n player1: {\n wins: winner === 0 ? 1 : 0,\n losses: winner === 0 ? 0 : 1,\n hitsTaken: 5,\n hitsLanded: 8,\n totalDamageDealt: winner === 0 ? 150 : 100,\n totalDamageReceived: winner === 0 ? 100 : 150,\n techniques: [\"천둥벽력\", \"유수연타\"],\n perfectStrikes: winner === 0 ? 1 : 0,\n vitalPointHits: winner === 0 ? 2 : 1,\n consecutiveWins: winner === 0 ? 1 : 0,\n matchDuration: 120,\n },\n player2: {\n wins: winner === 1 ? 1 : 0,\n losses: winner === 1 ? 0 : 1,\n hitsTaken: 8,\n hitsLanded: 5,\n totalDamageDealt: winner === 1 ? 150 : 100,\n totalDamageReceived: winner === 1 ? 100 : 150,\n techniques: [\"화염지창\", \"벽력일섬\"],\n perfectStrikes: winner === 1 ? 1 : 0,\n vitalPointHits: winner === 1 ? 2 : 1,\n consecutiveWins: winner === 1 ? 1 : 0,\n matchDuration: 120,\n },\n });\n },\n [selectedArchetype],\n );\n\n const handleReturnToMenu = useCallback(() => {\n // Use same transition logic for return to menu\n setIsTransitioning(true);\n setGameMode(null);\n setIsGameActive(false);\n setGameWinner(null);\n setMatchStats(null);\n // Reset combat players so they reinitialize next combat\n setCombatPlayers([]);\n setTimeout(() => setIsTransitioning(false), MENU_TRANSITION_DELAY_MS);\n }, []);\n\n const handleRematch = useCallback(() => {\n // Restart combat with same settings\n if (!gameMode) {\n console.error(\n \"Cannot rematch: gameMode is not set. This should not happen - EndScreen only renders when gameMode is set.\",\n { gameMode, isGameActive, gameWinner, matchStats }\n );\n return;\n }\n \n setIsTransitioning(true);\n setGameWinner(null);\n setMatchStats(null);\n // Reset combat players so they reinitialize for rematch\n setCombatPlayers([]);\n \n setTimeout(() => {\n setIsGameActive(true);\n setIsTransitioning(false);\n }, SCREEN_TRANSITION_DELAY_MS);\n // eslint-disable-next-line react-hooks/exhaustive-deps -- isGameActive, gameWinner, matchStats only used in error logging, not function logic\n }, [gameMode]);\n\n const handleViewTraining = useCallback(() => {\n // Navigate to training mode\n setIsTransitioning(true);\n setGameWinner(null);\n setMatchStats(null);\n setCombatPlayers([]);\n \n setTimeout(() => {\n setGameMode(GameMode.TRAINING);\n setIsGameActive(true);\n setIsTransitioning(false);\n }, SCREEN_TRANSITION_DELAY_MS);\n }, []);\n\n const renderCurrentScreen = () => {\n // Show loading during screen transitions\n if (isTransitioning) {\n return (\n <LoadingState\n progress={undefined}\n message=\"전환 중... | Transitioning...\"\n stage=\"assets\"\n />\n );\n }\n\n if (gameWinner && matchStats) {\n // ✅ NEW: Use EndScreen3D component\n return (\n <EndScreen3D\n winner={gameWinner}\n matchStats={matchStats}\n onReturnToMenu={handleReturnToMenu}\n onRematch={handleRematch}\n onViewReplay={handleViewTraining}\n width={screenSize.width}\n height={screenSize.height}\n />\n );\n }\n\n // ✅ NEW: Handle standalone screens first\n if (gameMode === GameMode.CONTROLS) {\n return (\n <ControlsScreen\n onReturnToMenu={handleReturnToMenu}\n width={screenSize.width}\n height={screenSize.height}\n />\n );\n }\n\n if (gameMode === GameMode.PHILOSOPHY) {\n return (\n <PhilosophyScreen\n onReturnToMenu={handleReturnToMenu}\n width={screenSize.width}\n height={screenSize.height}\n />\n );\n }\n\n // ✅ SIMPLIFIED: Only active game modes use isGameActive\n if (isGameActive && gameMode) {\n switch (gameMode) {\n case GameMode.TRAINING:\n return (\n <Suspense\n fallback={\n <LoadingState\n progress={undefined}\n message=\"훈련장 로딩 중... | Loading Training...\"\n stage=\"assets\"\n />\n }\n >\n <TrainingScreen\n onPlayerUpdate={(updates) => {\n console.log(\"Training player updated:\", updates);\n }}\n onReturnToMenu={handleReturnToMenu}\n width={screenSize.width}\n height={screenSize.height}\n initialArchetype={selectedArchetype}\n />\n </Suspense>\n );\n case GameMode.VERSUS:\n case GameMode.PRACTICE:\n // Initialize players if not already set\n if (combatPlayers.length === 0) {\n const player1 = createPlayerFromArchetype(selectedArchetype, 0);\n const player2 = createPlayerFromArchetype(\n PlayerArchetype.AMSALJA,\n 1,\n );\n // Use setTimeout to defer state update and avoid render-during-render\n setTimeout(() => setCombatPlayers([player1, player2]), 0);\n // Return loading state while players initialize\n return (\n <LoadingState\n progress={undefined}\n message=\"전투 준비 중... | Preparing Combat...\"\n stage=\"assets\"\n />\n );\n }\n\n return (\n <CombatScreen\n players={combatPlayers}\n currentRound={1}\n timeRemaining={180}\n isPaused={false}\n onPlayerUpdate={(playerIndex, updates) => {\n // Actually update the player state so damage persists!\n setCombatPlayers((prevPlayers) => {\n const newPlayers = [...prevPlayers];\n if (newPlayers[playerIndex]) {\n newPlayers[playerIndex] = {\n ...newPlayers[playerIndex],\n ...updates,\n };\n }\n return newPlayers;\n });\n }}\n onReturnToMenu={handleReturnToMenu}\n onGameEnd={handleGameEnd}\n gameMode={gameMode}\n width={screenSize.width}\n height={screenSize.height}\n />\n );\n default:\n return (\n <IntroScreen\n onMenuSelect={handleGameStart}\n onArchetypeSelect={setSelectedArchetype}\n selectedArchetype={selectedArchetype}\n width={screenSize.width}\n height={screenSize.height}\n />\n );\n }\n }\n\n // ✅ SIMPLIFIED: Default to intro screen\n return (\n <IntroScreen\n onMenuSelect={handleGameStart}\n onArchetypeSelect={setSelectedArchetype}\n selectedArchetype={selectedArchetype}\n width={screenSize.width}\n height={screenSize.height}\n />\n );\n };\n\n const containerRef = useRef<HTMLDivElement>(null);\n\n useEffect(() => {\n containerRef.current?.focus();\n }, [appReady]);\n\n useEffect(() => {\n const handleResize = () => {\n // Clear cached platform info to get fresh detection on resize\n clearPlatformCache();\n const platform = detectPlatform();\n setScreenSize({\n width: platform.screenWidth,\n height: platform.screenHeight,\n isMobile: platform.isMobile,\n isTablet: platform.isTablet,\n isDesktop: platform.isDesktop,\n });\n };\n\n window.addEventListener(\"resize\", handleResize);\n return () => window.removeEventListener(\"resize\", handleResize);\n }, []);\n\n // F9 key toggle for performance debug overlay (dev mode only)\n // Note: P key is reserved for Philosophy screen\n useEffect(() => {\n if (!import.meta.env.DEV) return;\n\n const handleKeyDown = (e: KeyboardEvent) => {\n if (e.key === \"F9\") {\n e.preventDefault();\n setShowPerformanceDebug((prev) => !prev);\n }\n };\n\n window.addEventListener(\"keydown\", handleKeyDown);\n return () => window.removeEventListener(\"keydown\", handleKeyDown);\n }, []);\n\n if (!appReady) {\n return (\n <div className=\"app loading\" data-testid=\"app-container\">\n <LoadingState\n progress={undefined}\n message=\"앱 초기화 중 | Initializing app...\"\n stage=\"initialization\"\n />\n </div>\n );\n }\n\n // Show splash screen first to get user gesture for audio\n if (showSplash) {\n return (\n <div className=\"app\" data-testid=\"app-container\">\n <SplashScreen\n onStart={handleSplashStart}\n width={screenSize.width}\n height={screenSize.height}\n />\n {showAudioError && (\n <ErrorModal\n message=\"오디오 초기화에 실패했습니다. 재시도하거나 소리 없이 계속할 수 있습니다. | Audio initialization failed. You can retry or continue without sound.\"\n onRetry={handleAudioErrorRetry}\n onContinue={handleAudioErrorContinue}\n />\n )}\n </div>\n );\n }\n\n return (\n <div\n className=\"app\"\n tabIndex={0}\n ref={containerRef}\n data-testid=\"app-container\"\n >\n {/* All screens now use Three.js or pure React/HTML */}\n {renderCurrentScreen()}\n\n {/* Performance debug overlay (dev mode only, toggle with P key) */}\n {showPerformanceDebug && <PerformanceDebugOverlayHtml />}\n </div>\n );\n}\n\nexport default App;\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"App2.js","names":[],"sources":["../src/App.tsx"],"sourcesContent":["import {\n lazy,\n Suspense,\n useCallback,\n useEffect,\n useRef,\n useState,\n} from \"react\";\nimport \"./App.css\";\n// Main application component - manages game state and screen navigation\nimport { useAudio } from \"./audio/AudioProvider\";\nimport { CombatScreen3D as CombatScreen } from \"./components/screens/combat/CombatScreen3D\";\nimport { ControlsScreen3D as ControlsScreen } from \"./components/screens/controls/ControlsScreen3D\";\nimport { EndScreen3D } from \"./components/screens/endscreen\";\nimport { IntroScreen3D as IntroScreen } from \"./components/screens/intro/IntroScreen3D\";\nimport { PhilosophyScreen3D as PhilosophyScreen } from \"./components/screens/philosophy/PhilosophyScreen3D\";\nimport { PerformanceDebugOverlayHtml } from \"./components/shared/debug/PerformanceDebugOverlayHtml\";\nimport { ErrorModal } from \"./components/shared/ui/ErrorModal\";\nimport { LoadingState } from \"./components/shared/ui/LoadingState\";\nimport { SplashScreen } from \"./components/shared/ui/SplashScreen\";\nimport { PlayerState } from \"./systems\";\nimport { MatchStatistics } from \"./systems/combat\";\nimport { GameMode, PlayerArchetype } from \"./types/common\";\nimport { clearPlatformCache, detectPlatform } from \"./utils/deviceDetection\";\nimport { createPlayerFromArchetype } from \"./utils/playerUtils\";\n\n// Lazy load heavy screens\nconst TrainingScreen = lazy(() =>\n import(\"./components/screens/training/TrainingScreen3D\").then((m) => ({\n default: m.TrainingScreen3D,\n })),\n);\n\n// 150ms delay to allow WebGL context cleanup between full-screen 3D scene transitions\nconst SCREEN_TRANSITION_DELAY_MS = 150;\n// 100ms delay for lighter menu/UI transitions where WebGL teardown/re-init cost is lower\nconst MENU_TRANSITION_DELAY_MS = 100;\n\nfunction App() {\n const [gameMode, setGameMode] = useState<GameMode | null>(null);\n const [selectedArchetype, setSelectedArchetype] = useState<PlayerArchetype>(\n PlayerArchetype.MUSA,\n );\n const [isGameActive, setIsGameActive] = useState(false);\n const [gameWinner, setGameWinner] = useState<PlayerState | null>(null);\n const [matchStats, setMatchStats] = useState<MatchStatistics | null>(null);\n const [appReady, setAppReady] = useState(false);\n const [showSplash, setShowSplash] = useState(true);\n const [showAudioError, setShowAudioError] = useState(false);\n // Performance debug overlay toggle (P key in dev mode)\n const [showPerformanceDebug, setShowPerformanceDebug] = useState(false);\n // Transition state to allow WebGL cleanup between screens\n const [isTransitioning, setIsTransitioning] = useState(false);\n const pendingModeRef = useRef<{\n mode: GameMode;\n archetype?: PlayerArchetype;\n } | null>(null);\n\n // Combat players state - managed here so updates persist\n const [combatPlayers, setCombatPlayers] = useState<PlayerState[]>([]);\n\n const audio = useAudio();\n\n // Add responsive screen size detection with proper device detection\n // Uses user-agent detection first for high-res mobile devices\n const [screenSize, setScreenSize] = useState(() => {\n const platform = detectPlatform();\n return {\n width: window.innerWidth,\n height: window.innerHeight,\n isMobile: platform.isMobile,\n isTablet: platform.isTablet,\n isDesktop: platform.isDesktop,\n };\n });\n\n useEffect(() => {\n // Define handlers outside async function for proper cleanup\n const handleGlobalError = (e: ErrorEvent) => {\n console.error(\"Global error:\", e.error);\n };\n\n const handleUnhandledRejection = (e: PromiseRejectionEvent) => {\n console.error(\"Unhandled promise rejection:\", e.reason);\n if (\n e.reason?.message?.includes(\"Failed to load\") ||\n e.reason?.message?.includes(\"no supported source\")\n ) {\n e.preventDefault();\n }\n };\n\n const initializeApp = async () => {\n try {\n window.focus();\n\n window.addEventListener(\"error\", handleGlobalError);\n window.addEventListener(\"unhandledrejection\", handleUnhandledRejection);\n\n // PHASE 2: Performance optimization initialization\n console.log(\"🔧 Initializing animation performance optimizations...\");\n\n // 1. Prewarm object pools for animation optimization\n // This eliminates GC pressure from ~1,344 allocations per frame\n const { ThreeObjectPools } = await import(\"./utils/threeObjectPool\");\n ThreeObjectPools.prewarmAll();\n const poolStatus = ThreeObjectPools.getStatus();\n console.log(\" ✓ Object pools prewarmed:\", poolStatus);\n\n // 2. Precompute all animations for 90%+ cache hit rate\n const { precomputeAnimation } =\n await import(\"./systems/animation/core/AnimationOptimizations\");\n const { ALL_ANIMATIONS } =\n await import(\"./systems/animation/core/AnimationRegistry\");\n\n let precomputedCount = 0;\n ALL_ANIMATIONS.forEach((animation) => {\n // Precompute at 60fps for smooth playback\n // Use animation.name as the unique identifier\n precomputeAnimation(animation.name, animation, 60);\n precomputedCount++;\n });\n console.log(` ✓ Precomputed ${precomputedCount} animations at 60fps`);\n\n console.log(\n \"✅ Animation optimizations ready (expect <5ms frame time, 90%+ cache hit)\",\n );\n\n setAppReady(true);\n console.log(\"🎯 Black Trigram app initialized\");\n } catch (error) {\n console.error(\"Failed to initialize app:\", error);\n setAppReady(true);\n }\n };\n\n initializeApp();\n\n // Cleanup global event handlers to prevent memory leaks\n return () => {\n window.removeEventListener(\"error\", handleGlobalError);\n window.removeEventListener(\n \"unhandledrejection\",\n handleUnhandledRejection,\n );\n };\n }, []);\n\n // Shared audio initialization logic for splash and retry\n const initializeAudioWithRetry = useCallback(async () => {\n if (!appReady) {\n console.warn(\"App not ready yet, please wait...\");\n return false;\n }\n try {\n await audio.initializeAudio();\n console.log(\"🎵 Audio initialized\");\n return true;\n } catch (error) {\n console.error(\"Failed to initialize audio:\", error);\n return false;\n }\n }, [audio, appReady]);\n\n // Handle splash screen start - initialize audio on user gesture\n const handleSplashStart = useCallback(async () => {\n setShowAudioError(false);\n const success = await initializeAudioWithRetry();\n if (success) {\n setShowSplash(false);\n } else {\n setShowAudioError(true);\n }\n }, [initializeAudioWithRetry]);\n\n const handleAudioErrorRetry = useCallback(async () => {\n setShowAudioError(false);\n const success = await initializeAudioWithRetry();\n if (success) {\n setShowSplash(false);\n } else {\n setShowAudioError(true);\n }\n }, [initializeAudioWithRetry]);\n\n const handleAudioErrorContinue = useCallback(() => {\n // Continue without sound\n setShowAudioError(false);\n setShowSplash(false);\n console.log(\"Continuing without audio (silent mode)\");\n }, []);\n\n // ✅ SIMPLIFIED: Handle game mode selection directly\n const handleGameStart = useCallback(\n (mode: GameMode, archetype?: PlayerArchetype) => {\n console.log(\"🎮 Starting game mode:\", mode, \"with archetype:\", archetype);\n\n // Store pending mode and start transition to allow WebGL cleanup\n pendingModeRef.current = { mode, archetype };\n setIsTransitioning(true);\n\n // Clear current mode first (unmounts Canvas)\n setGameMode(null);\n setIsGameActive(false);\n\n // After brief delay, mount new screen\n // Increased delay to allow proper WebGL context cleanup\n setTimeout(() => {\n const pending = pendingModeRef.current;\n if (!pending) return;\n\n // ✅ NEW: Handle controls and philosophy as separate modes\n if (\n pending.mode === GameMode.CONTROLS ||\n pending.mode === GameMode.PHILOSOPHY\n ) {\n setGameMode(pending.mode);\n setIsGameActive(false); // These are not game modes, just screens\n } else {\n setGameMode(pending.mode);\n setIsGameActive(true);\n }\n\n setGameWinner(null);\n setMatchStats(null);\n if (pending.archetype) {\n setSelectedArchetype(pending.archetype);\n }\n\n setIsTransitioning(false);\n pendingModeRef.current = null;\n }, SCREEN_TRANSITION_DELAY_MS); // Delay for WebGL cleanup\n },\n [],\n );\n\n const handleGameEnd = useCallback(\n (winner: number) => {\n setIsGameActive(false);\n setGameWinner(createPlayerFromArchetype(selectedArchetype, winner));\n // Reset combat players for next match\n setCombatPlayers([]);\n\n setMatchStats({\n totalDamageDealt: 150,\n totalDamageTaken: 100,\n criticalHits: 3,\n vitalPointHits: 2,\n techniquesUsed: 8,\n perfectStrikes: 1,\n consecutiveWins: 1,\n matchDuration: 120,\n totalMatches: 1,\n maxRounds: 3,\n winner: winner,\n totalRounds: 2,\n currentRound: 2,\n timeRemaining: 0,\n combatEvents: [],\n finalScore: {\n player1: winner === 0 ? 2 : 0,\n player2: winner === 1 ? 2 : 0,\n },\n roundsWon: {\n player1: winner === 0 ? 2 : 0,\n player2: winner === 1 ? 2 : 0,\n },\n player1: {\n wins: winner === 0 ? 1 : 0,\n losses: winner === 0 ? 0 : 1,\n hitsTaken: 5,\n hitsLanded: 8,\n totalDamageDealt: winner === 0 ? 150 : 100,\n totalDamageReceived: winner === 0 ? 100 : 150,\n techniques: [\"천둥벽력\", \"유수연타\"],\n perfectStrikes: winner === 0 ? 1 : 0,\n vitalPointHits: winner === 0 ? 2 : 1,\n consecutiveWins: winner === 0 ? 1 : 0,\n matchDuration: 120,\n },\n player2: {\n wins: winner === 1 ? 1 : 0,\n losses: winner === 1 ? 0 : 1,\n hitsTaken: 8,\n hitsLanded: 5,\n totalDamageDealt: winner === 1 ? 150 : 100,\n totalDamageReceived: winner === 1 ? 100 : 150,\n techniques: [\"화염지창\", \"벽력일섬\"],\n perfectStrikes: winner === 1 ? 1 : 0,\n vitalPointHits: winner === 1 ? 2 : 1,\n consecutiveWins: winner === 1 ? 1 : 0,\n matchDuration: 120,\n },\n });\n },\n [selectedArchetype],\n );\n\n const handleReturnToMenu = useCallback(() => {\n // Use same transition logic for return to menu\n setIsTransitioning(true);\n setGameMode(null);\n setIsGameActive(false);\n setGameWinner(null);\n setMatchStats(null);\n // Reset combat players so they reinitialize next combat\n setCombatPlayers([]);\n setTimeout(() => setIsTransitioning(false), MENU_TRANSITION_DELAY_MS);\n }, []);\n\n const handleRematch = useCallback(() => {\n // Restart combat with same settings\n if (!gameMode) {\n console.error(\n \"Cannot rematch: gameMode is not set. This should not happen - EndScreen only renders when gameMode is set.\",\n { gameMode, isGameActive, gameWinner, matchStats }\n );\n return;\n }\n \n setIsTransitioning(true);\n setGameWinner(null);\n setMatchStats(null);\n // Reset combat players so they reinitialize for rematch\n setCombatPlayers([]);\n \n setTimeout(() => {\n setIsGameActive(true);\n setIsTransitioning(false);\n }, SCREEN_TRANSITION_DELAY_MS);\n // eslint-disable-next-line react-hooks/exhaustive-deps -- isGameActive, gameWinner, matchStats only used in error logging, not function logic\n }, [gameMode]);\n\n const handleViewTraining = useCallback(() => {\n // Navigate to training mode\n setIsTransitioning(true);\n setGameWinner(null);\n setMatchStats(null);\n setCombatPlayers([]);\n \n setTimeout(() => {\n setGameMode(GameMode.TRAINING);\n setIsGameActive(true);\n setIsTransitioning(false);\n }, SCREEN_TRANSITION_DELAY_MS);\n }, []);\n\n const renderCurrentScreen = () => {\n // Show loading during screen transitions\n if (isTransitioning) {\n return (\n <LoadingState\n progress={undefined}\n message=\"전환 중... | Transitioning...\"\n stage=\"assets\"\n />\n );\n }\n\n if (gameWinner && matchStats) {\n // ✅ NEW: Use EndScreen3D component\n return (\n <EndScreen3D\n winner={gameWinner}\n matchStats={matchStats}\n onReturnToMenu={handleReturnToMenu}\n onRematch={handleRematch}\n onViewReplay={handleViewTraining}\n width={screenSize.width}\n height={screenSize.height}\n />\n );\n }\n\n // ✅ NEW: Handle standalone screens first\n if (gameMode === GameMode.CONTROLS) {\n return (\n <ControlsScreen\n onReturnToMenu={handleReturnToMenu}\n width={screenSize.width}\n height={screenSize.height}\n />\n );\n }\n\n if (gameMode === GameMode.PHILOSOPHY) {\n return (\n <PhilosophyScreen\n onReturnToMenu={handleReturnToMenu}\n width={screenSize.width}\n height={screenSize.height}\n />\n );\n }\n\n // ✅ SIMPLIFIED: Only active game modes use isGameActive\n if (isGameActive && gameMode) {\n switch (gameMode) {\n case GameMode.TRAINING:\n return (\n <Suspense\n fallback={\n <LoadingState\n progress={undefined}\n message=\"훈련장 로딩 중... | Loading Training...\"\n stage=\"assets\"\n />\n }\n >\n <TrainingScreen\n onPlayerUpdate={(updates) => {\n console.log(\"Training player updated:\", updates);\n }}\n onReturnToMenu={handleReturnToMenu}\n width={screenSize.width}\n height={screenSize.height}\n initialArchetype={selectedArchetype}\n />\n </Suspense>\n );\n case GameMode.VERSUS:\n case GameMode.PRACTICE:\n // Initialize players if not already set\n if (combatPlayers.length === 0) {\n const player1 = createPlayerFromArchetype(selectedArchetype, 0);\n const player2 = createPlayerFromArchetype(\n PlayerArchetype.AMSALJA,\n 1,\n );\n // Use setTimeout to defer state update and avoid render-during-render\n setTimeout(() => setCombatPlayers([player1, player2]), 0);\n // Return loading state while players initialize\n return (\n <LoadingState\n progress={undefined}\n message=\"전투 준비 중... | Preparing Combat...\"\n stage=\"assets\"\n />\n );\n }\n\n return (\n <CombatScreen\n players={combatPlayers}\n currentRound={1}\n timeRemaining={180}\n isPaused={false}\n onPlayerUpdate={(playerIndex, updates) => {\n // Actually update the player state so damage persists!\n setCombatPlayers((prevPlayers) => {\n const newPlayers = [...prevPlayers];\n if (newPlayers[playerIndex]) {\n newPlayers[playerIndex] = {\n ...newPlayers[playerIndex],\n ...updates,\n };\n }\n return newPlayers;\n });\n }}\n onReturnToMenu={handleReturnToMenu}\n onGameEnd={handleGameEnd}\n gameMode={gameMode}\n width={screenSize.width}\n height={screenSize.height}\n />\n );\n default:\n return (\n <IntroScreen\n onMenuSelect={handleGameStart}\n onArchetypeSelect={setSelectedArchetype}\n selectedArchetype={selectedArchetype}\n width={screenSize.width}\n height={screenSize.height}\n />\n );\n }\n }\n\n // ✅ SIMPLIFIED: Default to intro screen\n return (\n <IntroScreen\n onMenuSelect={handleGameStart}\n onArchetypeSelect={setSelectedArchetype}\n selectedArchetype={selectedArchetype}\n width={screenSize.width}\n height={screenSize.height}\n />\n );\n };\n\n const containerRef = useRef<HTMLDivElement>(null);\n\n useEffect(() => {\n containerRef.current?.focus();\n }, [appReady]);\n\n useEffect(() => {\n const handleResize = () => {\n // Clear cached platform info to get fresh detection on resize\n clearPlatformCache();\n const platform = detectPlatform();\n setScreenSize({\n width: platform.screenWidth,\n height: platform.screenHeight,\n isMobile: platform.isMobile,\n isTablet: platform.isTablet,\n isDesktop: platform.isDesktop,\n });\n };\n\n window.addEventListener(\"resize\", handleResize);\n return () => window.removeEventListener(\"resize\", handleResize);\n }, []);\n\n // F9 key toggle for performance debug overlay (dev mode only)\n // Note: P key is reserved for Philosophy screen\n useEffect(() => {\n if (!import.meta.env.DEV) return;\n\n const handleKeyDown = (e: KeyboardEvent) => {\n if (e.key === \"F9\") {\n e.preventDefault();\n setShowPerformanceDebug((prev) => !prev);\n }\n };\n\n window.addEventListener(\"keydown\", handleKeyDown);\n return () => window.removeEventListener(\"keydown\", handleKeyDown);\n }, []);\n\n if (!appReady) {\n return (\n <div className=\"app loading\" data-testid=\"app-container\">\n <LoadingState\n progress={undefined}\n message=\"앱 초기화 중 | Initializing app...\"\n stage=\"initialization\"\n />\n </div>\n );\n }\n\n // Show splash screen first to get user gesture for audio\n if (showSplash) {\n return (\n <div className=\"app\" data-testid=\"app-container\">\n <SplashScreen\n onStart={handleSplashStart}\n width={screenSize.width}\n height={screenSize.height}\n />\n {showAudioError && (\n <ErrorModal\n message=\"오디오 초기화에 실패했습니다. 재시도하거나 소리 없이 계속할 수 있습니다. | Audio initialization failed. You can retry or continue without sound.\"\n onRetry={handleAudioErrorRetry}\n onContinue={handleAudioErrorContinue}\n />\n )}\n </div>\n );\n }\n\n return (\n <div\n className=\"app\"\n tabIndex={0}\n ref={containerRef}\n data-testid=\"app-container\"\n >\n {/* All screens now use Three.js or pure React/HTML */}\n {renderCurrentScreen()}\n\n {/* Performance debug overlay (dev mode only, toggle with P key) */}\n {showPerformanceDebug && <PerformanceDebugOverlayHtml />}\n </div>\n );\n}\n\nexport default App;\n"],"mappings":"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"}
|