refactor: clean map gameplay architecture
This commit is contained in:
@@ -19,7 +19,7 @@ La-Fabrik est une expérience web 3D en React, Vite, Three.js et React Three Fib
|
||||
Le joueur est dans un monde 3D et avance dans une progression de réparation :
|
||||
|
||||
```txt
|
||||
intro -> bike -> pylone -> ferme -> outro
|
||||
intro -> ebike -> pylon -> farm -> outro
|
||||
```
|
||||
|
||||
Les trois piliers à connaître pour la review :
|
||||
@@ -62,7 +62,7 @@ HomePage
|
||||
-> World
|
||||
-> GameMap
|
||||
-> GameStageContent
|
||||
-> RepairGame bike/pylone/ferme
|
||||
-> RepairGame ebike/pylon/farm
|
||||
-> GameMusic
|
||||
-> GameDialogues
|
||||
-> Player
|
||||
@@ -324,7 +324,7 @@ Ouvrir dans cet ordre :
|
||||
`RepairGame` reçoit une mission :
|
||||
|
||||
```tsx
|
||||
<RepairGame mission="bike" position={[8, 0, -6]} />
|
||||
<RepairGame mission="ebike" position={[42.2399, 4.5484, 34.6468]} />
|
||||
```
|
||||
|
||||
Puis il vérifie :
|
||||
@@ -347,7 +347,7 @@ Les variations mission sont dans `repairMissions.ts` :
|
||||
### Pourquoi c'est bien
|
||||
|
||||
- Un seul flow réutilisable.
|
||||
- Moins de duplication entre `bike`, `pylone`, `ferme`.
|
||||
- Moins de duplication entre `ebike`, `pylon`, `farm`.
|
||||
- Les règles générales restent dans les composants.
|
||||
- Les variations restent dans la data.
|
||||
- Le debug panel peut tester les mêmes steps que le vrai jeu.
|
||||
@@ -471,9 +471,9 @@ Main states :
|
||||
|
||||
```txt
|
||||
intro
|
||||
bike
|
||||
pylone
|
||||
ferme
|
||||
ebike
|
||||
pylon
|
||||
farm
|
||||
outro
|
||||
```
|
||||
|
||||
|
||||
@@ -107,7 +107,7 @@ It owns:
|
||||
|
||||
- `mainState`
|
||||
- intro state
|
||||
- `bike`, `pylone`, and `ferme` mission state
|
||||
- `ebike`, `pylon`, and `farm` mission state
|
||||
- outro state
|
||||
- `isCinematicPlaying`
|
||||
- progression actions
|
||||
|
||||
@@ -1,164 +0,0 @@
|
||||
# Game Flow - La Fabrik
|
||||
|
||||
## Étapes du jeu
|
||||
|
||||
```
|
||||
intro → start-intro → naming → bienvenue → star-move → mission2 → searching_problem → preparation → outOfFabrik
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Détail des étapes
|
||||
|
||||
### 1. `intro` (initial)
|
||||
|
||||
- État initial au chargement du jeu
|
||||
- Aucune action, juste une étape de départ
|
||||
- Transition automatique vers `start-intro`
|
||||
|
||||
### 2. `start-intro`
|
||||
|
||||
- **Déclenchement** : Auto-transition depuis `intro` quand la scène est chargée
|
||||
- **Action** : Joue l'audio d'intro (`intro`)
|
||||
- **Attente** : Attend que l'audio se termine
|
||||
- **Transition** : Vers `naming` quand l'audio se termine
|
||||
|
||||
### 3. `naming`
|
||||
|
||||
- **Déclenchement** : Quand l'audio d'intro se termine
|
||||
- **Action** : Affiche un input pour demander le prénom du joueur
|
||||
- **Attente** : L'utilisateur entre son prénom et valide
|
||||
- **Transition** : Vers `bienvenue` quand l'utilisateur valide
|
||||
|
||||
### 4. `bienvenue`
|
||||
|
||||
- **Déclenchement** : Quand l'utilisateur valide son prénom
|
||||
- **Actions** :
|
||||
- Affiche "Bienvenue {prénom} !" à l'écran
|
||||
- Joue l'audio de bienvenue
|
||||
- **Attente** : Attend que l'audio se termine
|
||||
- **Transition** : Vers `star-move` quand l'audio se termine
|
||||
|
||||
### 5. `star-move`
|
||||
|
||||
- **Déclenchement** : Quand l'audio de bienvenue se termine
|
||||
- **Action** : Active le mouvement du joueur (`setCanMove(true)`)
|
||||
- **État** : Le joueur peut maintenant se déplacer librement
|
||||
- **Zone** : La détection de zone devient active (ZoneDetection)
|
||||
|
||||
### 6. `mission2`
|
||||
|
||||
- **Déclenchement** : Quand le joueur entre dans la zone `fabrikExit` (position: `[-5, 25, -15]`)
|
||||
- **Actions** :
|
||||
- Stocke `activityCity: false` dans le store Zustand
|
||||
- Joue l'audio `alertCentral`
|
||||
- **État** : Les systèmes lisent `activityCity` depuis `useGameStore` pour adapter leur comportement
|
||||
- **Attente** : Le joueur atteint la zone de trigger pour `searching_problem`
|
||||
|
||||
### 7. `searching_problem`
|
||||
|
||||
- **Déclenchement** : Quand le joueur entre dans la zone `searchingProblemZone` (position: `[-5, 25, -30]`)
|
||||
- **Actions** :
|
||||
- Joue l'audio `searchingProblem`
|
||||
- Affiche l'objet "central" (position: `[1, 15, -45]`)
|
||||
- **Attente** : Le joueur interagit avec l'objet "central"
|
||||
|
||||
### 8. `preparation`
|
||||
|
||||
- **Déclenchement** : Quand le joueur interagit avec l'objet "central"
|
||||
- **Actions** :
|
||||
- Bloque le mouvement (`setCanMove(false)`)
|
||||
- Cache l'objet "central"
|
||||
|
||||
### 9. `outOfFabrik`
|
||||
|
||||
- **Déclenchement** : (non implémenté pour le moment)
|
||||
- **Action** : Transition vers l'étape finale
|
||||
|
||||
---
|
||||
|
||||
## Fichiers clés
|
||||
|
||||
| Fichier | Rôle |
|
||||
| --------------------------------------- | --------------------------------------------------------- |
|
||||
| `src/managers/stores/useGameStore.ts` | Store Zustand pour l'état global du jeu |
|
||||
| `src/components/game/GameFlow.tsx` | Gère les transitions automatiques et la lecture audio |
|
||||
| `src/components/ui/IntroUI.tsx` | Affiche l'input pour le prénom et le message de bienvenue |
|
||||
| `src/components/zone/ZoneDetection.tsx` | Détecte quand le joueur entre dans une zone |
|
||||
| `src/world/GameStageContent.tsx` | Monte les contenus de mission dans la scène |
|
||||
| `src/data/audioConfig.ts` | Chemins des fichiers audio |
|
||||
| `src/data/zones.ts` | Configuration des zones de transition |
|
||||
|
||||
---
|
||||
|
||||
## Configuration audio
|
||||
|
||||
```typescript
|
||||
// src/data/audioConfig.ts
|
||||
export const AUDIO_PATHS = {
|
||||
intro: "/sounds/fa.mp3",
|
||||
bienvenue: "/sounds/fa.mp3",
|
||||
alertCentral: "/sounds/fa.mp3",
|
||||
searchingProblem: "/sounds/fa.mp3",
|
||||
};
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Configuration des zones
|
||||
|
||||
```typescript
|
||||
// src/data/zones.ts
|
||||
export const ZONES: Zone[] = [
|
||||
{
|
||||
id: "fabrikExit",
|
||||
position: [-5, 25, -15],
|
||||
radius: 10,
|
||||
height: 20,
|
||||
targetStep: "mission2",
|
||||
},
|
||||
{
|
||||
id: "searchingProblemZone",
|
||||
position: [-5, 25, -30],
|
||||
radius: 10,
|
||||
height: 20,
|
||||
targetStep: "searching_problem",
|
||||
},
|
||||
];
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Store Zustand
|
||||
|
||||
```typescript
|
||||
// src/managers/stores/useGameStore.ts
|
||||
interface GameState {
|
||||
mainState: MainGameState;
|
||||
missionFlow: {
|
||||
activityCity: boolean;
|
||||
canMove: boolean;
|
||||
playerName: string;
|
||||
};
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Debug
|
||||
|
||||
En mode debug (`?debug` dans l'URL), on peut voir :
|
||||
|
||||
- **Game Step** : L'étape actuelle dans le panneau lil-gui
|
||||
- **Player Position** : Position X, Y, Z du joueur en temps réel
|
||||
- **Zone Visualization** : Anneaux visuels au sol pour les zones + cylindres transparents
|
||||
|
||||
---
|
||||
|
||||
## Notes techniques
|
||||
|
||||
- Le mouvement du joueur est bloqué tant que `canMove` est `false`
|
||||
- Le store Zustand (`useGameStore`) est la source principale de vérité
|
||||
- `GameStepManager` synchronise automatiquement avec le store Zustand lors des transitions
|
||||
- Les transitions via les zones utilisent `GameStepManager.transitionTo()` qui met à jour le store
|
||||
- L'audio utilise un callback `onEnded` pour déclencher les transitions automatiques
|
||||
@@ -32,7 +32,7 @@ This keeps hand tracking active while the player is inside an interaction zone,
|
||||
|
||||
The production repair activation conditions are:
|
||||
|
||||
- active `mainState` is `bike`, `pylone`, or `ferme`
|
||||
- active `mainState` is `ebike`, `pylon`, or `farm`
|
||||
- the active mission step is `inspected`, `repairing`, `reassembling`, or `done`
|
||||
|
||||
This keeps the webcam off during `waiting`, `fragmented`, and `scanning`, then enables hand input only when the repair flow is expected to use hands.
|
||||
|
||||
@@ -8,11 +8,11 @@ The repair game is the current core gameplay loop. It gives three missions the s
|
||||
|
||||
Implemented missions:
|
||||
|
||||
| Mission | Object | Role |
|
||||
| -------- | ------------- | --------------------------------------------- |
|
||||
| `bike` | E-bike | Repair a damaged cooling core |
|
||||
| `pylone` | Power pylon | Restore relay/panel-like broken parts |
|
||||
| `ferme` | Vertical farm | Stabilize irrigation/sensor-like broken parts |
|
||||
| Mission | Object | Role |
|
||||
| ------- | ------------- | --------------------------------------------- |
|
||||
| `ebike` | E-bike | Repair a damaged cooling core |
|
||||
| `pylon` | Power pylon | Restore relay/panel-like broken parts |
|
||||
| `farm` | Vertical farm | Stabilize irrigation/sensor-like broken parts |
|
||||
|
||||
## Main Files
|
||||
|
||||
@@ -79,7 +79,7 @@ src/managers/stores/useGameStore.ts
|
||||
- `setMissionStep(mission, nextStep)`
|
||||
- `completeMission(mission)`
|
||||
|
||||
The important architectural choice is that reusable repair components do not call `setBikeState`, `setPyloneState`, or `setFermeState` directly. They use generic mission actions so the same component can run for all three missions.
|
||||
The important architectural choice is that reusable repair components do not call `setEbikeState`, `setPylonState`, or `setFarmState` directly. They use generic mission actions so the same component can run for all three missions.
|
||||
|
||||
## Data-Driven Mission Config
|
||||
|
||||
@@ -324,9 +324,9 @@ src/world/GameStageContent.tsx
|
||||
Current positions:
|
||||
|
||||
```tsx
|
||||
<RepairGame mission="bike" position={[8, 0, -6]} />
|
||||
<RepairGame mission="pylone" position={[64, 0, -66]} />
|
||||
<RepairGame mission="ferme" position={[-24, 0, 42]} />
|
||||
<RepairGame mission="ebike" position={[42.2399, 4.5484, 34.6468]} />
|
||||
<RepairGame mission="pylon" position={[64, 0, -66]} />
|
||||
<RepairGame mission="farm" position={[-24, 0, 42]} />
|
||||
```
|
||||
|
||||
Only the repair game whose `mission` matches `useGameStore().mainState` renders active content.
|
||||
|
||||
+17
-17
@@ -65,18 +65,18 @@ Main states:
|
||||
| Main state | Role |
|
||||
| ---------- | ------------------------------- |
|
||||
| `intro` | Onboarding and opening sequence |
|
||||
| `bike` | E-bike repair sequence |
|
||||
| `pylone` | Power pylon repair sequence |
|
||||
| `ferme` | Vertical farm repair sequence |
|
||||
| `ebike` | E-bike repair sequence |
|
||||
| `pylon` | Power pylon repair sequence |
|
||||
| `farm` | Vertical farm repair sequence |
|
||||
| `outro` | Ending sequence |
|
||||
|
||||
Other important state:
|
||||
|
||||
- `isCinematicPlaying`
|
||||
- `intro`
|
||||
- `bike`
|
||||
- `pylone`
|
||||
- `ferme`
|
||||
- `ebike`
|
||||
- `pylon`
|
||||
- `farm`
|
||||
- `outro`
|
||||
|
||||
Mission steps:
|
||||
@@ -125,28 +125,28 @@ For development and debug tooling, direct setters also exist:
|
||||
```ts
|
||||
const setMainState = useGameStore((state) => state.setMainState);
|
||||
|
||||
setMainState("bike");
|
||||
setMainState("ebike");
|
||||
```
|
||||
|
||||
Direct setters are useful for debug panels, but production gameplay should prefer business actions such as:
|
||||
|
||||
- `advanceGameState`
|
||||
- `completeBike`
|
||||
- `completePylone`
|
||||
- `completeFerme`
|
||||
- `completeEbike`
|
||||
- `completePylon`
|
||||
- `completeFarm`
|
||||
- `completeMission`
|
||||
|
||||
Mission gameplay that can target `bike`, `pylone`, or `ferme` should prefer generic mission actions:
|
||||
Mission gameplay that can target `ebike`, `pylon`, or `farm` should prefer generic mission actions:
|
||||
|
||||
```ts
|
||||
const setMissionStep = useGameStore((state) => state.setMissionStep);
|
||||
const completeMission = useGameStore((state) => state.completeMission);
|
||||
|
||||
setMissionStep("bike", "inspected");
|
||||
completeMission("bike");
|
||||
setMissionStep("ebike", "inspected");
|
||||
completeMission("ebike");
|
||||
```
|
||||
|
||||
This keeps reusable gameplay components such as `RepairGame` from duplicating mission-specific branches like `setBikeState`, `setPyloneState`, and `setFermeState`.
|
||||
This keeps reusable gameplay components such as `RepairGame` from duplicating mission-specific branches like `setEbikeState`, `setPylonState`, and `setFarmState`.
|
||||
|
||||
## Settings Store
|
||||
|
||||
@@ -188,9 +188,9 @@ State/actions:
|
||||
Current production repair placement:
|
||||
|
||||
```tsx
|
||||
<RepairGame mission="bike" position={[8, 0, -6]} />
|
||||
<RepairGame mission="pylone" position={[64, 0, -66]} />
|
||||
<RepairGame mission="ferme" position={[-24, 0, 42]} />
|
||||
<RepairGame mission="ebike" position={[42.2399, 4.5484, 34.6468]} />
|
||||
<RepairGame mission="pylon" position={[64, 0, -66]} />
|
||||
<RepairGame mission="farm" position={[-24, 0, 42]} />
|
||||
```
|
||||
|
||||
`RepairGame` reads the active mission step from the store and writes transitions through generic actions such as `setMissionStep` and `completeMission`.
|
||||
|
||||
@@ -63,12 +63,12 @@ This document lists the user-visible and developer-facing features implemented i
|
||||
|
||||
## Repair Gameplay
|
||||
|
||||
- Reusable `RepairGame` mounted for `bike`, `pylone`, and `ferme`
|
||||
- Reusable `RepairGame` mounted for `ebike`, `pylon`, and `farm`
|
||||
- Mission progression driven by Zustand and shared `MissionStep` types
|
||||
- Production repair positions:
|
||||
- `bike` at `[8, 0, -6]`
|
||||
- `pylone` at `[64, 0, -66]`
|
||||
- `ferme` at `[-24, 0, 42]`
|
||||
- `ebike` at `[42.2399, 4.5484, 34.6468]`
|
||||
- `pylon` at `[64, 0, -66]`
|
||||
- `farm` at `[-24, 0, 42]`
|
||||
- Debug physics repair playground zones for all three missions
|
||||
- Data-driven mission config in `src/data/gameplay/repairMissions.ts`
|
||||
- Mission flow: `locked -> waiting -> inspected -> fragmented -> scanning -> repairing -> reassembling -> done`
|
||||
@@ -95,7 +95,7 @@ This document lists the user-visible and developer-facing features implemented i
|
||||
## Game Progression Store
|
||||
|
||||
- Zustand `useGameStore` for durable gameplay progression
|
||||
- Main states: `intro`, `bike`, `pylone`, `ferme`, `outro`
|
||||
- Main states: `intro`, `ebike`, `pylon`, `farm`, `outro`
|
||||
- Per-mission repair step state
|
||||
- Per-mission completion flags
|
||||
- Generic mission helpers: `setMissionStep`, `completeMission`, `advanceGameState`, `rewindGameState`, `resetGame`
|
||||
|
||||
@@ -8,7 +8,7 @@ The main feature is a reusable repair flow mounted in the production game scene.
|
||||
|
||||
The current user flow is:
|
||||
|
||||
1. Enter a mission state such as `bike`, `pylone`, or `ferme`.
|
||||
1. Enter a mission state such as `ebike`, `pylon`, or `farm`.
|
||||
2. Move close to the active repair object in the game scene.
|
||||
3. Aim at the object and press the interaction key when prompted.
|
||||
4. The mission step moves from `waiting` to `inspected`.
|
||||
@@ -21,7 +21,7 @@ The current user flow is:
|
||||
11. Move each scanned broken part into a compatible placeholder so the damaged parts are stored in the case.
|
||||
12. Press `E` on the green install target to move to `reassembling`. Wrong parts turn the target red and cannot finish the repair.
|
||||
13. The exploded object animates back into its assembled form with completion particles, then moves to `done`.
|
||||
14. Press `E` on the completion target. The repair case closes, returns to the ground, disappears, then `completeMission` moves to the next mission or to `outro` after `ferme`.
|
||||
14. Press `E` on the completion target. The repair case closes, returns to the ground, disappears, then `completeMission` moves to the next mission or to `outro` after `farm`.
|
||||
|
||||
## Why It Matters
|
||||
|
||||
@@ -39,11 +39,11 @@ In `inspected`, `RepairGame` can also move to `fragmented`. Keyboard input goes
|
||||
|
||||
In `fragmented`, the repair object is rendered with `ExplodableModel`, then automatically advances to `scanning`. In `scanning`, the exploded model remains visible, a blue scan visual moves from part to part, and a red halo/wire marker plus the configured broken UI video stay attached to configured broken parts after the scanner reaches them. The scan matches configured broken parts by `nodeName` and reports diagnostics when a configured node is missing. In `repairing`, the case opens in a larger focused transform, `RepairCaseModel` traverses the case GLTF for empty nodes named `placeholder_*`, several grabbable replacement parts appear on those slot positions, and releasing a part near a slot snaps it into place with a short GSAP animation. Scanned broken parts are also rendered as grabbable objects and must be deposited into a compatible slot before the final install target validates. If `brokenParts[].caseSlotName` is configured, that broken part snaps only to the matching slot; otherwise it can use any available slot. If the current case asset has no slot nodes, the flow keeps using fallback focus positions and logs the fallback. Replacement parts show green or red placement feedback after snapping, broken parts show stored feedback after deposit, and the install target gives a short blocked feedback if the player tries to validate too early. The install target only validates when the configured correct replacement part is placed and all scanned broken parts have been deposited. In `reassembling`, the exploded model animates back into its assembled position with green completion particles before the flow moves to `done`. In `done`, the repaired object remains visible with a completion target; validating closes the repair case first, then plays the case exit animation before advancing the global mission progression.
|
||||
|
||||
The mission config now carries the mission-specific variations. `bike` repairs one cooling core, `pylone` scans and stores both the lamp relay and a damaged panel with slower scan/reassembly timing, and `ferme` scans and stores an irrigation pump plus humidity sensor with faster scan/reassembly timing.
|
||||
The mission config now carries the mission-specific variations. `ebike` repairs one cooling core, `pylon` scans and stores both the lamp relay and a damaged panel with slower scan/reassembly timing, and `farm` scans and stores an irrigation pump plus humidity sensor with faster scan/reassembly timing.
|
||||
|
||||
## Key Files
|
||||
|
||||
- `src/world/GameStageContent.tsx` mounts production `RepairGame` instances for `bike`, `pylone`, and `ferme`.
|
||||
- `src/world/GameStageContent.tsx` mounts production `RepairGame` instances for `ebike`, `pylon`, and `farm`.
|
||||
- `src/components/three/gameplay/RepairCompletionStep.tsx` renders the final repaired object, completion target, case exit animation, and mission UI prompt.
|
||||
- `src/components/three/gameplay/RepairGame.tsx` composes the reusable production repair flow.
|
||||
- `src/components/three/gameplay/RepairBrokenPartHighlight.tsx` renders the red halo and wire marker around detected broken parts during scanning.
|
||||
@@ -65,7 +65,7 @@ The mission config now carries the mission-specific variations. `bike` repairs o
|
||||
- `src/components/three/models/ExplodableModel.tsx` renders selectable models with split/exploded visualization.
|
||||
- `src/data/gameplay/repairCaseConfig.ts` stores repair case model, sound, and animation constants.
|
||||
- `src/data/gameplay/repairGameConfig.ts` stores repair flow timing constants.
|
||||
- `src/data/gameplay/repairMissions.ts` stores reusable repair mission config for `bike`, `pylone`, and `ferme`.
|
||||
- `src/data/gameplay/repairMissions.ts` stores reusable repair mission config for `ebike`, `pylon`, and `farm`.
|
||||
- `src/managers/stores/useGameStore.ts` stores mission progression state and generic mission step helpers.
|
||||
- `src/types/gameplay/repairMission.ts` contains shared repair mission ids, mission steps, and guards used by the store, data config, debug UI, and gameplay components.
|
||||
|
||||
@@ -73,7 +73,7 @@ The mission config now carries the mission-specific variations. `bike` repairs o
|
||||
|
||||
The production repair flow currently requires:
|
||||
|
||||
- the active `mainState` to be one of `bike`, `pylone`, or `ferme`
|
||||
- the active `mainState` to be one of `ebike`, `pylon`, or `farm`
|
||||
- `GameStageContent` mounted inside the game scene Rapier `Physics` boundary
|
||||
- model assets available under `public/models/`
|
||||
- sound assets available under `public/sounds/`
|
||||
|
||||
Reference in New Issue
Block a user