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/README.md +41 -5
- package/dist/index.cjs +1453 -1311
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +437 -363
- package/dist/index.d.cts.map +1 -0
- package/dist/index.d.mts +533 -0
- package/dist/index.d.mts.map +1 -0
- package/dist/index.mjs +1551 -0
- package/dist/index.mjs.map +1 -0
- package/package.json +21 -24
- package/dist/index.d.ts +0 -459
- package/dist/index.js +0 -1409
- package/dist/index.js.map +0 -1
package/dist/index.cjs
CHANGED
|
@@ -1,1411 +1,1552 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
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
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
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
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
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
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
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
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
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
|
-
|
|
91
|
-
|
|
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
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
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
|
-
|
|
120
|
-
}
|
|
121
|
-
function
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
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
|
-
|
|
130
|
+
return el.getAttribute(name) ?? fallback;
|
|
398
131
|
}
|
|
399
132
|
function int(el, name, fallback = 0) {
|
|
400
|
-
|
|
401
|
-
|
|
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
|
-
|
|
405
|
-
|
|
137
|
+
const v = el.getAttribute(name);
|
|
138
|
+
return v != null ? parseFloat(v) : fallback;
|
|
406
139
|
}
|
|
407
140
|
function bool(el, name, fallback = false) {
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
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
|
-
|
|
414
|
-
|
|
146
|
+
const v = el.getAttribute(name);
|
|
147
|
+
return v != null ? v : void 0;
|
|
415
148
|
}
|
|
416
149
|
function optInt(el, name) {
|
|
417
|
-
|
|
418
|
-
|
|
150
|
+
const v = el.getAttribute(name);
|
|
151
|
+
return v != null ? parseInt(v, 10) : void 0;
|
|
419
152
|
}
|
|
420
153
|
function optFloat(el, name) {
|
|
421
|
-
|
|
422
|
-
|
|
154
|
+
const v = el.getAttribute(name);
|
|
155
|
+
return v != null ? parseFloat(v) : void 0;
|
|
423
156
|
}
|
|
424
157
|
function children(el, tag) {
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
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
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
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
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
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
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
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
|
-
|
|
473
|
-
|
|
474
|
-
|
|
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
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
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
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
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
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
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
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
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
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
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
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
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
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
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
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
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
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
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
|
-
|
|
636
|
-
|
|
637
|
-
|
|
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
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
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
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
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
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
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
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
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
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
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
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
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
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
479
|
+
return {
|
|
480
|
+
...parseLayerCommon(el),
|
|
481
|
+
type: "group",
|
|
482
|
+
layers: parseLayers(el)
|
|
483
|
+
};
|
|
747
484
|
}
|
|
748
485
|
function parseLayers(parentEl) {
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
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
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
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
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
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
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
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
|
-
|
|
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
|
-
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
|
|
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
|
-
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
|
|
1095
|
+
return {
|
|
1096
|
+
x: col * ctx.tilewidth,
|
|
1097
|
+
y: row * ctx.tileheight
|
|
1098
|
+
};
|
|
882
1099
|
}
|
|
883
1100
|
function isometricToPixel(col, row, ctx) {
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
|
|
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
|
-
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
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
|
-
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
}
|
|
926
|
-
|
|
927
|
-
|
|
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
|
-
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
|
|
945
|
-
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
|
|
966
|
-
|
|
967
|
-
|
|
968
|
-
|
|
969
|
-
|
|
970
|
-
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
|
|
978
|
-
|
|
979
|
-
|
|
980
|
-
|
|
981
|
-
|
|
982
|
-
|
|
983
|
-
|
|
984
|
-
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
|
|
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
|
-
|
|
1006
|
-
|
|
1007
|
-
|
|
1008
|
-
|
|
1009
|
-
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
|
|
1013
|
-
|
|
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
|
-
|
|
1021
|
-
|
|
1022
|
-
|
|
1023
|
-
|
|
1024
|
-
|
|
1025
|
-
|
|
1026
|
-
|
|
1027
|
-
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
|
-
|
|
1031
|
-
|
|
1032
|
-
|
|
1033
|
-
|
|
1034
|
-
|
|
1035
|
-
|
|
1036
|
-
}
|
|
1037
|
-
|
|
1038
|
-
|
|
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
|
-
|
|
1226
|
-
|
|
1227
|
-
|
|
1228
|
-
|
|
1229
|
-
|
|
1230
|
-
|
|
1231
|
-
|
|
1232
|
-
|
|
1233
|
-
|
|
1234
|
-
|
|
1235
|
-
|
|
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
|
-
|
|
1242
|
-
|
|
1243
|
-
|
|
1244
|
-
|
|
1245
|
-
|
|
1246
|
-
|
|
1247
|
-
|
|
1248
|
-
|
|
1249
|
-
|
|
1250
|
-
|
|
1251
|
-
|
|
1252
|
-
|
|
1253
|
-
|
|
1254
|
-
|
|
1255
|
-
|
|
1256
|
-
|
|
1257
|
-
|
|
1258
|
-
|
|
1259
|
-
|
|
1260
|
-
|
|
1261
|
-
|
|
1262
|
-
|
|
1263
|
-
|
|
1264
|
-
|
|
1265
|
-
|
|
1266
|
-
|
|
1267
|
-
|
|
1268
|
-
|
|
1269
|
-
|
|
1270
|
-
|
|
1271
|
-
|
|
1272
|
-
|
|
1273
|
-
|
|
1274
|
-
|
|
1275
|
-
|
|
1276
|
-
|
|
1277
|
-
|
|
1278
|
-
|
|
1279
|
-
|
|
1280
|
-
|
|
1281
|
-
|
|
1282
|
-
|
|
1283
|
-
|
|
1284
|
-
|
|
1285
|
-
|
|
1286
|
-
|
|
1287
|
-
|
|
1288
|
-
|
|
1289
|
-
|
|
1290
|
-
|
|
1291
|
-
|
|
1292
|
-
|
|
1293
|
-
|
|
1294
|
-
|
|
1295
|
-
|
|
1296
|
-
|
|
1297
|
-
|
|
1298
|
-
|
|
1299
|
-
|
|
1300
|
-
|
|
1301
|
-
|
|
1302
|
-
|
|
1303
|
-
|
|
1304
|
-
|
|
1305
|
-
|
|
1306
|
-
|
|
1307
|
-
|
|
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
|
|
1311
|
-
|
|
1312
|
-
|
|
1313
|
-
|
|
1314
|
-
|
|
1315
|
-
|
|
1316
|
-
|
|
1317
|
-
|
|
1318
|
-
|
|
1319
|
-
|
|
1320
|
-
|
|
1321
|
-
|
|
1322
|
-
|
|
1323
|
-
|
|
1324
|
-
|
|
1325
|
-
|
|
1326
|
-
|
|
1327
|
-
|
|
1328
|
-
|
|
1329
|
-
|
|
1330
|
-
|
|
1331
|
-
|
|
1332
|
-
|
|
1333
|
-
|
|
1334
|
-
|
|
1335
|
-
|
|
1336
|
-
|
|
1337
|
-
|
|
1338
|
-
|
|
1339
|
-
|
|
1340
|
-
|
|
1341
|
-
|
|
1342
|
-
|
|
1343
|
-
|
|
1344
|
-
|
|
1345
|
-
|
|
1346
|
-
|
|
1347
|
-
|
|
1348
|
-
|
|
1349
|
-
|
|
1350
|
-
|
|
1351
|
-
|
|
1352
|
-
|
|
1353
|
-
|
|
1354
|
-
|
|
1355
|
-
|
|
1356
|
-
|
|
1357
|
-
|
|
1358
|
-
|
|
1359
|
-
|
|
1360
|
-
|
|
1361
|
-
|
|
1362
|
-
|
|
1363
|
-
|
|
1364
|
-
|
|
1365
|
-
|
|
1366
|
-
|
|
1367
|
-
|
|
1368
|
-
|
|
1369
|
-
|
|
1370
|
-
|
|
1371
|
-
|
|
1372
|
-
|
|
1373
|
-
|
|
1374
|
-
|
|
1375
|
-
|
|
1376
|
-
|
|
1377
|
-
|
|
1378
|
-
|
|
1379
|
-
|
|
1380
|
-
|
|
1381
|
-
|
|
1382
|
-
|
|
1383
|
-
|
|
1384
|
-
|
|
1385
|
-
|
|
1386
|
-
|
|
1387
|
-
|
|
1388
|
-
|
|
1389
|
-
|
|
1390
|
-
|
|
1391
|
-
|
|
1392
|
-
|
|
1393
|
-
|
|
1394
|
-
|
|
1395
|
-
|
|
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
|
-
|
|
1400
|
-
|
|
1401
|
-
|
|
1402
|
-
|
|
1403
|
-
|
|
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
|
-
|
|
1572
|
+
|
|
1431
1573
|
//# sourceMappingURL=index.cjs.map
|