Newer
Older
rpg / apps / game / tools / aiart / tryon.mjs
/**
 * Примерка AI-атласа в игре ДО promote: временно подменяет целевой атлас
 * сборкой из build/<id>/, снимает скриншот героя в локации, возвращает файлы.
 * Апрув по обзорному листу — пиксели на клетке; примерка показывает якорь,
 * масштаб и палитру в контексте мира.
 *
 *   node tryon.mjs <id> [файл-скриншота=/tmp/rpg_tryon.png]
 *
 * Файлы assets восстанавливаются в finally (даже при падении).
 */
import { readFileSync, writeFileSync, copyFileSync, existsSync } from 'node:fs';
import { join } from 'node:path';
import { fileURLToPath } from 'node:url';
import { startDevServer, openGame } from '../lib.mjs';

const HERE = fileURLToPath(new URL('.', import.meta.url));
const id = process.argv[2] ?? 'bellringer';
const shot = process.argv[3] ?? '/tmp/rpg_tryon.png';

// Цель берём из манифеста (target), как её видит promote.
const manifest = JSON.parse(readFileSync(join(HERE, 'atlas-manifest.json'), 'utf8'));
const char = manifest.characters.find((c) => c.id === id);
if (!char) throw new Error(`персонаж «${id}» не найден в atlas-manifest.json`);
const buildPng = join(HERE, 'build', id, char.target);
if (!existsSync(buildPng)) throw new Error(`нет сборки ${buildPng} — сначала atlas.mjs build ${id}`);

const dst = join(HERE, '..', '..', 'assets');
const dstPng = join(dst, char.target);
const dstJson = dstPng.replace(/\.png$/, '.json');
const backup = {
    png: existsSync(dstPng) ? readFileSync(dstPng) : null,
    json: existsSync(dstJson) ? readFileSync(dstJson) : null
};

let server = null, browser = null;
try {
    copyFileSync(buildPng, dstPng);
    // Игра ждёт кадры с префиксом hero_ (LocationScene: hero_down/up/side) —
    // переписываем имена кадров сборки в JSON-копии.
    const atlasJson = JSON.parse(readFileSync(join(HERE, 'build', id, char.target.replace(/\.png$/, '.json')), 'utf8'));
    atlasJson.frames = Object.fromEntries(
        Object.entries(atlasJson.frames).map(([k, v]) => [k.replace(new RegExp(`^${id}_`), 'hero_'), v])
    );
    writeFileSync(dstJson, JSON.stringify(atlasJson));
    console.log(`[tryon] ${char.target} подменён сборкой «${id}» (status: ${char.status}, кадры → hero_*)`);

    server = await startDevServer({ port: 5199 });
    const { browser: b, page, agent } = await openGame({ url: server.url, newGame: true });
    browser = b;
    await agent.step(60); // пара шагов, чтобы герой попал в кадр ходьбы
    const snap = await agent.snapshot();
    console.log('снапшот:', JSON.stringify({ area: snap.area, hero: snap.hero?.tile }));
    await page.screenshot({ path: shot });
    console.log(`Скриншот: ${shot}`);
} finally {
    if (backup.png) writeFileSync(dstPng, backup.png);
    if (backup.json) writeFileSync(dstJson, backup.json);
    if (backup.png === null) console.log('[tryon] внимание: исходного атласа не было — проверь assets вручную');
    if (browser) await browser.close();
    if (server) server.stop();
}