@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
@@ -0,0 +1,840 @@
1
+ /* global platypus */
2
+ import {Skin, Spine, AtlasAttachmentLoader, SkeletonJson} from "@esotericsoftware/spine-pixi-v8";
3
+ import {arrayCache} from '../utils/array.js';
4
+ import Data from '../Data.js';
5
+ import RenderAnimator from './RenderAnimator.js';
6
+ import RenderContainer from './RenderContainer.js';
7
+ import StateMap from '../StateMap.js';
8
+ import createComponentClass from '../factory.js';
9
+
10
+ const
11
+ createTest = function (testStates, skin) {
12
+ if (testStates === 'default') {
13
+ return () => skin;
14
+ } else {
15
+ //TODO: Better clean-up: Create a lot of these without removing them later... DDD 2/5/2016
16
+ const
17
+ states = StateMap.setUp(testStates);
18
+
19
+ return (ownerState) => ownerState.includes(states) ? skin : null;
20
+ }
21
+ };
22
+
23
+ export default createComponentClass(/** @lends platypus.components.RenderSpine.prototype */{
24
+
25
+ id: 'RenderSpine',
26
+
27
+ properties: {
28
+ /**
29
+ * An object containing key-value pairs that define a mapping from entity states to the animation that should play. The list is processed from top to bottom, so the most important actions should be listed first (for example, a jumping animation might take precedence over an idle animation). If not specified, an 1-to-1 animation map is created from the list of animations in the sprite sheet definition using the animation names as the keys.
30
+ *
31
+ * "animationStates":{
32
+ * "standing": "default-animation" // On receiving a "standing" event, or when this.owner.state.standing === true, the "default" animation will begin playing.
33
+ * "ground,moving": "walking", // Comma separated values have a special meaning when evaluating "state-changed" messages. The above example will cause the "walking" animation to play ONLY if the entity's state includes both "moving" and "ground" equal to true.
34
+ * "ground,striking": "swing!", // Putting an exclamation after an animation name causes this animation to complete before going to the next animation. This is useful for animations that would look poorly if interrupted.
35
+ * "default": "default-animation" // Optional. "default" is a special property that matches all states. If none of the above states are valid for the entity, it will use the default animation listed here.
36
+ * }
37
+ *
38
+ * If `stateBased` is `true` and this property is not set, this component will use the `animationMap` property value to define state mappings.
39
+ *
40
+ * @property animationStates
41
+ * @type Object
42
+ * @default animationMap
43
+ */
44
+ animationStates: null,
45
+
46
+ /**
47
+ * An object containg key-value pairs that define a mapping from triggered events to the animation that should play.
48
+ *
49
+ * "animationEvents":{
50
+ * "move": "walk-animation",
51
+ * "jump": "jumping-animation"
52
+ * }
53
+ *
54
+ * The above will create two event listeners on the entity, "move" and "jump", that will play their corresponding animations when the events are triggered.
55
+ *
56
+ * If `eventBased` is `true` and this property is not set, this component will use the `animationMap` property value to define event mappings.
57
+ *
58
+ * @property animationEvents
59
+ * @type Object
60
+ * @default animationMap
61
+ */
62
+ animationEvents: null,
63
+
64
+ /**
65
+ * Optional. An object containing key-value pairs that define a mapping from triggered events or entity states to the animation that should play. The list is processed from top to bottom, so the most important actions should be listed first (for example, a jumping animation might take precedence over an idle animation). If not specified, an 1-to-1 animation map is created from the list of animations in the skeleton definition using the animation names as the keys.
66
+ *
67
+ * "animationMap":{
68
+ * "standing": "default-animation" // On receiving a "standing" event, or when this.owner.state.standing === true, the "default" animation will begin playing.
69
+ * "ground,moving": "walking", // Comma separated values have a special meaning when evaluating "state-changed" messages. The above example will cause the "walking" animation to play ONLY if the entity's state includes both "moving" and "ground" equal to true.
70
+ * "ground,striking": "swing!", // Putting an exclamation after an animation name causes this animation to complete before going to the next animation. This is useful for animations that would look poorly if interrupted.
71
+ * "default": "default-animation" // Optional. "default" is a special property that matches all states. If none of the above states are valid for the entity, it will use the default animation listed here.
72
+ * }
73
+ *
74
+ * @property animationMap
75
+ * @type Object
76
+ * @default null
77
+ */
78
+ animationMap: null,
79
+
80
+ /**
81
+ * Optional. No, this isn't a Stephen R. Lawhead novel. Use this to specify a skin according to the entity's state.
82
+ *
83
+ * "skinMap":{
84
+ * "cloaked": "cloak" // On receiving a "cloaked" event, or when `this.owner.state.get('cloaked') === true`, this skin will be activated.
85
+ * "default": "normal_attire" // Optional. "default" is a special property that matches all states. If none of the above states are valid for the entity, it will use the default skin listed here.
86
+ * }
87
+ *
88
+ * @property skinMap
89
+ * @type Object
90
+ * @default null
91
+ */
92
+ skinMap: null,
93
+
94
+ /**
95
+ * The scaling factor for this sprite relative to the scale of the container.
96
+ *
97
+ * @property localScaleX
98
+ * @type Number|Array|Object
99
+ * @default 1
100
+ */
101
+ localScaleX: 1,
102
+
103
+ /**
104
+ * The scaling factor for this sprite relative to the scale of the container.
105
+ *
106
+ * @property localScaleY
107
+ * @type Number|Array|Object
108
+ * @default 1
109
+ */
110
+ localScaleY: 1,
111
+
112
+ /**
113
+ * Optional. A mask definition that determines where the image should clip. A string can also be used to create more complex shapes via the PIXI graphics API like: "mask": "r(10,20,40,40).circle(30,10,12)". Defaults to no mask or, if simply set to true, a rectangle using the entity's dimensions.
114
+ *
115
+ * "mask": {
116
+ * "x": 10,
117
+ * "y": 10,
118
+ * "width": 40,
119
+ * "height": 40
120
+ * },
121
+ *
122
+ * -OR-
123
+ *
124
+ * "mask": "r(10,20,40,40).circle(30,10,12)"
125
+ *
126
+ * @property mask
127
+ * @type Object
128
+ * @default null
129
+ */
130
+ mask: null,
131
+
132
+ /**
133
+ * Defines whether the entity will respond to touch and click events. Setting this value will create an Interactive component on this entity with these properties. For example:
134
+ *
135
+ * "interactive": {
136
+ * "hover": false,
137
+ * "hitArea": {
138
+ * "x": 10,
139
+ * "y": 10,
140
+ * "width": 40,
141
+ * "height": 40
142
+ * }
143
+ * }
144
+ *
145
+ * @property interactive
146
+ * @type Boolean|Object
147
+ * @default false
148
+ */
149
+ interactive: false,
150
+
151
+ /**
152
+ * Sets the transition time between animations. If a number is defined, the transition time applies to all animation changes. If an object is specified, the key value pairs should match this syntax where the first part of the key lists the animation currently playing and the second part of the key lists the animation being transitioned to:
153
+ *
154
+ * {
155
+ * "jump:walk": 0.4,
156
+ * "walk:jump": 0.2
157
+ * }
158
+ *
159
+ * @property mixTimes
160
+ * @type Number|Object
161
+ * @default 0
162
+ */
163
+ mixTimes: 0,
164
+
165
+ /**
166
+ * The offset of the x-axis position of the sprite from the entity's x-axis position.
167
+ *
168
+ * @property offsetX
169
+ * @type Number
170
+ * @default 0
171
+ */
172
+ offsetX: 0,
173
+
174
+ /**
175
+ * The offset of the y-axis position of the sprite from the entity's y-axis position.
176
+ *
177
+ * @property offsetY
178
+ * @type Number
179
+ * @default 0
180
+ */
181
+ offsetY: 0,
182
+
183
+ /**
184
+ * The z-index relative to other render components on the entity.
185
+ *
186
+ * @property offsetZ
187
+ * @type Number
188
+ * @default 0
189
+ */
190
+ offsetZ: 0,
191
+
192
+ /**
193
+ * Text describing an atlas of graphic assets for the Spine animation or an asset id for the same.
194
+ *
195
+ * @property atlas
196
+ * @type String
197
+ * @default ""
198
+ */
199
+ atlas: "",
200
+
201
+ /**
202
+ * A JSON structure defining a Spine skeleton and behaviors for the animation, or an asset id for the same.
203
+ *
204
+ * @property skeleton
205
+ * @type String|Object
206
+ * @default null
207
+ */
208
+ skeleton: null,
209
+
210
+ /**
211
+ * Whether this object can be mirrored over X. To mirror it over X set the this.owner.rotation value to be > 90 and < 270.
212
+ *
213
+ * @property mirror
214
+ * @type Boolean
215
+ * @default false
216
+ */
217
+ mirror: false,
218
+
219
+ /**
220
+ * Optional. Whether this object can be flipped over Y. To flip it over Y set the this.owner.rotation to be > 180.
221
+ *
222
+ * @property flip
223
+ * @type Boolean
224
+ * @default false
225
+ */
226
+ flip: false,
227
+
228
+ /**
229
+ * Optional. Whether this object is visible or not. To change the visible value dynamically set this.owner.state.visible to true or false.
230
+ *
231
+ * @property visible
232
+ * @type Boolean
233
+ * @default false
234
+ */
235
+ visible: true,
236
+
237
+ /**
238
+ * Optional. Specifies whether this component should create a RenderAnimator component to listen to events matching the animationMap to animate. Set this to true if the component should animate for on events. Default is `false`.
239
+ *
240
+ * @property eventBased
241
+ * @type Boolean
242
+ * @default false
243
+ */
244
+ eventBased: false,
245
+
246
+ /**
247
+ * Optional. Specifies whether the spine image alpha has been premultiplied. Set this to `false` if you see bright borders around image parts. Make sure it's `true` if you see thin black lines around image pieces.
248
+ *
249
+ * @property preMultipliedAlpha
250
+ * @type Boolean
251
+ * @default true
252
+ */
253
+ preMultipliedAlpha: true,
254
+
255
+ /**
256
+ * Optional. Specifies whether this component should create a RenderAnimator component to handle changes in the entity's state that match the animationMap to animate. Set this to true if the component should animate based on `this.owner.state`. Default is `true`.
257
+ *
258
+ * @property stateBased
259
+ * @type Boolean
260
+ * @default true
261
+ */
262
+ stateBased: true
263
+ },
264
+
265
+ publicProperties: {
266
+ /**
267
+ * Prevents the spine from becoming invisible out of frame and losing mouse input connection.
268
+ *
269
+ * @property dragMode
270
+ * @type Boolean
271
+ * @default false
272
+ */
273
+ dragMode: false,
274
+
275
+ /**
276
+ * Optional. The X scaling factor for the image. Defaults to 1.
277
+ *
278
+ * @property scaleX
279
+ * @type Number
280
+ * @default 1
281
+ */
282
+ scaleX: 1,
283
+
284
+ /**
285
+ * Optional. The Y scaling factor for the image. Defaults to 1.
286
+ *
287
+ * @property scaleY
288
+ * @type Number
289
+ * @default 1
290
+ */
291
+ scaleY: 1,
292
+
293
+ /**
294
+ * Optional. The X swek factor of the sprite. Defaults to 0.
295
+ *
296
+ * @property skewX
297
+ * @type Number
298
+ * @default 0
299
+ */
300
+ skewX: 0,
301
+
302
+ /**
303
+ * Optional. The Y skew factor for the image. Defaults to 0.
304
+ *
305
+ * @property skewY
306
+ * @type Number
307
+ * @default 0
308
+ */
309
+ skewY: 0,
310
+
311
+ /**
312
+ * Optional. The x position of the entity. Defaults to 0.
313
+ *
314
+ * @property x
315
+ * @type Number
316
+ * @default 0
317
+ */
318
+ x: 0,
319
+
320
+ /**
321
+ * Optional. The y position of the entity. Defaults to 0.
322
+ *
323
+ * @property y
324
+ * @type Number
325
+ * @default 0
326
+ */
327
+ y: 0,
328
+
329
+ /**
330
+ * Optional. The z position of the entity. Defaults to 0.
331
+ *
332
+ * @property z
333
+ * @type Number
334
+ * @default 0
335
+ */
336
+ z: 0
337
+ },
338
+
339
+ /**
340
+ * This component is attached to entities that will appear in the game world. It renders a spine-based puppet. It listens for messages triggered on the entity or changes in the logical state of the entity to play a corresponding animation.
341
+ *
342
+ * @memberof platypus.components
343
+ * @uses platypus.Component
344
+ * @constructs
345
+ * @listens platypus.Entity#handle-render
346
+ * @listens platypus.Entity#hide
347
+ * @listens platypus.Entity#play-animation
348
+ * @listens platypus.Entity#set-mix-times
349
+ * @listens platypus.Entity#show
350
+ * @listens platypus.Entity#state-changed
351
+ * @listens platypus.Entity#stop-animation
352
+ * @fires platypus.Entity#animation-ended
353
+ * @fires platypus.Entity#update-animation
354
+ */
355
+ initialize: (function () {
356
+ const
357
+ createAnimationMap = function (animationMap, animations) {
358
+ if (animationMap) {
359
+ return animationMap;
360
+ } else {
361
+ // Create 1-to-1 animation map since none was defined
362
+ return Object.keys(animations).reduce((map, value) => {
363
+ map[value] = value;
364
+ return map;
365
+ }, {});
366
+ }
367
+ };
368
+
369
+ return function () {
370
+ // If PIXI.spine is unavailable, this component doesn't work.
371
+ if (!Spine) {
372
+ platypus.debug.error('RenderSpine requires `PIXI.spine` to function.');
373
+ } else {
374
+ const
375
+ skeleton = platypus.assetCache.get(this.skeleton),
376
+ spineAtlas = platypus.assetCache.get(this.atlas),
377
+ spineJsonParser = new SkeletonJson(new AtlasAttachmentLoader(spineAtlas)),
378
+ skeletonData = spineJsonParser.readSkeletonData(skeleton),
379
+ spine = this.spine = new Spine({skeletonData, autoUpdate: false}),
380
+ map = createAnimationMap(this.animationMap, skeleton.animations);
381
+
382
+ // Spine 4.3 inherits Pixi container world-transform deltas into skeleton physics by default.
383
+ // Platypus pans the camera by moving the world container, which shifts every entity's world
384
+ // transform even when owner.x/y are unchanged — causing unwanted physics on stationary characters.
385
+ // Use the `apply-spine-physics` event for intentional movement-driven physics instead.
386
+ spine.setPhysicsPositionInheritanceFactor(0, 0);
387
+ spine.physicsRotationInheritanceFactor = 0;
388
+ spine.resetPhysicsTransform();
389
+
390
+ let animation = '';
391
+
392
+ spine.state.addListener({
393
+ event: (entry, event) => {
394
+ const
395
+ eventName = event.data.name;
396
+
397
+ if (this.playAnimation(eventName)) {
398
+ this.spine.update(0.000001);
399
+ }
400
+
401
+ this.owner.trigger(eventName, event.data);
402
+ },
403
+ complete: (animationData) => {
404
+ const
405
+ animationName = animationData.animation.name;
406
+
407
+ if (this.playSequence) {
408
+ const
409
+ last = this.playIndex === this.playSequence.length - 1;
410
+
411
+ this.playIndex += 1;
412
+ if (this.playIndex < this.playSequence.length || this.loopSequence) {
413
+ this.playIndex = this.playIndex % this.playSequence.length;
414
+ this.innerPlayAnimation(this.playSequence[this.playIndex], false);
415
+ if (last) {
416
+ this.owner.triggerEvent('animation-looped');
417
+ }
418
+ return;
419
+ }
420
+ }
421
+
422
+ this.owner.triggerEvent('animation-ended', animationName);
423
+ }
424
+ });
425
+
426
+ this.stateBased = map && this.stateBased;
427
+ this.eventBased = map && this.eventBased;
428
+ if (map) {
429
+ const
430
+ definition = Data.setUp(
431
+ 'animationEvents', this.eventBased ? this.animationEvents || map : null,
432
+ 'animationStates', this.stateBased ? this.animationStates || map : null,
433
+ 'forcePlayThrough', this.forcePlayThrough,
434
+ 'component', this
435
+ );
436
+
437
+ this.owner.addComponent(new RenderAnimator(this.owner, definition));
438
+ definition.recycle();
439
+
440
+ animation = map.default || '';
441
+ }
442
+
443
+ // set up the mixes!
444
+ if (this.mixTimes) {
445
+ this.setMixTimes(this.mixTimes);
446
+ }
447
+
448
+ // play animation
449
+ this.currentAnimations = arrayCache.setUp();
450
+ if (animation) {
451
+ this.playAnimation(animation);
452
+ }
453
+
454
+ this.playSequence = null;
455
+ this.playIndex = 0;
456
+
457
+ spine.x = this.offsetX;
458
+ spine.y = this.offsetY;
459
+ spine.zIndex = this.offsetZ;
460
+ spine.scale.x = this.localScaleX;
461
+ spine.scale.y = this.localScaleY;
462
+
463
+ if (this.skinMap) { // Set up skin map handling.
464
+ const map = this.skinMap;
465
+
466
+ this.currentSkin = null;
467
+
468
+ //Handle Events:
469
+ if (this.eventBased) {
470
+ const
471
+ keys = Object.keys(map),
472
+ {length} = keys;
473
+
474
+ for (let i = 0; i < length; i++) {
475
+ const
476
+ key = keys[i];
477
+
478
+ this.addEventListener(key, () => this.switchSkin(map[key]));
479
+ }
480
+ }
481
+
482
+ //Handle States:
483
+ if (this.stateBased) {
484
+ const
485
+ keys = Object.keys(map),
486
+ {length} = keys;
487
+
488
+ this.state = this.owner.state;
489
+ this.stateChange = true; //Check state against entity's prior state to update skin if necessary on instantiation.
490
+ this.checkStates = arrayCache.setUp();
491
+ this.skins = arrayCache.setUp();
492
+
493
+ for (let i = 0; i < length; i++) {
494
+ const
495
+ key = keys[i];
496
+
497
+ this.checkStates.push(createTest(key, map[key]));
498
+ this.skins.push(key);
499
+ }
500
+
501
+ this.addEventListener('state-changed', () => {
502
+ this.stateChange = true;
503
+ });
504
+ this.addEventListener('handle-render', () => {
505
+ if (this.stateChange) {
506
+ for (let i = 0; i < this.checkStates.length; i++) {
507
+ const testCase = this.checkStates[i](this.state);
508
+
509
+ if (testCase !== null) {
510
+ this.switchSkin(testCase);
511
+ break;
512
+ }
513
+ }
514
+ this.stateChange = false;
515
+ }
516
+ });
517
+
518
+ // set skin for initial state.
519
+ for (let i = 0; i < this.checkStates.length; i++) {
520
+ const testCase = this.checkStates[i](this.state);
521
+
522
+ if (testCase !== null) {
523
+ this.switchSkin(testCase);
524
+ break;
525
+ }
526
+ }
527
+ }
528
+ }
529
+
530
+ if (!this.owner.container) {
531
+ const
532
+ definition = Data.setUp(
533
+ 'interactive', this.interactive,
534
+ 'mask', this.mask,
535
+ 'mirror', this.mirror,
536
+ 'flip', this.flip,
537
+ 'visible', this.visible,
538
+ 'cache', this.cache,
539
+ 'ignoreOpacity', this.ignoreOpacity,
540
+ 'scaleX', this.scaleX,
541
+ 'scaleY', this.scaleY,
542
+ 'skewX', this.skewX,
543
+ 'skewY', this.skewY
544
+ );
545
+ this.owner.addComponent(new RenderContainer(this.owner, definition, () => this.addToContainer()));
546
+ definition.recycle();
547
+ } else {
548
+ this.addToContainer();
549
+ }
550
+ }
551
+ };
552
+ }()),
553
+
554
+ events: {
555
+ "handle-render": function (renderData) {
556
+ if (this.spine && !this.paused) {
557
+ this.owner.triggerEvent('update-animation', true);
558
+
559
+ this.spine.update(renderData.delta / 1000);
560
+ }
561
+ },
562
+
563
+ /**
564
+ * This sets the mix times.
565
+ *
566
+ * @event platypus.Entity#set-mix-times
567
+ * @param mixTimes {Object} This matches the syntax required for this component's `mixTimes` property
568
+ */
569
+ "set-mix-times": function (mixTimes) {
570
+ this.setMixTimes(mixTimes);
571
+ },
572
+
573
+ /**
574
+ * This event makes the spine invisible.
575
+ *
576
+ * @event platypus.Entity#hide
577
+ */
578
+ "hide": function () {
579
+ this.visible = false;
580
+ },
581
+
582
+ /**
583
+ * This event makes the spine visible.
584
+ *
585
+ * @event platypus.Entity#show
586
+ */
587
+ "show": function () {
588
+ this.visible = true;
589
+ },
590
+
591
+ "stop-animation": function (animation) {
592
+ this.stopAnimation(animation);
593
+ },
594
+
595
+ "play-animation": function (animation, loop) {
596
+ this.playAnimation(animation, loop);
597
+ },
598
+
599
+ "pause-animation": function () {
600
+ this.paused = true;
601
+ },
602
+
603
+ "unpause-animation": function () {
604
+ this.paused = false;
605
+ },
606
+
607
+ /**
608
+ * Change the skin of the character.
609
+ *
610
+ * @event platypus.Entity#switch-skin
611
+ * @param skin {string|Array.<string>} The skin or set of skins you would like to apply.
612
+ */
613
+ "switch-skin": function (skin) {
614
+ this.switchSkin(skin);
615
+ },
616
+
617
+ /**
618
+ * Tint a slot(s).
619
+ *
620
+ * @event platypus.Entity#tint-slots
621
+ * @param slots {Array.<string>} The slot[s] you would like to tint.
622
+ * @param color {String} The string of the hexidecimal color you would like to apply (e.g. "FF0000" for red)
623
+ */
624
+ "tint-slots": function (slots, color) {
625
+ let slot = null,
626
+ x = 0;
627
+
628
+ for (x = 0; x < slots.length; x++) {
629
+ slot = this.spine.skeleton.findSlot(slots[x]);
630
+ if (slot) {
631
+ slot.pose.color.setFromString(color);
632
+ }
633
+ }
634
+ },
635
+
636
+ /**
637
+ * Tint an attachment.
638
+ *
639
+ * @event platypus.Entity#tint-attachment
640
+ * @param slot {String} The slot the attachment belongs to
641
+ * @param attachment {String} The attachment to color
642
+ * @param color {String} The string of the hexidecimal color you would like to apply (e.g. "FF0000" for red)
643
+ */
644
+ "tint-attachment": function (slotName, attachmentName, color) {
645
+ let slot = null,
646
+ attachment = null;
647
+
648
+ slot = this.spine.skeleton.findSlot(slotName);
649
+ if (slot) {
650
+ attachment = this.spine.skeleton.getAttachment(slotName, attachmentName);
651
+ if (attachment) {
652
+ attachment.color.setFromString(color);
653
+ } else {
654
+ console.warn("Cannot tint attachment, doesn't exist");
655
+ }
656
+ } else {
657
+ console.warn("Cannot tint attachment, slot doesn't exist");
658
+ }
659
+ },
660
+
661
+ /**
662
+ * Apply physics to the entire skeleton.
663
+ *
664
+ * Accepts an object with translate and rotate objects.
665
+ * {
666
+ * translate: {x: 0, y: 0},
667
+ * rotate: {x: 0, y: 0, degrees: 0}
668
+ * }
669
+ *
670
+ * Has known vibration issues at low frame rates. Spine 4.3 improves physics constraint stability.
671
+ * Potential fixes: https://esotericsoftware.com/forum/d/28093-jittering-when-applying-physicstranslate-pixispine/5
672
+ *
673
+ * @event platypus.Entity#apply-spine-physics
674
+ * @param object An object describing how the entity moved to generate the physics response.
675
+ */
676
+ "apply-spine-physics": function (changes) {
677
+ if (changes.translate) {
678
+ this.spine.skeleton.physicsTranslate(changes.translate.x, changes.translate.y);
679
+ }
680
+
681
+ if (changes.rotate) {
682
+ this.spine.skeleton.physicsRotate(changes.rotate.x, changes.rotate.y, changes.rotate.degrees);
683
+ }
684
+ }
685
+ },
686
+
687
+ methods: {
688
+ switchSkin: function (newSkin) {
689
+ const skeletonData = this.spine.skeleton.data;
690
+ const skin = new Skin("custom");
691
+ let x = 0,
692
+ foundSkin = null;
693
+
694
+ if (typeof newSkin === 'string') {
695
+ newSkin = [newSkin];
696
+ }
697
+
698
+ for (x = 0; x < newSkin.length; x++) {
699
+ foundSkin = skeletonData.findSkin(newSkin[x]);
700
+ if (foundSkin) {
701
+ skin.addSkin(foundSkin);
702
+ } else {
703
+ console.warn('Cannot find skin: ' + newSkin[x]);
704
+ }
705
+ }
706
+
707
+ this.spine.skeleton.setSkin(skin);
708
+ this.spine.skeleton.setupPoseSlots();
709
+ },
710
+ addToContainer: function () {
711
+ this.owner.container.cullable = false;
712
+ this.owner.container.addChild(this.spine);
713
+ },
714
+
715
+ playAnimation: function (animation, loop = true) {
716
+ if (Array.isArray(animation)) {
717
+ if (animation !== this.playSequence) {
718
+ this.playSequence = animation;
719
+ this.playIndex = 0;
720
+ this.loopSequence = loop;
721
+ return this.innerPlayAnimation(animation[0], false);
722
+ } else {
723
+ return 0; //not sure if this is used
724
+ }
725
+ } else {
726
+ this.playSequence = null;
727
+ this.playIndex = 0;
728
+ return this.innerPlayAnimation(animation, loop);
729
+ }
730
+ },
731
+
732
+ innerPlayAnimation: function (animation, loop) {
733
+ const
734
+ {currentAnimations, spine} = this,
735
+ {state, skeleton} = spine,
736
+ {data} = skeleton;
737
+ let animated = 0,
738
+ remaining = animation;
739
+
740
+ while (remaining) {
741
+ const
742
+ semicolon = remaining.indexOf(';'),
743
+ next = (semicolon >= 0) ? remaining.substring(0, semicolon) : remaining;
744
+
745
+ remaining = (semicolon >= 0) ? remaining.substring(semicolon + 1) : '';
746
+
747
+ if (next === '_') {
748
+ currentAnimations[animated] = next;
749
+ state.setEmptyAnimation(animated);
750
+ } else if (next && data.findAnimation(next)) {
751
+ currentAnimations[animated] = next;
752
+ state.setAnimation(animated, next, loop);
753
+ }
754
+ animated += 1;
755
+ }
756
+
757
+ return animated;
758
+ },
759
+
760
+ stopAnimation: function (animation) {
761
+ const
762
+ {currentAnimations, spine} = this,
763
+ {state, skeleton} = spine,
764
+ {data} = skeleton;
765
+ let animated = 0,
766
+ remaining = animation;
767
+
768
+ while (remaining) {
769
+ const
770
+ semicolon = remaining.indexOf(';'),
771
+ next = (semicolon >= 0) ? remaining.substring(0, semicolon) : remaining;
772
+
773
+ remaining = (semicolon >= 0) ? remaining.substring(semicolon + 1) : '';
774
+
775
+ if (next === '_') {
776
+ currentAnimations[animated] = next;
777
+ state.setEmptyAnimation(animated);
778
+ } else if (next && data.findAnimation(next)) {
779
+ currentAnimations[animated] = next;
780
+ state.setAnimation(animated, next, false);
781
+ }
782
+ animated += 1;
783
+ }
784
+
785
+ return animated;
786
+ },
787
+
788
+ setMixTimes: function (mixTimes) {
789
+ const
790
+ {spine} = this,
791
+ {animations} = spine.skeleton.data,
792
+ {data: stateData} = spine.state;
793
+
794
+ if (typeof mixTimes === 'number') {
795
+ let i = animations.length;
796
+ while (i--) {
797
+ let j = animations.length;
798
+ while (j--) {
799
+ if (i !== j) {
800
+ stateData.setMix(animations[i].name, animations[j].name, mixTimes);
801
+ }
802
+ }
803
+ }
804
+ } else {
805
+ const
806
+ keys = Object.keys(mixTimes),
807
+ {length} = keys;
808
+
809
+ for (let i = 0; i < length; i++) {
810
+ const
811
+ key = keys[i],
812
+ colon = key.indexOf(':');
813
+
814
+ if (colon >= 0) {
815
+ stateData.setMix(key.substring(0, colon), key.substring(colon + 1), mixTimes[key]);
816
+ }
817
+ }
818
+ }
819
+
820
+ this.mixTimes = stateData.animationToMixTime;
821
+ },
822
+
823
+ destroy: function () {
824
+ this.owner.container.removeChild(this.spine);
825
+ this.spine.destroy();
826
+ this.spine = null;
827
+ this.mixTimes = null;
828
+ }
829
+ },
830
+
831
+ getAssetList (component, props, defaultProps) {
832
+ return [{
833
+ alias: [component?.atlas ?? props?.atlas ?? defaultProps?.atlas],
834
+ src: component?.atlas ?? props?.atlas ?? defaultProps?.atlas
835
+ }, {
836
+ alias: [component?.skeleton ?? props?.skeleton ?? defaultProps?.skeleton],
837
+ src: component?.skeleton ?? props?.skeleton ?? defaultProps?.skeleton
838
+ }];
839
+ }
840
+ });