/**
* Сценарий music — музыкальный слой: тема лугов генерируется правилами
* (data/music.ts, сид) и компилируется движковым секвенсором в рантайме
* (playTheme → playLoopBuffer), факт запуска — в window.__gameAudioLog.
* 1) новая игра в лугах: music/meadows в логе (тема играет) + амбиент;
* 2) тема не перезапускается при возврате в ту же область (guard);
* 3) уход в Звенец (темы нет): слот темы снят (themeKey '') и
* новый music/* в логе не появился.
* Запуск: node tools/agent.mjs run tools/checks/music.mjs
*/
import { startDevServer, openGame, Checks } from '../lib.mjs';
const readLog = (page) => page.evaluate(() => window.__gameAudioLog ?? null);
/** Подождать появления ключа в логе (компиляция темы занимает кадры). */
async function waitKey({ page, agent }, key, tries = 12) {
for (let i = 0; i < tries; i++) {
if ((await readLog(page)).some((e) => e.key === key)) return true;
await agent.step(30);
}
return false;
}
export default async function ({ pretty }) {
const c = new Checks('music');
const server = await startDevServer();
let ctx;
try {
await c.run('новая игра в лугах: тема скомпилирована и играет', async () => {
ctx = await openGame({ url: server.url, newGame: true });
await ctx.agent.waitFor('!s.transitioning', { timeoutTicks: 600 });
c.expect(
await waitKey(ctx, 'music/meadows'),
'темы лугов нет в аудио-логе (компиляция не удалась?)',
await readLog(ctx.page)
);
c.expect(
(await readLog(ctx.page)).some((e) => e.key === 'ambience/meadows'),
'амбиент лугов пропал при старте темы'
);
return null;
});
await c.run('возврат в ту же область: тема не перезапускается (guard)', async () => {
const count = () =>
readLog(ctx.page).then((log) => log.filter((e) => e.key === 'music/meadows').length);
const before = await count();
// Шагнуть туда-обратно мимо переходов, сцена не меняется — guard ключа.
await ctx.agent.walkTo(15, 14, { timeoutTicks: 1500 });
await ctx.agent.walkTo(14, 14, { timeoutTicks: 1500 });
c.expect((await count()) === before, 'тема перезапустилась без смены ключа', {
before,
after: await count()
});
return null;
});
await c.run('уход в Звенец: слот темы снят, новый music/* не играет', async () => {
// Пешком к тропе в Звенец (26,14) — честный step-переход.
const walked = await ctx.agent.walkTo(26, 14, { timeoutTicks: 3000 });
c.expect(walked, 'walkTo(26,14) не дошёл до перехода');
const themeKey = () =>
ctx.page.evaluate(() => {
const g = window.__game;
return g ? g.themeKey : null;
});
for (let i = 0; i < 12 && (await themeKey()) !== ''; i++) {
await ctx.agent.step(30);
}
c.expect((await themeKey()) === '', 'слот темы не снят при уходе из лугов');
const music = (await readLog(ctx.page)).filter((e) => e.key.startsWith('music/'));
c.expect(
music.every((e) => e.key === 'music/meadows'),
'в Звенце играет музыка, которой быть не должно',
music
);
return null;
});
} finally {
await ctx?.browser?.close();
server.stop();
}
return c.finish({ pretty }).ok ? 0 : 1;
}