8.1 KiB
Current Architecture
This document describes the code that exists today in the repository.
Runtime Structure
src/main.tsxmounts React inStrictMode.src/App.tsxmounts TanStackRouterProvider.src/router.tsxdeclares/,/editor, and/docs.src/pages/page.tsxcomposes the playable route withHandTrackingProvider, React Three FiberCanvas,World,DebugPerf,GameUI, andSceneLoadingOverlay.src/pages/editor/page.tsxcomposes the local editor route.src/components/docs/DocsLayout.tsxcomposes the in-app documentation route.
Detailed runtime-loading notes live in docs/technical/scene-runtime.md.
World Composition
src/world/World.tsx is the main 3D scene composer.
Always-mounted scene systems:
EnvironmentLighting- debug helpers when
?debugis active - optional hand-tracking glove overlays
- optional debug camera controls
Game scene systems:
GameMap- Rapier
PhysicswrappingGameStageContent GameMusicGameDialoguesGameCinematicsonly whilemainState === "outro"Playerafter gameplay is ready
Debug physics scene systems:
TestMapPlayerafter the debug octree is ready
Debug scene and camera mode are controlled by src/utils/debug/Debug.ts and enabled with ?debug.
Scene Loading
The production game scene is considered ready only after:
- map data and visible map nodes have settled
- collision source models have settled
- the player octree exists
- the Rapier gameplay stage has mounted
The player is not spawned until that readiness gate is satisfied. This avoids starting player movement, music, dialogue timing, and interactions while the map/stage is still loading.
Physics Boundaries
The project currently uses two collision systems with separate responsibilities:
- Player movement uses a Three.js
Capsuleand anOctree. - Gameplay objects use Rapier rigid bodies and colliders.
GameMapCollision builds the player octree from explicit collision nodes. It currently uses only the terrain node.
GameStageContent is wrapped in Rapier Physics so repair cases, triggers, and grabbable parts can use physics without migrating the player controller to Rapier.
This split is deliberate. It keeps the player controller simple while still enabling physical manipulation for gameplay objects.
Gameplay Layer
The current core gameplay feature is the reusable repair game.
Production placements live in:
src/world/GameStageContent.tsx
The reusable flow lives in:
src/components/three/gameplay/RepairGame.tsx
Mission-specific data lives in:
src/data/gameplay/repairMissions.ts
The repair game supports:
locked -> waiting -> inspected -> fragmented -> scanning -> repairing -> reassembling -> done
Detailed repair-game implementation notes live in docs/technical/repair-game.md.
State Management
Durable progression state lives in:
src/managers/stores/useGameStore.ts
It owns:
mainState- intro state
ebike,pylon, andfarmmission state- outro state
isCinematicPlaying- progression actions
- generic mission actions
Settings state lives in:
src/managers/stores/useSettingsStore.ts
Subtitle display state lives in:
src/managers/stores/useSubtitleStore.ts
Detailed Zustand notes live in docs/technical/zustand.md.
Managers
Managers are used for imperative runtime systems that own browser or frame-adjacent objects.
Current managers:
src/managers/AudioManager.tssrc/managers/InteractionManager.ts
AudioManager owns HTMLAudioElement instances, music playback, one-shot pools, category volumes, and optional stereo panning.
InteractionManager owns focused/nearby/holding state for trigger and grab interactions and exposes a snapshot through useSyncExternalStore.
Interaction Model
Core interaction files:
src/components/three/interaction/InteractableObject.tsxsrc/components/three/interaction/TriggerObject.tsxsrc/components/three/interaction/GrabbableObject.tsxsrc/hooks/interaction/useInteraction.tssrc/components/ui/InteractPrompt.tsx
The player controller bridges raw input to semantic interaction actions:
Etriggers focused trigger objects- primary mouse button grabs focused grabbable objects
- hand tracking can grab hand-controlled grabbable objects
Detailed interaction notes live in docs/technical/interaction.md.
Audio, Dialogue, And Subtitles
Audio is split into:
musicsfxdialogue
Runtime dialogue data lives under:
public/sounds/dialogue/
The current subtitle model is one SRT file per voice and language. A dialogue entry references one cue by subtitleCueIndex.
src/utils/dialogues/playDialogue.ts queues dialogue playback and synchronizes the active subtitle cue against the playing audio element.
Detailed audio notes live in docs/technical/audio.md.
Cinematics
Runtime cinematic data lives in:
public/cinematics.json
Cinematics support camera keyframes, GSAP timelines, optional dialogue cues, and isCinematicPlaying input locking. Current world integration mounts GameCinematics only during the outro state.
Hand Tracking
Hand tracking can use:
- local Python backend over WebSocket
- browser-side MediaPipe through
@mediapipe/tasks-vision
Important files:
src/providers/gameplay/HandTrackingProvider.tsxsrc/hooks/handTracking/useRemoteHandTracking.tssrc/hooks/handTracking/useBrowserHandTracking.tssrc/hooks/handTracking/useBothFistsHold.tssrc/components/three/handTracking/HandTrackingGlove.tsxbackend/main.py
Hand tracking is activated lazily. In production it is enabled during repair steps that need hand input. In debug physics mode it is enabled when interaction context makes hand input useful.
Detailed hand-tracking notes live in docs/technical/hand-tracking.md.
Editor System
The editor route is:
/editor
Important editor files:
src/pages/editor/page.tsxsrc/components/editor/EditorControls.tsxsrc/components/editor/scene/EditorScene.tsxsrc/components/editor/scene/EditorMap.tsxsrc/components/editor/EditorDialogueManifestPanel.tsxsrc/components/editor/EditorCinematicManifestPanel.tsxsrc/components/editor/EditorSrtPanel.tsxsrc/hooks/editor/useEditorSceneData.tssrc/hooks/editor/useEditorHistory.tssrc/controls/editor/FlyController.tsx
The editor shares MapNode data with the runtime map loader.
Local save endpoints live in vite.config.ts:
POST /api/save-mapPOST /api/save-srtGET /api/validate-dialoguesPOST /api/save-dialoguesPOST /api/save-cinematics
These are Vite dev-server helpers, not production backend APIs.
Detailed editor notes live in docs/technical/editor.md.
Documentation System
The docs route uses:
src/components/docs/DocsLayout.tsxsrc/components/docs/DocsDocument.tsxsrc/data/docs/docsSections.tssrc/routes/DocsRoute.tsxsrc/pages/docs/**/page.tsx
Docs pages import Markdown files with ?raw and render them through react-markdown plus remark-gfm.
3D Component Domains
src/components/three/ is organized by domain:
gameplay: repair-game flow and repair componentshandTracking: glove overlaysinteraction: trigger/grab/focus wrappersmodels: animated, simple, and explodable model helpersworld: world/environment objects
Map Data
Runtime map data:
public/map.json
Expected shape:
interface MapNode {
name: string;
type: string;
position: [number, number, number];
rotation: [number, number, number];
scale: [number, number, number];
}
Each name maps to:
public/models/{name}/model.glb
public/models/{name}/model.gltf
Current Limitations
- The repository is still a prototype.
- There is no central production
GameManager. - The repair game is implemented, but broader mission orchestration is still light.
useRepairMovementLocked()locks player movement during focused repair steps.- Player collision and Rapier gameplay physics are separate systems.
- Editor persistence is local development tooling only.
- Debug systems are still part of active scene composition and should remain easy to identify.