diff --git a/public/cinematics.json b/public/cinematics.json index c05949a..767ccf7 100644 --- a/public/cinematics.json +++ b/public/cinematics.json @@ -1,6 +1,21 @@ { "version": 1, "cinematics": [ + { + "id": "intro_sequence", + "trigger": "intro_sequence", + "cameraKeyframes": [ + { "time": 0, "position": [8, 5, 12], "target": [0, 2, 0] }, + { "time": 8, "position": [12, 4, -6], "target": [10, 1.4, -8] }, + { "time": 16, "position": [5, 6, -15], "target": [0, 3, -20] }, + { "time": 24, "position": [0, 8, -30], "target": [0, 0, -40] } + ], + "dialogueCues": [ + { "time": 0, "dialogueId": "intro_welcome" }, + { "time": 8, "dialogueId": "intro_explanation" }, + { "time": 16, "dialogueId": "intro_mission" } + ] + }, { "id": "intro_overview", "timecode": 0, diff --git a/src/components/game/GameFlow.tsx b/src/components/game/GameFlow.tsx index 1434ac7..2c06372 100644 --- a/src/components/game/GameFlow.tsx +++ b/src/components/game/GameFlow.tsx @@ -6,6 +6,7 @@ import { AUDIO_PATHS } from "@/data/audioConfig"; export function GameFlow(): null { const step = useGameStore((state) => state.intro.currentStep); const setStep = useGameStore((state) => state.setIntroStep); + const isCinematicPlaying = useGameStore((state) => state.isCinematicPlaying); const setActivityCity = useGameStore((state) => state.setActivityCity); const setCanMove = useGameStore((state) => state.setCanMove); const hasInitialized = useRef(false); @@ -13,20 +14,17 @@ export function GameFlow(): null { useEffect(() => { if (!hasInitialized.current && step === "intro") { hasInitialized.current = true; - setStep("start-intro"); + setStep("intro_sequence"); } }, [step, setStep]); useEffect(() => { - if (step === "start-intro") { - const audio = AudioManager.getInstance(); - audio.playSoundWithCallback(AUDIO_PATHS.intro, 0.5, () => { - setStep("naming"); - }); - - return () => {}; + if (step === "intro_sequence" && !isCinematicPlaying) { + setStep("naming"); } + }, [step, isCinematicPlaying, setStep]); + useEffect(() => { if (step === "bienvenue") { const audio = AudioManager.getInstance(); audio.playSoundWithCallback(AUDIO_PATHS.bienvenue, 0.5, () => { diff --git a/src/types/cinematics/cinematics.ts b/src/types/cinematics/cinematics.ts index 7512911..8ce7c99 100644 --- a/src/types/cinematics/cinematics.ts +++ b/src/types/cinematics/cinematics.ts @@ -14,6 +14,7 @@ export interface CinematicDialogueCue { export interface CinematicDefinition { id: string; timecode?: number; + trigger?: string; cameraKeyframes: CinematicCameraKeyframe[]; dialogueCues?: CinematicDialogueCue[]; } diff --git a/src/types/game.ts b/src/types/game.ts index 44bd7a5..3b528aa 100644 --- a/src/types/game.ts +++ b/src/types/game.ts @@ -2,6 +2,7 @@ import type { Vector3Tuple } from "@/types/three/three"; export type GameStep = | "intro" + | "intro_sequence" | "start-intro" | "naming" | "bienvenue" @@ -14,6 +15,7 @@ export type GameStep = export const GAME_STEPS: readonly GameStep[] = [ "intro", + "intro_sequence", "start-intro", "naming", "bienvenue", diff --git a/src/world/GameCinematics.tsx b/src/world/GameCinematics.tsx index 7f60ca7..94a6dda 100644 --- a/src/world/GameCinematics.tsx +++ b/src/world/GameCinematics.tsx @@ -20,6 +20,7 @@ export function GameCinematics(): null { const [dialogueManifest, setDialogueManifest] = useState(null); const playedCinematicsRef = useRef(new Set()); + const triggeredCinematicsRef = useRef(new Set()); const timelineRef = useRef(null); const activeAudiosRef = useRef(new Set()); const startedAtRef = useRef(null); @@ -64,7 +65,25 @@ export function GameCinematics(): null { const elapsedTime = clock.getElapsedTime() - startedAtRef.current; + const currentStep = useGameStore.getState().intro.currentStep; + manifest.cinematics.forEach((cinematic) => { + if (cinematic.trigger) { + if (triggeredCinematicsRef.current.has(cinematic.id)) return; + if (currentStep !== cinematic.trigger) return; + if (cinematic.dialogueCues && !dialogueManifest) return; + + triggeredCinematicsRef.current.add(cinematic.id); + playCinematic(camera, cinematic, timelineRef, { + dialogueManifest, + activeAudiosRef, + onComplete: () => { + triggeredCinematicsRef.current.delete(cinematic.id); + }, + }); + return; + } + if (cinematic.timecode === undefined) return; if (cinematic.timecode > elapsedTime) return; if (cinematic.dialogueCues && !dialogueManifest) return; @@ -95,6 +114,7 @@ function playCinematic( dialogueOptions: { dialogueManifest: DialogueManifest | null; activeAudiosRef: MutableRefObject>; + onComplete?: () => void; }, ): void { const firstKeyframe = cinematic.cameraKeyframes[0]; @@ -113,6 +133,7 @@ function playCinematic( onComplete: () => { timelineRef.current = null; useGameStore.getState().setCinematicPlaying(false); + dialogueOptions.onComplete?.(); }, });