@makefully/platypus 4.1.5 → 4.1.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +31 -0
- package/README.md +3 -3
- package/lib/platypus.js +1 -1
- package/package.json +7 -6
- package/src/AABB.js +685 -0
- package/src/ActionState.js +121 -0
- package/src/AssetManager.js +206 -0
- package/src/CollisionData.js +96 -0
- package/src/CollisionDataContainer.js +127 -0
- package/src/CollisionShape.js +315 -0
- package/src/Component.js +231 -0
- package/src/Data.js +64 -0
- package/src/DataMap.js +204 -0
- package/src/Entity.js +404 -0
- package/src/EntityLinker.js +113 -0
- package/src/EventHandler.js +115 -0
- package/src/EventHandlerList.js +251 -0
- package/src/Game.js +840 -0
- package/src/Gamepad.js +175 -0
- package/src/ID3CaptionPlayer.js +61 -0
- package/src/Messenger.js +365 -0
- package/src/PIXIAnimation.js +622 -0
- package/src/RandomSet.js +140 -0
- package/src/SFXPlayer.js +120 -0
- package/src/StateMap.js +252 -0
- package/src/Storage.js +152 -0
- package/src/TickerClient.js +48 -0
- package/src/TickerServer.js +23 -0
- package/src/TimeEvent.js +50 -0
- package/src/TimeEventList.js +100 -0
- package/src/VOPlayer.js +684 -0
- package/src/Vector.js +702 -0
- package/src/components/AIChaser.js +161 -0
- package/src/components/AIPacer.js +107 -0
- package/src/components/AudioMusic.js +151 -0
- package/src/components/AudioSFX.js +673 -0
- package/src/components/AudioVO.js +301 -0
- package/src/components/CollisionBasic.js +826 -0
- package/src/components/CollisionFilter.js +74 -0
- package/src/components/CollisionGroup.js +448 -0
- package/src/components/CollisionTiles.js +608 -0
- package/src/components/ComponentSwitcher.js +173 -0
- package/src/components/ControllerInput.js +65 -0
- package/src/components/Counter.js +165 -0
- package/src/components/EntityCamera.js +215 -0
- package/src/components/EntityContainer.js +549 -0
- package/src/components/EntityController.js +541 -0
- package/src/components/HandlerCamera.js +1052 -0
- package/src/components/HandlerCollision.js +1318 -0
- package/src/components/HandlerController.js +291 -0
- package/src/components/HandlerLogic.js +394 -0
- package/src/components/HandlerRender.js +217 -0
- package/src/components/Interactive.js +502 -0
- package/src/components/LevelBuilder.js +524 -0
- package/src/components/LogicAngularMovement.js +154 -0
- package/src/components/LogicAttachment.js +265 -0
- package/src/components/LogicButton.js +466 -0
- package/src/components/LogicCarrier.js +39 -0
- package/src/components/LogicDestroyMe.js +58 -0
- package/src/components/LogicDirectionalMovement.js +325 -0
- package/src/components/LogicDragDrop.js +371 -0
- package/src/components/LogicImpactLaunch.js +144 -0
- package/src/components/LogicPacingPlatform.js +64 -0
- package/src/components/LogicPortable.js +112 -0
- package/src/components/LogicPortal.js +115 -0
- package/src/components/LogicPushable.js +98 -0
- package/src/components/LogicRebounder.js +136 -0
- package/src/components/LogicRegionSpawner.js +139 -0
- package/src/components/LogicRotationalMovement.js +205 -0
- package/src/components/LogicSpawner.js +159 -0
- package/src/components/LogicStateMachine.js +382 -0
- package/src/components/LogicSwitch.js +77 -0
- package/src/components/LogicTeleportee.js +68 -0
- package/src/components/LogicTeleporter.js +93 -0
- package/src/components/LogicTimer.js +178 -0
- package/src/components/LogicWindUpRacer.js +179 -0
- package/src/components/Motion.js +371 -0
- package/src/components/Mover.js +566 -0
- package/src/components/Node.js +289 -0
- package/src/components/NodeMap.js +295 -0
- package/src/components/NodeResident.js +636 -0
- package/src/components/Orientation.js +697 -0
- package/src/components/RandomEvents.js +73 -0
- package/src/components/RelativePosition.js +204 -0
- package/src/components/RelayFamily.js +106 -0
- package/src/components/RelayGame.js +53 -0
- package/src/components/RelayLinker.js +94 -0
- package/src/components/RelayParent.js +57 -0
- package/src/components/RelaySelf.js +55 -0
- package/src/components/Render.js +178 -0
- package/src/components/RenderAnimator.js +275 -0
- package/src/components/RenderContainer.js +646 -0
- package/src/components/RenderDebug.js +319 -0
- package/src/components/RenderDestroyMe.js +59 -0
- package/src/components/RenderLottie.js +149 -0
- package/src/components/RenderProgress.js +120 -0
- package/src/components/RenderSpine.js +840 -0
- package/src/components/RenderSprite.js +527 -0
- package/src/components/RenderText.js +147 -0
- package/src/components/RenderTiles.js +1440 -0
- package/src/components/SceneChanger.js +128 -0
- package/src/components/TiledLoader.js +1459 -0
- package/src/components/Timeline.js +314 -0
- package/src/components/Tutorial.js +267 -0
- package/src/components/Tween.js +296 -0
- package/src/components/VoiceOver.js +373 -0
- package/src/components/XHR.js +129 -0
- package/src/components/index.js +79 -0
- package/src/factory.js +305 -0
- package/src/index.js +159 -0
- package/src/mixins/PIXI.Graphics.js +41 -0
- package/src/platypus.css +68 -0
- package/src/sayHello.js +50 -0
- package/src/shapes/Capsule.js +74 -0
- package/src/shapes/Circle.js +43 -0
- package/src/shapes/Ellipse.js +117 -0
- package/src/shapes/Point.js +191 -0
- package/src/shapes/Polygon.js +112 -0
- package/src/shapes/Polyline.js +97 -0
- package/src/shapes/Rectangle.js +78 -0
- package/src/shapes/RoundedRectangle.js +33 -0
- package/src/shapes/Segment.js +346 -0
- package/src/shapes/adjustedXY.js +19 -0
- package/src/shapes/castFromBox2D.js +92 -0
- package/src/shapes/castFromObject.js +53 -0
- package/src/shapes/castFromPixiGraphics.js +6 -0
- package/src/shapes/castFromPixiShape.js +89 -0
- package/src/shapes/castToBox2D.js +190 -0
- package/src/shapes/castToPixiGraphics.js +100 -0
- package/src/shapes/castToPixiShape.js +61 -0
- package/src/shapes/checkPointsMap.js +313 -0
- package/src/shapes/index.js +35 -0
- package/src/utils/array.js +73 -0
- package/src/utils/string.js +39 -0
- package/lib/243.platypus.mjs +0 -1
- 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
|
+
});
|