Newer
Older
rpg / apps / game / src / systems / quest / ResonatorQuest.ts
import { CutsceneRunner, tileToWorld, worldToScreen } from '@rpg/engine';
import { Game } from '../../Game';
import { FLAGS, type FlagId } from '../../data/ids';
import { QUEST_TONE_TILES } from '../../data/quests';

/**
 * Квест «Голос для резонатора» (docs/world.md, акт 2): звон у деревьев-
 * механизмов рощи (интерактивы, тон — варом через реакции) и кат-сцена
 * настройки — резонатор ловит собранные тоны, роща отвечает.
 */
export class ResonatorQuest {
    constructor(
        private game: Game,
        /** Экранный центр ромба тайла (с Origin карты). */
        private tileCenter: (tx: number, ty: number) => { x: number; y: number },
        /** Всплеск пеплинок в точке (CombatViews.hitBurst). */
        private burst: (s: { x: number; y: number }) => void,
        private showToast: (text: string) => void,
        private cutscene: CutsceneRunner
    ) {}

    /** Кат-сцена настройки: камера к резонатору Клинта, звон, ответ рощи. */
    startTuneCutscene(): void {
        // Резонатор у поляны Клинта (интерактив clint_resonator).
        const at = tileToWorld(12, 8);
        const s = worldToScreen(at.x, at.y);
        this.cutscene.play([
            // Камера подходит и наезжает: настройка — крупный план.
            { kind: 'cameraMove', x: at.x, y: at.y, seconds: 1, camera: this.game.camera },
            { kind: 'cameraZoom', camera: this.game.camera, z: 2, seconds: 0.8 },
            // Резонатор ловит тон: звон, тряска, всплеск.
            {
                kind: 'burst',
                fn: () => {
                    void this.game.audio.play('sfx/bell_low', 1);
                    this.game.camera.addShake(2, 0.4);
                    this.burst(s);
                },
                seconds: 1
            },
            // Роща отвечает: прозвеневшие деревья вспыхивают в такт.
            {
                kind: 'burst',
                fn: () => {
                    QUEST_TONE_TILES.forEach(([tx, ty], i) => {
                        if (!this.game.state.hasFlag(FLAGS[`tone_tree_${i + 1}` as FlagId])) return;
                        this.burst(this.tileCenter(tx, ty));
                    });
                },
                seconds: 1.2
            },
            // Отъезд и тост-финал квеста.
            { kind: 'cameraZoom', camera: this.game.camera, z: 1, seconds: 0.6 },
            { kind: 'call', fn: () => this.showToast('Резонатор держит тон. Роща слышит.') }
        ]);
    }
}