import { describe, expect, it } from 'vitest';
import { GameState } from '../../core/GameState';
import { Camera } from '../../render/Camera';
import { CutsceneRunner } from '../CutsceneRunner';
/** Продвинуть раннер: dt N раз. */
function tick(runner: CutsceneRunner, dt: number, times: number): void {
for (let i = 0; i < times; i++) runner.update(dt);
}
/** Фиктивный «взгляд»: камера с заданным центром. */
function cameraAt(x: number, y: number): Camera {
const cam = new Camera(480, 270);
cam.x = x;
cam.y = y;
return cam;
}
describe('CutsceneRunner — последовательность шагов', () => {
it('шаги выполняются по порядку, wait держит паузу', () => {
const runner = new CutsceneRunner();
const order: string[] = [];
runner.play([
{ kind: 'call', fn: () => order.push('a') },
{ kind: 'wait', seconds: 0.5 },
{ kind: 'call', fn: () => order.push('b') }
]);
// Первый шаг стартовал сразу, второй ещё ждёт.
expect(order).toEqual(['a']);
expect(runner.active).toBe(true);
tick(runner, 0.1, 4); // 0.4с — всё ещё wait
expect(order).toEqual(['a']);
tick(runner, 0.2, 1); // 0.6с — wait кончился
expect(order).toEqual(['a', 'b']);
expect(runner.active).toBe(false);
});
it('шаг без seconds параллелен: следующий стартует сразу', () => {
const runner = new CutsceneRunner();
const order: string[] = [];
runner.play([
{ kind: 'burst', fn: () => order.push('burst') },
{ kind: 'call', fn: () => order.push('call') }
]);
// Шаги без длительности не блокируют: оба стартовали в том же тике.
expect(order).toEqual(['burst', 'call']);
expect(runner.active).toBe(false);
});
it('cameraMove плавно ведёт камеру к точке', () => {
const runner = new CutsceneRunner();
const cam = cameraAt(0, 0);
runner.play([{ kind: 'cameraMove', x: 10, y: -20, seconds: 1, camera: cam }]);
tick(runner, 0.25, 2); // половина пути
expect(cam.x).toBeGreaterThan(0);
expect(cam.x).toBeLessThan(10);
expect(cam.y).toBeLessThan(0);
tick(runner, 0.25, 3); // время вышло — точно в точке
expect(cam.x).toBe(10);
expect(cam.y).toBe(-20);
expect(runner.active).toBe(false);
});
it('setFlag поднимает флаги, onComplete вызывается в конце', () => {
const runner = new CutsceneRunner();
const state = new GameState();
let done = 0;
runner.onComplete = () => done++;
runner.play([
{ kind: 'setFlag', flag: 'flag1', state },
{ kind: 'setFlag', flag: 'flag2', state }
]);
expect(state.hasFlag('flag1')).toBe(true);
expect(state.hasFlag('flag2')).toBe(true);
expect(done).toBe(1);
});
it('onStep наблюдает порядок шагов', () => {
const seen: string[] = [];
const runner = new CutsceneRunner((s) => seen.push(s.kind));
runner.play([
{ kind: 'call', fn: () => undefined, seconds: 0.1 },
{ kind: 'wait', seconds: 0.1 }
]);
// Первый шаг стартовал; второй — после его длительности.
expect(seen).toEqual(['call']);
tick(runner, 0.2, 1);
expect(seen).toEqual(['call', 'wait']);
});
});