@makefully/platypus 4.1.9 → 4.1.10

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@makefully/platypus",
3
- "version": "4.1.9",
3
+ "version": "4.1.10",
4
4
  "description": "2D tile-based HTML5 game engine with a component-based entity model",
5
5
  "license": "MIT",
6
6
  "contributors": [
@@ -26,6 +26,9 @@ const
26
26
  'src', path,
27
27
  'data', asset.data ?? null
28
28
  );
29
+ },
30
+ isRegisteredWithPixi = function (alias, src) {
31
+ return Assets.resolver.hasKey(alias) || (src && src !== alias && Assets.resolver.hasKey(src));
29
32
  };
30
33
 
31
34
  export default class AssetManager {
@@ -71,7 +74,18 @@ export default class AssetManager {
71
74
  * @return {Object} Returns the asset if defined.
72
75
  */
73
76
  get (alias) {
74
- return this.assets.get(alias);
77
+ if (this.assets.has(alias)) {
78
+ return this.assets.get(alias);
79
+ }
80
+
81
+ const
82
+ fileId = this.getFileId(alias);
83
+
84
+ if (fileId !== alias && this.assets.has(fileId)) {
85
+ return this.assets.get(fileId);
86
+ }
87
+
88
+ return undefined;
75
89
  }
76
90
 
77
91
  /**
@@ -131,6 +145,7 @@ export default class AssetManager {
131
145
  const
132
146
  counts = this.counts,
133
147
  needsLoading = arrayCache.setUp(),
148
+ needsRelink = arrayCache.setUp(),
134
149
  adds = Data.setUp();
135
150
 
136
151
  if (platypus.game?.options?.images ?? platypus.game?.options?.audio) {
@@ -154,13 +169,39 @@ export default class AssetManager {
154
169
  adds[alias] += 1;
155
170
  } else {
156
171
  adds[alias] = 1;
172
+ if (isRegisteredWithPixi(alias, item.src)) {
173
+ needsRelink.push(item);
174
+ } else {
175
+ needsLoading.push(item);
176
+ }
177
+ }
178
+ }
179
+
180
+ for (let i = needsRelink.length - 1; i >= 0; i--) {
181
+ const
182
+ item = needsRelink[i],
183
+ alias = item.alias[0],
184
+ asset = Assets.get(alias) ?? Assets.get(item.src);
185
+
186
+ if (asset) {
187
+ this.set(alias, asset, adds[alias]);
188
+ needsRelink.splice(i, 1);
189
+ } else {
157
190
  needsLoading.push(item);
191
+ needsRelink.splice(i, 1);
158
192
  }
159
193
  }
160
194
 
161
195
  if (needsLoading.length) {
162
196
  // Do this first to pass `data` property if needed
163
- needsLoading.forEach((asset) => Assets.add(asset));
197
+ needsLoading.forEach((asset) => {
198
+ const
199
+ alias = asset.alias[0];
200
+
201
+ if (!isRegisteredWithPixi(alias, asset.src)) {
202
+ Assets.add(asset);
203
+ }
204
+ });
164
205
 
165
206
  const
166
207
  aliases = needsLoading.map((asset) => asset.alias[0]),
@@ -188,6 +229,7 @@ export default class AssetManager {
188
229
  }
189
230
 
190
231
  arrayCache.recycle(needsLoading);
232
+ arrayCache.recycle(needsRelink);
191
233
  }
192
234
 
193
235
  /**
package/src/VOPlayer.js CHANGED
@@ -662,7 +662,7 @@ class VOPlayer extends Messenger {
662
662
  * @public
663
663
  */
664
664
  unloadSound (sound) {
665
- this.assetCache.delete(sound);
665
+ this.assetCache.delete(this.assetCache.getFileId(sound));
666
666
  }
667
667
 
668
668
  /**
@@ -1,5 +1,6 @@
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';
@@ -31,6 +32,21 @@ const
31
32
  }
32
33
  return layer;
33
34
  },
35
+ cloneLevelDefinition = cloneDefinition,
36
+ copySegmentProperty = function (target, source, key) {
37
+ if (target[key]) {
38
+ return;
39
+ }
40
+
41
+ const
42
+ value = source[key];
43
+
44
+ if (value != null && typeof value === 'object') {
45
+ target[key] = cloneDefinition(value);
46
+ } else {
47
+ target[key] = value;
48
+ }
49
+ },
34
50
  mergeData = function (levelData, levelMergeAxisLength, segmentData, segmentMergeAxisLength, nonMergeAxisLength, mergeAxis) {
35
51
  const
36
52
  combined = greenSlice(levelData);
@@ -54,9 +70,7 @@ const
54
70
 
55
71
  for (let i = 0; i < obj2s.length; i++) {
56
72
  const
57
- obj = {
58
- ...obj2s[i]
59
- };
73
+ obj = cloneLevelDefinition(obj2s[i]);
60
74
 
61
75
  if (mergeAxis === 'horizontal') {
62
76
  obj.x += mergeAxisLength;
@@ -68,6 +82,8 @@ const
68
82
  return list;
69
83
  },
70
84
  mergeSegment = function (level, segment, mergeAxis) {
85
+ segment = cloneLevelDefinition(segment);
86
+
71
87
  if (!level.tilewidth && !level.tileheight) {
72
88
  //set level tile size data if it's not already set.
73
89
  level.tilewidth = segment.tilewidth;
@@ -96,9 +112,7 @@ const
96
112
  decodeLayer(segment.layers[i]);
97
113
 
98
114
  const
99
- layer = level.layers[i] = {
100
- ...segment.layers[i]
101
- };
115
+ layer = level.layers[i] = cloneLevelDefinition(segment.layers[i]);
102
116
 
103
117
  // If we're adding objects, make sure that they're offset correctly.
104
118
  if (layer.objects) {
@@ -149,9 +163,7 @@ const
149
163
  const
150
164
  key = keys[i];
151
165
 
152
- if (!level[key]) {
153
- level[key] = segment[key];
154
- }
166
+ copySegmentProperty(level, segment, key);
155
167
  }
156
168
  }
157
169
  },
@@ -200,6 +212,8 @@ const
200
212
  return level;
201
213
  },
202
214
  mirrorSegment = function (segment) {
215
+ segment = cloneLevelDefinition(segment);
216
+
203
217
  const
204
218
  newSegment = {
205
219
  layers: []
@@ -211,9 +225,7 @@ const
211
225
 
212
226
  const
213
227
  fromLayer = segment.layers[i],
214
- toLayer = newSegment.layers[i] = {
215
- ...fromLayer
216
- };
228
+ toLayer = newSegment.layers[i] = cloneLevelDefinition(fromLayer);
217
229
 
218
230
  if (fromLayer.data) {
219
231
  const
@@ -237,9 +249,7 @@ const
237
249
  for (let j = 0; j < fromObjects.length; j++) {
238
250
  const
239
251
  fromObject = fromObjects[j],
240
- toObject = toObjects[j] = {
241
- ...fromObject
242
- };
252
+ toObject = toObjects[j] = cloneLevelDefinition(fromObject);
243
253
 
244
254
  toObject.x = width - fromObject.x - (fromObject.width || 0); // subtract object width since its top-left corner is the origin.
245
255
  if (fromObject.rotation) {
@@ -265,9 +275,7 @@ const
265
275
  const
266
276
  key = keys[i];
267
277
 
268
- if (!newSegment[key]) {
269
- newSegment[key] = segment[key];
270
- }
278
+ copySegmentProperty(newSegment, segment, key);
271
279
  }
272
280
  }
273
281
 
@@ -351,6 +359,10 @@ export default createComponentClass(/** @lends platypus.components.LevelBuilder.
351
359
 
352
360
  events: {
353
361
  "layer-loaded": function (data) {
362
+ if (data?.level && typeof data.level === 'object') {
363
+ return;
364
+ }
365
+
354
366
  const
355
367
  piecesToCopy = data?.levelPieces ?? this.levelPieces;
356
368
 
@@ -417,7 +429,10 @@ export default createComponentClass(/** @lends platypus.components.LevelBuilder.
417
429
  * @param data.persistentData.levelPieces {Object} An object of key/value pairs listing the pieces that map to an id in the level template.
418
430
  * @param data.persistentData.useUniques {Boolean} If true, no single map piece is used twice in the creation of the combined map.
419
431
  */
420
- this.owner.triggerEvent('created-level', this.levelMessage);
432
+ this.owner.triggerEvent('created-level', {
433
+ level: cloneLevelDefinition(this.levelMessage.level),
434
+ persistentData: this.levelMessage.persistentData
435
+ });
421
436
 
422
437
  /**
423
438
  * Dispatched when the scene has loaded and the level has been composited so TileLoader can begin loading the level.
@@ -462,6 +477,9 @@ export default createComponentClass(/** @lends platypus.components.LevelBuilder.
462
477
  throw new Error(`Level Builder: There are no level pieces of type: ${type}`);
463
478
  }
464
479
  },
480
+ cloneLevel: function (level) {
481
+ return cloneLevelDefinition(level);
482
+ },
465
483
  destroy: function () {
466
484
  this.levelMessage.level = null;
467
485
  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 ? {
@@ -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
+ }