Files
DndGamePlayer/app/renderer/control/freezeSfx.ts
T
Ivan Fontosh 2c0e6fbf09 feat(effects): extinguish fire with water/rain and ambient SFX volume
Water and rain erase fire under a tight brush hit radius; add looping fire/rain ambients and an Effects sound slider that also scales one-shot effect SFX.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-07-23 13:12:41 +08:00

55 lines
2.0 KiB
TypeScript

/** Звук и длительность эффекта «Заморозка» (`public/zamorozka.mp3`). */
import { getEffectsSfxGain } from './effectsSfxGain';
const DEFAULT_FREEZE_LIFE_MS = 820;
const FREEZE_SFX_BASE_VOLUME = 0.88;
/** На 25% тише базовой громкости эффекта (до множителя «Звук эффектов»). */
export const FREEZE_SFX_VOLUME = FREEZE_SFX_BASE_VOLUME * 0.75;
export function freezeEffectSoundUrl(): string {
return new URL('zamorozka.mp3', window.location.href).href;
}
let cachedFreezeSfxDurationMs: number | null = null;
/** Длительность трека в мс (кэш после первого чтения метаданных). */
export function getFreezeSfxDurationMs(): Promise<number> {
if (cachedFreezeSfxDurationMs !== null) {
return Promise.resolve(cachedFreezeSfxDurationMs);
}
const url = freezeEffectSoundUrl();
return new Promise((resolve) => {
const a = new Audio();
const done = (ms: number): void => {
cachedFreezeSfxDurationMs = ms;
a.removeAttribute('src');
resolve(ms);
};
a.addEventListener('loadedmetadata', () => {
const d = a.duration;
done(Number.isFinite(d) && d > 0 ? Math.round(d * 1000) : DEFAULT_FREEZE_LIFE_MS);
});
a.addEventListener('error', () => done(DEFAULT_FREEZE_LIFE_MS));
a.src = url;
a.load();
});
}
/** Длительность полноэкранной заморозки и льда — по времени звука, с разумными пределами. */
export async function getFreezeEffectLifeMs(): Promise<number> {
const raw = await getFreezeSfxDurationMs();
return Math.min(45_000, Math.max(400, raw));
}
export function playFreezeEffectSound(): void {
try {
const el = new Audio(freezeEffectSoundUrl());
el.volume = Math.max(0, Math.min(1, FREEZE_SFX_VOLUME * getEffectsSfxGain()));
void el.play().catch(() => undefined);
} catch {
/* ignore */
}
}