Files
healer-man/src/game/manastormClientCues.ts
2026-09-05 11:40:50 -04:00

158 lines
4.4 KiB
TypeScript

import { playMasterAudioCue } from "./combatAudio";
export type ManastormPortalCueSignal = "portal-coming-up" | "portal-ready";
export interface ManastormPortalAudioCue {
readonly id: string;
readonly signal: ManastormPortalCueSignal;
readonly variant: "generic" | "single";
readonly alternate: boolean;
readonly fileName: string;
readonly url: string;
}
export interface ManastormClientCueRequest {
readonly cueId: ManastormPortalCueId;
readonly revision: number;
}
export interface ManastormAudioHandle {
currentTime: number;
preload: string;
volume: number;
pause: () => void;
play: () => Promise<void> | void;
}
export type ManastormAudioFactory = (url: string) => ManastormAudioHandle;
export const MANASTORM_PORTAL_AUDIO_ROOT =
"/assets/game/combat-effects/audio/manastorm";
function packagedPortalAudioUrl(fileName: string): string {
return `${MANASTORM_PORTAL_AUDIO_ROOT}/${fileName}`;
}
export const MANASTORM_PORTAL_AUDIO_CUES = [
{
id: "generic-portal-coming-up",
signal: "portal-coming-up",
variant: "generic",
alternate: false,
fileName: "Manastorm_BossKill_GenericPortalComingUp.wav",
url: packagedPortalAudioUrl("Manastorm_BossKill_GenericPortalComingUp.wav"),
},
{
id: "single-portal-coming-up",
signal: "portal-coming-up",
variant: "single",
alternate: false,
fileName: "Manastorm_BossKill_1PortalComingUp.wav",
url: packagedPortalAudioUrl("Manastorm_BossKill_1PortalComingUp.wav"),
},
{
id: "single-portal-coming-up-alt",
signal: "portal-coming-up",
variant: "single",
alternate: true,
fileName: "Manastorm_BossKill_1PortalComingUp_2.wav",
url: packagedPortalAudioUrl("Manastorm_BossKill_1PortalComingUp_2.wav"),
},
{
id: "generic-portals-ready",
signal: "portal-ready",
variant: "generic",
alternate: false,
fileName: "Manastorm_BossKill_GenericPortalsReady.wav",
url: packagedPortalAudioUrl("Manastorm_BossKill_GenericPortalsReady.wav"),
},
{
id: "generic-portals-ready-alt",
signal: "portal-ready",
variant: "generic",
alternate: true,
fileName: "Manastorm_BossKill_GenericPortalsReady_2.wav",
url: packagedPortalAudioUrl("Manastorm_BossKill_GenericPortalsReady_2.wav"),
},
] as const satisfies readonly ManastormPortalAudioCue[];
export type ManastormPortalCueId = typeof MANASTORM_PORTAL_AUDIO_CUES[number]["id"];
export const MANASTORM_DEFAULT_PORTAL_READY_CUE_ID: ManastormPortalCueId =
"generic-portals-ready";
const cueById = new Map<ManastormPortalCueId, ManastormPortalAudioCue>(
MANASTORM_PORTAL_AUDIO_CUES.map((cue) => [cue.id, cue]),
);
let activeAudio: ManastormAudioHandle | null = null;
export function manastormPortalAudioCue(
cueId: ManastormPortalCueId,
): ManastormPortalAudioCue {
const cue = cueById.get(cueId);
if (!cue) throw new Error(`Unknown Manastorm client cue ${cueId}.`);
return cue;
}
export function isManastormPortalCueId(value: string): value is ManastormPortalCueId {
return cueById.has(value as ManastormPortalCueId);
}
export function manastormCueForStatusTransition(
previousStatus: string,
nextStatus: string,
): ManastormPortalCueId | null {
return previousStatus !== "portal-ready" && nextStatus === "portal-ready"
? MANASTORM_DEFAULT_PORTAL_READY_CUE_ID
: null;
}
function browserAudioFactory(url: string): ManastormAudioHandle {
let stopPlayback: (() => void) | null = null;
let stopped = false;
const handle: ManastormAudioHandle = {
currentTime: 0,
preload: "auto",
volume: 1,
pause: () => {
stopped = true;
stopPlayback?.();
stopPlayback = null;
},
play: async () => {
stopped = false;
const stop = await playMasterAudioCue(url, handle.volume);
if (!stop) throw new Error("Web Audio is unavailable.");
if (stopped) stop();
else stopPlayback = stop;
},
};
return handle;
}
export async function playManastormClientCue(
cueId: ManastormPortalCueId,
audioFactory: ManastormAudioFactory = browserAudioFactory,
): Promise<boolean> {
const cue = manastormPortalAudioCue(cueId);
try {
activeAudio?.pause();
const audio = audioFactory(cue.url);
audio.preload = "auto";
audio.volume = 0.72;
audio.currentTime = 0;
activeAudio = audio;
await audio.play();
return true;
} catch {
activeAudio = null;
return false;
}
}
export function stopManastormClientCue(): void {
activeAudio?.pause();
activeAudio = null;
}