import type { TileMapData } from './IsometricTileMap';
/**
* Импортёр карт из Tiled (https://www.mapeditor.org).
* Поддерживается isometric-карта с tilelayer-слоями (CSV или сжатые —
* Tiled отдаёт их уже распакованными в JSON). GID 0 — пустой тайл;
* gid смещаются на firstgid первого тайлсета, чтобы id шли с 0.
*/
/** Минимум того, что читаем из JSON Tiled. */
export interface TiledMap {
width: number;
height: number;
tilewidth: number;
tileheight: number;
orientation: string;
layers: {
type: string;
name?: string;
data?: number[];
visible?: boolean;
}[];
tilesets?: { firstgid: number }[];
}
export interface FromTiledOptions {
/** Имена слоёв в порядке снизу вверх (по умолчанию — все tilelayer в порядке следования). */
layers?: string[];
/** id тайлов, которые считаются блокирующими (после смещения). */
blocked?: number[];
/** Высокие объекты. */
tall?: TileMapData['tall'];
}
/**
* Преобразовать isometric-карту Tiled в TileMapData движка.
* Верхние слои перезаписывают нули (пустые клетки) нижних.
*/
export function fromTiledIso(tiled: TiledMap, options: FromTiledOptions = {}): TileMapData {
if (tiled.orientation !== 'isometric') {
throw new Error(`Tiled: ожидается orientation=isometric, получен ${tiled.orientation}`);
}
const { width, height } = tiled;
const firstGid = tiled.tilesets?.[0]?.firstgid ?? 1;
const layers = tiled.layers.filter(
(l) =>
l.type === 'tilelayer' &&
l.visible !== false &&
(options.layers ? options.layers.includes(l.name ?? '') : true)
);
if (layers.length === 0) {
throw new Error('Tiled: в карте нет tilelayer-слоёв');
}
const tiles = new Array<number>(width * height).fill(0);
for (const layer of layers) {
if (!layer.data || layer.data.length !== width * height) {
throw new Error(`Tiled: слой ${layer.name ?? '?'} неверного размера`);
}
for (let i = 0; i < layer.data.length; i++) {
const gid = layer.data[i]!;
if (gid !== 0 && tiles[i] === 0) {
tiles[i] = gid - firstGid;
}
}
}
return {
width,
height,
tiles,
blocked: options.blocked ?? [],
tall: options.tall
};
}