feat(ebike): play narrator cues during repair flow (scan hint, diagnostic, completion)
This commit is contained in:
@@ -0,0 +1,61 @@
|
|||||||
|
import { useEffect, useRef } from "react";
|
||||||
|
import {
|
||||||
|
EBIKE_DIAGNOSTIC_DIALOGUE_ID,
|
||||||
|
EBIKE_REPAIRED_DIALOGUE_ID,
|
||||||
|
EBIKE_SCAN_HINT_DIALOGUE_ID,
|
||||||
|
} from "@/data/ebike/ebikeConfig";
|
||||||
|
import { useGameStore } from "@/managers/stores/useGameStore";
|
||||||
|
import type { MissionStep } from "@/types/gameplay/repairMission";
|
||||||
|
import { loadDialogueManifest } from "@/utils/dialogues/loadDialogueManifest";
|
||||||
|
import { playDialogueById } from "@/utils/dialogues/playDialogue";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Plays narrator cues during the ebike repair game:
|
||||||
|
* - `fragmented` -> "Alors? Pas magnifique ça?... ces galets vont scanner..."
|
||||||
|
* - `repairing` -> "Parfait! C'est le refroidisseur qui a lâché..."
|
||||||
|
* - `done` -> "Eeeet voilà! Il fonctionne comme une horloge!..."
|
||||||
|
*
|
||||||
|
* Each cue is one-shot per mission run; the played-set resets when the
|
||||||
|
* mission state rolls back to `locked`/`waiting` so debug-panel replays
|
||||||
|
* still trigger the narration.
|
||||||
|
*/
|
||||||
|
const STEP_TO_DIALOGUE_ID: Partial<Record<MissionStep, string>> = {
|
||||||
|
fragmented: EBIKE_SCAN_HINT_DIALOGUE_ID,
|
||||||
|
repairing: EBIKE_DIAGNOSTIC_DIALOGUE_ID,
|
||||||
|
done: EBIKE_REPAIRED_DIALOGUE_ID,
|
||||||
|
};
|
||||||
|
|
||||||
|
export function EbikeRepairNarrator(): null {
|
||||||
|
const mainState = useGameStore((state) => state.mainState);
|
||||||
|
const ebikeStep = useGameStore((state) => state.ebike.currentStep);
|
||||||
|
const playedRef = useRef<Set<MissionStep>>(new Set());
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (ebikeStep === "locked" || ebikeStep === "waiting") {
|
||||||
|
playedRef.current.clear();
|
||||||
|
}
|
||||||
|
}, [ebikeStep]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (mainState !== "ebike") return;
|
||||||
|
|
||||||
|
const dialogueId = STEP_TO_DIALOGUE_ID[ebikeStep];
|
||||||
|
if (!dialogueId) return;
|
||||||
|
if (playedRef.current.has(ebikeStep)) return;
|
||||||
|
|
||||||
|
playedRef.current.add(ebikeStep);
|
||||||
|
|
||||||
|
let cancelled = false;
|
||||||
|
void (async () => {
|
||||||
|
const manifest = await loadDialogueManifest();
|
||||||
|
if (cancelled || !manifest) return;
|
||||||
|
await playDialogueById(manifest, dialogueId);
|
||||||
|
})();
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
cancelled = true;
|
||||||
|
};
|
||||||
|
}, [mainState, ebikeStep]);
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
@@ -33,3 +33,7 @@ export const EBIKE_SOUNDS = {
|
|||||||
} as const;
|
} as const;
|
||||||
|
|
||||||
export const EBIKE_BREAKDOWN_DIALOGUE_ID = "narrateur_ebikecasse";
|
export const EBIKE_BREAKDOWN_DIALOGUE_ID = "narrateur_ebikecasse";
|
||||||
|
export const EBIKE_SCAN_HINT_DIALOGUE_ID = "narrateur_galetscan";
|
||||||
|
export const EBIKE_DIAGNOSTIC_DIALOGUE_ID =
|
||||||
|
"narrateur_refroidisseur_diagnostic";
|
||||||
|
export const EBIKE_REPAIRED_DIALOGUE_ID = "narrateur_ebikerepare";
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import { Canvas } from "@react-three/fiber";
|
|||||||
import * as THREE from "three";
|
import * as THREE from "three";
|
||||||
import { DebugPerf } from "@/components/debug/DebugPerf";
|
import { DebugPerf } from "@/components/debug/DebugPerf";
|
||||||
import { EbikeIntroSequence } from "@/components/game/EbikeIntroSequence";
|
import { EbikeIntroSequence } from "@/components/game/EbikeIntroSequence";
|
||||||
|
import { EbikeRepairNarrator } from "@/components/game/EbikeRepairNarrator";
|
||||||
import { AppLoadingIndicator } from "@/components/ui/AppLoadingIndicator";
|
import { AppLoadingIndicator } from "@/components/ui/AppLoadingIndicator";
|
||||||
import { DialogMessage } from "@/components/ui/DialogMessage";
|
import { DialogMessage } from "@/components/ui/DialogMessage";
|
||||||
import { GameUI } from "@/components/ui/GameUI";
|
import { GameUI } from "@/components/ui/GameUI";
|
||||||
@@ -259,6 +260,7 @@ export function HomePage(): React.JSX.Element | null {
|
|||||||
) : null}
|
) : null}
|
||||||
{renderIntroOverlay()}
|
{renderIntroOverlay()}
|
||||||
<EbikeIntroSequence />
|
<EbikeIntroSequence />
|
||||||
|
<EbikeRepairNarrator />
|
||||||
</HandTrackingProvider>
|
</HandTrackingProvider>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user