import { renderSpec } from '@rpg/engine';
import type { AudioManager } from '@rpg/engine';
import { SPEC_SFX, playSfx } from '../data/sfxSpecs';
/**
* Вариативные шаги: 3 варианта на поверхность, компилируются в рантайме
* из спеков sfxSpecs.ts (сид варианта = базовый + i·10 — звучат похоже,
* но не идентично). Выбор варианта — по кругу. До unlock буферы не создать
* (createBuffer → null) — играем базовый спек, варианты синтезируются
* заново при следующих шагах, пока не получится (обычно вторая попытка,
* после первого play).
*/
export type StepSurface = 'grass' | 'ash' | 'water' | 'floor';
const VARIANTS = 3;
export class StepVariants {
/** Буферы по поверхности (синтезируются при первом успешном шаге). */
private cache = new Map<StepSurface, AudioBuffer[]>();
private seq = 0;
constructor(private audio: AudioManager) {}
/** Играть очередной вариант шага (до готовности буферов — базовый спек). */
play(surface: StepSurface, volume: number): void {
const key = `sfx/step_${surface}`;
const list = this.ensure(surface);
if (list.length > 0) {
const i = this.seq++ % list.length;
void this.audio.playBuffer(`${key}#${i}`, list[i]!, { volume });
} else {
playSfx(this.audio, key, { volume });
}
}
/** Синтезировать варианты поверхности (если контекст ещё не готов — пустой список). */
private ensure(surface: StepSurface): AudioBuffer[] {
const existing = this.cache.get(surface);
if (existing) return existing;
const base = SPEC_SFX[`sfx/step_${surface}`];
if (!base) return [];
const list: AudioBuffer[] = [];
for (let i = 0; i < VARIANTS; i++) {
const buf = this.audio.createBuffer(renderSpec({ ...base, seed: (base.seed ?? 1) + i * 10 }), 22050);
if (!buf) return []; // контекста нет — попробуем на следующем шаге
list.push(buf);
}
this.cache.set(surface, list);
return list;
}
}