/**
* Реестр тайлей мира (трек 29, скины): слоты тайлей моделей локальны —
* при штамповке opts.texRemap переводит их в глобальные id 1..255 этого
* реестра. Тайли дедуплицируются по контенту: одинаковый пиксель-арт у
* разных моделей (домов с разными сидами, манекенов разных персонажей)
* занимает один id — реестр остаётся в пределах байта.
*/
import { resolveTiles } from '@rpg/engine';
import type { CloudTiles, VoxelModel, ResolvedTile } from '@rpg/engine';
/** Глобальные тайли мира: id → разрешённый тайль (вход атласа рендера). */
export const worldTiles: CloudTiles = {};
let nextId = 1;
const byKey = new Map<string, number>(); // ключ контента тайля → глобальный id
const remapCache = new Map<VoxelModel, (localId: number) => number>();
/** Ключ дедупликации: размер + грани (hex-пиксели resolveTiles). */
function tileKey(t: ResolvedTile): string {
return `${t.size}|${t.faces.map((f) => f.join(',')).join(';')}`;
}
/** Переводчик локальных id тайлей модели → глобальные (кэшируется по модели). */
export function texRemapFor(model: VoxelModel): (localId: number) => number {
let m = remapCache.get(model);
if (!m) {
const local = new Map<number, number>();
for (const [k, t] of Object.entries(resolveTiles(model))) {
const id = Number(k);
if (id <= 0) continue; // 0 — плоский цвет, не тайль
const key = tileKey(t);
let gid = byKey.get(key);
if (gid === undefined) {
if (nextId > 255) throw new Error('Реестр тайлей мира переполнен (>255)');
gid = nextId++;
worldTiles[gid] = t;
byKey.set(key, gid);
}
local.set(id, gid);
}
m = (lid) => local.get(lid) ?? 0; // незарегистрированный — плоский цвет
remapCache.set(model, m);
}
return m;
}