@makefully/platypus 4.1.4 → 4.1.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (134) hide show
  1. package/CHANGELOG.md +20 -0
  2. package/README.md +4 -2
  3. package/lib/platypus.js +1 -1
  4. package/package.json +7 -2
  5. package/src/AABB.js +685 -0
  6. package/src/ActionState.js +121 -0
  7. package/src/AssetManager.js +206 -0
  8. package/src/CollisionData.js +96 -0
  9. package/src/CollisionDataContainer.js +127 -0
  10. package/src/CollisionShape.js +315 -0
  11. package/src/Component.js +231 -0
  12. package/src/Data.js +64 -0
  13. package/src/DataMap.js +204 -0
  14. package/src/Entity.js +404 -0
  15. package/src/EntityLinker.js +113 -0
  16. package/src/EventHandler.js +115 -0
  17. package/src/EventHandlerList.js +251 -0
  18. package/src/Game.js +840 -0
  19. package/src/Gamepad.js +175 -0
  20. package/src/ID3CaptionPlayer.js +61 -0
  21. package/src/Messenger.js +365 -0
  22. package/src/PIXIAnimation.js +622 -0
  23. package/src/RandomSet.js +140 -0
  24. package/src/SFXPlayer.js +120 -0
  25. package/src/StateMap.js +252 -0
  26. package/src/Storage.js +152 -0
  27. package/src/TickerClient.js +48 -0
  28. package/src/TickerServer.js +23 -0
  29. package/src/TimeEvent.js +50 -0
  30. package/src/TimeEventList.js +100 -0
  31. package/src/VOPlayer.js +684 -0
  32. package/src/Vector.js +702 -0
  33. package/src/components/AIChaser.js +161 -0
  34. package/src/components/AIPacer.js +107 -0
  35. package/src/components/AudioMusic.js +151 -0
  36. package/src/components/AudioSFX.js +673 -0
  37. package/src/components/AudioVO.js +301 -0
  38. package/src/components/CollisionBasic.js +826 -0
  39. package/src/components/CollisionFilter.js +74 -0
  40. package/src/components/CollisionGroup.js +448 -0
  41. package/src/components/CollisionTiles.js +608 -0
  42. package/src/components/ComponentSwitcher.js +173 -0
  43. package/src/components/ControllerInput.js +65 -0
  44. package/src/components/Counter.js +165 -0
  45. package/src/components/EntityCamera.js +215 -0
  46. package/src/components/EntityContainer.js +549 -0
  47. package/src/components/EntityController.js +541 -0
  48. package/src/components/HandlerCamera.js +1052 -0
  49. package/src/components/HandlerCollision.js +1318 -0
  50. package/src/components/HandlerController.js +291 -0
  51. package/src/components/HandlerLogic.js +394 -0
  52. package/src/components/HandlerRender.js +217 -0
  53. package/src/components/Interactive.js +502 -0
  54. package/src/components/LevelBuilder.js +524 -0
  55. package/src/components/LogicAngularMovement.js +154 -0
  56. package/src/components/LogicAttachment.js +265 -0
  57. package/src/components/LogicButton.js +466 -0
  58. package/src/components/LogicCarrier.js +39 -0
  59. package/src/components/LogicDestroyMe.js +58 -0
  60. package/src/components/LogicDirectionalMovement.js +325 -0
  61. package/src/components/LogicDragDrop.js +371 -0
  62. package/src/components/LogicImpactLaunch.js +144 -0
  63. package/src/components/LogicPacingPlatform.js +64 -0
  64. package/src/components/LogicPortable.js +112 -0
  65. package/src/components/LogicPortal.js +115 -0
  66. package/src/components/LogicPushable.js +98 -0
  67. package/src/components/LogicRebounder.js +136 -0
  68. package/src/components/LogicRegionSpawner.js +139 -0
  69. package/src/components/LogicRotationalMovement.js +205 -0
  70. package/src/components/LogicSpawner.js +159 -0
  71. package/src/components/LogicStateMachine.js +382 -0
  72. package/src/components/LogicSwitch.js +77 -0
  73. package/src/components/LogicTeleportee.js +68 -0
  74. package/src/components/LogicTeleporter.js +93 -0
  75. package/src/components/LogicTimer.js +178 -0
  76. package/src/components/LogicWindUpRacer.js +179 -0
  77. package/src/components/Motion.js +371 -0
  78. package/src/components/Mover.js +566 -0
  79. package/src/components/Node.js +289 -0
  80. package/src/components/NodeMap.js +295 -0
  81. package/src/components/NodeResident.js +636 -0
  82. package/src/components/Orientation.js +697 -0
  83. package/src/components/RandomEvents.js +73 -0
  84. package/src/components/RelativePosition.js +204 -0
  85. package/src/components/RelayFamily.js +106 -0
  86. package/src/components/RelayGame.js +53 -0
  87. package/src/components/RelayLinker.js +94 -0
  88. package/src/components/RelayParent.js +57 -0
  89. package/src/components/RelaySelf.js +55 -0
  90. package/src/components/Render.js +178 -0
  91. package/src/components/RenderAnimator.js +275 -0
  92. package/src/components/RenderContainer.js +646 -0
  93. package/src/components/RenderDebug.js +319 -0
  94. package/src/components/RenderDestroyMe.js +59 -0
  95. package/src/components/RenderLottie.js +149 -0
  96. package/src/components/RenderProgress.js +120 -0
  97. package/src/components/RenderSpine.js +833 -0
  98. package/src/components/RenderSprite.js +527 -0
  99. package/src/components/RenderText.js +147 -0
  100. package/src/components/RenderTiles.js +1391 -0
  101. package/src/components/SceneChanger.js +128 -0
  102. package/src/components/TiledLoader.js +1451 -0
  103. package/src/components/Timeline.js +314 -0
  104. package/src/components/Tutorial.js +267 -0
  105. package/src/components/Tween.js +296 -0
  106. package/src/components/VoiceOver.js +373 -0
  107. package/src/components/XHR.js +129 -0
  108. package/src/components/index.js +79 -0
  109. package/src/factory.js +305 -0
  110. package/src/index.js +159 -0
  111. package/src/mixins/PIXI.Graphics.js +41 -0
  112. package/src/platypus.css +68 -0
  113. package/src/sayHello.js +50 -0
  114. package/src/shapes/Capsule.js +74 -0
  115. package/src/shapes/Circle.js +43 -0
  116. package/src/shapes/Ellipse.js +117 -0
  117. package/src/shapes/Point.js +191 -0
  118. package/src/shapes/Polygon.js +112 -0
  119. package/src/shapes/Polyline.js +97 -0
  120. package/src/shapes/Rectangle.js +78 -0
  121. package/src/shapes/RoundedRectangle.js +33 -0
  122. package/src/shapes/Segment.js +346 -0
  123. package/src/shapes/adjustedXY.js +19 -0
  124. package/src/shapes/castFromBox2D.js +92 -0
  125. package/src/shapes/castFromObject.js +53 -0
  126. package/src/shapes/castFromPixiGraphics.js +6 -0
  127. package/src/shapes/castFromPixiShape.js +89 -0
  128. package/src/shapes/castToBox2D.js +190 -0
  129. package/src/shapes/castToPixiGraphics.js +100 -0
  130. package/src/shapes/castToPixiShape.js +61 -0
  131. package/src/shapes/checkPointsMap.js +313 -0
  132. package/src/shapes/index.js +35 -0
  133. package/src/utils/array.js +73 -0
  134. package/src/utils/string.js +39 -0
@@ -0,0 +1,833 @@
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),
380
+ map = createAnimationMap(this.animationMap, skeleton.animations);
381
+
382
+ let animation = '';
383
+
384
+ spine.state.addListener({
385
+ event: (entry, event) => {
386
+ const
387
+ eventName = event.data.name;
388
+
389
+ if (this.playAnimation(eventName)) {
390
+ this.spine.update(0.000001);
391
+ }
392
+
393
+ this.owner.trigger(eventName, event.data);
394
+ },
395
+ complete: (animationData) => {
396
+ const
397
+ animationName = animationData.animation.name;
398
+
399
+ if (this.playSequence) {
400
+ const
401
+ last = this.playIndex === this.playSequence.length - 1;
402
+
403
+ this.playIndex += 1;
404
+ if (this.playIndex < this.playSequence.length || this.loopSequence) {
405
+ this.playIndex = this.playIndex % this.playSequence.length;
406
+ this.innerPlayAnimation(this.playSequence[this.playIndex], false);
407
+ if (last) {
408
+ this.owner.triggerEvent('animation-looped');
409
+ }
410
+ return;
411
+ }
412
+ }
413
+
414
+ this.owner.triggerEvent('animation-ended', animationName);
415
+ }
416
+ });
417
+ spine.autoUpdate = false;
418
+
419
+ this.stateBased = map && this.stateBased;
420
+ this.eventBased = map && this.eventBased;
421
+ if (map) {
422
+ const
423
+ definition = Data.setUp(
424
+ 'animationEvents', this.eventBased ? this.animationEvents || map : null,
425
+ 'animationStates', this.stateBased ? this.animationStates || map : null,
426
+ 'forcePlayThrough', this.forcePlayThrough,
427
+ 'component', this
428
+ );
429
+
430
+ this.owner.addComponent(new RenderAnimator(this.owner, definition));
431
+ definition.recycle();
432
+
433
+ animation = map.default || '';
434
+ }
435
+
436
+ // set up the mixes!
437
+ if (this.mixTimes) {
438
+ this.setMixTimes(this.mixTimes);
439
+ }
440
+
441
+ // play animation
442
+ this.currentAnimations = arrayCache.setUp();
443
+ if (animation) {
444
+ this.playAnimation(animation);
445
+ }
446
+
447
+ this.playSequence = null;
448
+ this.playIndex = 0;
449
+
450
+ spine.x = this.offsetX;
451
+ spine.y = this.offsetY;
452
+ spine.zIndex = this.offsetZ;
453
+ spine.scale.x = this.localScaleX;
454
+ spine.scale.y = this.localScaleY;
455
+
456
+ if (this.skinMap) { // Set up skin map handling.
457
+ const map = this.skinMap;
458
+
459
+ this.currentSkin = null;
460
+
461
+ //Handle Events:
462
+ if (this.eventBased) {
463
+ const
464
+ keys = Object.keys(map),
465
+ {length} = keys;
466
+
467
+ for (let i = 0; i < length; i++) {
468
+ const
469
+ key = keys[i];
470
+
471
+ this.addEventListener(key, () => this.switchSkin(map[key]));
472
+ }
473
+ }
474
+
475
+ //Handle States:
476
+ if (this.stateBased) {
477
+ const
478
+ keys = Object.keys(map),
479
+ {length} = keys;
480
+
481
+ this.state = this.owner.state;
482
+ this.stateChange = true; //Check state against entity's prior state to update skin if necessary on instantiation.
483
+ this.checkStates = arrayCache.setUp();
484
+ this.skins = arrayCache.setUp();
485
+
486
+ for (let i = 0; i < length; i++) {
487
+ const
488
+ key = keys[i];
489
+
490
+ this.checkStates.push(createTest(key, map[key]));
491
+ this.skins.push(key);
492
+ }
493
+
494
+ this.addEventListener('state-changed', () => {
495
+ this.stateChange = true;
496
+ });
497
+ this.addEventListener('handle-render', () => {
498
+ if (this.stateChange) {
499
+ for (let i = 0; i < this.checkStates.length; i++) {
500
+ const testCase = this.checkStates[i](this.state);
501
+
502
+ if (testCase !== null) {
503
+ this.switchSkin(testCase);
504
+ break;
505
+ }
506
+ }
507
+ this.stateChange = false;
508
+ }
509
+ });
510
+
511
+ // set skin for initial state.
512
+ for (let i = 0; i < this.checkStates.length; i++) {
513
+ const testCase = this.checkStates[i](this.state);
514
+
515
+ if (testCase !== null) {
516
+ this.switchSkin(testCase);
517
+ break;
518
+ }
519
+ }
520
+ }
521
+ }
522
+
523
+ if (!this.owner.container) {
524
+ const
525
+ definition = Data.setUp(
526
+ 'interactive', this.interactive,
527
+ 'mask', this.mask,
528
+ 'mirror', this.mirror,
529
+ 'flip', this.flip,
530
+ 'visible', this.visible,
531
+ 'cache', this.cache,
532
+ 'ignoreOpacity', this.ignoreOpacity,
533
+ 'scaleX', this.scaleX,
534
+ 'scaleY', this.scaleY,
535
+ 'skewX', this.skewX,
536
+ 'skewY', this.skewY
537
+ );
538
+ this.owner.addComponent(new RenderContainer(this.owner, definition, () => this.addToContainer()));
539
+ definition.recycle();
540
+ } else {
541
+ this.addToContainer();
542
+ }
543
+ }
544
+ };
545
+ }()),
546
+
547
+ events: {
548
+ "handle-render": function (renderData) {
549
+ if (this.spine && !this.paused) {
550
+ this.owner.triggerEvent('update-animation', true);
551
+
552
+ this.spine.update(renderData.delta / 1000);
553
+ }
554
+ },
555
+
556
+ /**
557
+ * This sets the mix times.
558
+ *
559
+ * @event platypus.Entity#set-mix-times
560
+ * @param mixTimes {Object} This matches the syntax required for this component's `mixTimes` property
561
+ */
562
+ "set-mix-times": function (mixTimes) {
563
+ this.setMixTimes(mixTimes);
564
+ },
565
+
566
+ /**
567
+ * This event makes the spine invisible.
568
+ *
569
+ * @event platypus.Entity#hide
570
+ */
571
+ "hide": function () {
572
+ this.visible = false;
573
+ },
574
+
575
+ /**
576
+ * This event makes the spine visible.
577
+ *
578
+ * @event platypus.Entity#show
579
+ */
580
+ "show": function () {
581
+ this.visible = true;
582
+ },
583
+
584
+ "stop-animation": function (animation) {
585
+ this.stopAnimation(animation);
586
+ },
587
+
588
+ "play-animation": function (animation, loop) {
589
+ this.playAnimation(animation, loop);
590
+ },
591
+
592
+ "pause-animation": function () {
593
+ this.paused = true;
594
+ },
595
+
596
+ "unpause-animation": function () {
597
+ this.paused = false;
598
+ },
599
+
600
+ /**
601
+ * Change the skin of the character.
602
+ *
603
+ * @event platypus.Entity#switch-skin
604
+ * @param skin {string|Array.<string>} The skin or set of skins you would like to apply.
605
+ */
606
+ "switch-skin": function (skin) {
607
+ this.switchSkin(skin);
608
+ },
609
+
610
+ /**
611
+ * Tint a slot(s).
612
+ *
613
+ * @event platypus.Entity#tint-slots
614
+ * @param slots {Array.<string>} The slot[s] you would like to tint.
615
+ * @param color {String} The string of the hexidecimal color you would like to apply (e.g. "FF0000" for red)
616
+ */
617
+ "tint-slots": function (slots, color) {
618
+ let slot = null,
619
+ x = 0;
620
+
621
+ for (x = 0; x < slots.length; x++) {
622
+ slot = this.spine.skeleton.findSlot(slots[x]);
623
+ if (slot) {
624
+ slot.color.setFromString(color);
625
+ }
626
+ }
627
+ },
628
+
629
+ /**
630
+ * Tint an attachment.
631
+ *
632
+ * @event platypus.Entity#tint-attachment
633
+ * @param slot {String} The slot the attachment belongs to
634
+ * @param attachment {String} The attachment to color
635
+ * @param color {String} The string of the hexidecimal color you would like to apply (e.g. "FF0000" for red)
636
+ */
637
+ "tint-attachment": function (slotName, attachmentName, color) {
638
+ let slot = null,
639
+ attachment = null;
640
+
641
+ slot = this.spine.skeleton.findSlot(slotName);
642
+ if (slot) {
643
+ attachment = this.spine.skeleton.getAttachment(slotName, attachmentName);
644
+ if (attachment) {
645
+ attachment.color.setFromString(color);
646
+ } else {
647
+ console.warn("Cannot tint attachment, doesn't exist");
648
+ }
649
+ } else {
650
+ console.warn("Cannot tint attachment, slot doesn't exist");
651
+ }
652
+ },
653
+
654
+ /**
655
+ * Apply physics to the entire skeleton.
656
+ *
657
+ * Accepts an object with translate and rotate objects.
658
+ * {
659
+ * translate: {x: 0, y: 0},
660
+ * rotate: {x: 0, y: 0, degrees: 0}
661
+ * }
662
+ *
663
+ * Has known vibration issues. Until pixi-spine 4.3, recommend 120 FPS on physics constraints on joints.
664
+ * Potential fixes: https://esotericsoftware.com/forum/d/28093-jittering-when-applying-physicstranslate-pixispine/5
665
+ *
666
+ * @event platypus.Entity#apply-spine-physics
667
+ * @param object An object describing how the entity moved to generate the physics response.
668
+ */
669
+ "apply-spine-physics": function (changes) {
670
+ if (changes.translate) {
671
+ this.spine.skeleton.physicsTranslate(changes.translate.x, changes.translate.y);
672
+ }
673
+
674
+ if (changes.rotate) {
675
+ this.spine.skeleton.physicsTranslate(changes.rotate.x, changes.rotate.y, changes.rotate.degrees);
676
+ }
677
+ }
678
+ },
679
+
680
+ methods: {
681
+ switchSkin: function (newSkin) {
682
+ const skeletonData = this.spine.skeleton.data;
683
+ const skin = new Skin("custom");
684
+ let x = 0,
685
+ foundSkin = null;
686
+
687
+ if (typeof newSkin === 'string') {
688
+ newSkin = [newSkin];
689
+ }
690
+
691
+ for (x = 0; x < newSkin.length; x++) {
692
+ foundSkin = skeletonData.findSkin(newSkin[x]);
693
+ if (foundSkin) {
694
+ skin.addSkin(foundSkin);
695
+ } else {
696
+ console.warn('Cannot find skin: ' + newSkin[x]);
697
+ }
698
+ }
699
+
700
+ this.spine.skeleton.setSkin(skin);
701
+ this.spine.skeleton.setSlotsToSetupPose();
702
+ },
703
+ addToContainer: function () {
704
+ this.owner.container.cullable = false;
705
+ this.owner.container.addChild(this.spine);
706
+ },
707
+
708
+ playAnimation: function (animation, loop = true) {
709
+ if (Array.isArray(animation)) {
710
+ if (animation !== this.playSequence) {
711
+ this.playSequence = animation;
712
+ this.playIndex = 0;
713
+ this.loopSequence = loop;
714
+ return this.innerPlayAnimation(animation[0], false);
715
+ } else {
716
+ return 0; //not sure if this is used
717
+ }
718
+ } else {
719
+ this.playSequence = null;
720
+ this.playIndex = 0;
721
+ return this.innerPlayAnimation(animation, loop);
722
+ }
723
+ },
724
+
725
+ innerPlayAnimation: function (animation, loop) {
726
+ const
727
+ {currentAnimations, spine} = this,
728
+ {state, skeleton} = spine,
729
+ {data} = skeleton;
730
+ let animated = 0,
731
+ remaining = animation;
732
+
733
+ while (remaining) {
734
+ const
735
+ semicolon = remaining.indexOf(';'),
736
+ next = (semicolon >= 0) ? remaining.substring(0, semicolon) : remaining;
737
+
738
+ remaining = (semicolon >= 0) ? remaining.substring(semicolon + 1) : '';
739
+
740
+ if (next === '_') {
741
+ currentAnimations[animated] = next;
742
+ state.setEmptyAnimation(animated);
743
+ } else if (next && data.findAnimation(next)) {
744
+ currentAnimations[animated] = next;
745
+ state.setAnimation(animated, next, loop);
746
+ }
747
+ animated += 1;
748
+ }
749
+
750
+ return animated;
751
+ },
752
+
753
+ stopAnimation: function (animation) {
754
+ const
755
+ {currentAnimations, spine} = this,
756
+ {state, skeleton} = spine,
757
+ {data} = skeleton;
758
+ let animated = 0,
759
+ remaining = animation;
760
+
761
+ while (remaining) {
762
+ const
763
+ semicolon = remaining.indexOf(';'),
764
+ next = (semicolon >= 0) ? remaining.substring(0, semicolon) : remaining;
765
+
766
+ remaining = (semicolon >= 0) ? remaining.substring(semicolon + 1) : '';
767
+
768
+ if (next === '_') {
769
+ currentAnimations[animated] = next;
770
+ state.setEmptyAnimation(animated);
771
+ } else if (next && data.findAnimation(next)) {
772
+ currentAnimations[animated] = next;
773
+ state.setAnimation(animated, next, false);
774
+ }
775
+ animated += 1;
776
+ }
777
+
778
+ return animated;
779
+ },
780
+
781
+ setMixTimes: function (mixTimes) {
782
+ const
783
+ {spine} = this,
784
+ {animations} = spine.skeleton.data,
785
+ {data: stateData} = spine.state;
786
+
787
+ if (typeof mixTimes === 'number') {
788
+ let i = animations.length;
789
+ while (i--) {
790
+ let j = animations.length;
791
+ while (j--) {
792
+ if (i !== j) {
793
+ stateData.setMix(animations[i].name, animations[j].name, mixTimes);
794
+ }
795
+ }
796
+ }
797
+ } else {
798
+ const
799
+ keys = Object.keys(mixTimes),
800
+ {length} = keys;
801
+
802
+ for (let i = 0; i < length; i++) {
803
+ const
804
+ key = keys[i],
805
+ colon = key.indexOf(':');
806
+
807
+ if (colon >= 0) {
808
+ stateData.setMix(key.substring(0, colon), key.substring(colon + 1), mixTimes[key]);
809
+ }
810
+ }
811
+ }
812
+
813
+ this.mixTimes = stateData.animationToMixTime;
814
+ },
815
+
816
+ destroy: function () {
817
+ this.owner.container.removeChild(this.spine);
818
+ this.spine.destroy();
819
+ this.spine = null;
820
+ this.mixTimes = null;
821
+ }
822
+ },
823
+
824
+ getAssetList (component, props, defaultProps) {
825
+ return [{
826
+ alias: [component?.atlas ?? props?.atlas ?? defaultProps?.atlas],
827
+ src: component?.atlas ?? props?.atlas ?? defaultProps?.atlas
828
+ }, {
829
+ alias: [component?.skeleton ?? props?.skeleton ?? defaultProps?.skeleton],
830
+ src: component?.skeleton ?? props?.skeleton ?? defaultProps?.skeleton
831
+ }];
832
+ }
833
+ });