pixi-tiledmap 2.0.0 → 2.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs CHANGED
@@ -1,1411 +1,1552 @@
1
- 'use strict';
2
-
3
- var pixi_js = require('pixi.js');
4
-
5
- // src/types/index.ts
6
- var FLIPPED_HORIZONTALLY_FLAG = 2147483648;
7
- var FLIPPED_VERTICALLY_FLAG = 1073741824;
8
- var FLIPPED_DIAGONALLY_FLAG = 536870912;
9
- var ROTATED_HEXAGONAL_120_FLAG = 268435456;
10
- var GID_MASK = 268435455;
11
-
12
- // src/parser/decodeGid.ts
13
- function decodeGid(raw) {
14
- const gid = raw & GID_MASK;
15
- if (gid === 0) return null;
16
- return {
17
- gid,
18
- localId: 0,
19
- tilesetIndex: 0,
20
- horizontalFlip: (raw & FLIPPED_HORIZONTALLY_FLAG) !== 0,
21
- verticalFlip: (raw & FLIPPED_VERTICALLY_FLAG) !== 0,
22
- diagonalFlip: (raw & FLIPPED_DIAGONALLY_FLAG) !== 0
23
- };
24
- }
25
-
26
- // src/parser/decodeData.ts
1
+ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
+ let pixi_js = require("pixi.js");
3
+ //#region src/parser/decodeData.ts
27
4
  function decodeLayerData(data, encoding, compression) {
28
- if (Array.isArray(data)) {
29
- return data;
30
- }
31
- if (encoding === "base64") {
32
- const bytes = base64ToBytes(data);
33
- if (compression === "gzip" || compression === "zlib") {
34
- const decompressed = decompressBytes(bytes, compression);
35
- return bytesToGids(decompressed);
36
- }
37
- if (compression === "zstd") {
38
- throw new Error("zstd compression is not supported in the browser");
39
- }
40
- return bytesToGids(bytes);
41
- }
42
- throw new Error(`Unsupported encoding: ${encoding ?? "unknown"}`);
5
+ if (Array.isArray(data)) return data;
6
+ if (encoding === "csv") return csvToGids(data);
7
+ if (encoding === "base64") {
8
+ const bytes = base64ToBytes(data);
9
+ if (compression === "gzip" || compression === "zlib") return bytesToGids(decompressBytes(bytes, compression));
10
+ if (compression === "zstd") throw new Error("zstd compression is not supported in the browser");
11
+ return bytesToGids(bytes);
12
+ }
13
+ throw new Error(`Unsupported encoding: ${encoding ?? "unknown"}`);
14
+ }
15
+ function csvToGids(csv) {
16
+ const out = [];
17
+ let cur = 0;
18
+ let hasDigit = false;
19
+ for (let i = 0; i < csv.length; i++) {
20
+ const code = csv.charCodeAt(i);
21
+ if (code >= 48 && code <= 57) {
22
+ cur = cur * 10 + (code - 48);
23
+ hasDigit = true;
24
+ } else if (code === 44) {
25
+ if (hasDigit) out.push(cur);
26
+ cur = 0;
27
+ hasDigit = false;
28
+ }
29
+ }
30
+ if (hasDigit) out.push(cur);
31
+ return out;
43
32
  }
44
33
  function base64ToBytes(base64) {
45
- const trimmed = base64.trim();
46
- if (typeof globalThis.atob === "function") {
47
- const binary = globalThis.atob(trimmed);
48
- const bytes = new Uint8Array(binary.length);
49
- for (let i = 0; i < binary.length; i++) {
50
- bytes[i] = binary.charCodeAt(i);
51
- }
52
- return bytes;
53
- }
54
- const buf = Buffer.from(trimmed, "base64");
55
- return new Uint8Array(buf.buffer, buf.byteOffset, buf.byteLength);
34
+ const binary = globalThis.atob(base64.trim());
35
+ const bytes = new Uint8Array(binary.length);
36
+ for (let i = 0; i < binary.length; i++) bytes[i] = binary.charCodeAt(i);
37
+ return bytes;
56
38
  }
57
39
  function bytesToGids(bytes) {
58
- const view = new DataView(bytes.buffer, bytes.byteOffset, bytes.byteLength);
59
- const count = bytes.byteLength / 4;
60
- const gids = new Array(count);
61
- for (let i = 0; i < count; i++) {
62
- gids[i] = view.getUint32(i * 4, true);
63
- }
64
- return gids;
40
+ const view = new DataView(bytes.buffer, bytes.byteOffset, bytes.byteLength);
41
+ const count = bytes.byteLength / 4;
42
+ const gids = new Array(count);
43
+ for (let i = 0; i < count; i++) gids[i] = view.getUint32(i * 4, true);
44
+ return gids;
65
45
  }
66
46
  async function decompressBytesAsync(bytes, compression) {
67
- const format = compression === "gzip" ? "gzip" : "deflate";
68
- const ds = new DecompressionStream(format);
69
- const writer = ds.writable.getWriter();
70
- writer.write(bytes);
71
- writer.close();
72
- const reader = ds.readable.getReader();
73
- const chunks = [];
74
- let totalLength = 0;
75
- for (; ; ) {
76
- const { done, value } = await reader.read();
77
- if (done) break;
78
- chunks.push(value);
79
- totalLength += value.byteLength;
80
- }
81
- const result = new Uint8Array(totalLength);
82
- let offset = 0;
83
- for (const chunk of chunks) {
84
- result.set(chunk, offset);
85
- offset += chunk.byteLength;
86
- }
87
- return result;
47
+ const format = compression === "gzip" ? "gzip" : "deflate";
48
+ const ds = new DecompressionStream(format);
49
+ const writer = ds.writable.getWriter();
50
+ writer.write(bytes);
51
+ writer.close();
52
+ const reader = ds.readable.getReader();
53
+ const chunks = [];
54
+ let totalLength = 0;
55
+ for (;;) {
56
+ const { done, value } = await reader.read();
57
+ if (done) break;
58
+ chunks.push(value);
59
+ totalLength += value.byteLength;
60
+ }
61
+ const result = new Uint8Array(totalLength);
62
+ let offset = 0;
63
+ for (const chunk of chunks) {
64
+ result.set(chunk, offset);
65
+ offset += chunk.byteLength;
66
+ }
67
+ return result;
88
68
  }
89
69
  function decompressBytes(_bytes, compression) {
90
- if (typeof DecompressionStream !== "undefined") {
91
- throw new Error(
92
- `Compressed tile data (${compression}) requires the async parser. Use parseMapAsync() instead of parseMap() for compressed maps.`
93
- );
94
- }
95
- throw new Error(
96
- `Compressed tile data (${compression}) is not supported in this environment. DecompressionStream API is required.`
97
- );
70
+ if (typeof DecompressionStream !== "undefined") throw new Error(`Compressed tile data (${compression}) requires the async parser. Use parseMapAsync() instead of parseMap() for compressed maps.`);
71
+ throw new Error(`Compressed tile data (${compression}) is not supported in this environment. DecompressionStream API is required.`);
98
72
  }
99
73
  async function decodeLayerDataAsync(data, encoding, compression) {
100
- if (Array.isArray(data)) {
101
- return data;
102
- }
103
- if (encoding === "base64") {
104
- const bytes = base64ToBytes(data);
105
- if (compression === "gzip" || compression === "zlib") {
106
- const decompressed = await decompressBytesAsync(bytes, compression);
107
- return bytesToGids(decompressed);
108
- }
109
- if (compression === "zstd") {
110
- throw new Error("zstd compression is not supported in the browser");
111
- }
112
- return bytesToGids(bytes);
113
- }
114
- throw new Error(`Unsupported encoding: ${encoding ?? "unknown"}`);
115
- }
116
-
117
- // src/parser/resolveMap.ts
74
+ if (Array.isArray(data)) return data;
75
+ if (encoding === "csv") return csvToGids(data);
76
+ if (encoding === "base64") {
77
+ const bytes = base64ToBytes(data);
78
+ if (compression === "gzip" || compression === "zlib") return bytesToGids(await decompressBytesAsync(bytes, compression));
79
+ if (compression === "zstd") throw new Error("zstd compression is not supported in the browser");
80
+ return bytesToGids(bytes);
81
+ }
82
+ throw new Error(`Unsupported encoding: ${encoding ?? "unknown"}`);
83
+ }
84
+ //#endregion
85
+ //#region src/types/index.ts
86
+ const FLIPPED_HORIZONTALLY_FLAG = 2147483648;
87
+ const FLIPPED_VERTICALLY_FLAG = 1073741824;
88
+ const FLIPPED_DIAGONALLY_FLAG = 536870912;
89
+ const ROTATED_HEXAGONAL_120_FLAG = 268435456;
90
+ const GID_MASK = 268435455;
91
+ //#endregion
92
+ //#region src/parser/decodeGid.ts
93
+ function decodeGid(raw) {
94
+ const gid = raw & GID_MASK;
95
+ if (gid === 0) return null;
96
+ return {
97
+ gid,
98
+ localId: 0,
99
+ tilesetIndex: 0,
100
+ horizontalFlip: (raw & FLIPPED_HORIZONTALLY_FLAG) !== 0,
101
+ verticalFlip: (raw & FLIPPED_VERTICALLY_FLAG) !== 0,
102
+ diagonalFlip: (raw & FLIPPED_DIAGONALLY_FLAG) !== 0
103
+ };
104
+ }
105
+ //#endregion
106
+ //#region src/parser/tilesetHelpers.ts
118
107
  function isTilesetRef(ts) {
119
- return "source" in ts && !("name" in ts);
120
- }
121
- function resolveTileset(raw) {
122
- const tiles = /* @__PURE__ */ new Map();
123
- if (raw.tiles) {
124
- for (const tile of raw.tiles) {
125
- tiles.set(tile.id, tile);
126
- }
127
- }
128
- return {
129
- firstgid: raw.firstgid,
130
- name: raw.name,
131
- tilewidth: raw.tilewidth,
132
- tileheight: raw.tileheight,
133
- columns: raw.columns > 0 ? raw.columns : raw.imagewidth && raw.tilewidth > 0 ? Math.floor((raw.imagewidth - 2 * raw.margin + raw.spacing) / (raw.tilewidth + raw.spacing)) : 0,
134
- tilecount: raw.tilecount,
135
- margin: raw.margin,
136
- spacing: raw.spacing,
137
- image: raw.image,
138
- imagewidth: raw.imagewidth,
139
- imageheight: raw.imageheight,
140
- tileoffset: raw.tileoffset ?? { x: 0, y: 0 },
141
- objectalignment: raw.objectalignment ?? "unspecified",
142
- tilerendersize: raw.tilerendersize ?? "tile",
143
- fillmode: raw.fillmode ?? "stretch",
144
- tiles,
145
- properties: raw.properties ?? [],
146
- transformations: raw.transformations,
147
- grid: raw.grid,
148
- wangsets: raw.wangsets,
149
- terrains: raw.terrains
150
- };
151
- }
152
- function findTilesetIndex(gid, tilesets) {
153
- for (let i = tilesets.length - 1; i >= 0; i--) {
154
- const ts = tilesets[i];
155
- if (ts && ts.firstgid <= gid) {
156
- return i;
157
- }
158
- }
159
- return 0;
160
- }
161
- function resolveGids(rawGids, tilesets) {
162
- const result = new Array(rawGids.length);
163
- for (let i = 0; i < rawGids.length; i++) {
164
- const rawGid = rawGids[i];
165
- if (rawGid === void 0 || rawGid === 0) {
166
- result[i] = null;
167
- continue;
168
- }
169
- const decoded = decodeGid(rawGid);
170
- if (!decoded) {
171
- result[i] = null;
172
- continue;
173
- }
174
- const tsIdx = findTilesetIndex(decoded.gid, tilesets);
175
- const ts = tilesets[tsIdx];
176
- if (ts) {
177
- decoded.tilesetIndex = tsIdx;
178
- decoded.localId = decoded.gid - ts.firstgid;
179
- }
180
- result[i] = decoded;
181
- }
182
- return result;
183
- }
184
- function layerDefaults(layer) {
185
- return {
186
- id: layer.id,
187
- name: layer.name,
188
- opacity: layer.opacity,
189
- visible: layer.visible,
190
- offsetx: layer.offsetx ?? 0,
191
- offsety: layer.offsety ?? 0,
192
- parallaxx: layer.parallaxx ?? 1,
193
- parallaxy: layer.parallaxy ?? 1,
194
- tintcolor: layer.tintcolor,
195
- properties: layer.properties ?? []
196
- };
197
- }
198
- function resolveLayer(layer, tilesets) {
199
- switch (layer.type) {
200
- case "tilelayer": {
201
- const hasChunks = layer.chunks && layer.chunks.length > 0;
202
- if (hasChunks) {
203
- const resolvedChunks = resolveChunksSync(
204
- layer.chunks,
205
- layer.encoding,
206
- layer.compression,
207
- tilesets
208
- );
209
- return {
210
- type: "tilelayer",
211
- ...layerDefaults(layer),
212
- width: layer.width ?? 0,
213
- height: layer.height ?? 0,
214
- infinite: true,
215
- tiles: [],
216
- chunks: resolvedChunks
217
- };
218
- }
219
- const rawGids = decodeLayerData(layer.data ?? [], layer.encoding, layer.compression);
220
- const tiles = resolveGids(rawGids, tilesets);
221
- return {
222
- type: "tilelayer",
223
- ...layerDefaults(layer),
224
- width: layer.width ?? 0,
225
- height: layer.height ?? 0,
226
- infinite: false,
227
- tiles
228
- };
229
- }
230
- case "imagelayer":
231
- return {
232
- type: "imagelayer",
233
- ...layerDefaults(layer),
234
- image: layer.image ?? "",
235
- imagewidth: layer.imagewidth,
236
- imageheight: layer.imageheight,
237
- repeatx: layer.repeatx ?? false,
238
- repeaty: layer.repeaty ?? false,
239
- transparentcolor: layer.transparentcolor
240
- };
241
- case "objectgroup":
242
- return {
243
- type: "objectgroup",
244
- ...layerDefaults(layer),
245
- draworder: layer.draworder ?? "topdown",
246
- objects: layer.objects ?? []
247
- };
248
- case "group":
249
- return {
250
- type: "group",
251
- ...layerDefaults(layer),
252
- layers: (layer.layers ?? []).map((l) => resolveLayer(l, tilesets))
253
- };
254
- }
255
- }
256
- async function resolveLayerAsync(layer, tilesets) {
257
- switch (layer.type) {
258
- case "tilelayer": {
259
- const hasChunks = layer.chunks && layer.chunks.length > 0;
260
- if (hasChunks) {
261
- const resolvedChunks = await resolveChunksAsync(
262
- layer.chunks,
263
- layer.encoding,
264
- layer.compression,
265
- tilesets
266
- );
267
- return {
268
- type: "tilelayer",
269
- ...layerDefaults(layer),
270
- width: layer.width ?? 0,
271
- height: layer.height ?? 0,
272
- infinite: true,
273
- tiles: [],
274
- chunks: resolvedChunks
275
- };
276
- }
277
- const rawGids = await decodeLayerDataAsync(
278
- layer.data ?? [],
279
- layer.encoding,
280
- layer.compression
281
- );
282
- const tiles = resolveGids(rawGids, tilesets);
283
- return {
284
- type: "tilelayer",
285
- ...layerDefaults(layer),
286
- width: layer.width ?? 0,
287
- height: layer.height ?? 0,
288
- infinite: false,
289
- tiles
290
- };
291
- }
292
- case "imagelayer":
293
- return {
294
- type: "imagelayer",
295
- ...layerDefaults(layer),
296
- image: layer.image ?? "",
297
- imagewidth: layer.imagewidth,
298
- imageheight: layer.imageheight,
299
- repeatx: layer.repeatx ?? false,
300
- repeaty: layer.repeaty ?? false,
301
- transparentcolor: layer.transparentcolor
302
- };
303
- case "objectgroup":
304
- return {
305
- type: "objectgroup",
306
- ...layerDefaults(layer),
307
- draworder: layer.draworder ?? "topdown",
308
- objects: layer.objects ?? []
309
- };
310
- case "group": {
311
- const resolvedChildren = await Promise.all(
312
- (layer.layers ?? []).map((l) => resolveLayerAsync(l, tilesets))
313
- );
314
- return {
315
- type: "group",
316
- ...layerDefaults(layer),
317
- layers: resolvedChildren
318
- };
319
- }
320
- }
321
- }
322
- function parseMap(data, options) {
323
- const resolvedTilesets = resolveTilesets(data.tilesets, options);
324
- const layers = data.layers.map((l) => resolveLayer(l, resolvedTilesets));
325
- return buildResolvedMap(data, resolvedTilesets, layers);
326
- }
327
- async function parseMapAsync(data, options) {
328
- const resolvedTilesets = resolveTilesets(data.tilesets, options);
329
- const layers = await Promise.all(data.layers.map((l) => resolveLayerAsync(l, resolvedTilesets)));
330
- return buildResolvedMap(data, resolvedTilesets, layers);
331
- }
332
- function resolveChunksSync(chunks, encoding, compression, tilesets) {
333
- return chunks.map((chunk) => {
334
- const rawGids = decodeLayerData(chunk.data, encoding, compression);
335
- return {
336
- x: chunk.x,
337
- y: chunk.y,
338
- width: chunk.width,
339
- height: chunk.height,
340
- tiles: resolveGids(rawGids, tilesets)
341
- };
342
- });
343
- }
344
- async function resolveChunksAsync(chunks, encoding, compression, tilesets) {
345
- return Promise.all(
346
- chunks.map(async (chunk) => {
347
- const rawGids = await decodeLayerDataAsync(chunk.data, encoding, compression);
348
- return {
349
- x: chunk.x,
350
- y: chunk.y,
351
- width: chunk.width,
352
- height: chunk.height,
353
- tiles: resolveGids(rawGids, tilesets)
354
- };
355
- })
356
- );
357
- }
358
- function resolveTilesets(raw, options) {
359
- return raw.map((ts) => {
360
- if (isTilesetRef(ts)) {
361
- const external = options?.externalTilesets?.get(ts.source);
362
- if (!external) {
363
- throw new Error(
364
- `External tileset "${ts.source}" not provided. Pass it via options.externalTilesets.`
365
- );
366
- }
367
- return resolveTileset({ ...external, firstgid: ts.firstgid });
368
- }
369
- return resolveTileset(ts);
370
- });
371
- }
372
- function buildResolvedMap(data, tilesets, layers) {
373
- return {
374
- orientation: data.orientation,
375
- renderorder: data.renderorder ?? "right-down",
376
- width: data.width,
377
- height: data.height,
378
- tilewidth: data.tilewidth,
379
- tileheight: data.tileheight,
380
- infinite: data.infinite,
381
- backgroundcolor: data.backgroundcolor,
382
- hexsidelength: data.hexsidelength,
383
- staggeraxis: data.staggeraxis,
384
- staggerindex: data.staggerindex,
385
- parallaxoriginx: data.parallaxoriginx ?? 0,
386
- parallaxoriginy: data.parallaxoriginy ?? 0,
387
- properties: data.properties ?? [],
388
- tilesets,
389
- layers,
390
- version: data.version,
391
- tiledversion: data.tiledversion
392
- };
393
- }
394
-
395
- // src/parser/parseTmx.ts
108
+ return "source" in ts && !("name" in ts);
109
+ }
110
+ function computeTilesetColumns(ts) {
111
+ if (ts.columns && ts.columns > 0) return ts.columns;
112
+ if (!ts.imagewidth || ts.tilewidth <= 0) return 0;
113
+ return Math.floor((ts.imagewidth - 2 * ts.margin + ts.spacing) / (ts.tilewidth + ts.spacing));
114
+ }
115
+ /**
116
+ * Finds the index of the tileset whose firstgid range contains `gid`.
117
+ * Works with any array where each element exposes `firstgid`.
118
+ * Returns -1 when no tileset matches.
119
+ */
120
+ function findTilesetIndexForGid(gid, tilesets) {
121
+ for (let i = tilesets.length - 1; i >= 0; i--) {
122
+ const ts = tilesets[i];
123
+ if (ts && ts.firstgid <= gid) return i;
124
+ }
125
+ return -1;
126
+ }
127
+ //#endregion
128
+ //#region src/parser/parseTmx.ts
396
129
  function str(el, name, fallback = "") {
397
- return el.getAttribute(name) ?? fallback;
130
+ return el.getAttribute(name) ?? fallback;
398
131
  }
399
132
  function int(el, name, fallback = 0) {
400
- const v = el.getAttribute(name);
401
- return v != null ? parseInt(v, 10) : fallback;
133
+ const v = el.getAttribute(name);
134
+ return v != null ? parseInt(v, 10) : fallback;
402
135
  }
403
136
  function float(el, name, fallback = 0) {
404
- const v = el.getAttribute(name);
405
- return v != null ? parseFloat(v) : fallback;
137
+ const v = el.getAttribute(name);
138
+ return v != null ? parseFloat(v) : fallback;
406
139
  }
407
140
  function bool(el, name, fallback = false) {
408
- const v = el.getAttribute(name);
409
- if (v == null) return fallback;
410
- return v === "1" || v === "true";
141
+ const v = el.getAttribute(name);
142
+ if (v == null) return fallback;
143
+ return v === "1" || v === "true";
411
144
  }
412
145
  function optStr(el, name) {
413
- const v = el.getAttribute(name);
414
- return v != null ? v : void 0;
146
+ const v = el.getAttribute(name);
147
+ return v != null ? v : void 0;
415
148
  }
416
149
  function optInt(el, name) {
417
- const v = el.getAttribute(name);
418
- return v != null ? parseInt(v, 10) : void 0;
150
+ const v = el.getAttribute(name);
151
+ return v != null ? parseInt(v, 10) : void 0;
419
152
  }
420
153
  function optFloat(el, name) {
421
- const v = el.getAttribute(name);
422
- return v != null ? parseFloat(v) : void 0;
154
+ const v = el.getAttribute(name);
155
+ return v != null ? parseFloat(v) : void 0;
423
156
  }
424
157
  function children(el, tag) {
425
- const result = [];
426
- for (let i = 0; i < el.children.length; i++) {
427
- const child2 = el.children[i];
428
- if (child2.tagName === tag) result.push(child2);
429
- }
430
- return result;
158
+ const result = [];
159
+ for (let i = 0; i < el.children.length; i++) {
160
+ const child = el.children[i];
161
+ if (child.tagName === tag) result.push(child);
162
+ }
163
+ return result;
431
164
  }
432
165
  function child(el, tag) {
433
- for (let i = 0; i < el.children.length; i++) {
434
- const c = el.children[i];
435
- if (c.tagName === tag) return c;
436
- }
437
- return null;
166
+ for (let i = 0; i < el.children.length; i++) {
167
+ const c = el.children[i];
168
+ if (c.tagName === tag) return c;
169
+ }
170
+ return null;
438
171
  }
439
172
  function parseProperties(el) {
440
- const propsEl = child(el, "properties");
441
- if (!propsEl) return void 0;
442
- const props = [];
443
- for (const pEl of children(propsEl, "property")) {
444
- const type = str(pEl, "type", "string");
445
- let value = str(pEl, "value", "");
446
- if (!pEl.hasAttribute("value")) {
447
- value = pEl.textContent ?? "";
448
- }
449
- if (type === "int") value = parseInt(value, 10);
450
- else if (type === "float") value = parseFloat(value);
451
- else if (type === "bool") value = value === "true";
452
- props.push({
453
- name: str(pEl, "name"),
454
- type,
455
- propertytype: optStr(pEl, "propertytype"),
456
- value
457
- });
458
- }
459
- return props.length > 0 ? props : void 0;
173
+ const propsEl = child(el, "properties");
174
+ if (!propsEl) return void 0;
175
+ const props = [];
176
+ for (const pEl of children(propsEl, "property")) {
177
+ const type = str(pEl, "type", "string");
178
+ let value = str(pEl, "value", "");
179
+ if (!pEl.hasAttribute("value")) value = pEl.textContent ?? "";
180
+ if (type === "int") value = parseInt(value, 10);
181
+ else if (type === "float") value = parseFloat(value);
182
+ else if (type === "bool") value = value === "true";
183
+ props.push({
184
+ name: str(pEl, "name"),
185
+ type,
186
+ propertytype: optStr(pEl, "propertytype"),
187
+ value
188
+ });
189
+ }
190
+ return props.length > 0 ? props : void 0;
460
191
  }
461
192
  function parseImage(el) {
462
- const imgEl = child(el, "image");
463
- if (!imgEl) return {};
464
- return {
465
- image: optStr(imgEl, "source"),
466
- imagewidth: optInt(imgEl, "width"),
467
- imageheight: optInt(imgEl, "height"),
468
- transparentcolor: optStr(imgEl, "trans")
469
- };
193
+ const imgEl = child(el, "image");
194
+ if (!imgEl) return {};
195
+ return {
196
+ image: optStr(imgEl, "source"),
197
+ imagewidth: optInt(imgEl, "width"),
198
+ imageheight: optInt(imgEl, "height"),
199
+ transparentcolor: optStr(imgEl, "trans")
200
+ };
470
201
  }
471
202
  function parseTileOffset(el) {
472
- const to = child(el, "tileoffset");
473
- if (!to) return void 0;
474
- return { x: int(to, "x"), y: int(to, "y") };
203
+ const to = child(el, "tileoffset");
204
+ if (!to) return void 0;
205
+ return {
206
+ x: int(to, "x"),
207
+ y: int(to, "y")
208
+ };
475
209
  }
476
210
  function parseGrid(el) {
477
- const g = child(el, "grid");
478
- if (!g) return void 0;
479
- return {
480
- orientation: str(g, "orientation", "orthogonal"),
481
- width: int(g, "width"),
482
- height: int(g, "height")
483
- };
211
+ const g = child(el, "grid");
212
+ if (!g) return void 0;
213
+ return {
214
+ orientation: str(g, "orientation", "orthogonal"),
215
+ width: int(g, "width"),
216
+ height: int(g, "height")
217
+ };
484
218
  }
485
219
  function parseTransformations(el) {
486
- const t = child(el, "transformations");
487
- if (!t) return void 0;
488
- return {
489
- hflip: bool(t, "hflip"),
490
- vflip: bool(t, "vflip"),
491
- rotate: bool(t, "rotate"),
492
- preferuntransformed: bool(t, "preferuntransformed")
493
- };
220
+ const t = child(el, "transformations");
221
+ if (!t) return void 0;
222
+ return {
223
+ hflip: bool(t, "hflip"),
224
+ vflip: bool(t, "vflip"),
225
+ rotate: bool(t, "rotate"),
226
+ preferuntransformed: bool(t, "preferuntransformed")
227
+ };
494
228
  }
495
229
  function parseTerrains(el) {
496
- const ttEl = child(el, "terraintypes");
497
- if (!ttEl) return void 0;
498
- return children(ttEl, "terrain").map((t) => ({
499
- name: str(t, "name"),
500
- tile: int(t, "tile"),
501
- properties: parseProperties(t)
502
- }));
230
+ const ttEl = child(el, "terraintypes");
231
+ if (!ttEl) return void 0;
232
+ return children(ttEl, "terrain").map((t) => ({
233
+ name: str(t, "name"),
234
+ tile: int(t, "tile"),
235
+ properties: parseProperties(t)
236
+ }));
503
237
  }
504
238
  function parseWangSets(el) {
505
- const wsEl = child(el, "wangsets");
506
- if (!wsEl) return void 0;
507
- return children(wsEl, "wangset").map((ws) => {
508
- const colors = children(ws, "wangcolor").map((wc) => ({
509
- class: optStr(wc, "class"),
510
- color: str(wc, "color"),
511
- name: str(wc, "name"),
512
- probability: float(wc, "probability"),
513
- tile: int(wc, "tile"),
514
- properties: parseProperties(wc)
515
- }));
516
- const tiles = children(ws, "wangtile").map((wt) => ({
517
- tileid: int(wt, "tileid"),
518
- wangid: str(wt, "wangid").split(",").map(Number)
519
- }));
520
- return {
521
- class: optStr(ws, "class"),
522
- colors,
523
- name: str(ws, "name"),
524
- properties: parseProperties(ws),
525
- tile: int(ws, "tile"),
526
- type: str(ws, "type", "corner"),
527
- wangtiles: tiles
528
- };
529
- });
239
+ const wsEl = child(el, "wangsets");
240
+ if (!wsEl) return void 0;
241
+ return children(wsEl, "wangset").map((ws) => {
242
+ const colors = children(ws, "wangcolor").map((wc) => ({
243
+ class: optStr(wc, "class"),
244
+ color: str(wc, "color"),
245
+ name: str(wc, "name"),
246
+ probability: float(wc, "probability"),
247
+ tile: int(wc, "tile"),
248
+ properties: parseProperties(wc)
249
+ }));
250
+ const tiles = children(ws, "wangtile").map((wt) => ({
251
+ tileid: int(wt, "tileid"),
252
+ wangid: str(wt, "wangid").split(",").map(Number)
253
+ }));
254
+ return {
255
+ class: optStr(ws, "class"),
256
+ colors,
257
+ name: str(ws, "name"),
258
+ properties: parseProperties(ws),
259
+ tile: int(ws, "tile"),
260
+ type: str(ws, "type", "corner"),
261
+ wangtiles: tiles
262
+ };
263
+ });
530
264
  }
531
265
  function parseAnimation(el) {
532
- const animEl = child(el, "animation");
533
- if (!animEl) return void 0;
534
- return children(animEl, "frame").map((f) => ({
535
- tileid: int(f, "tileid"),
536
- duration: int(f, "duration")
537
- }));
266
+ const animEl = child(el, "animation");
267
+ if (!animEl) return void 0;
268
+ return children(animEl, "frame").map((f) => ({
269
+ tileid: int(f, "tileid"),
270
+ duration: int(f, "duration")
271
+ }));
538
272
  }
539
273
  function parseTileDefinitions(el) {
540
- const tileEls = children(el, "tile");
541
- if (tileEls.length === 0) return void 0;
542
- return tileEls.map((t) => {
543
- const img = parseImage(t);
544
- const terrainAttr = optStr(t, "terrain");
545
- const terrain = terrainAttr ? terrainAttr.split(",").map((v) => v === "" ? -1 : parseInt(v, 10)) : void 0;
546
- const ogEl = child(t, "objectgroup");
547
- const objectgroup = ogEl ? parseObjectGroup(ogEl) : void 0;
548
- const def = {
549
- id: int(t, "id"),
550
- type: optStr(t, "type") ?? optStr(t, "class"),
551
- probability: optFloat(t, "probability"),
552
- x: optInt(t, "x"),
553
- y: optInt(t, "y"),
554
- width: optInt(t, "width"),
555
- height: optInt(t, "height"),
556
- properties: parseProperties(t),
557
- animation: parseAnimation(t),
558
- terrain,
559
- objectgroup,
560
- ...img
561
- };
562
- return def;
563
- });
274
+ const tileEls = children(el, "tile");
275
+ if (tileEls.length === 0) return void 0;
276
+ return tileEls.map((t) => {
277
+ const img = parseImage(t);
278
+ const terrainAttr = optStr(t, "terrain");
279
+ const terrain = terrainAttr ? terrainAttr.split(",").map((v) => v === "" ? -1 : parseInt(v, 10)) : void 0;
280
+ const ogEl = child(t, "objectgroup");
281
+ const objectgroup = ogEl ? parseObjectGroup(ogEl) : void 0;
282
+ return {
283
+ id: int(t, "id"),
284
+ type: optStr(t, "type") ?? optStr(t, "class"),
285
+ probability: optFloat(t, "probability"),
286
+ x: optInt(t, "x"),
287
+ y: optInt(t, "y"),
288
+ width: optInt(t, "width"),
289
+ height: optInt(t, "height"),
290
+ properties: parseProperties(t),
291
+ animation: parseAnimation(t),
292
+ terrain,
293
+ objectgroup,
294
+ ...img
295
+ };
296
+ });
564
297
  }
565
298
  function parseTileset(el) {
566
- const source = optStr(el, "source");
567
- if (source) {
568
- return {
569
- firstgid: int(el, "firstgid"),
570
- source
571
- };
572
- }
573
- const img = parseImage(el);
574
- const tiles = parseTileDefinitions(el);
575
- const tilewidth = int(el, "tilewidth");
576
- const spacing = int(el, "spacing");
577
- const margin = int(el, "margin");
578
- const rawColumns = int(el, "columns");
579
- const columns = rawColumns > 0 ? rawColumns : img.imagewidth && tilewidth > 0 ? Math.floor((img.imagewidth - 2 * margin + spacing) / (tilewidth + spacing)) : 0;
580
- return {
581
- backgroundcolor: optStr(el, "backgroundcolor"),
582
- class: optStr(el, "class"),
583
- columns,
584
- fillmode: optStr(el, "fillmode"),
585
- firstgid: int(el, "firstgid"),
586
- grid: parseGrid(el),
587
- margin,
588
- name: str(el, "name"),
589
- objectalignment: optStr(el, "objectalignment"),
590
- properties: parseProperties(el),
591
- spacing,
592
- terrains: parseTerrains(el),
593
- tilecount: int(el, "tilecount"),
594
- tileheight: int(el, "tileheight"),
595
- tileoffset: parseTileOffset(el),
596
- tilerendersize: optStr(el, "tilerendersize"),
597
- tiles,
598
- tilewidth,
599
- transformations: parseTransformations(el),
600
- wangsets: parseWangSets(el),
601
- ...img
602
- };
299
+ const source = optStr(el, "source");
300
+ if (source) return {
301
+ firstgid: int(el, "firstgid"),
302
+ source
303
+ };
304
+ const img = parseImage(el);
305
+ const tiles = parseTileDefinitions(el);
306
+ const tilewidth = int(el, "tilewidth");
307
+ const spacing = int(el, "spacing");
308
+ const margin = int(el, "margin");
309
+ const columns = computeTilesetColumns({
310
+ columns: int(el, "columns"),
311
+ imagewidth: img.imagewidth,
312
+ tilewidth,
313
+ margin,
314
+ spacing
315
+ });
316
+ return {
317
+ backgroundcolor: optStr(el, "backgroundcolor"),
318
+ class: optStr(el, "class"),
319
+ columns,
320
+ fillmode: optStr(el, "fillmode"),
321
+ firstgid: int(el, "firstgid"),
322
+ grid: parseGrid(el),
323
+ margin,
324
+ name: str(el, "name"),
325
+ objectalignment: optStr(el, "objectalignment"),
326
+ properties: parseProperties(el),
327
+ spacing,
328
+ terrains: parseTerrains(el),
329
+ tilecount: int(el, "tilecount"),
330
+ tileheight: int(el, "tileheight"),
331
+ tileoffset: parseTileOffset(el),
332
+ tilerendersize: optStr(el, "tilerendersize"),
333
+ tiles,
334
+ tilewidth,
335
+ transformations: parseTransformations(el),
336
+ wangsets: parseWangSets(el),
337
+ ...img
338
+ };
603
339
  }
604
340
  function parseData(dataEl) {
605
- const encoding = optStr(dataEl, "encoding");
606
- const compression = optStr(dataEl, "compression");
607
- const chunkEls = children(dataEl, "chunk");
608
- if (chunkEls.length > 0) {
609
- const chunks = chunkEls.map((c) => ({
610
- x: int(c, "x"),
611
- y: int(c, "y"),
612
- width: int(c, "width"),
613
- height: int(c, "height"),
614
- data: parseDataContent(c, encoding)
615
- }));
616
- return { encoding, compression, chunks };
617
- }
618
- return {
619
- data: parseDataContent(dataEl, encoding),
620
- encoding,
621
- compression
622
- };
341
+ const encoding = optStr(dataEl, "encoding");
342
+ const compression = optStr(dataEl, "compression");
343
+ const chunkEls = children(dataEl, "chunk");
344
+ if (chunkEls.length > 0) return {
345
+ encoding,
346
+ compression,
347
+ chunks: chunkEls.map((c) => ({
348
+ x: int(c, "x"),
349
+ y: int(c, "y"),
350
+ width: int(c, "width"),
351
+ height: int(c, "height"),
352
+ data: parseDataContent(c, encoding)
353
+ }))
354
+ };
355
+ return {
356
+ data: parseDataContent(dataEl, encoding),
357
+ encoding,
358
+ compression
359
+ };
623
360
  }
624
361
  function parseDataContent(el, encoding) {
625
- if (encoding === "base64") {
626
- return (el.textContent ?? "").trim();
627
- }
628
- if (encoding === "csv") {
629
- return (el.textContent ?? "").trim().split(",").map((s) => parseInt(s.trim(), 10));
630
- }
631
- const tileEls = children(el, "tile");
632
- return tileEls.map((t) => int(t, "gid"));
362
+ if (encoding === "base64") return (el.textContent ?? "").trim();
363
+ if (encoding === "csv") {
364
+ const out = [];
365
+ for (const token of (el.textContent ?? "").split(",")) {
366
+ const trimmed = token.trim();
367
+ if (trimmed.length === 0) continue;
368
+ out.push(parseInt(trimmed, 10));
369
+ }
370
+ return out;
371
+ }
372
+ return children(el, "tile").map((t) => int(t, "gid"));
633
373
  }
634
374
  function parsePoints(pointStr) {
635
- return pointStr.trim().split(/\s+/).map((pair) => {
636
- const [x, y] = pair.split(",").map(Number);
637
- return { x, y };
638
- });
375
+ return pointStr.trim().split(/\s+/).map((pair) => {
376
+ const [x, y] = pair.split(",").map(Number);
377
+ return {
378
+ x,
379
+ y
380
+ };
381
+ });
639
382
  }
640
383
  function parseTextObject(el) {
641
- return {
642
- text: el.textContent ?? "",
643
- fontfamily: optStr(el, "fontfamily"),
644
- pixelsize: optInt(el, "pixelsize"),
645
- wrap: bool(el, "wrap") ? true : void 0,
646
- color: optStr(el, "color"),
647
- bold: bool(el, "bold") ? true : void 0,
648
- italic: bool(el, "italic") ? true : void 0,
649
- underline: bool(el, "underline") ? true : void 0,
650
- strikeout: bool(el, "strikeout") ? true : void 0,
651
- kerning: el.hasAttribute("kerning") ? bool(el, "kerning", true) : void 0,
652
- halign: optStr(el, "halign"),
653
- valign: optStr(el, "valign")
654
- };
384
+ return {
385
+ text: el.textContent ?? "",
386
+ fontfamily: optStr(el, "fontfamily"),
387
+ pixelsize: optInt(el, "pixelsize"),
388
+ wrap: bool(el, "wrap") ? true : void 0,
389
+ color: optStr(el, "color"),
390
+ bold: bool(el, "bold") ? true : void 0,
391
+ italic: bool(el, "italic") ? true : void 0,
392
+ underline: bool(el, "underline") ? true : void 0,
393
+ strikeout: bool(el, "strikeout") ? true : void 0,
394
+ kerning: el.hasAttribute("kerning") ? bool(el, "kerning", true) : void 0,
395
+ halign: optStr(el, "halign"),
396
+ valign: optStr(el, "valign")
397
+ };
655
398
  }
656
399
  function parseObject(el) {
657
- const obj = {
658
- id: int(el, "id"),
659
- name: str(el, "name"),
660
- type: str(el, "type") || str(el, "class"),
661
- x: float(el, "x"),
662
- y: float(el, "y"),
663
- width: float(el, "width"),
664
- height: float(el, "height"),
665
- rotation: float(el, "rotation"),
666
- visible: el.hasAttribute("visible") ? bool(el, "visible", true) : true,
667
- properties: parseProperties(el)
668
- };
669
- const gid = optInt(el, "gid");
670
- if (gid != null) obj.gid = gid;
671
- const template = optStr(el, "template");
672
- if (template) obj.template = template;
673
- if (child(el, "ellipse")) obj.ellipse = true;
674
- if (child(el, "point")) obj.point = true;
675
- const polygonEl = child(el, "polygon");
676
- if (polygonEl) {
677
- obj.polygon = parsePoints(str(polygonEl, "points"));
678
- }
679
- const polylineEl = child(el, "polyline");
680
- if (polylineEl) {
681
- obj.polyline = parsePoints(str(polylineEl, "points"));
682
- }
683
- const textEl = child(el, "text");
684
- if (textEl) {
685
- obj.text = parseTextObject(textEl);
686
- }
687
- return obj;
400
+ const obj = {
401
+ id: int(el, "id"),
402
+ name: str(el, "name"),
403
+ type: str(el, "type") || str(el, "class"),
404
+ x: float(el, "x"),
405
+ y: float(el, "y"),
406
+ width: float(el, "width"),
407
+ height: float(el, "height"),
408
+ rotation: float(el, "rotation"),
409
+ visible: el.hasAttribute("visible") ? bool(el, "visible", true) : true,
410
+ properties: parseProperties(el)
411
+ };
412
+ const gid = optInt(el, "gid");
413
+ if (gid != null) obj.gid = gid;
414
+ const template = optStr(el, "template");
415
+ if (template) obj.template = template;
416
+ if (child(el, "ellipse")) obj.ellipse = true;
417
+ if (child(el, "point")) obj.point = true;
418
+ const polygonEl = child(el, "polygon");
419
+ if (polygonEl) obj.polygon = parsePoints(str(polygonEl, "points"));
420
+ const polylineEl = child(el, "polyline");
421
+ if (polylineEl) obj.polyline = parsePoints(str(polylineEl, "points"));
422
+ const textEl = child(el, "text");
423
+ if (textEl) obj.text = parseTextObject(textEl);
424
+ return obj;
688
425
  }
689
426
  function parseLayerCommon(el) {
690
- return {
691
- id: int(el, "id"),
692
- name: str(el, "name"),
693
- class: optStr(el, "class"),
694
- opacity: float(el, "opacity", 1),
695
- visible: el.hasAttribute("visible") ? bool(el, "visible", true) : true,
696
- tintcolor: optStr(el, "tintcolor"),
697
- offsetx: optFloat(el, "offsetx"),
698
- offsety: optFloat(el, "offsety"),
699
- parallaxx: optFloat(el, "parallaxx"),
700
- parallaxy: optFloat(el, "parallaxy"),
701
- locked: el.hasAttribute("locked") ? bool(el, "locked") : void 0,
702
- properties: parseProperties(el),
703
- x: int(el, "x"),
704
- y: int(el, "y")
705
- };
427
+ return {
428
+ id: int(el, "id"),
429
+ name: str(el, "name"),
430
+ class: optStr(el, "class"),
431
+ opacity: float(el, "opacity", 1),
432
+ visible: el.hasAttribute("visible") ? bool(el, "visible", true) : true,
433
+ tintcolor: optStr(el, "tintcolor"),
434
+ offsetx: optFloat(el, "offsetx"),
435
+ offsety: optFloat(el, "offsety"),
436
+ parallaxx: optFloat(el, "parallaxx"),
437
+ parallaxy: optFloat(el, "parallaxy"),
438
+ locked: el.hasAttribute("locked") ? bool(el, "locked") : void 0,
439
+ properties: parseProperties(el),
440
+ x: int(el, "x"),
441
+ y: int(el, "y")
442
+ };
706
443
  }
707
444
  function parseTileLayer(el) {
708
- const dataEl = child(el, "data");
709
- const dataInfo = dataEl ? parseData(dataEl) : {};
710
- return {
711
- ...parseLayerCommon(el),
712
- type: "tilelayer",
713
- width: optInt(el, "width"),
714
- height: optInt(el, "height"),
715
- startx: optInt(el, "startx"),
716
- starty: optInt(el, "starty"),
717
- ...dataInfo
718
- };
445
+ const dataEl = child(el, "data");
446
+ const dataInfo = dataEl ? parseData(dataEl) : {};
447
+ return {
448
+ ...parseLayerCommon(el),
449
+ type: "tilelayer",
450
+ width: optInt(el, "width"),
451
+ height: optInt(el, "height"),
452
+ startx: optInt(el, "startx"),
453
+ starty: optInt(el, "starty"),
454
+ ...dataInfo
455
+ };
719
456
  }
720
457
  function parseObjectGroup(el) {
721
- return {
722
- ...parseLayerCommon(el),
723
- type: "objectgroup",
724
- draworder: optStr(el, "draworder"),
725
- objects: children(el, "object").map(parseObject)
726
- };
458
+ return {
459
+ ...parseLayerCommon(el),
460
+ type: "objectgroup",
461
+ draworder: optStr(el, "draworder"),
462
+ objects: children(el, "object").map(parseObject)
463
+ };
727
464
  }
728
465
  function parseImageLayer(el) {
729
- const img = parseImage(el);
730
- return {
731
- ...parseLayerCommon(el),
732
- type: "imagelayer",
733
- repeatx: el.hasAttribute("repeatx") ? bool(el, "repeatx") : void 0,
734
- repeaty: el.hasAttribute("repeaty") ? bool(el, "repeaty") : void 0,
735
- transparentcolor: img.transparentcolor,
736
- image: img.image,
737
- imagewidth: img.imagewidth,
738
- imageheight: img.imageheight
739
- };
466
+ const img = parseImage(el);
467
+ return {
468
+ ...parseLayerCommon(el),
469
+ type: "imagelayer",
470
+ repeatx: el.hasAttribute("repeatx") ? bool(el, "repeatx") : void 0,
471
+ repeaty: el.hasAttribute("repeaty") ? bool(el, "repeaty") : void 0,
472
+ transparentcolor: img.transparentcolor,
473
+ image: img.image,
474
+ imagewidth: img.imagewidth,
475
+ imageheight: img.imageheight
476
+ };
740
477
  }
741
478
  function parseGroupLayer(el) {
742
- return {
743
- ...parseLayerCommon(el),
744
- type: "group",
745
- layers: parseLayers(el)
746
- };
479
+ return {
480
+ ...parseLayerCommon(el),
481
+ type: "group",
482
+ layers: parseLayers(el)
483
+ };
747
484
  }
748
485
  function parseLayers(parentEl) {
749
- const layers = [];
750
- for (let i = 0; i < parentEl.children.length; i++) {
751
- const el = parentEl.children[i];
752
- switch (el.tagName) {
753
- case "layer":
754
- layers.push(parseTileLayer(el));
755
- break;
756
- case "objectgroup":
757
- layers.push(parseObjectGroup(el));
758
- break;
759
- case "imagelayer":
760
- layers.push(parseImageLayer(el));
761
- break;
762
- case "group":
763
- layers.push(parseGroupLayer(el));
764
- break;
765
- }
766
- }
767
- return layers;
486
+ const layers = [];
487
+ for (let i = 0; i < parentEl.children.length; i++) {
488
+ const el = parentEl.children[i];
489
+ switch (el.tagName) {
490
+ case "layer":
491
+ layers.push(parseTileLayer(el));
492
+ break;
493
+ case "objectgroup":
494
+ layers.push(parseObjectGroup(el));
495
+ break;
496
+ case "imagelayer":
497
+ layers.push(parseImageLayer(el));
498
+ break;
499
+ case "group":
500
+ layers.push(parseGroupLayer(el));
501
+ break;
502
+ }
503
+ }
504
+ return layers;
768
505
  }
769
506
  function parseTmx(xml) {
770
- const parser = new DOMParser();
771
- const doc = parser.parseFromString(xml, "text/xml");
772
- const errorNode = doc.querySelector("parsererror");
773
- if (errorNode) {
774
- throw new Error(`TMX XML parse error: ${errorNode.textContent}`);
775
- }
776
- const mapEl = doc.documentElement;
777
- if (mapEl.tagName !== "map") {
778
- throw new Error(`Expected root <map> element, got <${mapEl.tagName}>`);
779
- }
780
- const tilesets = children(mapEl, "tileset").map(parseTileset);
781
- const layers = parseLayers(mapEl);
782
- return {
783
- backgroundcolor: optStr(mapEl, "backgroundcolor"),
784
- class: optStr(mapEl, "class"),
785
- compressionlevel: optInt(mapEl, "compressionlevel"),
786
- height: int(mapEl, "height"),
787
- hexsidelength: optInt(mapEl, "hexsidelength"),
788
- infinite: bool(mapEl, "infinite"),
789
- layers,
790
- nextlayerid: int(mapEl, "nextlayerid"),
791
- nextobjectid: int(mapEl, "nextobjectid"),
792
- orientation: str(mapEl, "orientation", "orthogonal"),
793
- parallaxoriginx: optFloat(mapEl, "parallaxoriginx"),
794
- parallaxoriginy: optFloat(mapEl, "parallaxoriginy"),
795
- properties: parseProperties(mapEl),
796
- renderorder: optStr(mapEl, "renderorder"),
797
- staggeraxis: optStr(mapEl, "staggeraxis"),
798
- staggerindex: optStr(mapEl, "staggerindex"),
799
- tiledversion: optStr(mapEl, "tiledversion"),
800
- tileheight: int(mapEl, "tileheight"),
801
- tilesets,
802
- tilewidth: int(mapEl, "tilewidth"),
803
- type: "map",
804
- version: str(mapEl, "version", "1.0"),
805
- width: int(mapEl, "width")
806
- };
507
+ const doc = new DOMParser().parseFromString(xml, "text/xml");
508
+ const errorNode = doc.querySelector("parsererror");
509
+ if (errorNode) throw new Error(`TMX XML parse error: ${errorNode.textContent}`);
510
+ const mapEl = doc.documentElement;
511
+ if (mapEl.tagName !== "map") throw new Error(`Expected root <map> element, got <${mapEl.tagName}>`);
512
+ const tilesets = children(mapEl, "tileset").map(parseTileset);
513
+ const layers = parseLayers(mapEl);
514
+ return {
515
+ backgroundcolor: optStr(mapEl, "backgroundcolor"),
516
+ class: optStr(mapEl, "class"),
517
+ compressionlevel: optInt(mapEl, "compressionlevel"),
518
+ height: int(mapEl, "height"),
519
+ hexsidelength: optInt(mapEl, "hexsidelength"),
520
+ infinite: bool(mapEl, "infinite"),
521
+ layers,
522
+ nextlayerid: int(mapEl, "nextlayerid"),
523
+ nextobjectid: int(mapEl, "nextobjectid"),
524
+ orientation: str(mapEl, "orientation", "orthogonal"),
525
+ parallaxoriginx: optFloat(mapEl, "parallaxoriginx"),
526
+ parallaxoriginy: optFloat(mapEl, "parallaxoriginy"),
527
+ properties: parseProperties(mapEl),
528
+ renderorder: optStr(mapEl, "renderorder"),
529
+ staggeraxis: optStr(mapEl, "staggeraxis"),
530
+ staggerindex: optStr(mapEl, "staggerindex"),
531
+ tiledversion: optStr(mapEl, "tiledversion"),
532
+ tileheight: int(mapEl, "tileheight"),
533
+ tilesets,
534
+ tilewidth: int(mapEl, "tilewidth"),
535
+ type: "map",
536
+ version: str(mapEl, "version", "1.0"),
537
+ width: int(mapEl, "width")
538
+ };
807
539
  }
808
540
  function parseTsx(xml) {
809
- const parser = new DOMParser();
810
- const doc = parser.parseFromString(xml, "text/xml");
811
- const errorNode = doc.querySelector("parsererror");
812
- if (errorNode) {
813
- throw new Error(`TSX XML parse error: ${errorNode.textContent}`);
814
- }
815
- const tsEl = doc.documentElement;
816
- if (tsEl.tagName !== "tileset") {
817
- throw new Error(`Expected root <tileset> element, got <${tsEl.tagName}>`);
818
- }
819
- const result = parseTileset(tsEl);
820
- if ("source" in result) {
821
- throw new Error("TSX file should not contain a source reference");
822
- }
823
- return result;
541
+ const doc = new DOMParser().parseFromString(xml, "text/xml");
542
+ const errorNode = doc.querySelector("parsererror");
543
+ if (errorNode) throw new Error(`TSX XML parse error: ${errorNode.textContent}`);
544
+ const tsEl = doc.documentElement;
545
+ if (tsEl.tagName !== "tileset") throw new Error(`Expected root <tileset> element, got <${tsEl.tagName}>`);
546
+ const result = parseTileset(tsEl);
547
+ if ("source" in result) throw new Error("TSX file should not contain a source reference");
548
+ return result;
549
+ }
550
+ function parseTx(xml) {
551
+ const doc = new DOMParser().parseFromString(xml, "text/xml");
552
+ const errorNode = doc.querySelector("parsererror");
553
+ if (errorNode) throw new Error(`TX XML parse error: ${errorNode.textContent}`);
554
+ const tplEl = doc.documentElement;
555
+ if (tplEl.tagName !== "template") throw new Error(`Expected root <template> element, got <${tplEl.tagName}>`);
556
+ const tilesetEl = child(tplEl, "tileset");
557
+ const objectEl = child(tplEl, "object");
558
+ if (!objectEl) throw new Error("Template is missing <object>");
559
+ return {
560
+ type: "template",
561
+ tileset: tilesetEl ? parseTileset(tilesetEl) : void 0,
562
+ object: parseObject(objectEl)
563
+ };
564
+ }
565
+ //#endregion
566
+ //#region src/parser/resolveMap.ts
567
+ function resolveTileset(raw, source) {
568
+ const tiles = /* @__PURE__ */ new Map();
569
+ if (raw.tiles) for (const tile of raw.tiles) tiles.set(tile.id, tile);
570
+ return {
571
+ firstgid: raw.firstgid,
572
+ name: raw.name,
573
+ source,
574
+ tilewidth: raw.tilewidth,
575
+ tileheight: raw.tileheight,
576
+ columns: computeTilesetColumns(raw),
577
+ tilecount: raw.tilecount,
578
+ margin: raw.margin,
579
+ spacing: raw.spacing,
580
+ image: raw.image,
581
+ imagewidth: raw.imagewidth,
582
+ imageheight: raw.imageheight,
583
+ tileoffset: raw.tileoffset ?? {
584
+ x: 0,
585
+ y: 0
586
+ },
587
+ objectalignment: raw.objectalignment ?? "unspecified",
588
+ tilerendersize: raw.tilerendersize ?? "tile",
589
+ fillmode: raw.fillmode ?? "stretch",
590
+ tiles,
591
+ properties: raw.properties ?? [],
592
+ transformations: raw.transformations,
593
+ grid: raw.grid,
594
+ wangsets: raw.wangsets,
595
+ terrains: raw.terrains
596
+ };
824
597
  }
825
- var TileSetRenderer = class {
826
- constructor(tileset, baseTexture) {
827
- this._textureCache = /* @__PURE__ */ new Map();
828
- this.tileset = tileset;
829
- this.baseTexture = baseTexture;
830
- }
831
- getTexture(localId) {
832
- const cached = this._textureCache.get(localId);
833
- if (cached) return cached;
834
- const tileDef = this.tileset.tiles.get(localId);
835
- if (tileDef?.image) {
836
- return null;
837
- }
838
- if (!this.baseTexture) return null;
839
- const { tilewidth, tileheight, columns, margin, spacing } = this.tileset;
840
- if (columns <= 0) return null;
841
- const col = localId % columns;
842
- const row = Math.floor(localId / columns);
843
- const x = margin + col * (tilewidth + spacing);
844
- const y = margin + row * (tileheight + spacing);
845
- const frame = new pixi_js.Rectangle(x, y, tilewidth, tileheight);
846
- const texture = new pixi_js.Texture({ source: this.baseTexture.source, frame });
847
- this._textureCache.set(localId, texture);
848
- return texture;
849
- }
850
- setTileTexture(localId, texture) {
851
- this._textureCache.set(localId, texture);
852
- }
853
- getAnimationFrames(localId) {
854
- return this.tileset.tiles.get(localId)?.animation;
855
- }
856
- destroy() {
857
- for (const tex of this._textureCache.values()) {
858
- tex.destroy();
859
- }
860
- this._textureCache.clear();
861
- }
598
+ function resolveGids(rawGids, tilesets) {
599
+ const result = new Array(rawGids.length);
600
+ for (let i = 0; i < rawGids.length; i++) {
601
+ const rawGid = rawGids[i];
602
+ if (rawGid === void 0 || rawGid === 0) {
603
+ result[i] = null;
604
+ continue;
605
+ }
606
+ const decoded = decodeGid(rawGid);
607
+ if (!decoded) {
608
+ result[i] = null;
609
+ continue;
610
+ }
611
+ const tsIdx = findTilesetIndexForGid(decoded.gid, tilesets);
612
+ const ts = tsIdx >= 0 ? tilesets[tsIdx] : void 0;
613
+ if (ts) {
614
+ decoded.tilesetIndex = tsIdx;
615
+ decoded.localId = decoded.gid - ts.firstgid;
616
+ }
617
+ result[i] = decoded;
618
+ }
619
+ return result;
620
+ }
621
+ function layerDefaults(layer) {
622
+ return {
623
+ id: layer.id,
624
+ name: layer.name,
625
+ opacity: layer.opacity,
626
+ visible: layer.visible,
627
+ offsetx: layer.offsetx ?? 0,
628
+ offsety: layer.offsety ?? 0,
629
+ parallaxx: layer.parallaxx ?? 1,
630
+ parallaxy: layer.parallaxy ?? 1,
631
+ tintcolor: layer.tintcolor,
632
+ properties: layer.properties ?? []
633
+ };
634
+ }
635
+ function mergeTemplate(obj, template, tilesets) {
636
+ const base = {
637
+ ...template.object,
638
+ id: obj.id,
639
+ x: obj.x,
640
+ y: obj.y,
641
+ rotation: obj.rotation,
642
+ visible: obj.visible
643
+ };
644
+ if (obj.name) base.name = obj.name;
645
+ if (obj.type) base.type = obj.type;
646
+ if (obj.width) base.width = obj.width;
647
+ if (obj.height) base.height = obj.height;
648
+ if (obj.properties) base.properties = obj.properties;
649
+ if (obj.text) base.text = obj.text;
650
+ if (obj.gid !== void 0) base.gid = obj.gid;
651
+ if (obj.polygon) base.polygon = obj.polygon;
652
+ if (obj.polyline) base.polyline = obj.polyline;
653
+ if (obj.ellipse) base.ellipse = obj.ellipse;
654
+ if (obj.point) base.point = obj.point;
655
+ if (base.gid !== void 0 && template.tileset && "source" in template.tileset && template.tileset.source) {
656
+ const src = template.tileset.source;
657
+ const mapTs = tilesets.find((t) => t.source === src);
658
+ if (mapTs) {
659
+ const templateFirstGid = template.tileset.firstgid ?? 1;
660
+ const flipBits = base.gid & ~GID_MASK;
661
+ const localId = (base.gid & GID_MASK) - templateFirstGid;
662
+ if (localId >= 0) base.gid = mapTs.firstgid + localId | flipBits;
663
+ }
664
+ }
665
+ return base;
666
+ }
667
+ function resolveObjects(objects, tilesets, templates) {
668
+ if (!templates || templates.size === 0) return objects;
669
+ return objects.map((obj) => {
670
+ if (!obj.template) return obj;
671
+ const tpl = templates.get(obj.template);
672
+ if (!tpl) return obj;
673
+ return mergeTemplate(obj, tpl, tilesets);
674
+ });
675
+ }
676
+ function resolveLayer(layer, tilesets, templates) {
677
+ switch (layer.type) {
678
+ case "tilelayer": {
679
+ if (layer.chunks && layer.chunks.length > 0) {
680
+ const resolvedChunks = resolveChunksSync(layer.chunks, layer.encoding, layer.compression, tilesets);
681
+ return {
682
+ type: "tilelayer",
683
+ ...layerDefaults(layer),
684
+ width: layer.width ?? 0,
685
+ height: layer.height ?? 0,
686
+ infinite: true,
687
+ tiles: [],
688
+ chunks: resolvedChunks
689
+ };
690
+ }
691
+ const tiles = resolveGids(decodeLayerData(layer.data ?? [], layer.encoding, layer.compression), tilesets);
692
+ return {
693
+ type: "tilelayer",
694
+ ...layerDefaults(layer),
695
+ width: layer.width ?? 0,
696
+ height: layer.height ?? 0,
697
+ infinite: false,
698
+ tiles
699
+ };
700
+ }
701
+ case "imagelayer": return {
702
+ type: "imagelayer",
703
+ ...layerDefaults(layer),
704
+ image: layer.image ?? "",
705
+ imagewidth: layer.imagewidth,
706
+ imageheight: layer.imageheight,
707
+ repeatx: layer.repeatx ?? false,
708
+ repeaty: layer.repeaty ?? false,
709
+ transparentcolor: layer.transparentcolor
710
+ };
711
+ case "objectgroup": return {
712
+ type: "objectgroup",
713
+ ...layerDefaults(layer),
714
+ draworder: layer.draworder ?? "topdown",
715
+ objects: resolveObjects(layer.objects ?? [], tilesets, templates)
716
+ };
717
+ case "group": return {
718
+ type: "group",
719
+ ...layerDefaults(layer),
720
+ layers: (layer.layers ?? []).map((l) => resolveLayer(l, tilesets, templates))
721
+ };
722
+ }
723
+ }
724
+ async function resolveLayerAsync(layer, tilesets, templates) {
725
+ switch (layer.type) {
726
+ case "tilelayer": {
727
+ if (layer.chunks && layer.chunks.length > 0) {
728
+ const resolvedChunks = await resolveChunksAsync(layer.chunks, layer.encoding, layer.compression, tilesets);
729
+ return {
730
+ type: "tilelayer",
731
+ ...layerDefaults(layer),
732
+ width: layer.width ?? 0,
733
+ height: layer.height ?? 0,
734
+ infinite: true,
735
+ tiles: [],
736
+ chunks: resolvedChunks
737
+ };
738
+ }
739
+ const tiles = resolveGids(await decodeLayerDataAsync(layer.data ?? [], layer.encoding, layer.compression), tilesets);
740
+ return {
741
+ type: "tilelayer",
742
+ ...layerDefaults(layer),
743
+ width: layer.width ?? 0,
744
+ height: layer.height ?? 0,
745
+ infinite: false,
746
+ tiles
747
+ };
748
+ }
749
+ case "imagelayer": return {
750
+ type: "imagelayer",
751
+ ...layerDefaults(layer),
752
+ image: layer.image ?? "",
753
+ imagewidth: layer.imagewidth,
754
+ imageheight: layer.imageheight,
755
+ repeatx: layer.repeatx ?? false,
756
+ repeaty: layer.repeaty ?? false,
757
+ transparentcolor: layer.transparentcolor
758
+ };
759
+ case "objectgroup": return {
760
+ type: "objectgroup",
761
+ ...layerDefaults(layer),
762
+ draworder: layer.draworder ?? "topdown",
763
+ objects: resolveObjects(layer.objects ?? [], tilesets, templates)
764
+ };
765
+ case "group": {
766
+ const resolvedChildren = await Promise.all((layer.layers ?? []).map((l) => resolveLayerAsync(l, tilesets, templates)));
767
+ return {
768
+ type: "group",
769
+ ...layerDefaults(layer),
770
+ layers: resolvedChildren
771
+ };
772
+ }
773
+ }
774
+ }
775
+ function parseMap(data, options) {
776
+ const resolvedTilesets = resolveTilesets(data.tilesets, options);
777
+ return buildResolvedMap(data, resolvedTilesets, data.layers.map((l) => resolveLayer(l, resolvedTilesets, options?.templates)));
778
+ }
779
+ async function parseMapAsync(data, options) {
780
+ const resolvedTilesets = resolveTilesets(data.tilesets, options);
781
+ return buildResolvedMap(data, resolvedTilesets, await Promise.all(data.layers.map((l) => resolveLayerAsync(l, resolvedTilesets, options?.templates))));
782
+ }
783
+ function resolveChunksSync(chunks, encoding, compression, tilesets) {
784
+ return chunks.map((chunk) => {
785
+ const rawGids = decodeLayerData(chunk.data, encoding, compression);
786
+ return {
787
+ x: chunk.x,
788
+ y: chunk.y,
789
+ width: chunk.width,
790
+ height: chunk.height,
791
+ tiles: resolveGids(rawGids, tilesets)
792
+ };
793
+ });
794
+ }
795
+ async function resolveChunksAsync(chunks, encoding, compression, tilesets) {
796
+ return Promise.all(chunks.map(async (chunk) => {
797
+ const rawGids = await decodeLayerDataAsync(chunk.data, encoding, compression);
798
+ return {
799
+ x: chunk.x,
800
+ y: chunk.y,
801
+ width: chunk.width,
802
+ height: chunk.height,
803
+ tiles: resolveGids(rawGids, tilesets)
804
+ };
805
+ }));
806
+ }
807
+ function resolveTilesets(raw, options) {
808
+ return raw.map((ts) => {
809
+ if (isTilesetRef(ts)) {
810
+ const external = options?.externalTilesets?.get(ts.source);
811
+ if (!external) throw new Error(`External tileset "${ts.source}" not provided. Pass it via options.externalTilesets.`);
812
+ return resolveTileset({
813
+ ...external,
814
+ firstgid: ts.firstgid
815
+ }, ts.source);
816
+ }
817
+ return resolveTileset(ts);
818
+ });
819
+ }
820
+ function buildResolvedMap(data, tilesets, layers) {
821
+ return {
822
+ orientation: data.orientation,
823
+ renderorder: data.renderorder ?? "right-down",
824
+ width: data.width,
825
+ height: data.height,
826
+ tilewidth: data.tilewidth,
827
+ tileheight: data.tileheight,
828
+ infinite: data.infinite,
829
+ backgroundcolor: data.backgroundcolor,
830
+ hexsidelength: data.hexsidelength,
831
+ staggeraxis: data.staggeraxis,
832
+ staggerindex: data.staggerindex,
833
+ parallaxoriginx: data.parallaxoriginx ?? 0,
834
+ parallaxoriginy: data.parallaxoriginy ?? 0,
835
+ properties: data.properties ?? [],
836
+ tilesets,
837
+ layers,
838
+ version: data.version,
839
+ tiledversion: data.tiledversion
840
+ };
841
+ }
842
+ //#endregion
843
+ //#region src/renderer/parseColor.ts
844
+ function parseTintColor(hex) {
845
+ const clean = hex.replace("#", "");
846
+ if (clean.length === 8) return parseInt(clean.slice(2), 16);
847
+ return parseInt(clean, 16);
848
+ }
849
+ //#endregion
850
+ //#region src/renderer/GroupLayerRenderer.ts
851
+ var GroupLayerRenderer = class extends pixi_js.Container {
852
+ constructor(layerData, tilesets, ctx, imageTextures) {
853
+ super();
854
+ this.layerData = layerData;
855
+ this.label = layerData.name;
856
+ this.alpha = layerData.opacity;
857
+ this.visible = layerData.visible;
858
+ this.layerBaseOffsetX = layerData.offsetx;
859
+ this.layerBaseOffsetY = layerData.offsety;
860
+ this.layerParallaxX = layerData.parallaxx;
861
+ this.layerParallaxY = layerData.parallaxy;
862
+ this.position.set(layerData.offsetx, layerData.offsety);
863
+ if (layerData.tintcolor) this.tint = parseTintColor(layerData.tintcolor);
864
+ for (const child of layerData.layers) {
865
+ const renderer = createLayerRenderer(child, tilesets, ctx, imageTextures);
866
+ if (renderer) this.addChild(renderer);
867
+ }
868
+ }
862
869
  };
863
-
864
- // src/renderer/tilePlacement.ts
870
+ //#endregion
871
+ //#region src/renderer/ImageLayerRenderer.ts
872
+ var ImageLayerRenderer = class extends pixi_js.Container {
873
+ constructor(layerData, texture, ctx) {
874
+ super();
875
+ this.layerData = layerData;
876
+ this.label = layerData.name;
877
+ this.alpha = layerData.opacity;
878
+ this.visible = layerData.visible;
879
+ this.layerBaseOffsetX = layerData.offsetx;
880
+ this.layerBaseOffsetY = layerData.offsety;
881
+ this.layerParallaxX = layerData.parallaxx;
882
+ this.layerParallaxY = layerData.parallaxy;
883
+ this.position.set(layerData.offsetx, layerData.offsety);
884
+ if (layerData.tintcolor) this.tint = parseTintColor(layerData.tintcolor);
885
+ if (texture) this._buildImage(texture, ctx);
886
+ }
887
+ _buildImage(texture, ctx) {
888
+ const { repeatx, repeaty } = this.layerData;
889
+ if (!repeatx && !repeaty) {
890
+ this.addChild(new pixi_js.Sprite(texture));
891
+ return;
892
+ }
893
+ const spanW = ctx?.mapPixelWidth && ctx.mapPixelWidth > 0 ? ctx.mapPixelWidth : texture.width;
894
+ const spanH = ctx?.mapPixelHeight && ctx.mapPixelHeight > 0 ? ctx.mapPixelHeight : texture.height;
895
+ this.addChild(new pixi_js.TilingSprite({
896
+ texture,
897
+ width: repeatx ? spanW : texture.width,
898
+ height: repeaty ? spanH : texture.height
899
+ }));
900
+ }
901
+ };
902
+ //#endregion
903
+ //#region src/renderer/ObjectLayerRenderer.ts
904
+ var ObjectLayerRenderer = class extends pixi_js.Container {
905
+ constructor(layerData, tilesets) {
906
+ super();
907
+ this.layerData = layerData;
908
+ this.label = layerData.name;
909
+ this.alpha = layerData.opacity;
910
+ this.visible = layerData.visible;
911
+ this.layerBaseOffsetX = layerData.offsetx;
912
+ this.layerBaseOffsetY = layerData.offsety;
913
+ this.layerParallaxX = layerData.parallaxx;
914
+ this.layerParallaxY = layerData.parallaxy;
915
+ this.position.set(layerData.offsetx, layerData.offsety);
916
+ if (layerData.tintcolor) this.tint = parseTintColor(layerData.tintcolor);
917
+ this._buildObjects(tilesets);
918
+ }
919
+ _buildObjects(tilesets) {
920
+ for (const obj of this.layerData.objects) {
921
+ const child = this._createObject(obj, tilesets);
922
+ if (child) {
923
+ child.label = obj.name || `object_${obj.id}`;
924
+ this.addChild(child);
925
+ }
926
+ }
927
+ }
928
+ _createObject(obj, tilesets) {
929
+ if (obj.gid !== void 0) return this._createTileObject(obj, tilesets);
930
+ if (obj.text) return this._createTextObject(obj);
931
+ if (obj.ellipse) return this._createEllipse(obj);
932
+ if (obj.point) return this._createPoint(obj);
933
+ if (obj.polygon) return this._createPolygon(obj, obj.polygon, true);
934
+ if (obj.polyline) return this._createPolygon(obj, obj.polyline, false);
935
+ if (obj.width > 0 && obj.height > 0) return this._createRectangle(obj);
936
+ return null;
937
+ }
938
+ _createTileObject(obj, tilesets) {
939
+ if (obj.gid === void 0) return null;
940
+ const decoded = decodeGid(obj.gid);
941
+ if (!decoded) return null;
942
+ const ts = findRendererForGid(tilesets, decoded.gid);
943
+ if (!ts) return null;
944
+ const localId = decoded.gid - ts.tileset.firstgid;
945
+ const texture = ts.getTexture(localId);
946
+ if (!texture) return null;
947
+ const sprite = new pixi_js.Sprite(texture);
948
+ const offset = ts.tileset.tileoffset;
949
+ const sized = this._fitTileSize(ts, localId, obj.width, obj.height);
950
+ sprite.width = sized.width;
951
+ sprite.height = sized.height;
952
+ sprite.position.set(obj.x + offset.x, obj.y - sized.height + offset.y);
953
+ sprite.angle = obj.rotation;
954
+ sprite.visible = obj.visible;
955
+ if (decoded.horizontalFlip) {
956
+ sprite.scale.x *= -1;
957
+ sprite.anchor.x = 1;
958
+ }
959
+ if (decoded.verticalFlip) {
960
+ sprite.scale.y *= -1;
961
+ sprite.anchor.y = 1;
962
+ }
963
+ return sprite;
964
+ }
965
+ _fitTileSize(ts, localId, objWidth, objHeight) {
966
+ if (objWidth <= 0 || objHeight <= 0) return ts.getTileSize(localId);
967
+ if (ts.tileset.fillmode !== "preserve-aspect-fit") return {
968
+ width: objWidth,
969
+ height: objHeight
970
+ };
971
+ const intrinsic = ts.getTileSize(localId);
972
+ if (intrinsic.width === 0 || intrinsic.height === 0) return {
973
+ width: objWidth,
974
+ height: objHeight
975
+ };
976
+ const scale = Math.min(objWidth / intrinsic.width, objHeight / intrinsic.height);
977
+ return {
978
+ width: intrinsic.width * scale,
979
+ height: intrinsic.height * scale
980
+ };
981
+ }
982
+ _createTextObject(obj) {
983
+ const td = obj.text;
984
+ const color = td.color ?? "#000000";
985
+ const text = new pixi_js.Text({
986
+ text: td.text,
987
+ style: {
988
+ fontFamily: td.fontfamily ?? "sans-serif",
989
+ fontSize: td.pixelsize ?? 16,
990
+ fill: color,
991
+ fontWeight: td.bold ? "bold" : "normal",
992
+ fontStyle: td.italic ? "italic" : "normal",
993
+ wordWrap: td.wrap ?? false,
994
+ wordWrapWidth: obj.width,
995
+ align: td.halign ?? "left"
996
+ }
997
+ });
998
+ if (!td.underline && !td.strikeout) {
999
+ text.position.set(obj.x, obj.y);
1000
+ text.angle = obj.rotation;
1001
+ text.visible = obj.visible;
1002
+ return text;
1003
+ }
1004
+ const container = new pixi_js.Container();
1005
+ container.addChild(text);
1006
+ const metrics = text.getSize();
1007
+ const lineThickness = Math.max(1, (td.pixelsize ?? 16) / 16);
1008
+ if (td.underline) {
1009
+ const ul = new pixi_js.Graphics().moveTo(0, metrics.height - lineThickness).lineTo(metrics.width, metrics.height - lineThickness).stroke({
1010
+ color,
1011
+ width: lineThickness
1012
+ });
1013
+ container.addChild(ul);
1014
+ }
1015
+ if (td.strikeout) {
1016
+ const y = metrics.height / 2;
1017
+ const so = new pixi_js.Graphics().moveTo(0, y).lineTo(metrics.width, y).stroke({
1018
+ color,
1019
+ width: lineThickness
1020
+ });
1021
+ container.addChild(so);
1022
+ }
1023
+ container.position.set(obj.x, obj.y);
1024
+ container.angle = obj.rotation;
1025
+ container.visible = obj.visible;
1026
+ return container;
1027
+ }
1028
+ _createRectangle(obj) {
1029
+ const g = new pixi_js.Graphics().rect(0, 0, obj.width, obj.height).stroke({
1030
+ color: 16777215,
1031
+ width: 1
1032
+ });
1033
+ g.position.set(obj.x, obj.y);
1034
+ g.angle = obj.rotation;
1035
+ g.visible = obj.visible;
1036
+ return g;
1037
+ }
1038
+ _createEllipse(obj) {
1039
+ const rx = obj.width / 2;
1040
+ const ry = obj.height / 2;
1041
+ const g = new pixi_js.Graphics().ellipse(rx, ry, rx, ry).stroke({
1042
+ color: 16777215,
1043
+ width: 1
1044
+ });
1045
+ g.position.set(obj.x, obj.y);
1046
+ g.angle = obj.rotation;
1047
+ g.visible = obj.visible;
1048
+ return g;
1049
+ }
1050
+ _createPoint(obj) {
1051
+ const g = new pixi_js.Graphics().circle(0, 0, 3).fill(16777215);
1052
+ g.position.set(obj.x, obj.y);
1053
+ g.visible = obj.visible;
1054
+ return g;
1055
+ }
1056
+ _createPolygon(obj, points, closed) {
1057
+ const g = new pixi_js.Graphics();
1058
+ if (points.length > 0) {
1059
+ const first = points[0];
1060
+ g.moveTo(first.x, first.y);
1061
+ for (let i = 1; i < points.length; i++) {
1062
+ const pt = points[i];
1063
+ g.lineTo(pt.x, pt.y);
1064
+ }
1065
+ if (closed) g.closePath();
1066
+ g.stroke({
1067
+ color: 16777215,
1068
+ width: 1
1069
+ });
1070
+ }
1071
+ g.position.set(obj.x, obj.y);
1072
+ g.angle = obj.rotation;
1073
+ g.visible = obj.visible;
1074
+ return g;
1075
+ }
1076
+ };
1077
+ function findRendererForGid(tilesets, gid) {
1078
+ for (let i = tilesets.length - 1; i >= 0; i--) {
1079
+ const ts = tilesets[i];
1080
+ if (ts && ts.tileset.firstgid <= gid) return ts;
1081
+ }
1082
+ return null;
1083
+ }
1084
+ //#endregion
1085
+ //#region src/renderer/tilePlacement.ts
865
1086
  function tileToPixel(col, row, ctx) {
866
- switch (ctx.orientation) {
867
- case "orthogonal":
868
- return orthogonalToPixel(col, row, ctx);
869
- case "isometric":
870
- return isometricToPixel(col, row, ctx);
871
- case "staggered":
872
- return staggeredToPixel(col, row, ctx);
873
- case "hexagonal":
874
- return hexagonalToPixel(col, row, ctx);
875
- }
1087
+ switch (ctx.orientation) {
1088
+ case "orthogonal": return orthogonalToPixel(col, row, ctx);
1089
+ case "isometric": return isometricToPixel(col, row, ctx);
1090
+ case "staggered": return staggeredToPixel(col, row, ctx);
1091
+ case "hexagonal": return hexagonalToPixel(col, row, ctx);
1092
+ }
876
1093
  }
877
1094
  function orthogonalToPixel(col, row, ctx) {
878
- return {
879
- x: col * ctx.tilewidth,
880
- y: row * ctx.tileheight
881
- };
1095
+ return {
1096
+ x: col * ctx.tilewidth,
1097
+ y: row * ctx.tileheight
1098
+ };
882
1099
  }
883
1100
  function isometricToPixel(col, row, ctx) {
884
- const halfW = ctx.tilewidth / 2;
885
- const halfH = ctx.tileheight / 2;
886
- return {
887
- x: (col - row) * halfW,
888
- y: (col + row) * halfH
889
- };
1101
+ const halfW = ctx.tilewidth / 2;
1102
+ const halfH = ctx.tileheight / 2;
1103
+ return {
1104
+ x: (col - row) * halfW,
1105
+ y: (col + row) * halfH
1106
+ };
890
1107
  }
891
1108
  function staggeredToPixel(col, row, ctx) {
892
- const staggerX = ctx.staggeraxis === "x";
893
- const staggerEven = ctx.staggerindex === "even";
894
- if (staggerX) {
895
- const isStaggered2 = staggerEven ? col % 2 === 0 : col % 2 !== 0;
896
- return {
897
- x: col * (ctx.tilewidth / 2),
898
- y: row * ctx.tileheight + (isStaggered2 ? ctx.tileheight / 2 : 0)
899
- };
900
- }
901
- const isStaggered = staggerEven ? row % 2 === 0 : row % 2 !== 0;
902
- return {
903
- x: col * ctx.tilewidth + (isStaggered ? ctx.tilewidth / 2 : 0),
904
- y: row * (ctx.tileheight / 2)
905
- };
1109
+ const staggerX = ctx.staggeraxis === "x";
1110
+ const staggerEven = ctx.staggerindex === "even";
1111
+ if (staggerX) {
1112
+ const isStaggered = staggerEven ? col % 2 === 0 : col % 2 !== 0;
1113
+ return {
1114
+ x: col * (ctx.tilewidth / 2),
1115
+ y: row * ctx.tileheight + (isStaggered ? ctx.tileheight / 2 : 0)
1116
+ };
1117
+ }
1118
+ const isStaggered = staggerEven ? row % 2 === 0 : row % 2 !== 0;
1119
+ return {
1120
+ x: col * ctx.tilewidth + (isStaggered ? ctx.tilewidth / 2 : 0),
1121
+ y: row * (ctx.tileheight / 2)
1122
+ };
906
1123
  }
907
1124
  function hexagonalToPixel(col, row, ctx) {
908
- const hexSide = ctx.hexsidelength ?? 0;
909
- const staggerX = ctx.staggeraxis === "x";
910
- const staggerEven = ctx.staggerindex === "even";
911
- if (staggerX) {
912
- const colWidth = (ctx.tilewidth + hexSide) / 2;
913
- const isStaggered2 = staggerEven ? col % 2 === 0 : col % 2 !== 0;
914
- return {
915
- x: col * colWidth,
916
- y: row * ctx.tileheight + (isStaggered2 ? ctx.tileheight / 2 : 0)
917
- };
918
- }
919
- const rowHeight = (ctx.tileheight + hexSide) / 2;
920
- const isStaggered = staggerEven ? row % 2 === 0 : row % 2 !== 0;
921
- return {
922
- x: col * ctx.tilewidth + (isStaggered ? ctx.tilewidth / 2 : 0),
923
- y: row * rowHeight
924
- };
925
- }
926
-
927
- // src/renderer/parseColor.ts
928
- function parseTintColor(hex) {
929
- const clean = hex.replace("#", "");
930
- if (clean.length === 8) {
931
- return parseInt(clean.slice(2), 16);
932
- }
933
- return parseInt(clean, 16);
934
- }
935
-
936
- // src/renderer/TileLayerRenderer.ts
1125
+ const hexSide = ctx.hexsidelength ?? 0;
1126
+ const staggerX = ctx.staggeraxis === "x";
1127
+ const staggerEven = ctx.staggerindex === "even";
1128
+ if (staggerX) {
1129
+ const colWidth = (ctx.tilewidth + hexSide) / 2;
1130
+ const isStaggered = staggerEven ? col % 2 === 0 : col % 2 !== 0;
1131
+ return {
1132
+ x: col * colWidth,
1133
+ y: row * ctx.tileheight + (isStaggered ? ctx.tileheight / 2 : 0)
1134
+ };
1135
+ }
1136
+ const rowHeight = (ctx.tileheight + hexSide) / 2;
1137
+ const isStaggered = staggerEven ? row % 2 === 0 : row % 2 !== 0;
1138
+ return {
1139
+ x: col * ctx.tilewidth + (isStaggered ? ctx.tilewidth / 2 : 0),
1140
+ y: row * rowHeight
1141
+ };
1142
+ }
1143
+ //#endregion
1144
+ //#region src/renderer/TileLayerRenderer.ts
937
1145
  var TileLayerRenderer = class extends pixi_js.Container {
938
- constructor(layerData, tilesets, ctx) {
939
- super();
940
- this.layerData = layerData;
941
- this.label = layerData.name;
942
- this.alpha = layerData.opacity;
943
- this.visible = layerData.visible;
944
- this.position.set(layerData.offsetx, layerData.offsety);
945
- if (layerData.tintcolor) {
946
- this.tint = parseTintColor(layerData.tintcolor);
947
- }
948
- if (layerData.infinite && layerData.chunks) {
949
- this._buildChunks(layerData.chunks, tilesets, ctx);
950
- } else {
951
- this._buildTiles(
952
- layerData.tiles,
953
- layerData.width,
954
- Math.floor(layerData.tiles.length / (layerData.width || 1)),
955
- 0,
956
- 0,
957
- tilesets,
958
- ctx
959
- );
960
- }
961
- }
962
- _buildChunks(chunks, tilesets, ctx) {
963
- for (const chunk of chunks) {
964
- this._buildTiles(chunk.tiles, chunk.width, chunk.height, chunk.x, chunk.y, tilesets, ctx);
965
- }
966
- }
967
- _buildTiles(tiles, layerWidth, layerHeight, originCol, originRow, tilesets, ctx) {
968
- const order = ctx.renderorder;
969
- for (const [col, row] of iterateTiles(layerWidth, layerHeight, order)) {
970
- const i = row * layerWidth + col;
971
- const tile = tiles[i];
972
- if (!tile) continue;
973
- const tsRenderer = tilesets[tile.tilesetIndex];
974
- if (!tsRenderer) continue;
975
- const pos = tileToPixel(originCol + col, originRow + row, ctx);
976
- const animFrames = tsRenderer.getAnimationFrames(tile.localId);
977
- if (animFrames && animFrames.length > 1) {
978
- const sprite = this._createAnimatedTile(tsRenderer, animFrames, tile, pos.x, pos.y);
979
- if (sprite) this.addChild(sprite);
980
- } else {
981
- const texture = tsRenderer.getTexture(tile.localId);
982
- if (!texture) continue;
983
- const sprite = new pixi_js.Sprite(texture);
984
- sprite.position.set(pos.x, pos.y);
985
- applyFlip(sprite, tile, ctx.tilewidth);
986
- this.addChild(sprite);
987
- }
988
- }
989
- }
990
- _createAnimatedTile(tsRenderer, frames, tile, x, y) {
991
- const textures = [];
992
- for (const frame of frames) {
993
- const tex = tsRenderer.getTexture(frame.tileid);
994
- if (!tex) return null;
995
- textures.push({ texture: tex, time: frame.duration });
996
- }
997
- const sprite = new pixi_js.AnimatedSprite(textures);
998
- sprite.position.set(x, y);
999
- sprite.play();
1000
- applyFlip(sprite, tile, tsRenderer.tileset.tilewidth);
1001
- return sprite;
1002
- }
1146
+ constructor(layerData, tilesets, ctx) {
1147
+ super();
1148
+ this.layerData = layerData;
1149
+ this.label = layerData.name;
1150
+ this.alpha = layerData.opacity;
1151
+ this.visible = layerData.visible;
1152
+ this.layerBaseOffsetX = layerData.offsetx;
1153
+ this.layerBaseOffsetY = layerData.offsety;
1154
+ this.layerParallaxX = layerData.parallaxx;
1155
+ this.layerParallaxY = layerData.parallaxy;
1156
+ this.position.set(layerData.offsetx, layerData.offsety);
1157
+ if (layerData.tintcolor) this.tint = parseTintColor(layerData.tintcolor);
1158
+ if (layerData.infinite && layerData.chunks) this._buildChunks(layerData.chunks, tilesets, ctx);
1159
+ else this._buildTiles(layerData.tiles, layerData.width, Math.floor(layerData.tiles.length / (layerData.width || 1)), 0, 0, tilesets, ctx);
1160
+ }
1161
+ _buildChunks(chunks, tilesets, ctx) {
1162
+ for (const chunk of chunks) this._buildTiles(chunk.tiles, chunk.width, chunk.height, chunk.x, chunk.y, tilesets, ctx);
1163
+ }
1164
+ _buildTiles(tiles, layerWidth, layerHeight, originCol, originRow, tilesets, ctx) {
1165
+ const order = ctx.renderorder;
1166
+ for (const [col, row] of iterateTiles(layerWidth, layerHeight, order)) {
1167
+ const tile = tiles[row * layerWidth + col];
1168
+ if (!tile) continue;
1169
+ const tsRenderer = tilesets[tile.tilesetIndex];
1170
+ if (!tsRenderer) continue;
1171
+ const pos = tileToPixel(originCol + col, originRow + row, ctx);
1172
+ const animFrames = tsRenderer.getAnimationFrames(tile.localId);
1173
+ const offset = tsRenderer.tileset.tileoffset;
1174
+ if (animFrames && animFrames.length > 1) {
1175
+ const sprite = this._createAnimatedTile(tsRenderer, animFrames, tile, pos.x, pos.y, ctx);
1176
+ if (sprite) {
1177
+ sprite.position.x += offset.x;
1178
+ sprite.position.y += offset.y;
1179
+ this.addChild(sprite);
1180
+ }
1181
+ } else {
1182
+ const texture = tsRenderer.getTexture(tile.localId);
1183
+ if (!texture) continue;
1184
+ const sprite = new pixi_js.Sprite(texture);
1185
+ const renderSize = tsRenderer.getRenderSize(tile.localId, ctx);
1186
+ sprite.width = renderSize.width;
1187
+ sprite.height = renderSize.height;
1188
+ sprite.position.set(pos.x + offset.x, pos.y + offset.y + ctx.tileheight - renderSize.height);
1189
+ applyFlip(sprite, tile, renderSize.width);
1190
+ this.addChild(sprite);
1191
+ }
1192
+ }
1193
+ }
1194
+ _createAnimatedTile(tsRenderer, frames, tile, x, y, ctx) {
1195
+ const textures = [];
1196
+ for (const frame of frames) {
1197
+ const tex = tsRenderer.getTexture(frame.tileid);
1198
+ if (!tex) return null;
1199
+ textures.push({
1200
+ texture: tex,
1201
+ time: frame.duration
1202
+ });
1203
+ }
1204
+ const sprite = new pixi_js.AnimatedSprite(textures);
1205
+ const renderSize = tsRenderer.getRenderSize(tile.localId, ctx);
1206
+ sprite.width = renderSize.width;
1207
+ sprite.height = renderSize.height;
1208
+ sprite.position.set(x, y + ctx.tileheight - renderSize.height);
1209
+ sprite.play();
1210
+ applyFlip(sprite, tile, renderSize.width);
1211
+ return sprite;
1212
+ }
1003
1213
  };
1004
1214
  function* iterateTiles(width, height, order) {
1005
- const rightToLeft = order === "left-down" || order === "left-up";
1006
- const bottomToTop = order === "right-up" || order === "left-up";
1007
- const rowStart = bottomToTop ? height - 1 : 0;
1008
- const rowEnd = bottomToTop ? -1 : height;
1009
- const rowStep = bottomToTop ? -1 : 1;
1010
- const colStart = rightToLeft ? width - 1 : 0;
1011
- const colEnd = rightToLeft ? -1 : width;
1012
- const colStep = rightToLeft ? -1 : 1;
1013
- for (let row = rowStart; row !== rowEnd; row += rowStep) {
1014
- for (let col = colStart; col !== colEnd; col += colStep) {
1015
- yield [col, row];
1016
- }
1017
- }
1215
+ const rightToLeft = order === "left-down" || order === "left-up";
1216
+ const bottomToTop = order === "right-up" || order === "left-up";
1217
+ const rowStart = bottomToTop ? height - 1 : 0;
1218
+ const rowEnd = bottomToTop ? -1 : height;
1219
+ const rowStep = bottomToTop ? -1 : 1;
1220
+ const colStart = rightToLeft ? width - 1 : 0;
1221
+ const colEnd = rightToLeft ? -1 : width;
1222
+ const colStep = rightToLeft ? -1 : 1;
1223
+ for (let row = rowStart; row !== rowEnd; row += rowStep) for (let col = colStart; col !== colEnd; col += colStep) yield [col, row];
1018
1224
  }
1019
1225
  function applyFlip(sprite, tile, tileWidth) {
1020
- if (tile.diagonalFlip) {
1021
- sprite.rotation = Math.PI / 2;
1022
- sprite.scale.x = tile.horizontalFlip ? -1 : 1;
1023
- sprite.scale.y = tile.verticalFlip ? -1 : 1;
1024
- sprite.anchor.set(0, 1);
1025
- sprite.position.x += tileWidth;
1026
- } else {
1027
- if (tile.horizontalFlip) {
1028
- sprite.scale.x = -1;
1029
- sprite.anchor.x = 1;
1030
- }
1031
- if (tile.verticalFlip) {
1032
- sprite.scale.y = -1;
1033
- sprite.anchor.y = 1;
1034
- }
1035
- }
1036
- }
1037
- var ImageLayerRenderer = class extends pixi_js.Container {
1038
- constructor(layerData, texture) {
1039
- super();
1040
- this.layerData = layerData;
1041
- this.label = layerData.name;
1042
- this.alpha = layerData.opacity;
1043
- this.visible = layerData.visible;
1044
- this.position.set(layerData.offsetx, layerData.offsety);
1045
- if (layerData.tintcolor) {
1046
- this.tint = parseTintColor(layerData.tintcolor);
1047
- }
1048
- if (texture) {
1049
- this._buildImage(texture);
1050
- }
1051
- }
1052
- _buildImage(texture) {
1053
- const { repeatx, repeaty } = this.layerData;
1054
- if (repeatx || repeaty) {
1055
- const tiling = new pixi_js.TilingSprite({
1056
- texture,
1057
- width: repeatx ? texture.width * 10 : texture.width,
1058
- height: repeaty ? texture.height * 10 : texture.height
1059
- });
1060
- this.addChild(tiling);
1061
- } else {
1062
- const sprite = new pixi_js.Sprite(texture);
1063
- this.addChild(sprite);
1064
- }
1065
- }
1066
- };
1067
- var ObjectLayerRenderer = class extends pixi_js.Container {
1068
- constructor(layerData, tilesets) {
1069
- super();
1070
- this.layerData = layerData;
1071
- this.label = layerData.name;
1072
- this.alpha = layerData.opacity;
1073
- this.visible = layerData.visible;
1074
- this.position.set(layerData.offsetx, layerData.offsety);
1075
- if (layerData.tintcolor) {
1076
- this.tint = parseTintColor(layerData.tintcolor);
1077
- }
1078
- this._buildObjects(tilesets);
1079
- }
1080
- _buildObjects(tilesets) {
1081
- for (const obj of this.layerData.objects) {
1082
- const child2 = this._createObject(obj, tilesets);
1083
- if (child2) {
1084
- child2.label = obj.name || `object_${obj.id}`;
1085
- this.addChild(child2);
1086
- }
1087
- }
1088
- }
1089
- _createObject(obj, tilesets) {
1090
- if (obj.gid !== void 0) {
1091
- return this._createTileObject(obj, tilesets);
1092
- }
1093
- if (obj.text) {
1094
- return this._createTextObject(obj);
1095
- }
1096
- if (obj.ellipse) {
1097
- return this._createEllipse(obj);
1098
- }
1099
- if (obj.point) {
1100
- return this._createPoint(obj);
1101
- }
1102
- if (obj.polygon) {
1103
- return this._createPolygon(obj, obj.polygon, true);
1104
- }
1105
- if (obj.polyline) {
1106
- return this._createPolygon(obj, obj.polyline, false);
1107
- }
1108
- if (obj.width > 0 && obj.height > 0) {
1109
- return this._createRectangle(obj);
1110
- }
1111
- return null;
1112
- }
1113
- _createTileObject(obj, tilesets) {
1114
- if (obj.gid === void 0) return null;
1115
- const decoded = decodeGid(obj.gid);
1116
- if (!decoded) return null;
1117
- for (let i = tilesets.length - 1; i >= 0; i--) {
1118
- const ts = tilesets[i];
1119
- if (!ts) continue;
1120
- if (ts.tileset.firstgid <= decoded.gid) {
1121
- const localId = decoded.gid - ts.tileset.firstgid;
1122
- const texture = ts.getTexture(localId);
1123
- if (!texture) return null;
1124
- const sprite = new pixi_js.Sprite(texture);
1125
- sprite.position.set(obj.x, obj.y - obj.height);
1126
- sprite.width = obj.width;
1127
- sprite.height = obj.height;
1128
- sprite.angle = obj.rotation;
1129
- sprite.visible = obj.visible;
1130
- if (decoded.horizontalFlip) {
1131
- sprite.scale.x *= -1;
1132
- sprite.anchor.x = 1;
1133
- }
1134
- if (decoded.verticalFlip) {
1135
- sprite.scale.y *= -1;
1136
- sprite.anchor.y = 1;
1137
- }
1138
- return sprite;
1139
- }
1140
- }
1141
- return null;
1142
- }
1143
- _createTextObject(obj) {
1144
- const td = obj.text;
1145
- const text = new pixi_js.Text({
1146
- text: td.text,
1147
- style: {
1148
- fontFamily: td.fontfamily ?? "sans-serif",
1149
- fontSize: td.pixelsize ?? 16,
1150
- fill: td.color ?? "#000000",
1151
- fontWeight: td.bold ? "bold" : "normal",
1152
- fontStyle: td.italic ? "italic" : "normal",
1153
- wordWrap: td.wrap ?? false,
1154
- wordWrapWidth: obj.width,
1155
- align: td.halign ?? "left"
1156
- }
1157
- });
1158
- text.position.set(obj.x, obj.y);
1159
- text.angle = obj.rotation;
1160
- text.visible = obj.visible;
1161
- return text;
1162
- }
1163
- _createRectangle(obj) {
1164
- const g = new pixi_js.Graphics().rect(0, 0, obj.width, obj.height).stroke({ color: 16777215, width: 1 });
1165
- g.position.set(obj.x, obj.y);
1166
- g.angle = obj.rotation;
1167
- g.visible = obj.visible;
1168
- return g;
1169
- }
1170
- _createEllipse(obj) {
1171
- const rx = obj.width / 2;
1172
- const ry = obj.height / 2;
1173
- const g = new pixi_js.Graphics().ellipse(rx, ry, rx, ry).stroke({ color: 16777215, width: 1 });
1174
- g.position.set(obj.x, obj.y);
1175
- g.angle = obj.rotation;
1176
- g.visible = obj.visible;
1177
- return g;
1178
- }
1179
- _createPoint(obj) {
1180
- const g = new pixi_js.Graphics().circle(0, 0, 3).fill(16777215);
1181
- g.position.set(obj.x, obj.y);
1182
- g.visible = obj.visible;
1183
- return g;
1184
- }
1185
- _createPolygon(obj, points, closed) {
1186
- const g = new pixi_js.Graphics();
1187
- if (points.length > 0) {
1188
- const first = points[0];
1189
- g.moveTo(first.x, first.y);
1190
- for (let i = 1; i < points.length; i++) {
1191
- const pt = points[i];
1192
- g.lineTo(pt.x, pt.y);
1193
- }
1194
- if (closed) {
1195
- g.closePath();
1196
- }
1197
- g.stroke({ color: 16777215, width: 1 });
1198
- }
1199
- g.position.set(obj.x, obj.y);
1200
- g.angle = obj.rotation;
1201
- g.visible = obj.visible;
1202
- return g;
1203
- }
1204
- };
1205
- var GroupLayerRenderer = class extends pixi_js.Container {
1206
- constructor(layerData, tilesets, ctx, imageTextures) {
1207
- super();
1208
- this.layerData = layerData;
1209
- this.label = layerData.name;
1210
- this.alpha = layerData.opacity;
1211
- this.visible = layerData.visible;
1212
- this.position.set(layerData.offsetx, layerData.offsety);
1213
- if (layerData.tintcolor) {
1214
- this.tint = parseTintColor(layerData.tintcolor);
1215
- }
1216
- for (const child2 of layerData.layers) {
1217
- const renderer = createLayerRenderer(child2, tilesets, ctx, imageTextures);
1218
- if (renderer) this.addChild(renderer);
1219
- }
1220
- }
1221
- };
1222
-
1223
- // src/renderer/createLayerRenderer.ts
1226
+ if (tile.diagonalFlip) {
1227
+ sprite.rotation = Math.PI / 2;
1228
+ sprite.scale.x = tile.horizontalFlip ? -1 : 1;
1229
+ sprite.scale.y = tile.verticalFlip ? -1 : 1;
1230
+ sprite.anchor.set(0, 1);
1231
+ sprite.position.x += tileWidth;
1232
+ } else {
1233
+ if (tile.horizontalFlip) {
1234
+ sprite.scale.x = -1;
1235
+ sprite.anchor.x = 1;
1236
+ }
1237
+ if (tile.verticalFlip) {
1238
+ sprite.scale.y = -1;
1239
+ sprite.anchor.y = 1;
1240
+ }
1241
+ }
1242
+ }
1243
+ //#endregion
1244
+ //#region src/renderer/createLayerRenderer.ts
1224
1245
  function createLayerRenderer(layer, tilesets, ctx, imageTextures) {
1225
- switch (layer.type) {
1226
- case "tilelayer":
1227
- return new TileLayerRenderer(layer, tilesets, ctx);
1228
- case "imagelayer": {
1229
- const tex = layer.image ? imageTextures.get(layer.image) ?? null : null;
1230
- return new ImageLayerRenderer(layer, tex);
1231
- }
1232
- case "objectgroup":
1233
- return new ObjectLayerRenderer(layer, tilesets);
1234
- case "group":
1235
- return new GroupLayerRenderer(layer, tilesets, ctx, imageTextures);
1236
- }
1246
+ switch (layer.type) {
1247
+ case "tilelayer": return new TileLayerRenderer(layer, tilesets, ctx);
1248
+ case "imagelayer": return new ImageLayerRenderer(layer, layer.image ? imageTextures.get(layer.image) ?? null : null, ctx);
1249
+ case "objectgroup": return new ObjectLayerRenderer(layer, tilesets);
1250
+ case "group": return new GroupLayerRenderer(layer, tilesets, ctx, imageTextures);
1251
+ }
1252
+ }
1253
+ //#endregion
1254
+ //#region src/renderer/mapSize.ts
1255
+ function computeMapPixelSize(mapData) {
1256
+ const { orientation, width, height, tilewidth, tileheight, staggeraxis } = mapData;
1257
+ switch (orientation) {
1258
+ case "isometric": return {
1259
+ width: (width + height) * (tilewidth / 2),
1260
+ height: (width + height) * (tileheight / 2)
1261
+ };
1262
+ case "staggered":
1263
+ case "hexagonal": return staggeraxis === "x" ? {
1264
+ width: (width + 1) * (tilewidth / 2),
1265
+ height: height * tileheight + tileheight / 2
1266
+ } : {
1267
+ width: width * tilewidth + tilewidth / 2,
1268
+ height: (height + 1) * (tileheight / 2)
1269
+ };
1270
+ default: return {
1271
+ width: width * tilewidth,
1272
+ height: height * tileheight
1273
+ };
1274
+ }
1275
+ }
1276
+ //#endregion
1277
+ //#region src/renderer/TileSetRenderer.ts
1278
+ var TileSetRenderer = class {
1279
+ constructor(tileset, baseTexture) {
1280
+ this._ownedTextures = /* @__PURE__ */ new Map();
1281
+ this._externalTextures = /* @__PURE__ */ new Map();
1282
+ this.tileset = tileset;
1283
+ this.baseTexture = baseTexture;
1284
+ }
1285
+ getTexture(localId) {
1286
+ const external = this._externalTextures.get(localId);
1287
+ if (external) return external;
1288
+ const owned = this._ownedTextures.get(localId);
1289
+ if (owned) return owned;
1290
+ if (this.tileset.tiles.get(localId)?.image) return null;
1291
+ if (!this.baseTexture) return null;
1292
+ const { tilewidth, tileheight, columns, margin, spacing } = this.tileset;
1293
+ if (columns <= 0) return null;
1294
+ const col = localId % columns;
1295
+ const row = Math.floor(localId / columns);
1296
+ const frame = new pixi_js.Rectangle(margin + col * (tilewidth + spacing), margin + row * (tileheight + spacing), tilewidth, tileheight);
1297
+ const texture = new pixi_js.Texture({
1298
+ source: this.baseTexture.source,
1299
+ frame
1300
+ });
1301
+ this._ownedTextures.set(localId, texture);
1302
+ return texture;
1303
+ }
1304
+ setTileTexture(localId, texture) {
1305
+ this._externalTextures.set(localId, texture);
1306
+ }
1307
+ getAnimationFrames(localId) {
1308
+ return this.tileset.tiles.get(localId)?.animation;
1309
+ }
1310
+ /**
1311
+ * Returns the intrinsic pixel size of a tile based on tileset metadata.
1312
+ * For image-collection tilesets, each tile has its own image dimensions.
1313
+ * For regular tilesets, all tiles share the tileset's tilewidth/tileheight.
1314
+ *
1315
+ * This does not read from the pixi Texture, so it is safe to call before
1316
+ * textures have finished loading.
1317
+ */
1318
+ getTileSize(localId) {
1319
+ const tileDef = this.tileset.tiles.get(localId);
1320
+ if (tileDef?.image) return {
1321
+ width: tileDef.imagewidth ?? this.tileset.tilewidth,
1322
+ height: tileDef.imageheight ?? this.tileset.tileheight
1323
+ };
1324
+ return {
1325
+ width: this.tileset.tilewidth,
1326
+ height: this.tileset.tileheight
1327
+ };
1328
+ }
1329
+ /**
1330
+ * Returns the pixel size a tile should be drawn at on the map grid.
1331
+ * When `tilerendersize === 'grid'`, the tile is resized to the map cell
1332
+ * size, honouring `fillmode` for non-stretch aspect handling.
1333
+ * Otherwise the tile's intrinsic size is used.
1334
+ */
1335
+ getRenderSize(localId, ctx) {
1336
+ const intrinsic = this.getTileSize(localId);
1337
+ if (this.tileset.tilerendersize !== "grid") return intrinsic;
1338
+ const gridW = ctx.tilewidth;
1339
+ const gridH = ctx.tileheight;
1340
+ if (this.tileset.fillmode === "preserve-aspect-fit") {
1341
+ if (intrinsic.width === 0 || intrinsic.height === 0) return {
1342
+ width: gridW,
1343
+ height: gridH
1344
+ };
1345
+ const scale = Math.min(gridW / intrinsic.width, gridH / intrinsic.height);
1346
+ return {
1347
+ width: intrinsic.width * scale,
1348
+ height: intrinsic.height * scale
1349
+ };
1350
+ }
1351
+ return {
1352
+ width: gridW,
1353
+ height: gridH
1354
+ };
1355
+ }
1356
+ destroy() {
1357
+ for (const tex of this._ownedTextures.values()) tex.destroy();
1358
+ this._ownedTextures.clear();
1359
+ this._externalTextures.clear();
1360
+ }
1361
+ };
1362
+ //#endregion
1363
+ //#region src/renderer/TiledMap.ts
1364
+ function isParallaxLayer(c) {
1365
+ return c instanceof TileLayerRenderer || c instanceof ImageLayerRenderer || c instanceof ObjectLayerRenderer || c instanceof GroupLayerRenderer;
1237
1366
  }
1238
-
1239
- // src/renderer/TiledMap.ts
1240
1367
  var TiledMap = class extends pixi_js.Container {
1241
- constructor(mapData, options) {
1242
- super();
1243
- this._background = null;
1244
- this.mapData = mapData;
1245
- this.label = "TiledMap";
1246
- const tilesetTextures = options?.tilesetTextures ?? /* @__PURE__ */ new Map();
1247
- const imageLayerTextures = options?.imageLayerTextures ?? /* @__PURE__ */ new Map();
1248
- const tileImageTextures = options?.tileImageTextures ?? /* @__PURE__ */ new Map();
1249
- this.tileSetRenderers = mapData.tilesets.map((ts) => {
1250
- const baseTex = ts.image ? tilesetTextures.get(ts.image) ?? null : null;
1251
- const renderer = new TileSetRenderer(ts, baseTex);
1252
- for (const [localId, tileDef] of ts.tiles) {
1253
- if (tileDef.image) {
1254
- const tex = tileImageTextures.get(tileDef.image);
1255
- if (tex) renderer.setTileTexture(localId, tex);
1256
- }
1257
- }
1258
- return renderer;
1259
- });
1260
- const ctx = {
1261
- orientation: mapData.orientation,
1262
- renderorder: mapData.renderorder,
1263
- tilewidth: mapData.tilewidth,
1264
- tileheight: mapData.tileheight,
1265
- hexsidelength: mapData.hexsidelength,
1266
- staggeraxis: mapData.staggeraxis,
1267
- staggerindex: mapData.staggerindex
1268
- };
1269
- if (mapData.backgroundcolor) {
1270
- this._buildBackground(mapData);
1271
- }
1272
- for (const layer of mapData.layers) {
1273
- const child2 = createLayerRenderer(layer, this.tileSetRenderers, ctx, imageLayerTextures);
1274
- if (child2) this.addChild(child2);
1275
- }
1276
- }
1277
- get orientation() {
1278
- return this.mapData.orientation;
1279
- }
1280
- get mapWidth() {
1281
- return this.mapData.width;
1282
- }
1283
- get mapHeight() {
1284
- return this.mapData.height;
1285
- }
1286
- get tileWidth() {
1287
- return this.mapData.tilewidth;
1288
- }
1289
- get tileHeight() {
1290
- return this.mapData.tileheight;
1291
- }
1292
- getLayer(name) {
1293
- return this.children.find((c) => c.label === name);
1294
- }
1295
- _buildBackground(mapData) {
1296
- const color = parseTintColor(mapData.backgroundcolor);
1297
- const pixelWidth = mapData.width * mapData.tilewidth;
1298
- const pixelHeight = mapData.height * mapData.tileheight;
1299
- this._background = new pixi_js.Graphics().rect(0, 0, pixelWidth, pixelHeight).fill(color);
1300
- this._background.label = "background";
1301
- this.addChild(this._background);
1302
- }
1303
- destroy(options) {
1304
- for (const ts of this.tileSetRenderers) {
1305
- ts.destroy();
1306
- }
1307
- super.destroy(options);
1308
- }
1368
+ constructor(mapData, options) {
1369
+ super();
1370
+ this._background = null;
1371
+ this.mapData = mapData;
1372
+ this.label = "TiledMap";
1373
+ const pixelSize = computeMapPixelSize(mapData);
1374
+ this.boundsArea = new pixi_js.Rectangle(0, 0, pixelSize.width, pixelSize.height);
1375
+ const tilesetTextures = options?.tilesetTextures ?? /* @__PURE__ */ new Map();
1376
+ const imageLayerTextures = options?.imageLayerTextures ?? /* @__PURE__ */ new Map();
1377
+ const tileImageTextures = options?.tileImageTextures ?? /* @__PURE__ */ new Map();
1378
+ this.tileSetRenderers = mapData.tilesets.map((ts) => {
1379
+ const renderer = new TileSetRenderer(ts, ts.image ? tilesetTextures.get(ts.image) ?? null : null);
1380
+ for (const [localId, tileDef] of ts.tiles) if (tileDef.image) {
1381
+ const tex = tileImageTextures.get(tileDef.image);
1382
+ if (tex) renderer.setTileTexture(localId, tex);
1383
+ }
1384
+ return renderer;
1385
+ });
1386
+ const ctx = {
1387
+ orientation: mapData.orientation,
1388
+ renderorder: mapData.renderorder,
1389
+ tilewidth: mapData.tilewidth,
1390
+ tileheight: mapData.tileheight,
1391
+ hexsidelength: mapData.hexsidelength,
1392
+ staggeraxis: mapData.staggeraxis,
1393
+ staggerindex: mapData.staggerindex,
1394
+ mapPixelWidth: pixelSize.width,
1395
+ mapPixelHeight: pixelSize.height
1396
+ };
1397
+ if (mapData.backgroundcolor) this._buildBackground(pixelSize.width, pixelSize.height, mapData.backgroundcolor);
1398
+ for (const layer of mapData.layers) {
1399
+ const child = createLayerRenderer(layer, this.tileSetRenderers, ctx, imageLayerTextures);
1400
+ if (child) this.addChild(child);
1401
+ }
1402
+ }
1403
+ get orientation() {
1404
+ return this.mapData.orientation;
1405
+ }
1406
+ get mapWidth() {
1407
+ return this.mapData.width;
1408
+ }
1409
+ get mapHeight() {
1410
+ return this.mapData.height;
1411
+ }
1412
+ get tileWidth() {
1413
+ return this.mapData.tilewidth;
1414
+ }
1415
+ get tileHeight() {
1416
+ return this.mapData.tileheight;
1417
+ }
1418
+ getLayer(name) {
1419
+ return this.children.find((c) => c.label === name);
1420
+ }
1421
+ /**
1422
+ * Reposition layers to reflect the camera's current position through the
1423
+ * map's parallax factors. Call after moving the camera.
1424
+ *
1425
+ * Effective layer screen position (after your camera transform) is:
1426
+ * base_offset - parallax_origin * (1 - parallax) - camera * parallax
1427
+ *
1428
+ * so a layer with parallax 1 moves normally with the camera and a layer
1429
+ * with parallax 0 is pinned in screen space. Nested group layers compose
1430
+ * parallax multiplicatively per the Tiled spec.
1431
+ */
1432
+ applyParallax(cameraX, cameraY) {
1433
+ const ox = this.mapData.parallaxoriginx;
1434
+ const oy = this.mapData.parallaxoriginy;
1435
+ for (const child of this.children) if (isParallaxLayer(child)) applyParallaxRecursive(child, cameraX, cameraY, ox, oy, 1, 1);
1436
+ }
1437
+ _buildBackground(pixelWidth, pixelHeight, colorHex) {
1438
+ const color = parseTintColor(colorHex);
1439
+ this._background = new pixi_js.Graphics().rect(0, 0, pixelWidth, pixelHeight).fill(color);
1440
+ this._background.label = "background";
1441
+ this.addChild(this._background);
1442
+ }
1443
+ destroy(options) {
1444
+ for (const ts of this.tileSetRenderers) ts.destroy();
1445
+ super.destroy(options);
1446
+ }
1309
1447
  };
1310
- function isXmlExt(ext) {
1311
- return ext === ".tmx";
1312
- }
1313
- function isTsxExt(ext) {
1314
- return ext === ".tsx";
1315
- }
1316
- var tiledMapLoader = {
1317
- extension: {
1318
- type: pixi_js.ExtensionType.LoadParser,
1319
- name: "tiledmap-loader"
1320
- },
1321
- id: "tiledmap-loader",
1322
- name: "tiledmap-loader",
1323
- test(url) {
1324
- const ext = pixi_js.path.extname(url).toLowerCase();
1325
- return ext === ".tmx" || ext === ".tmj";
1326
- },
1327
- async load(url) {
1328
- const ext = pixi_js.path.extname(url).toLowerCase();
1329
- const response = await fetch(url);
1330
- let data;
1331
- if (isXmlExt(ext)) {
1332
- const xml = await response.text();
1333
- data = parseTmx(xml);
1334
- } else {
1335
- data = await response.json();
1336
- }
1337
- const basePath = pixi_js.path.dirname(url);
1338
- const externalTilesets = /* @__PURE__ */ new Map();
1339
- for (const ts of data.tilesets) {
1340
- if ("source" in ts && !("name" in ts)) {
1341
- const tsUrl = pixi_js.path.join(basePath, ts.source);
1342
- const tsResponse = await fetch(tsUrl);
1343
- const tsExt = pixi_js.path.extname(ts.source).toLowerCase();
1344
- let tsData;
1345
- if (isTsxExt(tsExt)) {
1346
- const tsXml = await tsResponse.text();
1347
- tsData = parseTsx(tsXml);
1348
- } else {
1349
- tsData = await tsResponse.json();
1350
- }
1351
- externalTilesets.set(ts.source, tsData);
1352
- }
1353
- }
1354
- const mapData = await parseMapAsync(data, { externalTilesets });
1355
- const tilesetTextures = /* @__PURE__ */ new Map();
1356
- const imageLayerTextures = /* @__PURE__ */ new Map();
1357
- const tileImageTextures = /* @__PURE__ */ new Map();
1358
- const textureLoads = [];
1359
- for (const ts of mapData.tilesets) {
1360
- if (ts.image) {
1361
- const imageUrl = pixi_js.path.join(basePath, ts.image);
1362
- textureLoads.push(
1363
- pixi_js.Assets.load(imageUrl).then((tex) => {
1364
- tilesetTextures.set(ts.image, tex);
1365
- })
1366
- );
1367
- }
1368
- for (const [_localId, tileDef] of ts.tiles) {
1369
- if (tileDef.image) {
1370
- const tileImgUrl = pixi_js.path.join(basePath, tileDef.image);
1371
- textureLoads.push(
1372
- pixi_js.Assets.load(tileImgUrl).then((tex) => {
1373
- tileImageTextures.set(tileDef.image, tex);
1374
- })
1375
- );
1376
- }
1377
- }
1378
- }
1379
- for (const layer of flattenLayers(mapData.layers)) {
1380
- if (layer.type === "imagelayer" && layer.image) {
1381
- const imgUrl = pixi_js.path.join(basePath, layer.image);
1382
- textureLoads.push(
1383
- pixi_js.Assets.load(imgUrl).then((tex) => {
1384
- imageLayerTextures.set(layer.image, tex);
1385
- })
1386
- );
1387
- }
1388
- }
1389
- await Promise.all(textureLoads);
1390
- const container = new TiledMap(mapData, {
1391
- tilesetTextures,
1392
- imageLayerTextures,
1393
- tileImageTextures
1394
- });
1395
- return { mapData, container };
1396
- }
1448
+ function applyParallaxRecursive(layer, cameraX, cameraY, originX, originY, parentParallaxX, parentParallaxY) {
1449
+ const px = layer.layerParallaxX * parentParallaxX;
1450
+ const py = layer.layerParallaxY * parentParallaxY;
1451
+ layer.position.set(layer.layerBaseOffsetX + (cameraX - originX) * (1 - px), layer.layerBaseOffsetY + (cameraY - originY) * (1 - py));
1452
+ if (layer instanceof GroupLayerRenderer) {
1453
+ for (const child of layer.children) if (isParallaxLayer(child)) applyParallaxRecursive(child, cameraX, cameraY, originX, originY, px, py);
1454
+ }
1455
+ }
1456
+ //#endregion
1457
+ //#region src/renderer/tiledAssetLoader.ts
1458
+ const tiledMapLoader = {
1459
+ extension: {
1460
+ type: pixi_js.ExtensionType.LoadParser,
1461
+ name: "tiledmap-loader"
1462
+ },
1463
+ id: "tiledmap-loader",
1464
+ name: "tiledmap-loader",
1465
+ test(url) {
1466
+ const ext = pixi_js.path.extname(url).toLowerCase();
1467
+ return ext === ".tmx" || ext === ".tmj";
1468
+ },
1469
+ async load(url) {
1470
+ const ext = pixi_js.path.extname(url).toLowerCase();
1471
+ const response = await fetch(url);
1472
+ let data;
1473
+ if (ext === ".tmx") data = parseTmx(await response.text());
1474
+ else data = await response.json();
1475
+ const basePath = pixi_js.path.dirname(url);
1476
+ const externalTilesets = /* @__PURE__ */ new Map();
1477
+ for (const ts of data.tilesets) {
1478
+ if (!isTilesetRef(ts)) continue;
1479
+ const tsUrl = pixi_js.path.join(basePath, ts.source);
1480
+ const tsResponse = await fetch(tsUrl);
1481
+ const tsExt = pixi_js.path.extname(ts.source).toLowerCase();
1482
+ let tsData;
1483
+ if (tsExt === ".tsx") tsData = parseTsx(await tsResponse.text());
1484
+ else tsData = await tsResponse.json();
1485
+ externalTilesets.set(ts.source, tsData);
1486
+ }
1487
+ const templates = /* @__PURE__ */ new Map();
1488
+ const templateSources = /* @__PURE__ */ new Set();
1489
+ for (const obj of walkObjects(data.layers)) if (obj.template) templateSources.add(obj.template);
1490
+ await Promise.all(Array.from(templateSources).map(async (src) => {
1491
+ const tplUrl = pixi_js.path.join(basePath, src);
1492
+ const tplResponse = await fetch(tplUrl);
1493
+ const tplExt = pixi_js.path.extname(src).toLowerCase();
1494
+ let tpl;
1495
+ if (tplExt === ".tx") tpl = parseTx(await tplResponse.text());
1496
+ else tpl = await tplResponse.json();
1497
+ templates.set(src, tpl);
1498
+ }));
1499
+ const mapData = await parseMapAsync(data, {
1500
+ externalTilesets,
1501
+ templates
1502
+ });
1503
+ const tilesetTextures = /* @__PURE__ */ new Map();
1504
+ const imageLayerTextures = /* @__PURE__ */ new Map();
1505
+ const tileImageTextures = /* @__PURE__ */ new Map();
1506
+ const textureLoads = [];
1507
+ for (const ts of mapData.tilesets) {
1508
+ if (ts.image) {
1509
+ const imageUrl = pixi_js.path.join(basePath, ts.image);
1510
+ textureLoads.push(pixi_js.Assets.load(imageUrl).then((tex) => {
1511
+ tilesetTextures.set(ts.image, tex);
1512
+ }));
1513
+ }
1514
+ for (const [_localId, tileDef] of ts.tiles) if (tileDef.image) {
1515
+ const tileImgUrl = pixi_js.path.join(basePath, tileDef.image);
1516
+ textureLoads.push(pixi_js.Assets.load(tileImgUrl).then((tex) => {
1517
+ tileImageTextures.set(tileDef.image, tex);
1518
+ }));
1519
+ }
1520
+ }
1521
+ for (const layer of flattenLayers(mapData.layers)) if (layer.type === "imagelayer" && layer.image) {
1522
+ const imgUrl = pixi_js.path.join(basePath, layer.image);
1523
+ textureLoads.push(pixi_js.Assets.load(imgUrl).then((tex) => {
1524
+ imageLayerTextures.set(layer.image, tex);
1525
+ }));
1526
+ }
1527
+ await Promise.all(textureLoads);
1528
+ return {
1529
+ mapData,
1530
+ container: new TiledMap(mapData, {
1531
+ tilesetTextures,
1532
+ imageLayerTextures,
1533
+ tileImageTextures
1534
+ })
1535
+ };
1536
+ }
1397
1537
  };
1398
1538
  function flattenLayers(layers) {
1399
- const result = [];
1400
- for (const layer of layers) {
1401
- result.push(layer);
1402
- if (layer.type === "group") {
1403
- result.push(...flattenLayers(layer.layers));
1404
- }
1405
- }
1406
- return result;
1539
+ const result = [];
1540
+ for (const layer of layers) {
1541
+ result.push(layer);
1542
+ if (layer.type === "group" && layer.layers) result.push(...flattenLayers(layer.layers));
1543
+ }
1544
+ return result;
1407
1545
  }
1408
-
1546
+ function* walkObjects(layers) {
1547
+ for (const layer of flattenLayers(layers)) if (layer.type === "objectgroup" && layer.objects) for (const obj of layer.objects) yield obj;
1548
+ }
1549
+ //#endregion
1409
1550
  exports.FLIPPED_DIAGONALLY_FLAG = FLIPPED_DIAGONALLY_FLAG;
1410
1551
  exports.FLIPPED_HORIZONTALLY_FLAG = FLIPPED_HORIZONTALLY_FLAG;
1411
1552
  exports.FLIPPED_VERTICALLY_FLAG = FLIPPED_VERTICALLY_FLAG;
@@ -1425,7 +1566,8 @@ exports.parseMap = parseMap;
1425
1566
  exports.parseMapAsync = parseMapAsync;
1426
1567
  exports.parseTmx = parseTmx;
1427
1568
  exports.parseTsx = parseTsx;
1569
+ exports.parseTx = parseTx;
1428
1570
  exports.tileToPixel = tileToPixel;
1429
1571
  exports.tiledMapLoader = tiledMapLoader;
1430
- //# sourceMappingURL=index.cjs.map
1572
+
1431
1573
  //# sourceMappingURL=index.cjs.map