@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.
Files changed (136) hide show
  1. package/CHANGELOG.md +31 -0
  2. package/README.md +3 -3
  3. package/lib/platypus.js +1 -1
  4. package/package.json +7 -6
  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 +840 -0
  98. package/src/components/RenderSprite.js +527 -0
  99. package/src/components/RenderText.js +147 -0
  100. package/src/components/RenderTiles.js +1440 -0
  101. package/src/components/SceneChanger.js +128 -0
  102. package/src/components/TiledLoader.js +1459 -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
package/src/Gamepad.js ADDED
@@ -0,0 +1,175 @@
1
+ import Data from './Data.js';
2
+ import Vector from './Vector.js';
3
+ import {arrayCache} from './utils/array.js';
4
+ import recycle from 'recycle';
5
+
6
+ const
7
+ /**
8
+ * This class defines an axis-aligned bounding box (AABB) which is used during the collision process to determine if two objects are colliding. This is used in a few places including [CollisionBasic](platypus.components.CollisionBasic.html) and [[Collision-Shape]].
9
+ *
10
+ * @memberof platypus
11
+ * @class Gamepad
12
+ * @param gamepad {number} The browser's gamepad object.
13
+ * @param id {number} Id of gamepad.
14
+ * @param deadzone {number} distance from axis `0` position to ignore.
15
+ * @param height {number} The height of the AABB.
16
+ * @return {platypus.Gamepad} Returns the new aabb object.
17
+ */
18
+ Gamepad = function (gamepad, onDown, onUp, onChange, id = '', deadzone = 0.1) {
19
+ this.source = gamepad;
20
+ this.deadzone = deadzone;
21
+ this.axes = this.axes || arrayCache.setUp();
22
+ for (let i = 0; i < (gamepad.axes.length >> 1); i++) {
23
+ this.axes.push(clampVector(Vector.setUp(gamepad.axes[i * 2], gamepad.axes[i * 2 + 1]), deadzone));
24
+ }
25
+ this.buttons = this.buttons || arrayCache.setUp();
26
+ for (let i = 0; i < gamepad.buttons.length; i++) {
27
+ this.buttons.push(gamepad.buttons[i].value);
28
+ }
29
+ this.id = id || `${gamepad.id} - ${gamepad.index}`; // Adding index to make it unique.
30
+
31
+ this.onDown = onDown;
32
+ this.onUp = onUp;
33
+ this.onChange = onChange;
34
+ },
35
+ playEffectOptions = {
36
+ duration: 500,
37
+ startDelay: 0,
38
+ strongMagnitude: 1.0,
39
+ weakMagnitude: 1.0
40
+ },
41
+ controllers = {
42
+ a0: 'LeftStick',
43
+ a1: 'RightStick',
44
+ b0: 'AButton',
45
+ b1: 'BButton',
46
+ b2: 'XButton',
47
+ b3: 'YButton',
48
+ b4: 'LeftBumper',
49
+ b5: 'RightBumper',
50
+ b6: 'LeftTrigger',
51
+ b7: 'RightTrigger',
52
+ b8: 'BackButton',
53
+ b9: 'StartButton',
54
+ b10: 'LeftStickButton',
55
+ b11: 'RightStickButton',
56
+ b12: 'UpDPad',
57
+ b13: 'DownDPad',
58
+ b14: 'LeftDPad',
59
+ b15: 'RightDPad'
60
+ },
61
+ clampVector = function (v, deadzone) {
62
+ const m = v.magnitude();
63
+
64
+ if (m > 1) {
65
+ v.x *= 1 / m;
66
+ v.y *= 1 / m;
67
+ } else if (m < deadzone) {
68
+ v.x = 0;
69
+ v.y = 0;
70
+ } else {
71
+ const span = 1 - deadzone;
72
+ v.x = (v.x - deadzone) / span;
73
+ v.y = (v.y - deadzone) / span;
74
+ }
75
+
76
+ return v;
77
+ },
78
+ proto = Gamepad.prototype;
79
+
80
+ proto.dualRumble = function (duration = 500, delay = 0, strong = 1.0, weak = 1.0) {
81
+ playEffectOptions.duration = duration;
82
+ playEffectOptions.startDelay = delay;
83
+ playEffectOptions.strongMagnitude = strong;
84
+ playEffectOptions.weakMagnitude = weak;
85
+ this.source.vibrationActuator.playEffect('dual-rumble', playEffectOptions);
86
+ };
87
+
88
+ proto.update = function (gamepad) {
89
+ const
90
+ axes = this.axes,
91
+ buttons = this.buttons,
92
+ onDown = this.onDown,
93
+ onUp = this.onUp,
94
+ onChange = this.onChange;
95
+
96
+ for (let i = 0; i < axes.length; i++) {
97
+ const
98
+ oldVector = axes[i],
99
+ newVector = clampVector(Vector.setUp(gamepad.axes[i * 2], gamepad.axes[i * 2 + 1]), this.deadzone);
100
+
101
+ if (!newVector.equals(oldVector, 2)) {
102
+ const event = Data.setUp(
103
+ 'code', controllers[`a${i}`],
104
+ 'direction', oldVector,
105
+ 'gamepad', this,
106
+ 'id', this.id
107
+ );
108
+
109
+ oldVector.set(newVector);
110
+ onChange(event);
111
+ event.recycle();
112
+ }
113
+
114
+ newVector.recycle();
115
+ }
116
+ for (let i = 0; i < gamepad.buttons.length; i++) {
117
+ const
118
+ newButton = gamepad.buttons[i],
119
+ newValue = newButton.value,
120
+ oldButton = buttons[i];
121
+
122
+ if (newValue !== oldButton) {
123
+ const event = Data.setUp(
124
+ 'code', controllers[`b${i}`],
125
+ 'button', newButton,
126
+ 'gamepad', this,
127
+ 'id', this.id
128
+ );
129
+
130
+ buttons[i] = newValue;
131
+
132
+ if (oldButton === 0) {
133
+ onDown(event);
134
+ onChange(event);
135
+ } else if (newValue === 0) {
136
+ onChange(event);
137
+ onUp(event);
138
+ } else {
139
+ onChange(event);
140
+ }
141
+
142
+ event.recycle();
143
+ }
144
+ }
145
+ };
146
+
147
+ /**
148
+ * Returns an Gamepad from cache or creates a new one if none are available.
149
+ *
150
+ * @method platypus.Gamepad.setUp
151
+ * @return {platypus.Gamepad} The instantiated Gamepad.
152
+ */
153
+ /**
154
+ * Returns a Gamepad back to the cache.
155
+ *
156
+ * @method platypus.Gamepad.recycle
157
+ * @param {platypus.Gamepad} gamepad The Gamepad to be recycled.
158
+ */
159
+ /**
160
+ * Relinquishes properties of the Gamepad and recycles it.
161
+ *
162
+ * @method platypus.Gamepad#recycle
163
+ */
164
+ recycle.add(Gamepad, 'Gamepad', Gamepad, function () {
165
+ for (let i = 0; i < this.axes.length; i++) {
166
+ this.axes[i].recycle();
167
+ }
168
+ this.axes.length = 0;
169
+ this.buttons.length = 0;
170
+ this.onDown = null;
171
+ this.onUp = null;
172
+ this.onChange = null;
173
+ }, true);
174
+
175
+ export default Gamepad;
@@ -0,0 +1,61 @@
1
+ import {CaptionFactory, CaptionPlayer, HtmlRenderer} from 'springroll';
2
+
3
+ const
4
+ nullCaption = [{
5
+ content: "Prevent empty captions error.",
6
+ start: 0,
7
+ end: 1
8
+ }],
9
+ findAndFormatCaptionsFromTags = ({tags}, length) => {
10
+ const
11
+ {SYLT} = tags;
12
+
13
+ if (SYLT) {
14
+ const
15
+ list = Array.isArray(SYLT) ? SYLT : [SYLT],
16
+ captionsSYLT = list.filter(({data}) => data.descriptor === 'captions')[0] ?? list.filter(({data}) => data.contentType === 'transcription')[0] ?? list.filter(({data}) => data.contentType === 'lyrics')[0] ?? null;
17
+
18
+ if (captionsSYLT) {
19
+ return CaptionFactory.createCaption(captionsSYLT.data.synchronisedText.map(({text: content, timeStamp: start}, index, arr) => {
20
+ const
21
+ next = arr[index + 1];
22
+
23
+ return {
24
+ content,
25
+ start: Math.max(start, 0.001), // Prevents CaptionPlayer from playing caption before the audio is loaded/playing.
26
+ end: next ? next.timeStamp : length
27
+ };
28
+ }));
29
+ }
30
+ }
31
+
32
+ return null;
33
+ },
34
+ ID3CaptionPlayer = class extends CaptionPlayer {
35
+ constructor (captions = {nullCaption}, captionsElement, tagReader = null) {
36
+ super(captions, new HtmlRenderer(captionsElement));
37
+
38
+ if (tagReader) {
39
+ this.getCaption = (audio) => new Promise((resolve, reject) => {
40
+ tagReader.read(audio.media?.file ?? audio.url, {
41
+ onSuccess: (tags) => resolve(findAndFormatCaptionsFromTags(tags, (audio.duration * 1000) >> 0)),
42
+ onError: reject
43
+ });
44
+ });
45
+ }
46
+ }
47
+
48
+ async start (audio, audioId, ...args) {
49
+ if (this.getCaption && typeof this.captions[audioId] === 'undefined') {
50
+ try {
51
+ this.captions[audioId] = await this.getCaption(audio);
52
+ } catch (e) {
53
+ this.captions[audioId] = null;
54
+ platypus.debug.warn(e.type, e.info);
55
+ }
56
+ }
57
+ super.start(this.captions[audioId] ? audioId : audio.url, ...args);
58
+ }
59
+ };
60
+
61
+ export default ID3CaptionPlayer;
@@ -0,0 +1,365 @@
1
+ /* global platypus, window */
2
+ import EventHandlerList from './EventHandlerList.js';
3
+ import {arrayCache, greenSlice} from './utils/array.js';
4
+
5
+ const
6
+ getPerfTools = () => typeof performance !== 'undefined' && performance.mark && performance.measure ? performance : null,
7
+ runBoth = function (f1, f2) {
8
+ return function () {
9
+ f1.apply(this, arguments);
10
+ f2.apply(this, arguments);
11
+ };
12
+ };
13
+
14
+ /**
15
+ * Messenger provides prioritized event-based communication between
16
+ * components, entities, and systems.
17
+ *
18
+ * Features include:
19
+ * - prioritized listeners
20
+ * - one-time listeners
21
+ * - deterministic execution ordering
22
+ * - safe listener mutation during dispatch
23
+ * - pooled listener structures
24
+ * - array/object event dispatch formats
25
+ * - optional debug instrumentation
26
+ *
27
+ * Messenger is commonly mixed into entities and components to
28
+ * provide lightweight pub/sub behavior without repeated allocations.
29
+ *
30
+ * @memberof platypus
31
+ * @class Messenger
32
+ */
33
+ class Messenger {
34
+ /**
35
+ * Creates a Messenger instance.
36
+ *
37
+ * @param {Object} [options]
38
+ * Configuration options.
39
+ *
40
+ * @param {Boolean} [options.debug=false]
41
+ * Enables nested-event debugging and performance instrumentation.
42
+ */
43
+ constructor ({debug} = {}) {
44
+ this._listeners = {};
45
+ this._destroyed = false;
46
+ this.loopCheck = arrayCache.setUp();
47
+
48
+ if (debug) {
49
+ const
50
+ triggerEvent = this.triggerEvent;
51
+
52
+ this.triggerEvent = function (event, value) {
53
+ const
54
+ debugLimit = 5,
55
+ debugLogging = value && value.debug;
56
+ let debugCount = 0;
57
+
58
+ // Debug logging.
59
+ if (debugLogging || this.debug) {
60
+ const perfTools = getPerfTools();
61
+
62
+ for (let i = 0; i < this.loopCheck.length; i++) {
63
+ if (this.loopCheck[i] === event) {
64
+ debugCount += 1;
65
+ if (debugCount > debugLimit) {
66
+ throw "Endless loop detected for '" + event + "'.";
67
+ } else {
68
+ platypus.debug.warn("Event '" + event + "' is nested inside another '" + event + "' event.");
69
+ }
70
+ }
71
+ }
72
+
73
+ this.loopCheck.push(event);
74
+
75
+ if (perfTools) {
76
+ perfTools.mark("a");
77
+ }
78
+
79
+ const
80
+ count = triggerEvent.apply(this, arguments);
81
+
82
+ if (perfTools) {
83
+ perfTools.mark("b");
84
+ perfTools.measure(this.type + ":" + event, 'a', 'b');
85
+ }
86
+ this.loopCheck.length = this.loopCheck.length - 1;
87
+ if (debugLogging) {
88
+ if (count) {
89
+ platypus.debug.log('Entity "' + this.type + '": Event "' + event + '" has ' + count + ' subscriber' + ((count > 1) ? 's' : '') + '.', value);
90
+ } else {
91
+ platypus.debug.warn('Entity "' + this.type + '": Event "' + event + '" has no subscribers.', value);
92
+ }
93
+ }
94
+ return count;
95
+ } else {
96
+ return triggerEvent.apply(this, arguments);
97
+ }
98
+ };
99
+ }
100
+ }
101
+
102
+ /**
103
+ * Registers an event listener.
104
+ *
105
+ * Listeners are executed in ascending priority order.
106
+ * Lower priority values execute first.
107
+ *
108
+ * When priorities match, listeners execute in
109
+ * registration order.
110
+ *
111
+ * If no context is supplied, the Messenger instance
112
+ * becomes the callback context.
113
+ *
114
+ * @method on
115
+ *
116
+ * @param {String} name
117
+ * Event name.
118
+ *
119
+ * @param {Function} callback
120
+ * Function invoked when the event fires.
121
+ *
122
+ * @param {Object} [context=this]
123
+ * Callback execution context.
124
+ *
125
+ * @param {Boolean} [once=false]
126
+ * Whether the listener should automatically remove
127
+ * itself after its first execution.
128
+ *
129
+ * @param {Number} [priority=MAX_SAFE_INTEGER]
130
+ * Listener execution priority.
131
+ *
132
+ * Lower numbers execute first.
133
+ */
134
+ on (name, callback, context = null, once = false, priority = -1) {
135
+ if (!this._destroyed) {
136
+ const
137
+ listenerList = this._listeners[name] = this._listeners[name] ?? EventHandlerList.setUp();
138
+
139
+ return listenerList.add(callback, context ?? this, once, priority === -1 ? Number.MAX_SAFE_INTEGER : priority);
140
+ }
141
+
142
+ return null;
143
+ }
144
+
145
+ /**
146
+ * Removes listeners.
147
+ *
148
+ * Calling without arguments removes all listeners.
149
+ *
150
+ * Calling with only an event name removes all listeners
151
+ * for that event.
152
+ *
153
+ * Calling with both name and callback removes matching
154
+ * listeners for that callback.
155
+ *
156
+ * @method off
157
+ *
158
+ * @param {String} [name]
159
+ * Event name.
160
+ *
161
+ * @param {Function} [callback]
162
+ * Listener callback.
163
+ */
164
+ off (name, callback, context) {
165
+ if (!this._destroyed) {
166
+ // remove all
167
+ if (typeof name === 'undefined') {
168
+ this.getMessageIds().forEach((id) => this._listeners[id].recycle());
169
+ this._listeners = {};
170
+ } else {
171
+ const
172
+ listenerList = this._listeners[name];
173
+
174
+ if (listenerList) {
175
+ // remove all listeners for that event
176
+ if (typeof callback === 'undefined') {
177
+ listenerList.recycle();
178
+ delete this._listeners[name];
179
+ } else {
180
+ //remove single listener
181
+ listenerList.remove(callback, context);
182
+ if (!listenerList.handlers.length) {
183
+ listenerList.recycle();
184
+ delete this._listeners[name];
185
+ }
186
+ }
187
+ }
188
+ }
189
+ }
190
+ }
191
+
192
+ /**
193
+ * Returns a string describing the Messenger as "[Messenger object]".
194
+ *
195
+ * @return String
196
+ */
197
+ toString () {
198
+ return "[Messenger Object]";
199
+ }
200
+
201
+ /**
202
+ * Dispatches one or more events.
203
+ *
204
+ * Supported formats:
205
+ *
206
+ * String:
207
+ * trigger('jump', value)
208
+ *
209
+ * Array:
210
+ * trigger(['jump', 'land'], value)
211
+ *
212
+ * Object:
213
+ * trigger({
214
+ * event: 'jump',
215
+ * message: value,
216
+ * debug: true
217
+ * })
218
+ *
219
+ * @method trigger
220
+ *
221
+ * @param {String|Array|Object} events
222
+ * Event descriptor.
223
+ *
224
+ * @param {*} [message]
225
+ * Event payload.
226
+ *
227
+ * @param {Boolean} [debug]
228
+ * Enables debug logging for this dispatch.
229
+ *
230
+ * @return {Number}
231
+ * Total number of listeners triggered.
232
+ */
233
+ trigger (events, message, debug) {
234
+ if (typeof events === 'string') {
235
+ return this.triggerEvent.apply(this, arguments);
236
+ } else if (Array.isArray(events)) {
237
+ const
238
+ args = greenSlice(arguments);
239
+ let count = 0;
240
+
241
+ for (let i = 0; i < events.length; i++) {
242
+ args[0] = events[i];
243
+ count += this.trigger.apply(this, args);
244
+ }
245
+ arrayCache.recycle(args);
246
+
247
+ return count;
248
+ } else if (events.event) {
249
+ if (typeof events.debug !== 'undefined') {
250
+ return this.triggerEvent(
251
+ events.event,
252
+ events.message ?? message,
253
+ events.debug
254
+ );
255
+ }
256
+
257
+ return this.triggerEvent(
258
+ events.event,
259
+ events.message ?? message
260
+ );
261
+ } else {
262
+ platypus.debug.warn('Event incorrectly formatted: must be string, array, or object containing an "event" property.', events);
263
+ return 0;
264
+ }
265
+ }
266
+
267
+ /**
268
+ * Dispatches a single event directly.
269
+ *
270
+ * This bypasses trigger-format normalization and is
271
+ * the fastest dispatch path.
272
+ *
273
+ * @method triggerEvent
274
+ *
275
+ * @param {String} type
276
+ * Event name.
277
+ *
278
+ * @param {...*} args
279
+ * Arguments forwarded to listeners.
280
+ *
281
+ * @return {Number}
282
+ * Number of listeners triggered.
283
+ */
284
+ triggerEvent (type, ...args) {
285
+ const
286
+ {_listeners} = this;
287
+
288
+ if (!this._destroyed && _listeners.hasOwnProperty(type) && (_listeners[type])) {
289
+ return _listeners[type].trigger(args);
290
+ }
291
+
292
+ return 0;
293
+ }
294
+
295
+ /**
296
+ * This method returns all the messages that this entity is concerned about.
297
+ *
298
+ * @return {Array} An array of strings listing all the messages for which this Messenger has handlers.
299
+ */
300
+ getMessageIds () {
301
+ return Object.keys(this._listeners);
302
+ }
303
+
304
+ /**
305
+ * This method relinguishes Messenger properties
306
+ *
307
+ */
308
+ destroy () {
309
+ arrayCache.recycle(this.loopCheck);
310
+ this.loopCheck = null;
311
+ this._destroyed = true;
312
+ this.getMessageIds().forEach((id) => this._listeners[id].recycle());
313
+ this._listeners = null;
314
+ }
315
+
316
+ /**
317
+ * This read-only property shows whether this Messenger is destroyed.
318
+ *
319
+ * @property destroyed
320
+ * @type Boolean
321
+ * @default false
322
+ */
323
+ get destroyed () {
324
+ return this._destroyed;
325
+ }
326
+
327
+ /**
328
+ * Adds Messenger functionality to a Class.
329
+ *
330
+ * @param {Class|Function} ClassObject The class to add Messenger behavior to.
331
+ */
332
+ static mixin (ClassObject) {
333
+ const
334
+ fromProto = Messenger.prototype,
335
+ toProto = ClassObject.prototype,
336
+ methods = Object.getOwnPropertyNames(fromProto);
337
+ let i = methods.length;
338
+
339
+ while (i--) {
340
+ const
341
+ key = methods[i];
342
+
343
+ if (key !== 'constructor') {
344
+ if (toProto[key]) {
345
+ toProto[key] = runBoth(toProto[key], fromProto[key]);
346
+ } else {
347
+ toProto[key] = fromProto[key];
348
+ }
349
+ }
350
+ }
351
+ }
352
+
353
+ /**
354
+ * Call this method in an Object's instantiation if `Messenger.mixin` has been called on its Class.
355
+ *
356
+ * @param {Object} object The object for which Messenger should be instantiated.
357
+ */
358
+ static initialize (object) {
359
+ object._listeners = {};
360
+ object._destroyed = false;
361
+ object.loopCheck = arrayCache.setUp();
362
+ }
363
+ }
364
+
365
+ export default Messenger;