@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.
@@ -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
- if (!level[key]) {
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
- //Merge horizontally
177
- if (typeof levelSegments[i][j] === 'string') {
178
- const
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
- levelDefinition = levelDefinitions[levelDefinitionLabel.substring(0, transformIndex)];
188
- if (transform === 'mirror') {
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
- decodeLayer(segment.layers[i]);
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
- if (!newSegment[key]) {
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
- if (this.levelTemplate) {
384
- this.levelMessage.level = [];
385
- for (let i = 0; i < this.levelTemplate.length; i++) {
386
- const
387
- templateRow = this.levelTemplate[i];
388
-
389
- if (typeof templateRow === "string") {
390
- this.levelMessage.level[i] = this.getLevelPiece(templateRow);
391
- } else if (templateRow.length) {
392
- this.levelMessage.level[i] = [];
393
- for (let j = 0; j < templateRow.length; j++) {
394
- this.levelMessage.level[i][j] = this.getLevelPiece(templateRow[j]);
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', this.levelMessage);
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;
@@ -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: platypus.assetCache.get(image)} : {textures: images.map((image) => platypus.assetCache.get(image))}),
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
- return (image ? [image] : a?.images ?? b?.images ?? c?.images ?? []).map((src) => ({
174
- alias: [src],
175
- src
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
- level.assets = greenSlice(assets); // Save for later in case this level is checked again.
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 = JSON.parse(JSON.stringify(platypus.game.settings.entities[entityKind] ?? standardEntityLayers[entityKind])), //TODO: a bit of a hack to copy an object instead of overwrite values
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
+ }