add: trigger dialogue with timecode
This commit is contained in:
@@ -0,0 +1,59 @@
|
|||||||
|
import { useEffect, useRef, useState } from "react";
|
||||||
|
import { useFrame } from "@react-three/fiber";
|
||||||
|
import type { DialogueManifest } from "@/types/dialogues/dialogues";
|
||||||
|
import { loadDialogueManifest } from "@/utils/dialogues/loadDialogueManifest";
|
||||||
|
import { playDialogueById } from "@/utils/dialogues/playDialogue";
|
||||||
|
import { logger } from "@/utils/core/logger";
|
||||||
|
|
||||||
|
export function GameDialogues(): null {
|
||||||
|
const [manifest, setManifest] = useState<DialogueManifest | null>(null);
|
||||||
|
const playedDialoguesRef = useRef(new Set<string>());
|
||||||
|
const activeAudiosRef = useRef(new Set<HTMLAudioElement>());
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
let mounted = true;
|
||||||
|
const activeAudios = activeAudiosRef.current;
|
||||||
|
|
||||||
|
void loadDialogueManifest()
|
||||||
|
.then((loadedManifest) => {
|
||||||
|
if (mounted) setManifest(loadedManifest);
|
||||||
|
})
|
||||||
|
.catch((error: unknown) => {
|
||||||
|
logger.error("GameDialogues", "Failed to load dialogue manifest", {
|
||||||
|
error: error instanceof Error ? error : String(error),
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
mounted = false;
|
||||||
|
activeAudios.forEach((audio) => audio.pause());
|
||||||
|
activeAudios.clear();
|
||||||
|
};
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
useFrame(({ clock }) => {
|
||||||
|
if (!manifest) return;
|
||||||
|
|
||||||
|
const elapsedTime = clock.getElapsedTime();
|
||||||
|
|
||||||
|
manifest.dialogues.forEach((dialogue) => {
|
||||||
|
if (dialogue.timecode === undefined) return;
|
||||||
|
if (dialogue.timecode > elapsedTime) return;
|
||||||
|
if (playedDialoguesRef.current.has(dialogue.id)) return;
|
||||||
|
|
||||||
|
playedDialoguesRef.current.add(dialogue.id);
|
||||||
|
|
||||||
|
void playDialogueById(manifest, dialogue.id).then((audio) => {
|
||||||
|
if (!audio) return;
|
||||||
|
activeAudiosRef.current.add(audio);
|
||||||
|
audio.addEventListener(
|
||||||
|
"ended",
|
||||||
|
() => activeAudiosRef.current.delete(audio),
|
||||||
|
{ once: true },
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
@@ -10,6 +10,7 @@ import { DebugCameraControls } from "@/components/debug/scene/DebugCameraControl
|
|||||||
import { DebugHelpers } from "@/components/debug/scene/DebugHelpers";
|
import { DebugHelpers } from "@/components/debug/scene/DebugHelpers";
|
||||||
import { HandTrackingGlove } from "@/components/three/handTracking/HandTrackingGlove";
|
import { HandTrackingGlove } from "@/components/three/handTracking/HandTrackingGlove";
|
||||||
import { Environment } from "@/world/Environment";
|
import { Environment } from "@/world/Environment";
|
||||||
|
import { GameDialogues } from "@/world/GameDialogues";
|
||||||
import { GameMusic } from "@/world/GameMusic";
|
import { GameMusic } from "@/world/GameMusic";
|
||||||
import { Lighting } from "@/world/Lighting";
|
import { Lighting } from "@/world/Lighting";
|
||||||
import { GameMap } from "@/world/GameMap";
|
import { GameMap } from "@/world/GameMap";
|
||||||
@@ -42,6 +43,7 @@ export function World(): React.JSX.Element {
|
|||||||
{sceneMode === "game" ? (
|
{sceneMode === "game" ? (
|
||||||
<>
|
<>
|
||||||
<GameMusic />
|
<GameMusic />
|
||||||
|
<GameDialogues />
|
||||||
<GameMap onOctreeReady={setOctree} />
|
<GameMap onOctreeReady={setOctree} />
|
||||||
<GameStageContent />
|
<GameStageContent />
|
||||||
</>
|
</>
|
||||||
|
|||||||
Reference in New Issue
Block a user