/** Одноразовая активация ловушек с собственным VFX/SFX (`public/vfx//…` + `public/.mp3`). * Яд/взрыв идут через effects store (как инструменты пульта) — см. ControlApp. */ import type { SceneTrapType } from '../../../shared/types'; import { getEffectsSfxGain } from '../../control/effectsSfxGain'; export type TrapMediaActivationKind = 'mimic' | 'pit' | 'arrow' | 'laser'; type TrapMediaSpec = { videoPath: string; soundPath: string; /** Длительность видео + запас на старт (мс). */ lifeMs: number; sfxVolume: number; /** * Точка кадра (0…1), которая совпадает с центром ловушки. * По умолчанию центр; у лазера — правый конец луча. */ anchorNx?: number; anchorNy?: number; }; const TRAP_MEDIA: Record = { mimic: { videoPath: 'vfx/mimic/tentacle-at-camera.webm', soundPath: 'mimic.mp3', lifeMs: 4500, sfxVolume: 0.9, }, pit: { videoPath: 'vfx/pit/infinity-portal.webm', soundPath: 'pit.mp3', lifeMs: 6000, sfxVolume: 0.9, }, arrow: { videoPath: 'vfx/arrow/arrows-grounding.webm', soundPath: 'arrow.mp3', lifeMs: 2000, sfxVolume: 0.9, }, laser: { videoPath: 'vfx/laser/eye-lasers.webm', soundPath: 'laser.mp3', lifeMs: 1800, sfxVolume: 0.9, // правый кончик луча в кадре ≈ (0.82, 0.39) anchorNx: 0.82, anchorNy: 0.39, }, }; export function isTrapMediaActivationKind(type: SceneTrapType): type is TrapMediaActivationKind { return type === 'mimic' || type === 'pit' || type === 'arrow' || type === 'laser'; } export function trapActivationLifeMs(kind: TrapMediaActivationKind): number { return TRAP_MEDIA[kind].lifeMs; } export function trapActivationVideoUrl(kind: TrapMediaActivationKind): string { return new URL(TRAP_MEDIA[kind].videoPath, window.location.href).href; } /** CSS transform, чтобы `anchor` кадра оказался в точке позиционирования. */ export function trapActivationAnchorTransform(kind: TrapMediaActivationKind): string { const spec = TRAP_MEDIA[kind]; const ax = Math.max(0, Math.min(1, spec.anchorNx ?? 0.5)); const ay = Math.max(0, Math.min(1, spec.anchorNy ?? 0.5)); return `translate(${(-ax * 100).toFixed(2)}%, ${(-ay * 100).toFixed(2)}%)`; } export function playTrapActivationSound(kind: TrapMediaActivationKind): void { try { const spec = TRAP_MEDIA[kind]; const el = new Audio(new URL(spec.soundPath, window.location.href).href); el.volume = Math.max(0, Math.min(1, spec.sfxVolume * getEffectsSfxGain())); void el.play().catch(() => undefined); } catch { /* ignore */ } } /** @deprecated используй trapActivation* */ export const MIMIC_ACTIVATION_MS = TRAP_MEDIA.mimic.lifeMs; export function mimicActivationVideoUrl(): string { return trapActivationVideoUrl('mimic'); } export function playMimicActivationSound(): void { playTrapActivationSound('mimic'); }