fed5674468
Wire mimic/pit/arrow/laser media on activate, poison/explosion via effects, and document the scene editor and traps in Instructions. Co-authored-by: Cursor <cursoragent@cursor.com>
93 lines
3.1 KiB
TypeScript
93 lines
3.1 KiB
TypeScript
/** Одноразовая активация ловушек с собственным VFX/SFX (`public/vfx/<type>/…` + `public/<type>.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<TrapMediaActivationKind, TrapMediaSpec> = {
|
|
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');
|
|
}
|