@makefully/platypus 4.1.9 → 4.1.11
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/CHANGELOG.md +30 -0
- package/lib/platypus.js +1 -1
- package/package.json +1 -1
- package/src/AssetManager.js +46 -2
- package/src/PIXIAnimation.js +102 -23
- package/src/VOPlayer.js +1 -1
- package/src/components/LevelBuilder.js +254 -65
- package/src/components/Render.js +23 -7
- package/src/components/RenderSprite.js +10 -1
- package/src/components/TiledLoader.js +8 -4
- package/src/utils/clone.js +11 -0
|
@@ -1,10 +1,181 @@
|
|
|
1
1
|
/* global atob, platypus */
|
|
2
2
|
import {arrayCache, greenSlice, greenSplice, union} from '../utils/array.js';
|
|
3
|
+
import {cloneDefinition} from '../utils/clone.js';
|
|
3
4
|
import createComponentClass from '../factory.js';
|
|
4
5
|
import {inflate} from 'pako';
|
|
5
6
|
import TiledLoader from './TiledLoader.js';
|
|
6
7
|
|
|
7
8
|
const
|
|
9
|
+
assertLevelPiece = function (piece, label) {
|
|
10
|
+
if (piece == null) {
|
|
11
|
+
throw new Error(`Level Builder: Level piece "${label}" is undefined. Check that it is defined in platypus.game.settings.levels or levelPieces.`);
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
if (typeof piece !== 'object') {
|
|
15
|
+
throw new Error(`Level Builder: Level piece "${label}" must be a level definition object, received ${typeof piece}.`);
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
if (piece.width == null) {
|
|
19
|
+
throw new Error(`Level Builder: Level piece "${label}" is missing width.`);
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
if (piece.height == null) {
|
|
23
|
+
throw new Error(`Level Builder: Level piece "${label}" is missing height.`);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
if (piece.tilewidth == null) {
|
|
27
|
+
throw new Error(`Level Builder: Level piece "${label}" is missing tilewidth.`);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
if (piece.tileheight == null) {
|
|
31
|
+
throw new Error(`Level Builder: Level piece "${label}" is missing tileheight.`);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
if (piece.layers == null) {
|
|
35
|
+
throw new Error(`Level Builder: Level piece "${label}" is missing a layers array.`);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
if (!Array.isArray(piece.layers)) {
|
|
39
|
+
throw new Error(`Level Builder: Level piece "${label}" layers must be an array.`);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
return piece;
|
|
43
|
+
},
|
|
44
|
+
assertLevelTemplate = function (template) {
|
|
45
|
+
if (template == null) {
|
|
46
|
+
throw new Error(`Level Builder: levelTemplate is required but was not provided.`);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
if (!Array.isArray(template)) {
|
|
50
|
+
throw new Error(`Level Builder: levelTemplate must be an array, received ${typeof template}.`);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
if (template.length === 0) {
|
|
54
|
+
throw new Error(`Level Builder: levelTemplate cannot be empty.`);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
let rowLength = null;
|
|
58
|
+
|
|
59
|
+
for (let i = 0; i < template.length; i++) {
|
|
60
|
+
const
|
|
61
|
+
row = template[i];
|
|
62
|
+
|
|
63
|
+
if (typeof row === 'string') {
|
|
64
|
+
if (!row) {
|
|
65
|
+
throw new Error(`Level Builder: levelTemplate[${i}] is an empty string.`);
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
if (rowLength != null && rowLength !== '1d') {
|
|
69
|
+
throw new Error(`Level Builder: levelTemplate is not rectangular: row 0 has ${rowLength} columns but row ${i} is a string.`);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
rowLength = '1d';
|
|
73
|
+
} else if (Array.isArray(row)) {
|
|
74
|
+
if (row.length === 0) {
|
|
75
|
+
throw new Error(`Level Builder: levelTemplate row ${i} is empty.`);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
if (rowLength === '1d') {
|
|
79
|
+
throw new Error(`Level Builder: levelTemplate is not rectangular: row 0 is a string but row ${i} is an array.`);
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
if (rowLength == null) {
|
|
83
|
+
rowLength = row.length;
|
|
84
|
+
} else if (rowLength !== row.length) {
|
|
85
|
+
throw new Error(`Level Builder: levelTemplate is not rectangular: row 0 has ${rowLength} columns but row ${i} has ${row.length}.`);
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
for (let j = 0; j < row.length; j++) {
|
|
89
|
+
if (typeof row[j] !== 'string') {
|
|
90
|
+
throw new Error(`Level Builder: levelTemplate[${i}][${j}] must be a piece id string, received ${typeof row[j]}.`);
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
if (!row[j]) {
|
|
94
|
+
throw new Error(`Level Builder: levelTemplate[${i}][${j}] is an empty string.`);
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
} else {
|
|
98
|
+
throw new Error(`Level Builder: levelTemplate[${i}] must be a string or array of strings, received ${typeof row}.`);
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
return template;
|
|
103
|
+
},
|
|
104
|
+
assertLevelPieces = function (pieces) {
|
|
105
|
+
if (pieces == null) {
|
|
106
|
+
return;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
if (typeof pieces !== 'object' || Array.isArray(pieces)) {
|
|
110
|
+
throw new Error(`Level Builder: levelPieces must be an object, received ${Array.isArray(pieces) ? 'array' : typeof pieces}.`);
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
const
|
|
114
|
+
keys = Object.keys(pieces),
|
|
115
|
+
{length} = keys;
|
|
116
|
+
|
|
117
|
+
for (let i = 0; i < length; i++) {
|
|
118
|
+
const
|
|
119
|
+
key = keys[i],
|
|
120
|
+
value = pieces[key];
|
|
121
|
+
|
|
122
|
+
if (typeof value === 'string') {
|
|
123
|
+
if (!value) {
|
|
124
|
+
throw new Error(`Level Builder: levelPieces["${key}"] is an empty string.`);
|
|
125
|
+
}
|
|
126
|
+
} else if (Array.isArray(value)) {
|
|
127
|
+
if (value.length === 0) {
|
|
128
|
+
throw new Error(`Level Builder: levelPieces["${key}"] is an empty array. Provide at least one level piece id.`);
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
for (let j = 0; j < value.length; j++) {
|
|
132
|
+
if (typeof value[j] !== 'string') {
|
|
133
|
+
throw new Error(`Level Builder: levelPieces["${key}"][${j}] must be a string level id, received ${typeof value[j]}.`);
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
if (!value[j]) {
|
|
137
|
+
throw new Error(`Level Builder: levelPieces["${key}"][${j}] is an empty string.`);
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
} else {
|
|
141
|
+
throw new Error(`Level Builder: levelPieces["${key}"] must be a string or array of strings, received ${typeof value}.`);
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
},
|
|
145
|
+
resolveLevelPiece = function (piece, label) {
|
|
146
|
+
if (typeof piece === 'string') {
|
|
147
|
+
const
|
|
148
|
+
levelDefinitions = platypus.game.settings.levels,
|
|
149
|
+
transformIndex = piece.indexOf(':');
|
|
150
|
+
let levelDefinition = levelDefinitions?.[piece];
|
|
151
|
+
|
|
152
|
+
if (!levelDefinition && transformIndex >= 0) {
|
|
153
|
+
const
|
|
154
|
+
baseLabel = piece.substring(0, transformIndex),
|
|
155
|
+
transform = piece.substring(transformIndex + 1);
|
|
156
|
+
|
|
157
|
+
levelDefinition = levelDefinitions?.[baseLabel];
|
|
158
|
+
|
|
159
|
+
if (!levelDefinition) {
|
|
160
|
+
throw new Error(`Level Builder: Level piece "${baseLabel}" (from "${piece}") was not found in platypus.game.settings.levels.`);
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
if (transform === 'mirror') {
|
|
164
|
+
return levelDefinitions[piece] = mirrorSegment(assertLevelPiece(levelDefinition, baseLabel));
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
throw new Error(`Level Builder: Unknown transform "${transform}" in level piece "${piece}". Supported transforms: mirror.`);
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
if (!levelDefinition) {
|
|
171
|
+
throw new Error(`Level Builder: Level piece "${piece}" was not found in platypus.game.settings.levels.`);
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
return assertLevelPiece(levelDefinition, piece);
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
return assertLevelPiece(piece, label);
|
|
178
|
+
},
|
|
8
179
|
maskXFlip = 0x80000000,
|
|
9
180
|
decodeString = (str, index) => (((str.charCodeAt(index)) + (str.charCodeAt(index + 1) << 8) + (str.charCodeAt(index + 2) << 16) + (str.charCodeAt(index + 3) << 24 )) >>> 0),
|
|
10
181
|
decodeArray = (arr, index) => ((arr[index] + (arr[index + 1] << 8) + (arr[index + 2] << 16) + (arr[index + 3] << 24 )) >>> 0),
|
|
@@ -24,13 +195,32 @@ const
|
|
|
24
195
|
|
|
25
196
|
return arr;
|
|
26
197
|
},
|
|
27
|
-
decodeLayer = function (layer) {
|
|
198
|
+
decodeLayer = function (layer, label) {
|
|
199
|
+
if (layer == null) {
|
|
200
|
+
throw new Error(`Level Builder: Cannot decode layer${label ? ` in "${label}"` : ''}: layer is undefined.`);
|
|
201
|
+
}
|
|
202
|
+
|
|
28
203
|
if (layer.encoding === 'base64') {
|
|
29
204
|
layer.data = decodeBase64(layer.data, layer.compression);
|
|
30
205
|
layer.encoding = 'csv'; // So we won't have to decode again.
|
|
31
206
|
}
|
|
32
207
|
return layer;
|
|
33
208
|
},
|
|
209
|
+
cloneLevelDefinition = cloneDefinition,
|
|
210
|
+
copySegmentProperty = function (target, source, key) {
|
|
211
|
+
if (target[key]) {
|
|
212
|
+
return;
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
const
|
|
216
|
+
value = source[key];
|
|
217
|
+
|
|
218
|
+
if (value != null && typeof value === 'object') {
|
|
219
|
+
target[key] = cloneDefinition(value);
|
|
220
|
+
} else {
|
|
221
|
+
target[key] = value;
|
|
222
|
+
}
|
|
223
|
+
},
|
|
34
224
|
mergeData = function (levelData, levelMergeAxisLength, segmentData, segmentMergeAxisLength, nonMergeAxisLength, mergeAxis) {
|
|
35
225
|
const
|
|
36
226
|
combined = greenSlice(levelData);
|
|
@@ -54,9 +244,7 @@ const
|
|
|
54
244
|
|
|
55
245
|
for (let i = 0; i < obj2s.length; i++) {
|
|
56
246
|
const
|
|
57
|
-
obj =
|
|
58
|
-
...obj2s[i]
|
|
59
|
-
};
|
|
247
|
+
obj = cloneLevelDefinition(obj2s[i]);
|
|
60
248
|
|
|
61
249
|
if (mergeAxis === 'horizontal') {
|
|
62
250
|
obj.x += mergeAxisLength;
|
|
@@ -67,7 +255,9 @@ const
|
|
|
67
255
|
}
|
|
68
256
|
return list;
|
|
69
257
|
},
|
|
70
|
-
mergeSegment = function (level, segment, mergeAxis) {
|
|
258
|
+
mergeSegment = function (level, segment, mergeAxis, segmentLabel) {
|
|
259
|
+
segment = cloneLevelDefinition(assertLevelPiece(segment, segmentLabel ?? 'segment'));
|
|
260
|
+
|
|
71
261
|
if (!level.tilewidth && !level.tileheight) {
|
|
72
262
|
//set level tile size data if it's not already set.
|
|
73
263
|
level.tilewidth = segment.tilewidth;
|
|
@@ -91,14 +281,16 @@ const
|
|
|
91
281
|
}
|
|
92
282
|
|
|
93
283
|
for (let i = 0; i < segment.layers.length; i++) {
|
|
284
|
+
if (segment.layers[i] == null) {
|
|
285
|
+
throw new Error(`Level Builder: Level piece "${segmentLabel ?? 'segment'}" layer ${i} is undefined.`);
|
|
286
|
+
}
|
|
287
|
+
|
|
94
288
|
if (!level.layers[i]) {
|
|
95
289
|
//if the level doesn't have a layer yet, we're creating it and then copying it from the segment.
|
|
96
|
-
decodeLayer(segment.layers[i]);
|
|
290
|
+
decodeLayer(segment.layers[i], segmentLabel);
|
|
97
291
|
|
|
98
292
|
const
|
|
99
|
-
layer = level.layers[i] =
|
|
100
|
-
...segment.layers[i]
|
|
101
|
-
};
|
|
293
|
+
layer = level.layers[i] = cloneLevelDefinition(segment.layers[i]);
|
|
102
294
|
|
|
103
295
|
// If we're adding objects, make sure that they're offset correctly.
|
|
104
296
|
if (layer.objects) {
|
|
@@ -112,7 +304,7 @@ const
|
|
|
112
304
|
//if the level does have a layer, we're appending the new data to it.
|
|
113
305
|
if (level.layers[i].data && segment.layers[i].data) {
|
|
114
306
|
// Make sure we're not trying to merge compressed levels.
|
|
115
|
-
decodeLayer(segment.layers[i]);
|
|
307
|
+
decodeLayer(segment.layers[i], segmentLabel);
|
|
116
308
|
|
|
117
309
|
if (mergeAxis === 'horizontal') {
|
|
118
310
|
level.layers[i].data = mergeData(level.layers[i].data, level.width, segment.layers[i].data, segment.width, level.height, mergeAxis);
|
|
@@ -149,15 +341,12 @@ const
|
|
|
149
341
|
const
|
|
150
342
|
key = keys[i];
|
|
151
343
|
|
|
152
|
-
|
|
153
|
-
level[key] = segment[key];
|
|
154
|
-
}
|
|
344
|
+
copySegmentProperty(level, segment, key);
|
|
155
345
|
}
|
|
156
346
|
}
|
|
157
347
|
},
|
|
158
348
|
mergeLevels = function (levelSegments) {
|
|
159
349
|
const
|
|
160
|
-
levelDefinitions = platypus.game.settings.levels,
|
|
161
350
|
level = {
|
|
162
351
|
height: 0,
|
|
163
352
|
width: 0,
|
|
@@ -172,34 +361,26 @@ const
|
|
|
172
361
|
layers: []
|
|
173
362
|
};
|
|
174
363
|
|
|
364
|
+
if (!Array.isArray(levelSegments[i])) {
|
|
365
|
+
throw new Error(`Level Builder: mergeLevels row ${i} must be an array of level pieces, received ${typeof levelSegments[i]}.`);
|
|
366
|
+
}
|
|
367
|
+
|
|
175
368
|
for (let j = 0; j < levelSegments[i].length; j++) {
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
levelDefinitionLabel = levelSegments[i][j],
|
|
180
|
-
transformIndex = levelDefinitionLabel.indexOf(':');
|
|
181
|
-
let levelDefinition = levelDefinitions[levelDefinitionLabel];
|
|
182
|
-
|
|
183
|
-
// check for transform
|
|
184
|
-
if (!levelDefinition && (transformIndex >= 0)) {
|
|
185
|
-
const transform = levelDefinitionLabel.substring(transformIndex + 1);
|
|
369
|
+
const
|
|
370
|
+
piece = levelSegments[i][j],
|
|
371
|
+
pieceLabel = typeof piece === 'string' ? piece : `row ${i}, column ${j}`;
|
|
186
372
|
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
levelDefinition = levelDefinitions[levelDefinitionLabel] = mirrorSegment(levelDefinition);
|
|
190
|
-
}
|
|
191
|
-
}
|
|
192
|
-
mergeSegment(row, levelDefinition, 'horizontal');
|
|
193
|
-
} else {
|
|
194
|
-
mergeSegment(row, levelSegments[i][j], 'horizontal');
|
|
195
|
-
}
|
|
373
|
+
//Merge horizontally
|
|
374
|
+
mergeSegment(row, resolveLevelPiece(piece, pieceLabel), 'horizontal', pieceLabel);
|
|
196
375
|
}
|
|
197
376
|
//Then merge vertically
|
|
198
|
-
mergeSegment(level, row, 'vertical');
|
|
377
|
+
mergeSegment(level, row, 'vertical', `row ${i}`);
|
|
199
378
|
}
|
|
200
379
|
return level;
|
|
201
380
|
},
|
|
202
|
-
mirrorSegment = function (segment) {
|
|
381
|
+
mirrorSegment = function (segment, segmentLabel) {
|
|
382
|
+
segment = cloneLevelDefinition(assertLevelPiece(segment, segmentLabel ?? 'segment'));
|
|
383
|
+
|
|
203
384
|
const
|
|
204
385
|
newSegment = {
|
|
205
386
|
layers: []
|
|
@@ -207,13 +388,15 @@ const
|
|
|
207
388
|
width = segment.width * segment.tilewidth;
|
|
208
389
|
|
|
209
390
|
for (let i = 0; i < segment.layers.length; i++) {
|
|
210
|
-
|
|
391
|
+
if (segment.layers[i] == null) {
|
|
392
|
+
throw new Error(`Level Builder: Level piece "${segmentLabel ?? 'segment'}" layer ${i} is undefined.`);
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
decodeLayer(segment.layers[i], segmentLabel);
|
|
211
396
|
|
|
212
397
|
const
|
|
213
398
|
fromLayer = segment.layers[i],
|
|
214
|
-
toLayer = newSegment.layers[i] =
|
|
215
|
-
...fromLayer
|
|
216
|
-
};
|
|
399
|
+
toLayer = newSegment.layers[i] = cloneLevelDefinition(fromLayer);
|
|
217
400
|
|
|
218
401
|
if (fromLayer.data) {
|
|
219
402
|
const
|
|
@@ -237,9 +420,7 @@ const
|
|
|
237
420
|
for (let j = 0; j < fromObjects.length; j++) {
|
|
238
421
|
const
|
|
239
422
|
fromObject = fromObjects[j],
|
|
240
|
-
toObject = toObjects[j] =
|
|
241
|
-
...fromObject
|
|
242
|
-
};
|
|
423
|
+
toObject = toObjects[j] = cloneLevelDefinition(fromObject);
|
|
243
424
|
|
|
244
425
|
toObject.x = width - fromObject.x - (fromObject.width || 0); // subtract object width since its top-left corner is the origin.
|
|
245
426
|
if (fromObject.rotation) {
|
|
@@ -265,9 +446,7 @@ const
|
|
|
265
446
|
const
|
|
266
447
|
key = keys[i];
|
|
267
448
|
|
|
268
|
-
|
|
269
|
-
newSegment[key] = segment[key];
|
|
270
|
-
}
|
|
449
|
+
copySegmentProperty(newSegment, segment, key);
|
|
271
450
|
}
|
|
272
451
|
}
|
|
273
452
|
|
|
@@ -351,12 +530,18 @@ export default createComponentClass(/** @lends platypus.components.LevelBuilder.
|
|
|
351
530
|
|
|
352
531
|
events: {
|
|
353
532
|
"layer-loaded": function (data) {
|
|
533
|
+
if (data?.level && typeof data.level === 'object') {
|
|
534
|
+
return;
|
|
535
|
+
}
|
|
536
|
+
|
|
354
537
|
const
|
|
355
538
|
piecesToCopy = data?.levelPieces ?? this.levelPieces;
|
|
356
539
|
|
|
357
540
|
this.levelMessage.persistentData = data;
|
|
358
541
|
this.levelTemplate = data?.levelTemplate ?? this.levelTemplate;
|
|
359
542
|
this.useUniques = data?.useUniques ?? this.useUniques;
|
|
543
|
+
assertLevelPieces(piecesToCopy);
|
|
544
|
+
|
|
360
545
|
this.levelPieces = {};
|
|
361
546
|
if (piecesToCopy) {
|
|
362
547
|
const
|
|
@@ -373,32 +558,26 @@ export default createComponentClass(/** @lends platypus.components.LevelBuilder.
|
|
|
373
558
|
this.levelPieces[key] = [
|
|
374
559
|
...piecesToCopy[key]
|
|
375
560
|
];
|
|
376
|
-
} else {
|
|
377
|
-
throw ('Level Builder: Level pieces of incorrect type: ' + piecesToCopy[key]);
|
|
378
561
|
}
|
|
379
562
|
}
|
|
380
563
|
}
|
|
381
564
|
|
|
382
565
|
if (this.levelTemplate) {
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
} else {
|
|
397
|
-
throw ('Level Builder: Template row is neither a string or array. What is it?');
|
|
566
|
+
assertLevelTemplate(this.levelTemplate);
|
|
567
|
+
|
|
568
|
+
this.levelMessage.level = [];
|
|
569
|
+
for (let i = 0; i < this.levelTemplate.length; i++) {
|
|
570
|
+
const
|
|
571
|
+
templateRow = this.levelTemplate[i];
|
|
572
|
+
|
|
573
|
+
if (typeof templateRow === "string") {
|
|
574
|
+
this.levelMessage.level[i] = this.getLevelPiece(templateRow);
|
|
575
|
+
} else {
|
|
576
|
+
this.levelMessage.level[i] = [];
|
|
577
|
+
for (let j = 0; j < templateRow.length; j++) {
|
|
578
|
+
this.levelMessage.level[i][j] = this.getLevelPiece(templateRow[j]);
|
|
398
579
|
}
|
|
399
580
|
}
|
|
400
|
-
} else {
|
|
401
|
-
platypus.debug.warn('Level Builder: Template is not defined');
|
|
402
581
|
}
|
|
403
582
|
} else {
|
|
404
583
|
platypus.debug.warn('Level Builder: There is no level template.');
|
|
@@ -417,7 +596,10 @@ export default createComponentClass(/** @lends platypus.components.LevelBuilder.
|
|
|
417
596
|
* @param data.persistentData.levelPieces {Object} An object of key/value pairs listing the pieces that map to an id in the level template.
|
|
418
597
|
* @param data.persistentData.useUniques {Boolean} If true, no single map piece is used twice in the creation of the combined map.
|
|
419
598
|
*/
|
|
420
|
-
this.owner.triggerEvent('created-level',
|
|
599
|
+
this.owner.triggerEvent('created-level', {
|
|
600
|
+
level: cloneLevelDefinition(this.levelMessage.level),
|
|
601
|
+
persistentData: this.levelMessage.persistentData
|
|
602
|
+
});
|
|
421
603
|
|
|
422
604
|
/**
|
|
423
605
|
* Dispatched when the scene has loaded and the level has been composited so TileLoader can begin loading the level.
|
|
@@ -437,6 +619,10 @@ export default createComponentClass(/** @lends platypus.components.LevelBuilder.
|
|
|
437
619
|
|
|
438
620
|
methods: {// These are methods that are called by this component.
|
|
439
621
|
getLevelPiece: function (type) {
|
|
622
|
+
if (type == null || type === '') {
|
|
623
|
+
throw new Error(`Level Builder: Template references an invalid piece id: ${type}.`);
|
|
624
|
+
}
|
|
625
|
+
|
|
440
626
|
const
|
|
441
627
|
pieces = this.levelPieces[type] ?? type;
|
|
442
628
|
|
|
@@ -462,6 +648,9 @@ export default createComponentClass(/** @lends platypus.components.LevelBuilder.
|
|
|
462
648
|
throw new Error(`Level Builder: There are no level pieces of type: ${type}`);
|
|
463
649
|
}
|
|
464
650
|
},
|
|
651
|
+
cloneLevel: function (level) {
|
|
652
|
+
return cloneLevelDefinition(level);
|
|
653
|
+
},
|
|
465
654
|
destroy: function () {
|
|
466
655
|
this.levelMessage.level = null;
|
|
467
656
|
this.levelMessage.persistentData = null;
|
package/src/components/Render.js
CHANGED
|
@@ -98,8 +98,18 @@ export default createComponentClass(/** @lends platypus.components.RenderSprite.
|
|
|
98
98
|
initialize () {
|
|
99
99
|
const
|
|
100
100
|
{image, images, localRotation, localScaleX, localScaleY, offsetX, offsetY, offsetZ, options, owner, spriteType} = this,
|
|
101
|
+
resolveTexture = (path) => {
|
|
102
|
+
const
|
|
103
|
+
texture = platypus.assetCache.get(path);
|
|
104
|
+
|
|
105
|
+
if (!texture) {
|
|
106
|
+
platypus.debug.warn(`Render: Image is not a loaded asset: ${path}`);
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
return texture;
|
|
110
|
+
},
|
|
101
111
|
sprite = this.sprite = new spriteTypes[spriteType === 'Sprite' && images && !images ? 'AnimatedSprite' : spriteType]({
|
|
102
|
-
...(image ? {texture:
|
|
112
|
+
...(image ? {texture: resolveTexture(image)} : {textures: images.map((path) => resolveTexture(path))}),
|
|
103
113
|
...options ?? {}
|
|
104
114
|
}),
|
|
105
115
|
matchEntity = this.matchEntity ? {
|
|
@@ -168,11 +178,17 @@ export default createComponentClass(/** @lends platypus.components.RenderSprite.
|
|
|
168
178
|
|
|
169
179
|
getAssetList (a, b, c) {
|
|
170
180
|
const
|
|
171
|
-
image = a?.image ?? b?.image ?? c?.image
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
181
|
+
image = a?.image ?? b?.image ?? c?.image,
|
|
182
|
+
formatImageAsset = (src) => {
|
|
183
|
+
const
|
|
184
|
+
alias = platypus.assetCache?.getFileId ? platypus.assetCache.getFileId(src) : src;
|
|
185
|
+
|
|
186
|
+
return {
|
|
187
|
+
alias: [alias],
|
|
188
|
+
src
|
|
189
|
+
};
|
|
190
|
+
};
|
|
191
|
+
|
|
192
|
+
return (image ? [image] : a?.images ?? b?.images ?? c?.images ?? []).map(formatImageAsset);
|
|
177
193
|
}
|
|
178
194
|
});
|
|
@@ -489,12 +489,21 @@ export default createComponentClass(/** @lends platypus.components.RenderSprite.
|
|
|
489
489
|
|
|
490
490
|
getAssetList: (function () {
|
|
491
491
|
const
|
|
492
|
+
formatImageAsset = function (src) {
|
|
493
|
+
const
|
|
494
|
+
alias = platypus.assetCache?.getFileId ? platypus.assetCache.getFileId(src) : src;
|
|
495
|
+
|
|
496
|
+
return {
|
|
497
|
+
alias: [alias],
|
|
498
|
+
src
|
|
499
|
+
};
|
|
500
|
+
},
|
|
492
501
|
getImages = function (ss, spriteSheets) {
|
|
493
502
|
if (ss) {
|
|
494
503
|
if (typeof ss === 'string') {
|
|
495
504
|
return getImages(spriteSheets[ss], spriteSheets);
|
|
496
505
|
} else if (ss.images) {
|
|
497
|
-
return greenSlice(ss.images);
|
|
506
|
+
return greenSlice(ss.images).map(formatImageAsset);
|
|
498
507
|
}
|
|
499
508
|
}
|
|
500
509
|
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
/* global atob, platypus */
|
|
2
2
|
import {arrayCache, greenSlice, union} from '../utils/array.js';
|
|
3
|
+
import {cloneDefinition} from '../utils/clone.js';
|
|
3
4
|
import AABB from '../AABB.js';
|
|
4
5
|
import Data from '../Data.js';
|
|
5
6
|
import DataMap from '../DataMap.js';
|
|
@@ -336,6 +337,7 @@ const
|
|
|
336
337
|
},
|
|
337
338
|
checkLevel = function (levelOrLevelId, ss) {
|
|
338
339
|
const
|
|
340
|
+
cacheAssetsOnLevel = typeof levelOrLevelId === 'string',
|
|
339
341
|
addObjectGroupAssets = (assets, objectGroup, tilesets) => {
|
|
340
342
|
const objects = objectGroup.objects;
|
|
341
343
|
|
|
@@ -356,7 +358,7 @@ const
|
|
|
356
358
|
level.tilesets = importTilesetData(level.tilesets);
|
|
357
359
|
}
|
|
358
360
|
|
|
359
|
-
if (level.assets) { // Property added by a previous parse (so that this algorithm isn't run on the same level multiple times)
|
|
361
|
+
if (cacheAssetsOnLevel && level.assets) { // Property added by a previous parse (so that this algorithm isn't run on the same level multiple times)
|
|
360
362
|
union(assets, level.assets);
|
|
361
363
|
} else if (level.layers) {
|
|
362
364
|
for (let i = 0; i < level.layers.length; i++) {
|
|
@@ -405,7 +407,7 @@ const
|
|
|
405
407
|
tile = tiles[l];
|
|
406
408
|
|
|
407
409
|
if (((tile.id + firstgid) === id) && tile.objectgroup) {
|
|
408
|
-
addObjectGroupAssets(assets, tile.objectgroup);
|
|
410
|
+
addObjectGroupAssets(assets, tile.objectgroup, level.tilesets);
|
|
409
411
|
}
|
|
410
412
|
}
|
|
411
413
|
}
|
|
@@ -452,7 +454,9 @@ const
|
|
|
452
454
|
union(assets, tilesets);
|
|
453
455
|
arrayCache.recycle(tilesets);
|
|
454
456
|
}
|
|
455
|
-
|
|
457
|
+
if (cacheAssetsOnLevel) {
|
|
458
|
+
level.assets = greenSlice(assets); // Save for later in case this level is checked again.
|
|
459
|
+
}
|
|
456
460
|
}
|
|
457
461
|
}
|
|
458
462
|
|
|
@@ -746,7 +750,7 @@ export default createComponentClass(/** @lends platypus.components.TiledLoader.p
|
|
|
746
750
|
} = layerProperties ? checkParallax(layer) : layer,
|
|
747
751
|
mapOffsetX = offsetX + layerOffsetX,
|
|
748
752
|
mapOffsetY = offsetY + layerOffsetY,
|
|
749
|
-
layerDefinition =
|
|
753
|
+
layerDefinition = cloneDefinition(platypus.game.settings.entities[entityKind] ?? standardEntityLayers[entityKind]),
|
|
750
754
|
layerDefinitionProperties = layerDefinition.properties = layerDefinition.properties ?? {},
|
|
751
755
|
{components} = layerDefinition,
|
|
752
756
|
importAnimation = {},
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Deep-clones plain JSON-like definitions (level pieces, entity templates, etc.)
|
|
3
|
+
* without sharing nested arrays or objects with the source.
|
|
4
|
+
*
|
|
5
|
+
* @method cloneDefinition
|
|
6
|
+
* @param {*} definition
|
|
7
|
+
* @return {*}
|
|
8
|
+
*/
|
|
9
|
+
export function cloneDefinition (definition) {
|
|
10
|
+
return structuredClone(definition);
|
|
11
|
+
}
|