@makefully/platypus 4.1.4 → 4.1.6

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.
Files changed (134) hide show
  1. package/CHANGELOG.md +20 -0
  2. package/README.md +4 -2
  3. package/lib/platypus.js +1 -1
  4. package/package.json +7 -2
  5. package/src/AABB.js +685 -0
  6. package/src/ActionState.js +121 -0
  7. package/src/AssetManager.js +206 -0
  8. package/src/CollisionData.js +96 -0
  9. package/src/CollisionDataContainer.js +127 -0
  10. package/src/CollisionShape.js +315 -0
  11. package/src/Component.js +231 -0
  12. package/src/Data.js +64 -0
  13. package/src/DataMap.js +204 -0
  14. package/src/Entity.js +404 -0
  15. package/src/EntityLinker.js +113 -0
  16. package/src/EventHandler.js +115 -0
  17. package/src/EventHandlerList.js +251 -0
  18. package/src/Game.js +840 -0
  19. package/src/Gamepad.js +175 -0
  20. package/src/ID3CaptionPlayer.js +61 -0
  21. package/src/Messenger.js +365 -0
  22. package/src/PIXIAnimation.js +622 -0
  23. package/src/RandomSet.js +140 -0
  24. package/src/SFXPlayer.js +120 -0
  25. package/src/StateMap.js +252 -0
  26. package/src/Storage.js +152 -0
  27. package/src/TickerClient.js +48 -0
  28. package/src/TickerServer.js +23 -0
  29. package/src/TimeEvent.js +50 -0
  30. package/src/TimeEventList.js +100 -0
  31. package/src/VOPlayer.js +684 -0
  32. package/src/Vector.js +702 -0
  33. package/src/components/AIChaser.js +161 -0
  34. package/src/components/AIPacer.js +107 -0
  35. package/src/components/AudioMusic.js +151 -0
  36. package/src/components/AudioSFX.js +673 -0
  37. package/src/components/AudioVO.js +301 -0
  38. package/src/components/CollisionBasic.js +826 -0
  39. package/src/components/CollisionFilter.js +74 -0
  40. package/src/components/CollisionGroup.js +448 -0
  41. package/src/components/CollisionTiles.js +608 -0
  42. package/src/components/ComponentSwitcher.js +173 -0
  43. package/src/components/ControllerInput.js +65 -0
  44. package/src/components/Counter.js +165 -0
  45. package/src/components/EntityCamera.js +215 -0
  46. package/src/components/EntityContainer.js +549 -0
  47. package/src/components/EntityController.js +541 -0
  48. package/src/components/HandlerCamera.js +1052 -0
  49. package/src/components/HandlerCollision.js +1318 -0
  50. package/src/components/HandlerController.js +291 -0
  51. package/src/components/HandlerLogic.js +394 -0
  52. package/src/components/HandlerRender.js +217 -0
  53. package/src/components/Interactive.js +502 -0
  54. package/src/components/LevelBuilder.js +524 -0
  55. package/src/components/LogicAngularMovement.js +154 -0
  56. package/src/components/LogicAttachment.js +265 -0
  57. package/src/components/LogicButton.js +466 -0
  58. package/src/components/LogicCarrier.js +39 -0
  59. package/src/components/LogicDestroyMe.js +58 -0
  60. package/src/components/LogicDirectionalMovement.js +325 -0
  61. package/src/components/LogicDragDrop.js +371 -0
  62. package/src/components/LogicImpactLaunch.js +144 -0
  63. package/src/components/LogicPacingPlatform.js +64 -0
  64. package/src/components/LogicPortable.js +112 -0
  65. package/src/components/LogicPortal.js +115 -0
  66. package/src/components/LogicPushable.js +98 -0
  67. package/src/components/LogicRebounder.js +136 -0
  68. package/src/components/LogicRegionSpawner.js +139 -0
  69. package/src/components/LogicRotationalMovement.js +205 -0
  70. package/src/components/LogicSpawner.js +159 -0
  71. package/src/components/LogicStateMachine.js +382 -0
  72. package/src/components/LogicSwitch.js +77 -0
  73. package/src/components/LogicTeleportee.js +68 -0
  74. package/src/components/LogicTeleporter.js +93 -0
  75. package/src/components/LogicTimer.js +178 -0
  76. package/src/components/LogicWindUpRacer.js +179 -0
  77. package/src/components/Motion.js +371 -0
  78. package/src/components/Mover.js +566 -0
  79. package/src/components/Node.js +289 -0
  80. package/src/components/NodeMap.js +295 -0
  81. package/src/components/NodeResident.js +636 -0
  82. package/src/components/Orientation.js +697 -0
  83. package/src/components/RandomEvents.js +73 -0
  84. package/src/components/RelativePosition.js +204 -0
  85. package/src/components/RelayFamily.js +106 -0
  86. package/src/components/RelayGame.js +53 -0
  87. package/src/components/RelayLinker.js +94 -0
  88. package/src/components/RelayParent.js +57 -0
  89. package/src/components/RelaySelf.js +55 -0
  90. package/src/components/Render.js +178 -0
  91. package/src/components/RenderAnimator.js +275 -0
  92. package/src/components/RenderContainer.js +646 -0
  93. package/src/components/RenderDebug.js +319 -0
  94. package/src/components/RenderDestroyMe.js +59 -0
  95. package/src/components/RenderLottie.js +149 -0
  96. package/src/components/RenderProgress.js +120 -0
  97. package/src/components/RenderSpine.js +833 -0
  98. package/src/components/RenderSprite.js +527 -0
  99. package/src/components/RenderText.js +147 -0
  100. package/src/components/RenderTiles.js +1391 -0
  101. package/src/components/SceneChanger.js +128 -0
  102. package/src/components/TiledLoader.js +1451 -0
  103. package/src/components/Timeline.js +314 -0
  104. package/src/components/Tutorial.js +267 -0
  105. package/src/components/Tween.js +296 -0
  106. package/src/components/VoiceOver.js +373 -0
  107. package/src/components/XHR.js +129 -0
  108. package/src/components/index.js +79 -0
  109. package/src/factory.js +305 -0
  110. package/src/index.js +159 -0
  111. package/src/mixins/PIXI.Graphics.js +41 -0
  112. package/src/platypus.css +68 -0
  113. package/src/sayHello.js +50 -0
  114. package/src/shapes/Capsule.js +74 -0
  115. package/src/shapes/Circle.js +43 -0
  116. package/src/shapes/Ellipse.js +117 -0
  117. package/src/shapes/Point.js +191 -0
  118. package/src/shapes/Polygon.js +112 -0
  119. package/src/shapes/Polyline.js +97 -0
  120. package/src/shapes/Rectangle.js +78 -0
  121. package/src/shapes/RoundedRectangle.js +33 -0
  122. package/src/shapes/Segment.js +346 -0
  123. package/src/shapes/adjustedXY.js +19 -0
  124. package/src/shapes/castFromBox2D.js +92 -0
  125. package/src/shapes/castFromObject.js +53 -0
  126. package/src/shapes/castFromPixiGraphics.js +6 -0
  127. package/src/shapes/castFromPixiShape.js +89 -0
  128. package/src/shapes/castToBox2D.js +190 -0
  129. package/src/shapes/castToPixiGraphics.js +100 -0
  130. package/src/shapes/castToPixiShape.js +61 -0
  131. package/src/shapes/checkPointsMap.js +313 -0
  132. package/src/shapes/index.js +35 -0
  133. package/src/utils/array.js +73 -0
  134. package/src/utils/string.js +39 -0
@@ -0,0 +1,524 @@
1
+ /* global atob, platypus */
2
+ import {arrayCache, greenSlice, greenSplice, union} from '../utils/array.js';
3
+ import createComponentClass from '../factory.js';
4
+ import {inflate} from 'pako';
5
+ import TiledLoader from './TiledLoader.js';
6
+
7
+ const
8
+ maskXFlip = 0x80000000,
9
+ decodeString = (str, index) => (((str.charCodeAt(index)) + (str.charCodeAt(index + 1) << 8) + (str.charCodeAt(index + 2) << 16) + (str.charCodeAt(index + 3) << 24 )) >>> 0),
10
+ decodeArray = (arr, index) => ((arr[index] + (arr[index + 1] << 8) + (arr[index + 2] << 16) + (arr[index + 3] << 24 )) >>> 0),
11
+ decodeBase64 = (data, compression) => {
12
+ const
13
+ arr = [],
14
+ compressed = compression === 'zlib',
15
+ step1 = window.atob(data.replace(/\\/g, '')),
16
+ step2 = compressed ? inflate(step1) : step1,
17
+ decode = compressed ? decodeArray : decodeString;
18
+ let index = 4;
19
+
20
+ while (index <= step2.length) {
21
+ arr.push(decode(step2, index - 4));
22
+ index += 4;
23
+ }
24
+
25
+ return arr;
26
+ },
27
+ decodeLayer = function (layer) {
28
+ if (layer.encoding === 'base64') {
29
+ layer.data = decodeBase64(layer.data, layer.compression);
30
+ layer.encoding = 'csv'; // So we won't have to decode again.
31
+ }
32
+ return layer;
33
+ },
34
+ mergeData = function (levelData, levelMergeAxisLength, segmentData, segmentMergeAxisLength, nonMergeAxisLength, mergeAxis) {
35
+ const
36
+ combined = greenSlice(levelData);
37
+
38
+ if (mergeAxis === 'horizontal') {
39
+ for (let y = nonMergeAxisLength - 1; y >= 0; y--) {
40
+ for (let x = y * segmentMergeAxisLength, z = 0; x < (y + 1) * segmentMergeAxisLength; x++, z++) {
41
+ combined.splice(((y + 1) * levelMergeAxisLength) + z, 0, segmentData[x]);
42
+ }
43
+ }
44
+ return combined;
45
+ } else if (mergeAxis === 'vertical') {
46
+ return levelData.concat(segmentData);
47
+ }
48
+
49
+ return null;
50
+ },
51
+ mergeObjects = function (obj1s, obj2s, mergeAxisLength, mergeAxis) {
52
+ const
53
+ list = greenSlice(obj1s);
54
+
55
+ for (let i = 0; i < obj2s.length; i++) {
56
+ const
57
+ obj = {
58
+ ...obj2s[i]
59
+ };
60
+
61
+ if (mergeAxis === 'horizontal') {
62
+ obj.x += mergeAxisLength;
63
+ } else if (mergeAxis === 'vertical') {
64
+ obj.y += mergeAxisLength;
65
+ }
66
+ list.push(obj);
67
+ }
68
+ return list;
69
+ },
70
+ mergeSegment = function (level, segment, mergeAxis) {
71
+ if (!level.tilewidth && !level.tileheight) {
72
+ //set level tile size data if it's not already set.
73
+ level.tilewidth = segment.tilewidth;
74
+ level.tileheight = segment.tileheight;
75
+ } else if (level.tilewidth !== segment.tilewidth || level.tileheight !== segment.tileheight) {
76
+ platypus.debug.warn('Component LevelBuilder: Your map has segments with different tile sizes. All tile sizes must match. Segment: ' + segment);
77
+ }
78
+
79
+ if (mergeAxis === 'horizontal') {
80
+ if (level.height === 0) {
81
+ level.height = segment.height;
82
+ } else if (level.height !== segment.height) {
83
+ platypus.debug.warn('Component LevelBuilder: You are trying to merge segments with different heights. All segments need to have the same height. Level: ' + level + ' Segment: ' + segment);
84
+ }
85
+ } else if (mergeAxis === 'vertical') {
86
+ if (level.width === 0) {
87
+ level.width = segment.width;
88
+ } else if (level.width !== segment.width) {
89
+ platypus.debug.warn('Component LevelBuilder: You are trying to merge segments with different widths. All segments need to have the same width. Level: ' + level + ' Segment: ' + segment);
90
+ }
91
+ }
92
+
93
+ for (let i = 0; i < segment.layers.length; i++) {
94
+ if (!level.layers[i]) {
95
+ //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]);
97
+
98
+ const
99
+ layer = level.layers[i] = {
100
+ ...segment.layers[i]
101
+ };
102
+
103
+ // If we're adding objects, make sure that they're offset correctly.
104
+ if (layer.objects) {
105
+ if (mergeAxis === 'horizontal') {
106
+ layer.objects = mergeObjects([], layer.objects, level.width * level.tilewidth, mergeAxis);
107
+ } else if (mergeAxis === 'vertical') {
108
+ layer.objects = mergeObjects([], layer.objects, level.height * level.tileheight, mergeAxis);
109
+ }
110
+ }
111
+ } else if (level.layers[i].type === segment.layers[i].type) {
112
+ //if the level does have a layer, we're appending the new data to it.
113
+ if (level.layers[i].data && segment.layers[i].data) {
114
+ // Make sure we're not trying to merge compressed levels.
115
+ decodeLayer(segment.layers[i]);
116
+
117
+ if (mergeAxis === 'horizontal') {
118
+ level.layers[i].data = mergeData(level.layers[i].data, level.width, segment.layers[i].data, segment.width, level.height, mergeAxis);
119
+ level.layers[i].width += segment.width;
120
+ } else if (mergeAxis === 'vertical') {
121
+ level.layers[i].data = mergeData(level.layers[i].data, level.height, segment.layers[i].data, segment.height, level.width, mergeAxis);
122
+ level.layers[i].height += segment.height;
123
+ }
124
+ } else if (level.layers[i].objects && segment.layers[i].objects) {
125
+ if (mergeAxis === 'horizontal') {
126
+ level.layers[i].objects = mergeObjects(level.layers[i].objects, segment.layers[i].objects, level.width * level.tilewidth, mergeAxis);
127
+ } else if (mergeAxis === 'vertical') {
128
+ level.layers[i].objects = mergeObjects(level.layers[i].objects, segment.layers[i].objects, level.height * level.tileheight, mergeAxis);
129
+ }
130
+ }
131
+ } else {
132
+ platypus.debug.warn('Component LevelBuilder: The layers in your level segments do not match. Level: ' + level + ' Segment: ' + segment);
133
+ }
134
+ }
135
+
136
+ if (mergeAxis === 'horizontal') {
137
+ level.width += segment.width;
138
+ } else if (mergeAxis === 'vertical') {
139
+ level.height += segment.height;
140
+ }
141
+
142
+ //Go through all the STUFF in segment and copy it to the level if it's not already there.
143
+ {
144
+ const
145
+ keys = Object.keys(segment),
146
+ {length} = keys;
147
+
148
+ for (let i = 0; i < length; i++) {
149
+ const
150
+ key = keys[i];
151
+
152
+ if (!level[key]) {
153
+ level[key] = segment[key];
154
+ }
155
+ }
156
+ }
157
+ },
158
+ mergeLevels = function (levelSegments) {
159
+ const
160
+ levelDefinitions = platypus.game.settings.levels,
161
+ level = {
162
+ height: 0,
163
+ width: 0,
164
+ layers: []
165
+ };
166
+
167
+ for (let i = 0; i < levelSegments.length; i++) {
168
+ const
169
+ row = {
170
+ height: 0,
171
+ width: 0,
172
+ layers: []
173
+ };
174
+
175
+ 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);
186
+
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
+ }
196
+ }
197
+ //Then merge vertically
198
+ mergeSegment(level, row, 'vertical');
199
+ }
200
+ return level;
201
+ },
202
+ mirrorSegment = function (segment) {
203
+ const
204
+ newSegment = {
205
+ layers: []
206
+ },
207
+ width = segment.width * segment.tilewidth;
208
+
209
+ for (let i = 0; i < segment.layers.length; i++) {
210
+ decodeLayer(segment.layers[i]);
211
+
212
+ const
213
+ fromLayer = segment.layers[i],
214
+ toLayer = newSegment.layers[i] = {
215
+ ...fromLayer
216
+ };
217
+
218
+ if (fromLayer.data) {
219
+ const
220
+ fromData = fromLayer.data,
221
+ segmentWidth = segment.width;
222
+
223
+ toLayer.data = fromData.map((value, index) => {
224
+ const
225
+ cell = fromData[segmentWidth * ((index / segmentWidth) >> 0) + segmentWidth - 1 - (index % segmentWidth)];
226
+
227
+ return cell ? maskXFlip ^ cell : 0;
228
+ });
229
+ }
230
+
231
+ // If we're adding objects, make sure that they're mirrored correctly.
232
+ if (fromLayer.objects) {
233
+ const
234
+ fromObjects = fromLayer.objects,
235
+ toObjects = toLayer.objects = [];
236
+
237
+ for (let j = 0; j < fromObjects.length; j++) {
238
+ const
239
+ fromObject = fromObjects[j],
240
+ toObject = toObjects[j] = {
241
+ ...fromObject
242
+ };
243
+
244
+ toObject.x = width - fromObject.x - (fromObject.width || 0); // subtract object width since its top-left corner is the origin.
245
+ if (fromObject.rotation) {
246
+ toObject.rotation = -fromObject.rotation;
247
+ }
248
+ if (fromObject.polygon) {
249
+ toObject.polygon = mirrorPoints(fromObject.polygon);
250
+ }
251
+ if (fromObject.polyline) {
252
+ toObject.polyline = mirrorPoints(fromObject.polyline);
253
+ }
254
+ }
255
+ }
256
+ }
257
+
258
+ //Go through all the STUFF in segment and copy it to the level if it's not already there.
259
+ {
260
+ const
261
+ keys = Object.keys(segment),
262
+ {length} = keys;
263
+
264
+ for (let i = 0; i < length; i++) {
265
+ const
266
+ key = keys[i];
267
+
268
+ if (!newSegment[key]) {
269
+ newSegment[key] = segment[key];
270
+ }
271
+ }
272
+ }
273
+
274
+ return newSegment;
275
+ },
276
+ mirrorPoints = function (points) {
277
+ const
278
+ arr = [];
279
+ let i = points.length;
280
+
281
+ while (i--) {
282
+ arr.push({
283
+ x: -points[i].x,
284
+ y: points[i].y
285
+ });
286
+ }
287
+ arr.unshift(arr.pop()); // so the same point is at the beginning.
288
+
289
+ return arr;
290
+ };
291
+
292
+ export default createComponentClass(/** @lends platypus.components.LevelBuilder.prototype */{
293
+ id: 'LevelBuilder',
294
+
295
+ properties: {
296
+ /**
297
+ * If true, no single map piece is used twice in the creation of the combined map.
298
+ *
299
+ * @property useUniques
300
+ * @type Boolean
301
+ * @default true
302
+ */
303
+ useUniques: true,
304
+
305
+ /**
306
+ * A 1D or 2D array of level piece ids. The template defines how the pieces will be arranged and which pieces can be used where. The template must be rectangular in dimensions.
307
+ *
308
+ * "levelTemplate": [ ["start", "forest"], ["forest", "end"] ]
309
+ *
310
+ * @property levelTemplate
311
+ * @type Array
312
+ * @default null
313
+ */
314
+ levelTemplate: null,
315
+
316
+ /**
317
+ * This is an object of key/value pairs listing the pieces that map to an id in the level template. The value can be specified as a string or array. A piece will be randomly chosen from an array when that idea is used. If levelPieces is not defined, ids in the template map directly to level names.
318
+ *
319
+ * "levelPieces": {
320
+ * "start" : "start-map",
321
+ * "end" : "end-map",
322
+ * "forest" : ["forest-1", "forest-2", "forest-3"],
323
+ * "river": ["river-1", "river-1:mirror"] // adding ":mirror" takes the referenced map and flips it horizontally to add variety.
324
+ * }
325
+ *
326
+ * @property levelPieces
327
+ * @type Object
328
+ * @default null
329
+ */
330
+ levelPieces: null
331
+ },
332
+
333
+ publicProperties: {
334
+ },
335
+
336
+ /**
337
+ * This component works in tandem with `TiledLoader` by taking several Tiled maps and combining them before `TiledLoader` processes them. Tiled maps must use the same tilesets for this to function correctly.
338
+ *
339
+ * Note: Set "manuallyLoad" to `true` in the `TiledLoader` component JSON definition so that it will wait for this component's "load-level" call.
340
+ *
341
+ * @memberof platypus.components
342
+ * @uses platypus.Component
343
+ * @constructs
344
+ * @listens platypus.Entity#layer-loaded
345
+ * @fires platypus.Entity#created-level
346
+ * @fires platypus.Entity#load-level
347
+ */
348
+ initialize: function () {
349
+ this.levelMessage = {level: null, persistentData: null};
350
+ },
351
+
352
+ events: {
353
+ "layer-loaded": function (data) {
354
+ const
355
+ piecesToCopy = data?.levelPieces ?? this.levelPieces;
356
+
357
+ this.levelMessage.persistentData = data;
358
+ this.levelTemplate = data?.levelTemplate ?? this.levelTemplate;
359
+ this.useUniques = data?.useUniques ?? this.useUniques;
360
+ this.levelPieces = {};
361
+ if (piecesToCopy) {
362
+ const
363
+ keys = Object.keys(piecesToCopy),
364
+ {length} = keys;
365
+
366
+ for (let i = 0; i < length; i++) {
367
+ const
368
+ key = keys[i];
369
+
370
+ if (typeof piecesToCopy[key] === "string") {
371
+ this.levelPieces[key] = piecesToCopy[key];
372
+ } else if (Array.isArray(piecesToCopy[key])) {
373
+ this.levelPieces[key] = [
374
+ ...piecesToCopy[key]
375
+ ];
376
+ } else {
377
+ throw ('Level Builder: Level pieces of incorrect type: ' + piecesToCopy[key]);
378
+ }
379
+ }
380
+ }
381
+
382
+ 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?');
398
+ }
399
+ }
400
+ } else {
401
+ platypus.debug.warn('Level Builder: Template is not defined');
402
+ }
403
+ } else {
404
+ platypus.debug.warn('Level Builder: There is no level template.');
405
+ }
406
+
407
+ if (this.levelMessage.level) {
408
+ this.levelMessage.level = mergeLevels(this.levelMessage.level);
409
+ /**
410
+ * Dispatched when the scene has loaded and the level has been composited. This occurs before "load-level" to send out level before it's loaded in case it needs to be saved or edited before being loaded.
411
+ *
412
+ * @event platypus.Entity#created-level
413
+ * @param data {Object}
414
+ * @param data.level {Object} An object describing the level dimensions, tiles, and entities.
415
+ * @param data.persistentData {Object} The persistent data passed from the last scene. We add levelBuilder data to it to pass on.
416
+ * @param data.persistentData.levelTemplate {Object} A 1D or 2D array of level piece ids. The template defines how the pieces will be arranged and which pieces can be used where. The template must be rectangular in dimensions.
417
+ * @param data.persistentData.levelPieces {Object} An object of key/value pairs listing the pieces that map to an id in the level template.
418
+ * @param data.persistentData.useUniques {Boolean} If true, no single map piece is used twice in the creation of the combined map.
419
+ */
420
+ this.owner.triggerEvent('created-level', this.levelMessage);
421
+
422
+ /**
423
+ * Dispatched when the scene has loaded and the level has been composited so TileLoader can begin loading the level.
424
+ *
425
+ * @event platypus.Entity#load-level
426
+ * @param data {Object}
427
+ * @param data.level {Object} An object describing the level dimensions, tiles, and entities.
428
+ * @param data.persistentData {Object} The persistent data passed from the last scene. We add levelBuilder data to it to pass on.
429
+ * @param data.persistentData.levelTemplate {Object} A 1D or 2D array of level piece ids. The template defines how the pieces will be arranged and which pieces can be used where. The template must be rectangular in dimensions.
430
+ * @param data.persistentData.levelPieces {Object} An object of key/value pairs listing the pieces that map to an id in the level template.
431
+ * @param data.persistentData.useUniques {Boolean} If true, no single map piece is used twice in the creation of the combined map.
432
+ */
433
+ this.owner.triggerEvent('load-level', this.levelMessage);
434
+ }
435
+ }
436
+ },
437
+
438
+ methods: {// These are methods that are called by this component.
439
+ getLevelPiece: function (type) {
440
+ const
441
+ pieces = this.levelPieces[type] ?? type;
442
+
443
+ if (pieces) {
444
+ if (typeof pieces === "string") {
445
+ if (this.useUniques) {
446
+ this.levelPieces[type] = null;
447
+ }
448
+ return pieces;
449
+ } else if (pieces.length) {
450
+ const
451
+ random = Math.floor(Math.random() * pieces.length);
452
+
453
+ if (this.useUniques) {
454
+ return greenSplice(this.levelPieces[type], random);
455
+ } else {
456
+ return pieces[random];
457
+ }
458
+ } else {
459
+ throw new Error(`Level Builder: There are no MORE level pieces of type: ${type}`);
460
+ }
461
+ } else {
462
+ throw new Error(`Level Builder: There are no level pieces of type: ${type}`);
463
+ }
464
+ },
465
+ destroy: function () {
466
+ this.levelMessage.level = null;
467
+ this.levelMessage.persistentData = null;
468
+ this.levelMessage = null;
469
+ }
470
+ },
471
+
472
+ publicMethods: {
473
+ /**
474
+ * Accepts a list of levels to be merged and returns a level definition with the references combined.
475
+ *
476
+ * @memberof LevelBuilder.prototype
477
+ * @param {Array} levels
478
+ * @return {Object}
479
+ */
480
+ mergeLevels: function (levels) {
481
+ return mergeLevels(levels);
482
+ }
483
+ },
484
+
485
+ getAssetList: function (def, props, defaultProps, data) {
486
+ const
487
+ assets = arrayCache.setUp(),
488
+ levels = def?.levelPieces ?? props?.levelPieces ?? defaultProps?.levelPieces ?? data?.levelPieces;
489
+
490
+ if (levels) {
491
+ const
492
+ keys = Object.keys(levels),
493
+ {length} = keys;
494
+
495
+ for (let i = 0; i < length; i++) {
496
+ const
497
+ key = keys[i];
498
+
499
+ // Offload to TiledLoader since it has level-parsing handling
500
+ if (Array.isArray(levels[key])) {
501
+ for (let j = 0; j < levels[key].length; j++) {
502
+ const
503
+ arr = TiledLoader.getAssetList({
504
+ level: levels[key][j]
505
+ }, props, defaultProps);
506
+
507
+ union(assets, arr);
508
+ arrayCache.recycle(arr);
509
+ }
510
+ } else {
511
+ const
512
+ arr = TiledLoader.getAssetList({
513
+ level: levels[key]
514
+ }, props, defaultProps);
515
+
516
+ union(assets, arr);
517
+ arrayCache.recycle(arr);
518
+ }
519
+ }
520
+ }
521
+
522
+ return assets;
523
+ }
524
+ });
@@ -0,0 +1,154 @@
1
+ import {arrayCache} from '../utils/array.js';
2
+ import createComponentClass from '../factory.js';
3
+
4
+ export default (function () {
5
+ return createComponentClass(/** @lends platypus.components.LogicAngularMovement.prototype */{
6
+
7
+ id: 'LogicAngularMovement',
8
+
9
+ properties: {
10
+ /**
11
+ * The max velocity.
12
+ *
13
+ * @property maxVelocity
14
+ * @type Number
15
+ * @default 3
16
+ */
17
+ maxVelocity: 3,
18
+
19
+ /**
20
+ * The rate of acceleration.
21
+ *
22
+ * @property acceleration
23
+ * @type Number
24
+ * @default 0.01
25
+ */
26
+ acceleration: 0.01,
27
+
28
+ /**
29
+ * The offset between the rotation value of the entity and the rotation of the art.
30
+ *
31
+ * @property visualOffset
32
+ * @type Number
33
+ * @default 0
34
+ */
35
+ visualOffset: 0,
36
+
37
+ /**
38
+ * The starting heading at which the entity will accelerate. In radians.
39
+ *
40
+ * @property startAngle
41
+ * @type Number
42
+ * @default 0
43
+ */
44
+ startAngle: 0
45
+ },
46
+
47
+ publicProperties: {
48
+
49
+ },
50
+
51
+ /**
52
+ * This component moves the entity in the direction of an internally stored angle value. When moving, the entity constantly accelerates the entity in a direction up to a max velocity.
53
+ *
54
+ * @memberof platypus.components
55
+ * @uses platypus.Component
56
+ * @constructs
57
+ * @listens platypus.Entity#handle-logic
58
+ * @listens platypus.Entity#move
59
+ * @listens platypus.Entity#set-angle
60
+ * @listens platypus.Entity#set-max-velocity
61
+ * @listens platypus.Entity#stop
62
+ */
63
+ initialize: function () {
64
+ this.angle = this.startAngle;
65
+ this.v = arrayCache.setUp(0, 0);
66
+ this.moving = false;
67
+ this.piOverTwo = Math.PI / 2;
68
+ this.owner.rotation = this.owner.rotation || this.visualOffset;
69
+ },
70
+
71
+ events: {
72
+ "handle-logic": function ({delta}) {
73
+ const
74
+ {PI, sin, cos, min, max} = Math;
75
+
76
+ if (this.moving) {
77
+ let currentAngle = 0;
78
+
79
+ this.v[0] += this.acceleration * cos(this.angle) * delta;
80
+ this.v[1] += this.acceleration * sin(this.angle) * delta;
81
+ if (this.v[0] === 0) {
82
+ if (this.v[1] > 0) {
83
+ currentAngle = this.piOverTwo;
84
+ } else if (this.v[1] < 0) {
85
+ currentAngle = -this.piOverTwo;
86
+ } else {
87
+ currentAngle = this.angle;
88
+ }
89
+ } else {
90
+ currentAngle = Math.atan(this.v[1] / this.v[0]);
91
+ if (this.v[0] < 0) {
92
+ currentAngle = PI + currentAngle;
93
+ }
94
+ }
95
+ if (this.v[0] >= 0) {
96
+ this.v[0] = min(this.v[0], this.maxVelocity * cos(currentAngle));
97
+ } else {
98
+ this.v[0] = max(this.v[0], this.maxVelocity * cos(currentAngle));
99
+ }
100
+ if (this.v[1] >= 0) {
101
+ this.v[1] = min(this.v[1], this.maxVelocity * sin(currentAngle));
102
+ } else {
103
+ this.v[1] = max(this.v[1], this.maxVelocity * sin(currentAngle));
104
+ }
105
+
106
+ this.owner.x += this.v[0];
107
+ this.owner.y += this.v[1];
108
+
109
+ this.owner.rotation = (currentAngle * (180 / PI)) + this.visualOffset;
110
+ }
111
+ },
112
+ /**
113
+ * Sets the internal heading angle in the component.
114
+ *
115
+ * @event platypus.Entity#set-angle
116
+ * @param angle {Number} The value you want to set the angle to.
117
+ */
118
+ "set-angle": function (angle) {
119
+ this.angle = angle;
120
+ },
121
+
122
+ /**
123
+ * Start the entity accelerating toward the heading angle.
124
+ *
125
+ * @event platypus.Entity#move
126
+ */
127
+ "move": function () {
128
+ this.moving = true;
129
+ },
130
+
131
+ "stop": function () {
132
+ this.moving = false;
133
+ this.v[0] = 0;
134
+ this.v[1] = 0;
135
+ },
136
+
137
+ /**
138
+ * Set the max velocity.
139
+ *
140
+ * @event platypus.Entity#set-max-velocity
141
+ * @param newMaxV {Number} The max velocity value.
142
+ */
143
+ "set-max-velocity": function (newMaxV) {
144
+ this.maxVelocity = newMaxV;
145
+ }
146
+ },
147
+
148
+ methods: {
149
+ destroy: function () {
150
+ this.v.recycle();
151
+ }
152
+ }
153
+ });
154
+ }());