/**
* Сценарий music — музыкальный слой: тема лугов генерируется правилами
* (data/music.ts, сид) и компилируется движковым секвенсором в рантайме
* (playTheme → playLoopBuffer), факт запуска — в window.__gameAudioLog.
* 1) новая игра в лугах: music/meadows в логе (тема играет) + амбиент;
* 2) тема не перезапускается при возврате в ту же область (guard);
* 3) уход в Звенец (темы нет): слот темы снят (themeKey '') и тема лугов
* не перезапускается. Динамическая музыка разрешена: стем (настороженность)
* и тема боя (погоня тяжеловеса у тропы — его аггро-радиус на пути).
* Запуск: node tools/agent.mjs run tools/checks/music.mjs
*/
import { withChecks } from '../lib.mjs';
export default async function ({ pretty }) {
return withChecks(
'music',
async (t) => {
const { c } = t;
/** Сколько раз ключ встречается в логе (null-лог → 0). */
const countKey = async (key) =>
((await t.readAudioLog()) ?? []).filter((e) => e.key === key).length;
/** Слот темы страницы (window.__game.themeKey) или null. */
const themeKey = () =>
t.ctx.page.evaluate(() => {
const g = window.__game;
return g ? g.themeKey : null;
});
await c.run('новая игра в лугах: тема скомпилирована и играет', async () => {
await t.boot();
const log = await t.waitAudioLog((l) => l.some((e) => e.key === 'music/meadows'));
c.expect(
log.some((e) => e.key === 'music/meadows'),
'темы лугов нет в аудио-логе (компиляция не удалась?)',
log
);
c.expect(
log.some((e) => e.key === 'ambience/meadows'),
'амбиент лугов пропал при старте темы'
);
return null;
});
await c.run('возврат в ту же область: тема не перезапускается (guard)', async () => {
const before = await countKey('music/meadows');
// Шагнуть туда-обратно мимо переходов, сцена не меняется — guard ключа.
await t.ctx.agent.walkTo(15, 14, { timeoutTicks: 1500 });
await t.ctx.agent.walkTo(14, 14, { timeoutTicks: 1500 });
c.expect((await countKey('music/meadows')) === before, 'тема перезапустилась без смены ключа', {
before,
after: await countKey('music/meadows')
});
return null;
});
await c.run('уход в Звенец: слот темы снят, тема лугов не перезапускается', async () => {
const before = await countKey('music/meadows');
// Пешком к тропе в Звенец (26,14) — честный step-переход.
const walked = await t.ctx.agent.walkTo(26, 14, { timeoutTicks: 3000 });
c.expect(walked, 'walkTo(26,14) не дошёл до перехода');
// Слот темы освобождается (тема Звенца пуста; тема боя погони —
// временный ключ, ждём его снятия после деаггро).
for (let i = 0; i < 12 && (await themeKey()) !== ''; i++) {
await t.ctx.agent.step(30);
}
c.expect((await themeKey()) === '', 'слот темы не снят при уходе из лугов');
c.expect(
(await countKey('music/meadows')) === before,
'тема лугов перезапустилась (guard ключа сломан)',
{ before, after: await countKey('music/meadows') }
);
return null;
});
},
{ pretty }
);
}