Newer
Older
rpg / v2 / apps / game / tools / convertMaps.mjs
/**
 * Конвертация карт v1 (assets/maps/*.map — RLE) в раскладку областей v2:
 * src/data/maps.json — по области {width, height, tiles (RLE), decor, props,
 * blocked, tall}. Раскладка тайлов v1 переносится как есть (позиции NPC,
 * переходов и квестов v1 в тайлах — сходятся с раскладкой), воксельная
 * генерация делает из неё мир (src/areas.ts). Прогон: node tools/convertMaps.mjs
 * (от корня v2/apps/game; путь к v1 — по умолчанию ../../v1/apps/game).
 */
import { readFileSync, writeFileSync, mkdirSync } from 'node:fs';
import { fileURLToPath } from 'node:url';
import path from 'node:path';

const ROOT = fileURLToPath(new URL('..', import.meta.url));
const V1_MAPS = process.argv[2] ?? path.resolve(ROOT, '../../../v1/apps/game/assets/maps');
const OUT = path.resolve(ROOT, 'src/data/maps.json');

/** Области v1 (порядок = порядок загрузки в игре). */
const AREAS = ['meadows', 'ponds', 'zvenets', 'rust_grove', 'old_siding', 'machine_hall', 'house_elder', 'shop'];

/** RLE-пары → плоский массив (распаковка на месте — parseMap v1 не импортируется). */
function rleDecode(pairs) {
    const out = [];
    for (const [id, run] of pairs) {
        for (let k = 0; k < run; k++) out.push(id);
    }
    return out;
}

/** Плоский массив → RLE-пары (на случай raw-карты v1). */
function rleEncode(flat) {
    const out = [];
    for (let i = 0; i < flat.length; i++) {
        if (i > 0 && flat[i] === flat[i - 1]) out[out.length - 1][1]++;
        else out.push([flat[i], 1]);
    }
    return out;
}

const maps = {};
for (const id of AREAS) {
    const raw = JSON.parse(readFileSync(path.join(V1_MAPS, `${id}.map`), 'utf8'));
    if (raw.format !== 'rpg-map' || raw.version !== 1) throw new Error(`${id}: не карта rpg-map`);
    maps[id] = {
        width: raw.width,
        height: raw.height,
        // RLE как есть (сериализация маленькая); генератор распакует при сборке
        tiles: raw.encoding === 'rle' ? raw.tiles : rleEncode(raw.tiles),
        // blocked/tall/decor/props — по сути копия (высоты в юнитах уже)
        blocked: raw.blocked,
        tall: raw.tall ?? {},
        decor: raw.decor ?? [],
        props: raw.props ?? [],
    };
}

mkdirSync(path.dirname(OUT), { recursive: true });
writeFileSync(OUT, JSON.stringify(maps));
const sizes = Object.entries(maps).map(([k, m]) => `${k} ${m.width}x${m.height}`);
console.log('ok →', path.relative(ROOT, OUT));
console.log(sizes.join(', '));