@makefully/platypus 4.1.5 → 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 (136) hide show
  1. package/CHANGELOG.md +10 -0
  2. package/README.md +3 -3
  3. package/lib/platypus.js +1 -1
  4. package/package.json +6 -5
  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
  135. package/lib/243.platypus.mjs +0 -1
  136. package/lib/platypus.mjs +0 -1
@@ -0,0 +1,291 @@
1
+ /* global platypus, window */
2
+ import {arrayCache, greenSplice} from '../utils/array.js';
3
+ import Gamepad from '../Gamepad.js';
4
+ import createComponentClass from '../factory.js';
5
+
6
+ const
7
+ broadcastHandleController = function (tick) {
8
+ if (hasGamepads) {
9
+ const
10
+ gamepads = [...window.navigator.getGamepads()],
11
+ standards = this.gamepads;
12
+
13
+ for (let i = 0; i < gamepads.length; i++) {
14
+ const gamepad = gamepads[i];
15
+
16
+ if (gamepad) {
17
+ const standard = standards[gamepad.index];
18
+
19
+ if (!standard) { // set up initial values so events can be triggered later
20
+ standards[gamepad.index] = Gamepad.setUp(gamepad, onDown.bind(this, 'gamepad'), onUp.bind(this, 'gamepad'), onChange.bind(this, 'gamepad'));
21
+ } else {
22
+ standard.update(gamepad);
23
+ }
24
+ }
25
+ }
26
+ }
27
+
28
+ /**
29
+ * Triggered on owner and child entities on each tick to handle whatever they need to regarding controls.
30
+ *
31
+ * @event platypus.Entity#handle-controller
32
+ * @param tick {Object} An object containing tick data.
33
+ */
34
+ this.triggerOnAll('handle-controller', tick);
35
+ },
36
+ onDown = function (type, event) {
37
+ /**
38
+ * Triggered on owner and child entities when a key goes from up to down.
39
+ *
40
+ * @event platypus.Entity#[event.code]:down
41
+ * @param event {DOMEvent} The DOM event that triggered the keydown event.
42
+ */
43
+ this.triggerControlAction(event.code, 'down', event);
44
+
45
+ updatePrecedence(type);
46
+
47
+ if (this.upOnBlur) {
48
+ this.upsToBlur.push(event.code);
49
+ }
50
+ },
51
+ onUp = function (type, event) {
52
+ /**
53
+ * Triggered on owner and child entities when a key goes from down to up.
54
+ *
55
+ * @event platypus.Entity#[event.code]:up
56
+ * @param event {DOMEvent} The DOM event that triggered the keyup event.
57
+ */
58
+ this.triggerControlAction(event.code, 'up', event);
59
+
60
+ updatePrecedence(type);
61
+
62
+ if (this.upOnBlur) {
63
+ const
64
+ i = this.upsToBlur.indexOf(event.code);
65
+
66
+ if (i >= 0) {
67
+ this.upsToBlur.splice(i, 1);
68
+ }
69
+ }
70
+ },
71
+ onChange = function (type, event) {
72
+ /**
73
+ * Triggered on owner and child entities when an axis changes.
74
+ *
75
+ * @event platypus.Entity#[event.code]:change
76
+ * @param event {DOMEvent} The event that triggered the change event.
77
+ */
78
+ this.triggerControlAction(event.code, 'change', event);
79
+
80
+ updatePrecedence(type);
81
+ },
82
+ updatePrecedence = (type) => {
83
+ if (type !== inputs[0]) {
84
+ const was = inputs.indexOf(type);
85
+
86
+ if (was > 0) {
87
+ greenSplice(inputs, was);
88
+ }
89
+
90
+ inputs.unshift(type);
91
+
92
+ for (let i = 0; i < notifies.length; i++) {
93
+ notifies[i](inputs);
94
+ }
95
+ }
96
+ },
97
+ inputs = [],
98
+ notifies = [],
99
+ detectInvalids = {},
100
+ detectProcessing = {};
101
+ let hasGamepads = false,
102
+ gameWideReady = false;
103
+
104
+ window.addEventListener("gamepadconnected", () => {
105
+ hasGamepads = true;
106
+ updatePrecedence('gamepad');
107
+ });
108
+
109
+ window.addEventListener('touchstart', () => {
110
+ updatePrecedence('touch');
111
+ });
112
+
113
+ window.addEventListener('mousedown', () => {
114
+ updatePrecedence('mouse');
115
+ });
116
+
117
+
118
+ //window.addEventListener("gamepaddisconnected", (event) => {});
119
+
120
+ export default createComponentClass(/** @lends platypus.components.HandlerController.prototype */{
121
+
122
+ id: 'HandlerController',
123
+ properties: {
124
+ /**
125
+ * Whether 'handle-controller' event should fire based on the 'handle-logic' event instead of the 'tick' event.
126
+ *
127
+ * @property useHandleLogic
128
+ * @type Boolean
129
+ * @default false
130
+ */
131
+ useHandleLogic: false,
132
+
133
+ /**
134
+ * Whether blur events should cause key release.
135
+ *
136
+ * @property upOnBlur
137
+ * @type Boolean
138
+ * @default true
139
+ */
140
+ upOnBlur: true,
141
+
142
+ /**
143
+ * Which default key events should be allowed. Defaults to 'none'. May be 'none', 'all', or an array of allowed keys.
144
+ *
145
+ * @property enableDefaultEvents
146
+ * @type String|Array
147
+ * @default 'none'
148
+ */
149
+ enableDefaultEvents: 'none'
150
+ },
151
+
152
+ publicProperties: {
153
+ inputPrecedence: inputs
154
+ },
155
+
156
+ /**
157
+ * This component handles capturing and relaying input information to the entities that care about it. It takes mouse, keyboard, and custom input messages. State messages are sent immediately to the entities when they are received, the 'HandlerController' message is sent to demarcate ticks.
158
+ *
159
+ * @memberof platypus.components
160
+ * @uses platypus.Component
161
+ * @constructs
162
+ * @listens platypus.Entity#handle-logic
163
+ * @listens platypus.Entity#tick
164
+ * @listens platypus.Game#tick
165
+ * @fires platypus.Entity#handle-controller
166
+ * @fires platypus.Entity#[event.code]:down
167
+ * @fires platypus.Entity#[event.code]:up
168
+ * @fires platypus.Entity#[event.code]:change
169
+ * @fires platypus.Entity#input-precedence-updated
170
+ */
171
+ initialize: function () {
172
+ const
173
+ {enableDefaultEvents} = this;
174
+
175
+ this.upsToBlur = [];
176
+
177
+ if (platypus.game.settings.debug || enableDefaultEvents === 'all') { // If this is a test build, leave in the browser key combinations so debug tools can be opened as expected.
178
+ this.callbackKeyDown = onDown.bind(this, 'keyboard');
179
+ this.callbackKeyUp = onUp.bind(this, 'keyboard');
180
+ } else { // Otherwise remove default browser behavior for key inputs so that they do not interfere with game-play.
181
+ const
182
+ preventDefault = Array.isArray(enableDefaultEvents) ? (event) => enableDefaultEvents.indexOf(event.code) === -1 ? event.preventDefault() : null : (event) => event.preventDefault();
183
+
184
+ this.callbackKeyDown = (event) => {
185
+ onDown.call(this, 'keyboard', event);
186
+ preventDefault(event); // default 'none' may be too aggressive - if problems arise, we may need to limit this to certain key combos that get in the way of game-play. Example: (event.metaKey && event.keyCode == 37) causes an accidental cmd key press to send the browser back a page while playing and hitting the left arrow button.
187
+ };
188
+ this.callbackKeyUp = (event) => {
189
+ onUp.call(this, 'keyboard', event);
190
+ preventDefault(event);
191
+ };
192
+ }
193
+
194
+ window.addEventListener('keydown', this.callbackKeyDown, true);
195
+ window.addEventListener('keyup', this.callbackKeyUp, true);
196
+
197
+ this.addEventListener('controller-input-down', (event) => this.triggerControlAction(event.code, 'down', event));
198
+ this.addEventListener('controller-input-up', (event) => this.triggerControlAction(event.code, 'up', event));
199
+
200
+ if (this.useHandleLogic) {
201
+ this.addEventListener('handle-logic', broadcastHandleController);
202
+ } else {
203
+ this.addEventListener('tick', broadcastHandleController);
204
+ }
205
+
206
+ this.gamepads = arrayCache.setUp();
207
+
208
+ this.notifier = (inputs) => {
209
+ /**
210
+ * Triggered on owner and child entities when player uses a new imput method. (For example, going from mouse to keyboard or keyboard to gamepad.)
211
+ *
212
+ * @event platypus.Entity#input-precedence-updated
213
+ * @param inputs {Array} A list of input methods, ordered by precedence with index 0 being the most recent input method.
214
+ */
215
+ this.triggerOnAll('input-precedence-updated', inputs);
216
+ };
217
+ notifies.push(this.notifier);
218
+
219
+ if (!gameWideReady) { // Need info at the game level to handle missed control input across layers
220
+ gameWideReady = true;
221
+ platypus.game.on('tick', function () {
222
+ const
223
+ keys = Object.keys(detectInvalids),
224
+ {length} = keys;
225
+
226
+ for (let i = 0; i < length; i++) {
227
+ const
228
+ key = keys[i];
229
+
230
+ if (detectInvalids[key] === 0) {
231
+ this.triggerOnChildren('invalid-input', key);
232
+ } else {
233
+ this.triggerOnChildren('valid-input', key);
234
+ }
235
+ delete detectInvalids[key];
236
+ }
237
+ }.bind(platypus.game));
238
+ window.addEventListener('blur', () => {
239
+ platypus.game.triggerOnChildren('blur-control');
240
+ });
241
+ }
242
+ },
243
+ events: {
244
+ 'blur-control': function (event) {
245
+ if (this.upOnBlur) {
246
+ this.upsToBlur.forEach((upToBlur) => this.triggerControlAction(upToBlur, 'up', event));
247
+ this.upsToBlur.length = 0;
248
+ }
249
+ },
250
+ 'invalid-input': function (input) {
251
+ this.owner.triggerEventOnChildren('invalid-input', input);
252
+ },
253
+ 'valid-input': function (input) {
254
+ this.owner.triggerEventOnChildren('valid-input', input);
255
+ }
256
+ },
257
+ methods: {
258
+ destroy: function () {
259
+ window.removeEventListener('keydown', this.callbackKeyDown);
260
+ window.removeEventListener('keyup', this.callbackKeyUp);
261
+ for (let i = 0; i < this.gamepads.length; i++) {
262
+ if (this.gamepads[i]) {
263
+ this.gamepads[i].recycle();
264
+ }
265
+ }
266
+ arrayCache.recycle(this.gamepads);
267
+ greenSplice(notifies, notifies.indexOf(this.notifier));
268
+ },
269
+ triggerControlAction: function (control, action, ...args) {
270
+ const
271
+ event = `${control}:${action}`;
272
+
273
+ detectProcessing[control] = (detectProcessing[control] || 0) + this.triggerOnAll(event, ...args);
274
+ if (action === 'up') {
275
+ detectInvalids[control] = (detectInvalids[control] || 0) + detectProcessing[control];
276
+ detectProcessing[control] = 0;
277
+ }
278
+ },
279
+ triggerOnAll: function (event, ...args) {
280
+ const
281
+ owner = this.owner;
282
+ let count = owner.triggerEvent(event, ...args);
283
+
284
+ if (owner.triggerEventOnChildren) {
285
+ count += owner.triggerEventOnChildren(event, ...args);
286
+ }
287
+
288
+ return count;
289
+ }
290
+ }
291
+ });
@@ -0,0 +1,394 @@
1
+ import {arrayCache, greenSplice} from '../utils/array.js';
2
+ import AABB from '../AABB.js';
3
+ import createComponentClass from '../factory.js';
4
+
5
+ const
6
+ addAll = function (all, active) {
7
+ let j = all.length;
8
+
9
+ active.length = 0;
10
+ while (j--) {
11
+ active.push(all[j]);
12
+ }
13
+ },
14
+ withinBounds = function (child, camera) {
15
+ return child.alwaysOn || (child.aabb && camera.collides(child.getAABB())) || (typeof child.x === 'undefined') || camera.containsPoint(child.x, child.y);
16
+ },
17
+ checkCamera = function (all, active, camera) {
18
+ let j = all.length;
19
+
20
+ active.length = 0;
21
+ while (j--) {
22
+ const
23
+ child = all[j];
24
+
25
+ if (withinBounds(child, camera)) {
26
+ active.push(child);
27
+ }
28
+ }
29
+ },
30
+ hasLogic = function (item/*, index, arr*/) {
31
+ return (item === 'handle-logic' || item === 'handle-post-collision-logic' || item === 'prepare-logic' || item === 'state-changed' || item === 'handle-movement');
32
+ };
33
+
34
+ export default createComponentClass(/** @lends platypus.components.HandlerLogic.prototype */{
35
+ id: "HandlerLogic",
36
+ properties: {
37
+ /**
38
+ * Whether logic should always run on all entities or only run on entities within the visible camera area (plus the buffer amount specified by the `buffer` property).
39
+ *
40
+ * @property alwaysOn
41
+ * @type Boolean
42
+ * @default false
43
+ */
44
+ alwaysOn: false
45
+ },
46
+ publicProperties: {
47
+ /**
48
+ * The buffer area around the camera in which entity logic is active. This property is available on the Entity as `entity.buffer`.
49
+ *
50
+ * @property buffer
51
+ * @type number
52
+ * @default camera width / 10
53
+ */
54
+ buffer: -1,
55
+
56
+ /**
57
+ * The length in milliseconds of a single logic step. If the framerate drops too low, logic is run for each step of this many milliseconds. This property is available on the Entity as `entity.stepLength`.
58
+ *
59
+ * @property stepLength
60
+ * @type number
61
+ * @default 5
62
+ */
63
+ stepLength: 5,
64
+
65
+ /**
66
+ * The maximum number of steps to take for a given tick, to prevent lag overflow.
67
+ *
68
+ * @property maxStepsPerTick
69
+ * @type number
70
+ * @default 100
71
+ */
72
+ maxStepsPerTick: 100,
73
+
74
+ /**
75
+ * A multiplier that alters the speed at which the game is running. This is achieved by scaling the delta time in each tick.
76
+ * Defaults to 1. Values < 1 will slow down the logic, > 1 will speed it up.
77
+ *
78
+ * @property timeMultiplier
79
+ * @type number
80
+ * @default 1
81
+ */
82
+ timeMultiplier: 1
83
+ },
84
+
85
+ /**
86
+ * A component that handles updating logic components. Each tick it calls all the entities that accept 'handle-logic' messages. This component is usually used on an "action-layer".
87
+ *
88
+ * @memberof platypus.components
89
+ * @uses platypus.Component
90
+ * @constructs
91
+ * @param {*} definition
92
+ * @listens platypus.Entity#camera-update
93
+ * @listens platypus.Entity#child-entity-added
94
+ * @listens platypus.Entity#child-entity-removed
95
+ * @listens platypus.Entity#child-entity-updated
96
+ * @listens platypus.Entity#pause-logic
97
+ * @listens platypus.Entity#tick
98
+ * @listens platypus.Entity#unpause-logic
99
+ * @fires platypus.Entity#handle-ai
100
+ * @fires platypus.Entity#handle-logic
101
+ * @fires platypus.Entity#logic-paused
102
+ * @fires platypus.Entity#logic-unpaused
103
+ * @fires platypus.Entity#prepare-logic
104
+ * @fires platypus.Entity#handle-movement
105
+ * @fires platypus.Entity#check-collision-group
106
+ * @fires platypus.Entity#handle-post-collision-logic
107
+ * @fires platypus.Entity#state-changed
108
+ */
109
+ initialize: function () {
110
+ this.entities = arrayCache.setUp();
111
+ this.activeEntities = arrayCache.setUp();
112
+ this.removals = arrayCache.setUp();
113
+
114
+ this.inLogicLoop = false;
115
+
116
+ if (this.alwaysOn) {
117
+ this.updateList = addAll;
118
+ this.camera = null;
119
+ } else {
120
+ this.updateList = checkCamera;
121
+ this.camera = AABB.setUp();
122
+ }
123
+
124
+ this.paused = 0;
125
+ this.leftoverTime = 0;
126
+ this.message = {
127
+ delta: this.stepLength,
128
+ tick: null,
129
+ camera: this.camera,
130
+ entities: this.activeEntities
131
+ };
132
+ },
133
+
134
+ events: {
135
+ "child-entity-updated": function (entity) {
136
+ const
137
+ j = this.entities.indexOf(entity),
138
+ logical = entity.getMessageIds().some(hasLogic);
139
+
140
+ if (logical && (j < 0)) {
141
+ this.entities.push(entity);
142
+
143
+ // Add to the active entities list so that the collision loop is aware of and can handle the addition.
144
+ if (this.inLogicLoop && (!this.camera || withinBounds(entity, this.camera))) {
145
+ this.activeEntities.push(entity);
146
+ }
147
+ } else if (!logical && (j >= 0)) {
148
+ greenSplice(this.entities, j);
149
+ if (this.inLogicLoop) {
150
+ this.removals.push(entity);
151
+ }
152
+ }
153
+ },
154
+
155
+ "child-entity-added": function (entity) {
156
+ if (entity.getMessageIds().some(hasLogic)) {
157
+ this.entities.push(entity);
158
+
159
+ // Add to the active entities list so that the collision loop is aware of and can handle the addition.
160
+ if (this.inLogicLoop && (!this.camera || withinBounds(entity, this.camera))) {
161
+ this.activeEntities.push(entity);
162
+ }
163
+ }
164
+ },
165
+
166
+ "child-entity-removed": function (entity) {
167
+ const
168
+ j = this.entities.indexOf(entity);
169
+
170
+ if (j >= 0) {
171
+ greenSplice(this.entities, j);
172
+ if (this.inLogicLoop) {
173
+ this.removals.push(entity);
174
+ }
175
+ }
176
+ },
177
+
178
+ "pause-logic": function (resp) {
179
+ if (resp && resp.time) {
180
+ this.paused = resp.time;
181
+ } else {
182
+ this.paused = -1;
183
+ }
184
+ if (this.owner.triggerEventOnChildren) {
185
+ /**
186
+ * Notifies children entities that logic has been paused.
187
+ *
188
+ * @event platypus.Entity#logic-paused
189
+ */
190
+ this.owner.triggerEventOnChildren('logic-paused');
191
+ }
192
+ },
193
+
194
+ "unpause-logic": function () {
195
+ this.paused = 0;
196
+ if (this.owner.triggerEventOnChildren) {
197
+ /**
198
+ * Notifies children entities that logic has been unpaused.
199
+ *
200
+ * @event platypus.Entity#logic-unpaused
201
+ */
202
+ this.owner.triggerEventOnChildren('logic-unpaused');
203
+ }
204
+ },
205
+
206
+ "camera-update": function (camera) {
207
+ const
208
+ cam = this.camera;
209
+
210
+ if (cam) {
211
+ const
212
+ buffer = this.buffer === -1 ? camera.viewport.width / 10 : this.buffer, // sets a default buffer based on the size of the world units if the buffer was not explicitly set.
213
+ vp = camera.viewport;
214
+
215
+ cam.setBounds(vp.left - buffer, vp.top - buffer, vp.right + buffer, vp.bottom + buffer);
216
+ }
217
+ },
218
+
219
+ "tick": function (resp) {
220
+ const
221
+ {activeEntities: actives, message: msg, removals} = this,
222
+ stepLength = this.stepLength * this.timeMultiplier;
223
+ let cycles = 0;
224
+
225
+ this.leftoverTime += (resp.delta * this.timeMultiplier);
226
+ cycles = Math.floor(this.leftoverTime / stepLength) || 1;
227
+
228
+ // This makes the frames smoother, but adds variance into the calculations
229
+ // msg.delta = this.leftoverTime / cycles;
230
+ // this.leftoverTime = 0;
231
+
232
+ // This makes the frames more exact, but varying step numbers between ticks can cause movement to be jerky
233
+ // msg.delta = Math.min(this.leftoverTime, this.stepLength);
234
+ // this.leftoverTime = Math.max(this.leftoverTime - (cycles * this.stepLength), 0);
235
+
236
+ // This makes the frames exact, but varying step numbers between ticks can cause movement to be jerky
237
+ msg.gameDelta = this.stepLength;
238
+ msg.delta = stepLength;
239
+ this.leftoverTime = Math.max(this.leftoverTime - (cycles * stepLength), 0);
240
+
241
+ msg.tick = resp;
242
+
243
+ this.updateList(this.entities, actives, this.camera);
244
+
245
+ //Prevents game lockdown when processing takes longer than time alotted.
246
+ cycles = Math.min(cycles, this.maxStepsPerTick);
247
+
248
+ while (cycles--) {
249
+
250
+ if (this.paused > 0) {
251
+ this.paused -= stepLength;
252
+ if (this.paused < 0) {
253
+ this.paused = 0;
254
+ }
255
+ }
256
+
257
+ if (!this.paused) {
258
+ let i = 0;
259
+
260
+ this.inLogicLoop = true;
261
+
262
+ /**
263
+ * This event is triggered on the top-level layer to signify a "handle-logic" event is about to be triggered on children, and is then subsequently triggered on all of the layer's child entities. This is unique from the layer's "tick" event in that it occurs the same number of times as the "handle-logic" event and will not occur if HandlerLogic is paused.
264
+ *
265
+ * @event platypus.Entity#handle-logic
266
+ * @param tick.delta {Number} The time that has passed since the last tick as manipulated by the timeMultiplier.
267
+ * @param tick.gameDelta {Number} The time that has passed since the last tick. Unmanipulated by timeMultiplier. Use for components that need to run on actual time.
268
+ * @param tick.camera {null|platypus.AABB} The range of the logic camera area. This is typically larger than the visible camera. This value is `null` if `alwaysOn` is set to `true` on this component.
269
+ * @param tick.entities {Array} This is a list of all the logically active entities.
270
+ * @param tick.tick {Object} Tick object from "tick" event.
271
+ */
272
+ this.owner.triggerEvent('handle-logic', msg);
273
+
274
+ if (this.owner.triggerEventOnChildren) {
275
+ /**
276
+ * AI components listen in order to perform their logic on each tick.
277
+ *
278
+ * @event platypus.Entity#handle-ai
279
+ * @param tick.delta {Number} The time that has passed since the last tick as manipulated by the timeMultiplier.
280
+ * @param tick.gameDelta {Number} The time that has passed since the last tick. Unmanipulated by timeMultiplier. Use for components that need to run on actual time.
281
+ * @param tick.camera {null|platypus.AABB} The range of the logic camera area. This is typically larger than the visible camera. This value is `null` if `alwaysOn` is set to `true` on this component.
282
+ * @param tick.entities {Array} This is a list of all the logically active entities.
283
+ * @param tick.tick {Object} Tick object from "tick" event.
284
+ */
285
+ this.owner.triggerEventOnChildren('handle-ai', msg);
286
+ }
287
+
288
+ i = actives.length;
289
+ while (i--) {
290
+ const
291
+ entity = actives[i];
292
+
293
+ /**
294
+ * This event is triggered on children entities to run anything that should occur before "handle-logic". For example, removing or adding components should happen here and not in "handle-logic".
295
+ *
296
+ * @event platypus.Entity#prepare-logic
297
+ * @param tick {Object}
298
+ * @param tick.delta {Number} The time that has passed since the last tick.
299
+ * @param tick.camera {null|platypus.AABB} The range of the logic camera area. This is typically larger than the visible camera. This value is `null` if `alwaysOn` is set to `true` on this component.
300
+ * @param tick.entities {Array} This is a list of all the logically active entities.
301
+ */
302
+ entity.triggerEvent('prepare-logic', msg);
303
+
304
+ entity.triggerEvent('handle-logic', msg);
305
+
306
+ /**
307
+ * This event is triggered on children entities to move. This happens immediately after logic so entity logic can determine movement.
308
+ *
309
+ * @event platypus.Entity#handle-movement
310
+ * @param tick {Object}
311
+ * @param tick.delta {Number} The time that has passed since the last tick.
312
+ * @param tick.camera {null|platypus.AABB} The range of the logic camera area. This is typically larger than the visible camera. This value is `null` if `alwaysOn` is set to `true` on this component.
313
+ * @param tick.entities {Array} This is a list of all the logically active entities.
314
+ */
315
+ entity.triggerEvent('handle-movement', msg);
316
+ }
317
+ this.inLogicLoop = false;
318
+
319
+ // This handles removing active entities from the list before collision checking, state-changing, etc.
320
+ if (removals.length) {
321
+ let j = removals.length;
322
+
323
+ while (j--) {
324
+ let k = actives.indexOf(removals[j]);
325
+
326
+ if (k >= 0) {
327
+ greenSplice(actives, k);
328
+ }
329
+ }
330
+ removals.length = 0;
331
+ }
332
+
333
+ i = actives.length;
334
+ /**
335
+ * This event is triggered on the entity (layer) to test collisions once logic has been completed.
336
+ *
337
+ * @event platypus.Entity#check-collision-group
338
+ * @param tick {Object}
339
+ * @param tick.delta {Number} The time that has passed since the last tick.
340
+ * @param tick.camera {null|platypus.AABB} The range of the logic camera area. This is typically larger than the visible camera. This value is `null` if `alwaysOn` is set to `true` on this component.
341
+ * @param tick.entities {Array} This is a list of all the logically active entities.
342
+ */
343
+ if (this.owner.triggerEvent('check-collision-group', msg)) { // If a collision group is attached, make sure collision is processed on each logic tick.
344
+ /**
345
+ * This event is triggered on entities to run logic that may depend upon collision responses.
346
+ *
347
+ * @event platypus.Entity#handle-post-collision-logic
348
+ * @param tick {Object}
349
+ * @param tick.delta {Number} The time that has passed since the last tick.
350
+ * @param tick.camera {null|platypus.AABB} The range of the logic camera area. This is typically larger than the visible camera. This value is `null` if `alwaysOn` is set to `true` on this component.
351
+ * @param tick.entities {Array} This is a list of all the logically active entities.
352
+ */
353
+
354
+ /**
355
+ * Triggered on entities when the entity's state has been changed.
356
+ *
357
+ * @event platypus.Entity#state-changed
358
+ * @param state {Object} A list of key/value pairs representing the owner's state (this value equals `entity.state`).
359
+ */
360
+ while (i--) {
361
+ const
362
+ entity = actives[i];
363
+
364
+ entity.triggerEvent('handle-post-collision-logic', msg);
365
+ if (entity.lastState?.update(entity.state)) {
366
+ entity.triggerEvent('state-changed', entity.state);
367
+ }
368
+ }
369
+ } else {
370
+ while (i--) {
371
+ const
372
+ entity = actives[i];
373
+
374
+ if (entity.lastState?.update(entity.state)) {
375
+ entity.triggerEvent('state-changed', entity.state);
376
+ }
377
+ }
378
+ }
379
+ }
380
+ }
381
+ }
382
+ },
383
+
384
+ methods: {
385
+ destroy: function () {
386
+ arrayCache.recycle(this.entities);
387
+ this.entities = null;
388
+ arrayCache.recycle(this.activeEntities);
389
+ this.activeEntities = null;
390
+ arrayCache.recycle(this.removals);
391
+ this.removals = null;
392
+ }
393
+ }
394
+ });