@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.
- package/CHANGELOG.md +20 -0
- package/README.md +4 -2
- package/lib/platypus.js +1 -1
- package/package.json +7 -2
- 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 +833 -0
- package/src/components/RenderSprite.js +527 -0
- package/src/components/RenderText.js +147 -0
- package/src/components/RenderTiles.js +1391 -0
- package/src/components/SceneChanger.js +128 -0
- package/src/components/TiledLoader.js +1451 -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
|
@@ -0,0 +1,296 @@
|
|
|
1
|
+
/* global platypus */
|
|
2
|
+
import TweenJS from '@tweenjs/tween.js';
|
|
3
|
+
import createComponentClass from '../factory.js';
|
|
4
|
+
|
|
5
|
+
const
|
|
6
|
+
Easing = TweenJS.Easing,
|
|
7
|
+
Group = TweenJS.Group,
|
|
8
|
+
Interpolation = TweenJS.Interpolation,
|
|
9
|
+
Tween = TweenJS.Tween,
|
|
10
|
+
eases = (function () {
|
|
11
|
+
const
|
|
12
|
+
easing = {},
|
|
13
|
+
keys = Object.keys(Easing),
|
|
14
|
+
{length} = keys;
|
|
15
|
+
|
|
16
|
+
for (let i = 0; i < length; i++) {
|
|
17
|
+
const
|
|
18
|
+
key = keys[i],
|
|
19
|
+
types = Object.keys(Easing[key]),
|
|
20
|
+
{length: typesLength} = types;
|
|
21
|
+
|
|
22
|
+
for (let j = 0; j < typesLength; j++) {
|
|
23
|
+
const
|
|
24
|
+
type = types[j];
|
|
25
|
+
|
|
26
|
+
easing[key + '.' + type] = Easing[key][type];
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
return easing;
|
|
31
|
+
}());
|
|
32
|
+
|
|
33
|
+
export default createComponentClass(/** @lends platypus.components.Tween.prototype */{
|
|
34
|
+
id: 'Tween',
|
|
35
|
+
|
|
36
|
+
properties: {
|
|
37
|
+
/**
|
|
38
|
+
* Required. A key/value list of events and an array or object representing the tween they should trigger.
|
|
39
|
+
*
|
|
40
|
+
* {
|
|
41
|
+
* "begin-flying": { // When "begin-flying" is triggered on this entity, the following tween begins. Tween definitions adhere to a similar structure outlined by the [TweenJS documentation](https://github.com/tweenjs/tween.js/blob/master/docs/user_guide.md).
|
|
42
|
+
* "target": "entityId", // This defaults to the entity that this component is on, but can be the id of any entity in this layer.
|
|
43
|
+
* "to": { // Specifies the values to change and what they should tween to.
|
|
44
|
+
* "scaleY": 1,
|
|
45
|
+
* "y": [400, 450, 425]
|
|
46
|
+
* },
|
|
47
|
+
* "time": 1000, // Time in MS to make transition.
|
|
48
|
+
* "easing": "Quadratic.In", Easing function to use.
|
|
49
|
+
* "onUpdate": "flying", // Event to trigger while transition is running.
|
|
50
|
+
* "onStart": "wave", // Event to trigger when tween begins (after delay).
|
|
51
|
+
* "onStop": "whoa", // Event to trigger when tween is stopped (not completed normally).
|
|
52
|
+
* "onComplete": "done", // Event to trigger when tween is complete.
|
|
53
|
+
* "onRepeat": "going-again", // Event to trigger when tween is beginning again.
|
|
54
|
+
* "chain": "stop-flying", // Specifies a tween to use next.
|
|
55
|
+
* "repeat": 0, // Sets how many times to repeat this tween once it completes.
|
|
56
|
+
* "yoyo": false, // If this tween repeats, yoyo makes it transition back-and-forth.
|
|
57
|
+
* "delay": 500, // Time in MS to delay before starting transition.
|
|
58
|
+
* "repeatDelay": 0, // Time in MS that the transition should wait between repeats if it shouldn't be the `delay` value.
|
|
59
|
+
* "interpolation": "Linear" // Interpolation method to use for an array of values.
|
|
60
|
+
* },
|
|
61
|
+
*
|
|
62
|
+
* "stop-flying": [{ // May also chain tweens by specifying an array.
|
|
63
|
+
* "to": {"y": 100},
|
|
64
|
+
* "time": 250
|
|
65
|
+
* }, {
|
|
66
|
+
* "to": {"y": 0},
|
|
67
|
+
* "time": 250
|
|
68
|
+
* }]
|
|
69
|
+
* }
|
|
70
|
+
*
|
|
71
|
+
* @property events
|
|
72
|
+
* @type Object
|
|
73
|
+
* @default null
|
|
74
|
+
*/
|
|
75
|
+
events: null
|
|
76
|
+
},
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* This component takes a list of tween definitions and plays them as needed. This component requires TweenJS.
|
|
80
|
+
*
|
|
81
|
+
* @memberof platypus.components
|
|
82
|
+
* @uses platypus.Component
|
|
83
|
+
* @constructs
|
|
84
|
+
* @listens platypus.Entity#handle-logic
|
|
85
|
+
* @listens platypus.Entity#pause-tween
|
|
86
|
+
* @listens platypus.Entity#stop-tween
|
|
87
|
+
* @listens platypus.Entity#tween
|
|
88
|
+
* @listens platypus.Entity#unpause-tween
|
|
89
|
+
*/
|
|
90
|
+
initialize: function () {
|
|
91
|
+
const
|
|
92
|
+
events = this.events;
|
|
93
|
+
|
|
94
|
+
this.group = new Group();
|
|
95
|
+
this.time = 0;
|
|
96
|
+
this.paused = false;
|
|
97
|
+
|
|
98
|
+
if (events) {
|
|
99
|
+
Object.keys(events).forEach((event) => this.addEventListener(event, (augmentTweenData) => {
|
|
100
|
+
const
|
|
101
|
+
data = events[event],
|
|
102
|
+
isArr = Array.isArray(data);
|
|
103
|
+
|
|
104
|
+
if (typeof augmentTweenData === typeof data && Array.isArray(augmentTweenData) === isArr) {
|
|
105
|
+
if (isArr) {
|
|
106
|
+
this.runTween([...data, ...augmentTweenData]);
|
|
107
|
+
} else {
|
|
108
|
+
this.runTween({...data, ...augmentTweenData});
|
|
109
|
+
}
|
|
110
|
+
} else {
|
|
111
|
+
this.runTween(data);
|
|
112
|
+
}
|
|
113
|
+
}));
|
|
114
|
+
}
|
|
115
|
+
},
|
|
116
|
+
|
|
117
|
+
events: {
|
|
118
|
+
/**
|
|
119
|
+
* Trigger this event to play a tween using the same spec used for a tween on this component's `events` property.
|
|
120
|
+
*
|
|
121
|
+
* @event platypus.Entity#tween
|
|
122
|
+
* @param {Object|Array} tween
|
|
123
|
+
*/
|
|
124
|
+
'tween': function (tween) {
|
|
125
|
+
this.runTween(tween);
|
|
126
|
+
},
|
|
127
|
+
|
|
128
|
+
'handle-logic': function (tick) {
|
|
129
|
+
if (!this.paused) {
|
|
130
|
+
this.time += tick.delta;
|
|
131
|
+
this.group.update(this.time);
|
|
132
|
+
}
|
|
133
|
+
},
|
|
134
|
+
|
|
135
|
+
/**
|
|
136
|
+
* This event stops all running tweens on this component.
|
|
137
|
+
*
|
|
138
|
+
* @event platypus.Entity#stop-tween
|
|
139
|
+
*/
|
|
140
|
+
'stop-tween': function () {
|
|
141
|
+
this.group.removeAll();
|
|
142
|
+
},
|
|
143
|
+
|
|
144
|
+
/**
|
|
145
|
+
* This event pauses all running tweens on this component.
|
|
146
|
+
*
|
|
147
|
+
* @event platypus.Entity#pause-tween
|
|
148
|
+
*/
|
|
149
|
+
"pause-tween": function () {
|
|
150
|
+
this.paused = true;
|
|
151
|
+
},
|
|
152
|
+
|
|
153
|
+
/**
|
|
154
|
+
* This event unpauses all running tweens on this component.
|
|
155
|
+
*
|
|
156
|
+
* @event platypus.Entity#unpause-tween
|
|
157
|
+
*/
|
|
158
|
+
"unpause-tween": function () {
|
|
159
|
+
this.paused = false;
|
|
160
|
+
}
|
|
161
|
+
},
|
|
162
|
+
|
|
163
|
+
methods: {
|
|
164
|
+
createTween: function (tweenDefinition, chainable) {
|
|
165
|
+
const
|
|
166
|
+
owner = this.owner,
|
|
167
|
+
entity = tweenDefinition.target ? (typeof tweenDefinition.target === 'string' ? owner.get(tweenDefinition.target) ?? owner.parent.get(tweenDefinition.target) : tweenDefinition.target) : owner;
|
|
168
|
+
|
|
169
|
+
if (!entity) {
|
|
170
|
+
platypus.debug.warn('Component Tween: Could not find entity as specified by `target` - ' + tweenDefinition.target);
|
|
171
|
+
return null;
|
|
172
|
+
} else if (!tweenDefinition.to || !tweenDefinition.time) {
|
|
173
|
+
platypus.debug.warn('Component Tween: Both `time` and `to` must be specified to create tween.');
|
|
174
|
+
return null;
|
|
175
|
+
} else {
|
|
176
|
+
const
|
|
177
|
+
tween = new Tween(entity, this.group);
|
|
178
|
+
|
|
179
|
+
tween.to(tweenDefinition.to, tweenDefinition.time);
|
|
180
|
+
|
|
181
|
+
if (tweenDefinition.onUpdate) {
|
|
182
|
+
tween.onUpdate((typeof tweenDefinition.onUpdate !== 'function') ? (...args) => owner.trigger(tweenDefinition.onUpdate, ...args) : tweenDefinition.onUpdate);
|
|
183
|
+
}
|
|
184
|
+
if (tweenDefinition.onStart) {
|
|
185
|
+
tween.onStart((typeof tweenDefinition.onStart !== 'function') ? (...args) => owner.trigger(tweenDefinition.onStart, ...args) : tweenDefinition.onStart);
|
|
186
|
+
}
|
|
187
|
+
if (tweenDefinition.onStop) {
|
|
188
|
+
tween.onStop((typeof tweenDefinition.onStop !== 'function') ? (...args) => owner.trigger(tweenDefinition.onStop, ...args) : tweenDefinition.onStop);
|
|
189
|
+
}
|
|
190
|
+
if (tweenDefinition.onComplete) {
|
|
191
|
+
tween.onComplete(() => {
|
|
192
|
+
tween.stop();
|
|
193
|
+
if (tweenDefinition.onComplete) {
|
|
194
|
+
if (typeof tweenDefinition.onComplete !== 'function') {
|
|
195
|
+
owner.trigger(tweenDefinition.onComplete);
|
|
196
|
+
} else {
|
|
197
|
+
tweenDefinition.onComplete();
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
});
|
|
201
|
+
}
|
|
202
|
+
if (tweenDefinition.onRepeat) {
|
|
203
|
+
tween.onRepeat((typeof tweenDefinition.onRepeat !== 'function') ? (...args) => owner.trigger(tweenDefinition.onRepeat, ...args) : tweenDefinition.onRepeat);
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
if (tweenDefinition.chain) {
|
|
207
|
+
if (!chainable) {
|
|
208
|
+
platypus.debug.warn('Component Tween: ignoring `chain` on tween since it is part of an array of tweens.');
|
|
209
|
+
} else if (typeof tweenDefinition.chain === 'string') {
|
|
210
|
+
if (this.events[tweenDefinition.chain]) {
|
|
211
|
+
tween.chain(this.createTweens(this.events[tweenDefinition.chain]));
|
|
212
|
+
} else {
|
|
213
|
+
platypus.debug.warn(`Component Tween: ignoring chain on tween since there is no tween event trigger "${tweenDefinition.chain}".`);
|
|
214
|
+
}
|
|
215
|
+
} else {
|
|
216
|
+
tween.chain(tweenDefinition.chain);
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
if (tweenDefinition.repeat) {
|
|
221
|
+
tween.repeat(tweenDefinition.repeat);
|
|
222
|
+
}
|
|
223
|
+
if (tweenDefinition.yoyo) {
|
|
224
|
+
tween.yoyo(true);
|
|
225
|
+
}
|
|
226
|
+
if (tweenDefinition.delay) {
|
|
227
|
+
tween.delay(tweenDefinition.delay);
|
|
228
|
+
}
|
|
229
|
+
if (typeof tweenDefinition.repeatDelay !== 'undefined') {
|
|
230
|
+
tween.repeatDelay(tweenDefinition.repeatDelay);
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
if (tweenDefinition.interpolation) {
|
|
234
|
+
if (typeof tweenDefinition.interpolation === 'function') {
|
|
235
|
+
tween.interpolation(tweenDefinition.interpolation);
|
|
236
|
+
} else if (Interpolation[tweenDefinition.interpolation]) {
|
|
237
|
+
tween.interpolation(Interpolation[tweenDefinition.interpolation]);
|
|
238
|
+
} else {
|
|
239
|
+
platypus.debug.warn('Component Tween: "' + tweenDefinition.interpolation + '" is not a valid interpolation value; must be "Linear", "Bezier", or "CatmullRom".');
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
if (tweenDefinition.easing) {
|
|
244
|
+
if (typeof tweenDefinition.easing === 'function') {
|
|
245
|
+
tween.easing(tweenDefinition.easing);
|
|
246
|
+
} else if (eases[tweenDefinition.easing]) {
|
|
247
|
+
tween.easing(eases[tweenDefinition.easing]);
|
|
248
|
+
} else {
|
|
249
|
+
const
|
|
250
|
+
joinEnd = '", or "',
|
|
251
|
+
join = '", "';
|
|
252
|
+
|
|
253
|
+
platypus.debug.warn(`Component Tween: "${tweenDefinition.easing}" is not a valid easing value; must be ${Object.keys(eases).reduce((str, value, index) => `${index === 0 ? joinEnd : join}${value}${str}`, '".').substring(3)}`);
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
return tween;
|
|
258
|
+
}
|
|
259
|
+
},
|
|
260
|
+
|
|
261
|
+
createTweens: function (tween) {
|
|
262
|
+
if (Array.isArray(tween)) {
|
|
263
|
+
let i = tween.length,
|
|
264
|
+
lastTween = null;
|
|
265
|
+
|
|
266
|
+
while (i--) {
|
|
267
|
+
const newTween = this.createTween(tween[i], !lastTween);
|
|
268
|
+
|
|
269
|
+
if (lastTween) {
|
|
270
|
+
newTween.chain(lastTween);
|
|
271
|
+
}
|
|
272
|
+
lastTween = newTween;
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
return lastTween;
|
|
276
|
+
} else {
|
|
277
|
+
return this.createTween(tween, true);
|
|
278
|
+
}
|
|
279
|
+
},
|
|
280
|
+
|
|
281
|
+
destroy: function () {
|
|
282
|
+
this.group.removeAll();
|
|
283
|
+
},
|
|
284
|
+
|
|
285
|
+
runTween: function (tweenDefinition) {
|
|
286
|
+
const
|
|
287
|
+
tween = this.createTweens(tweenDefinition);
|
|
288
|
+
|
|
289
|
+
if (tween) {
|
|
290
|
+
tween.start(this.time);
|
|
291
|
+
} else {
|
|
292
|
+
platypus.debug.warn('Component Tween: Unable to run requested tween.', tweenDefinition);
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
});
|
|
@@ -0,0 +1,373 @@
|
|
|
1
|
+
/* global platypus */
|
|
2
|
+
import {arrayCache, greenSlice} from '../utils/array.js';
|
|
3
|
+
import AudioVO from './AudioVO.js';
|
|
4
|
+
import createComponentClass from '../factory.js';
|
|
5
|
+
|
|
6
|
+
const
|
|
7
|
+
getEventName = function (msg, VO) {
|
|
8
|
+
if (VO === ' ') {
|
|
9
|
+
return msg + 'default';
|
|
10
|
+
} else {
|
|
11
|
+
return msg + VO;
|
|
12
|
+
}
|
|
13
|
+
},
|
|
14
|
+
createAudioDefinition = function (sound, events = [], message, frameLength) {
|
|
15
|
+
const
|
|
16
|
+
soundId = typeof sound === 'string' ? sound : typeof sound.sound === 'string' ? sound.sound : '',
|
|
17
|
+
soundObj = soundId ? {} : sound.sound,
|
|
18
|
+
definition = {
|
|
19
|
+
sound: soundId,
|
|
20
|
+
events: [...sound.events ?? []],
|
|
21
|
+
...soundObj
|
|
22
|
+
},
|
|
23
|
+
voice = sound.voice,
|
|
24
|
+
mouthCues = sound.mouthCues ?? platypus.game.settings.mouthCues?.[definition.sound] ?? platypus.game.settings.mouthCues?.[definition.sound.substring(definition.sound.lastIndexOf('/') + 1)];
|
|
25
|
+
|
|
26
|
+
if (voice) {
|
|
27
|
+
let lastFrame = null,
|
|
28
|
+
time = 0;
|
|
29
|
+
|
|
30
|
+
voice += ' ';
|
|
31
|
+
|
|
32
|
+
for (let i = 0; i < voice.length; i++) {
|
|
33
|
+
const
|
|
34
|
+
thisFrame = voice[i];
|
|
35
|
+
|
|
36
|
+
if (thisFrame !== lastFrame) {
|
|
37
|
+
lastFrame = thisFrame;
|
|
38
|
+
definition.events.push({
|
|
39
|
+
time,
|
|
40
|
+
event: getEventName(message, thisFrame)
|
|
41
|
+
});
|
|
42
|
+
}
|
|
43
|
+
time += frameLength;
|
|
44
|
+
}
|
|
45
|
+
} else if (mouthCues) {
|
|
46
|
+
// if in condensed format
|
|
47
|
+
if (typeof mouthCues[0] === 'number') {
|
|
48
|
+
const
|
|
49
|
+
length = (mouthCues.length / 2) >> 0;
|
|
50
|
+
|
|
51
|
+
for (let i = 0; i < length; i++) {
|
|
52
|
+
const
|
|
53
|
+
index = i * 2;
|
|
54
|
+
|
|
55
|
+
definition.events.push({
|
|
56
|
+
time: mouthCues[index] * 1000,
|
|
57
|
+
event: getEventName(message, mouthCues[index + 1])
|
|
58
|
+
});
|
|
59
|
+
}
|
|
60
|
+
} else {
|
|
61
|
+
for (let i = 0; i < mouthCues.length; i++) {
|
|
62
|
+
const
|
|
63
|
+
thisFrame = mouthCues[i];
|
|
64
|
+
|
|
65
|
+
definition.events.push({
|
|
66
|
+
time: thisFrame.start * 1000,
|
|
67
|
+
event: getEventName(message, thisFrame.value)
|
|
68
|
+
});
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
return definition;
|
|
74
|
+
},
|
|
75
|
+
createVO = function (sound, events, message, frameLength) {
|
|
76
|
+
if (!events[' ']) {
|
|
77
|
+
events[' '] = events.default;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
if (Array.isArray(sound)) {
|
|
81
|
+
return sound.map((clip) => {
|
|
82
|
+
if (typeof clip === 'number' || typeof clip === 'function') {
|
|
83
|
+
return clip;
|
|
84
|
+
} else {
|
|
85
|
+
return createAudioDefinition(clip, events, message, frameLength);
|
|
86
|
+
}
|
|
87
|
+
});
|
|
88
|
+
} else {
|
|
89
|
+
return createAudioDefinition(sound, events, message, frameLength);
|
|
90
|
+
}
|
|
91
|
+
};
|
|
92
|
+
|
|
93
|
+
export default createComponentClass(/** @lends platypus.components.VoiceOver.prototype */{
|
|
94
|
+
id: 'VoiceOver',
|
|
95
|
+
|
|
96
|
+
properties: {
|
|
97
|
+
aliases: null,
|
|
98
|
+
|
|
99
|
+
/**
|
|
100
|
+
* Sets the pairing between letters in the voice-over strings and the animation frame to play.
|
|
101
|
+
*
|
|
102
|
+
* "animationMap": {
|
|
103
|
+
* "default": "mouth-closed"
|
|
104
|
+
* // Required. Specifies animation of default position.
|
|
105
|
+
*
|
|
106
|
+
* "w": "mouth-o",
|
|
107
|
+
* "a": "mouth-aah",
|
|
108
|
+
* "t": "mouth-t"
|
|
109
|
+
* // Optional. Also list single characters that should map to a given voice-over animation frame.
|
|
110
|
+
* }
|
|
111
|
+
*
|
|
112
|
+
* @property animationMap
|
|
113
|
+
* @type Object
|
|
114
|
+
* @default: {"default": "default"}
|
|
115
|
+
*/
|
|
116
|
+
animationMap: {"default": "default"},
|
|
117
|
+
|
|
118
|
+
/**
|
|
119
|
+
* Specifies the type of component to add to handle VO lip-sync animation.
|
|
120
|
+
*
|
|
121
|
+
* @property renderComponent
|
|
122
|
+
* @type String
|
|
123
|
+
* @default 'RenderSprite'
|
|
124
|
+
*/
|
|
125
|
+
renderComponent: 'RenderSprite',
|
|
126
|
+
|
|
127
|
+
/**
|
|
128
|
+
* Specifies how long a described voice-over frame should last in milliseconds.
|
|
129
|
+
*
|
|
130
|
+
* @property frameLength
|
|
131
|
+
* @type Number
|
|
132
|
+
* @default 100
|
|
133
|
+
*/
|
|
134
|
+
frameLength: 100,
|
|
135
|
+
|
|
136
|
+
/**
|
|
137
|
+
* Specifies the prefix that messages between the render and Audio components should use. This will cause the audio to trigger events like "i-say-w" and "i-say-a" (characters listed in the animationMap), that the RenderSprite uses to show the proper frame.
|
|
138
|
+
*
|
|
139
|
+
* @property messagePrefix
|
|
140
|
+
* @type String
|
|
141
|
+
* @default ""
|
|
142
|
+
*/
|
|
143
|
+
messagePrefix: "",
|
|
144
|
+
|
|
145
|
+
/**
|
|
146
|
+
* This maps events to audio clips and voice over strings.
|
|
147
|
+
*
|
|
148
|
+
* "voiceOverMap": {
|
|
149
|
+
* "message-triggered": [{
|
|
150
|
+
* "sound": "audio-id",
|
|
151
|
+
* // Required. This is the audio clip to play when "message-triggered" is triggered. It may be a string as shown or an object of key/value pairs as described in an [[audio]] component definition.
|
|
152
|
+
* "voice": "waat"
|
|
153
|
+
* // Optional. This string defines the voice-over sequence according to the frames defined by animationMap. Each character lasts the length specified by "frameLength" above. If not specified, voice will be the default frame.
|
|
154
|
+
* }]
|
|
155
|
+
* }
|
|
156
|
+
*
|
|
157
|
+
* @property voiceOverMap
|
|
158
|
+
* @type Object
|
|
159
|
+
* @default null
|
|
160
|
+
*/
|
|
161
|
+
voiceOverMap: null,
|
|
162
|
+
|
|
163
|
+
/**
|
|
164
|
+
* This generates voice over maps. An array of specifications for batches of voice maps to generate. Includes basic properties that can add a prefix to the event name, initial delay before the audio, and an onEnd event that fires when the voice over completes.
|
|
165
|
+
*
|
|
166
|
+
* "generatedVoiceOverMap": [{
|
|
167
|
+
* "eventPrefix": "vo-" //Optional. Defaults to "vo-". Is prefixed to the audio file name to create the event to call to trigger to VO.
|
|
168
|
+
* "initialDelay": 0 //Optional. Defaults to 0. An intial audio delay before the VO starts. Useful to prevent audio from triggering as a scene is loading.
|
|
169
|
+
* "onEndEvent": "an-event" //Optional. Defaults to "". This event fires when the VO completes.
|
|
170
|
+
* "endEventTime": 500 //Optional. Defaults to 99999. When the onEnd event fires.
|
|
171
|
+
* "audio": ["audio-0", "audio-1", "audio-2"] //Required. An array of strings that coorespond to the audio files to create a VOMap for, or a key/value list of id to audio path pairings.
|
|
172
|
+
* }]
|
|
173
|
+
*
|
|
174
|
+
* A generated VO Map is equivalent to this structure:
|
|
175
|
+
*
|
|
176
|
+
* "prefix-audio-0": [
|
|
177
|
+
* 500, //initialDelay
|
|
178
|
+
* {
|
|
179
|
+
* "sound": {
|
|
180
|
+
* "sound": "audio-0", //the audio string
|
|
181
|
+
* "events": [
|
|
182
|
+
* {
|
|
183
|
+
* "event": "on-end-event", //onEndEvent
|
|
184
|
+
* "time": 99999
|
|
185
|
+
* }
|
|
186
|
+
* ]
|
|
187
|
+
* }
|
|
188
|
+
* }
|
|
189
|
+
* ],
|
|
190
|
+
*
|
|
191
|
+
* @property generatedVoiceOverMap
|
|
192
|
+
* @type Object[]
|
|
193
|
+
* @default null
|
|
194
|
+
*/
|
|
195
|
+
generatedVoiceOverMaps: null,
|
|
196
|
+
|
|
197
|
+
acceptInput: null,
|
|
198
|
+
animation: null,
|
|
199
|
+
flip: null,
|
|
200
|
+
hidden: null,
|
|
201
|
+
interactive: null,
|
|
202
|
+
mask: null,
|
|
203
|
+
mirror: null,
|
|
204
|
+
offsetZ: null,
|
|
205
|
+
regX: null,
|
|
206
|
+
regY: null,
|
|
207
|
+
restart: null,
|
|
208
|
+
scaleX: null,
|
|
209
|
+
scaleY: null,
|
|
210
|
+
spriteSheet: null,
|
|
211
|
+
stateBased: null,
|
|
212
|
+
},
|
|
213
|
+
|
|
214
|
+
/**
|
|
215
|
+
* This component uses its definition to load an AudioVO component and a RenderSprite component. These work in an interconnected way to render animations corresponding to one or more audio tracks.
|
|
216
|
+
*
|
|
217
|
+
* In addition to its own properties, this component also accepts all properties accepted by [RenderSprite](platypus.components.RenderSprite.html) and [AudioVO](platypus.components.AudioVO.html) and passes them along when it creates those components.
|
|
218
|
+
*
|
|
219
|
+
* @memberof platypus.components
|
|
220
|
+
* @uses platypus.Component
|
|
221
|
+
* @uses platypus.AudioVO
|
|
222
|
+
* @uses platypus.RenderSprite
|
|
223
|
+
* @constructs
|
|
224
|
+
* @listens platypus.Entity#load
|
|
225
|
+
*/
|
|
226
|
+
initialize: function (definition, callback) {
|
|
227
|
+
const
|
|
228
|
+
{aliases, acceptInput, animation, animationMap, flip, hidden, interactive, mask, messagePrefix, mirror, offsetZ, owner, regX, regY, renderComponent, restart, scaleX, scaleY, spriteSheet, stateBased, voiceOverMap = {}} = this,
|
|
229
|
+
animationKeys = Object.keys(animationMap),
|
|
230
|
+
{length: animationLength} = animationKeys,
|
|
231
|
+
componentInit = (Component, definition) => new Promise((resolve) => {
|
|
232
|
+
owner.addComponent(new Component(owner, definition, resolve));
|
|
233
|
+
}),
|
|
234
|
+
voMapKeys = Object.keys(voiceOverMap),
|
|
235
|
+
{length: voMapLength} = voMapKeys,
|
|
236
|
+
audioDefinition = {
|
|
237
|
+
audioMap: {},
|
|
238
|
+
aliases
|
|
239
|
+
},
|
|
240
|
+
animationDefinition = {
|
|
241
|
+
acceptInput,
|
|
242
|
+
aliases,
|
|
243
|
+
animation,
|
|
244
|
+
animationMap: {},
|
|
245
|
+
eventBased: true, // VO triggers events for changing lip-sync frames.
|
|
246
|
+
flip,
|
|
247
|
+
hidden,
|
|
248
|
+
interactive,
|
|
249
|
+
mask,
|
|
250
|
+
mirror,
|
|
251
|
+
offsetZ,
|
|
252
|
+
regX,
|
|
253
|
+
regY,
|
|
254
|
+
restart,
|
|
255
|
+
scaleX,
|
|
256
|
+
scaleY,
|
|
257
|
+
spriteSheet,
|
|
258
|
+
stateBased
|
|
259
|
+
};
|
|
260
|
+
|
|
261
|
+
if (messagePrefix) {
|
|
262
|
+
this.message = `${messagePrefix}-`;
|
|
263
|
+
} else {
|
|
264
|
+
this.message = '';
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
this.relayLipSyncEvents = ({descriptor, content}) => {
|
|
268
|
+
if (descriptor === 'lipsync') {
|
|
269
|
+
this.owner.triggerEvent(getEventName(this.message, content));
|
|
270
|
+
}
|
|
271
|
+
};
|
|
272
|
+
platypus.game.voPlayer.on('lyric', this.relayLipSyncEvents);
|
|
273
|
+
|
|
274
|
+
for (let i = 0; i < animationLength; i++) {
|
|
275
|
+
const
|
|
276
|
+
key = animationKeys[i];
|
|
277
|
+
|
|
278
|
+
animationDefinition.animationMap[getEventName(this.message, key)] = animationMap[key];
|
|
279
|
+
}
|
|
280
|
+
animationDefinition.animationMap.default = this.animationMap.default;
|
|
281
|
+
|
|
282
|
+
if (this.generatedVoiceOverMaps) {
|
|
283
|
+
const
|
|
284
|
+
createMapping = (key, path, voBatch) => {
|
|
285
|
+
if (!this.voiceOverMap[key]) {
|
|
286
|
+
const
|
|
287
|
+
delay = voBatch.initialDelay || 0,
|
|
288
|
+
endEventTime = voBatch.endEventTime || 99999,
|
|
289
|
+
onEnd = voBatch.onEndEvent || "";
|
|
290
|
+
|
|
291
|
+
this.voiceOverMap[key] = [
|
|
292
|
+
delay,
|
|
293
|
+
{
|
|
294
|
+
"sound": {
|
|
295
|
+
"sound": path,
|
|
296
|
+
"events": [
|
|
297
|
+
{
|
|
298
|
+
"event": onEnd,
|
|
299
|
+
"time": endEventTime
|
|
300
|
+
}
|
|
301
|
+
]
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
];
|
|
305
|
+
}
|
|
306
|
+
};
|
|
307
|
+
|
|
308
|
+
for (let y = 0; y < this.generatedVoiceOverMaps.length; y++) {
|
|
309
|
+
const
|
|
310
|
+
voBatch = this.generatedVoiceOverMaps[y],
|
|
311
|
+
prefix = voBatch.eventPrefix || "vo-",
|
|
312
|
+
audios = voBatch.audio;
|
|
313
|
+
|
|
314
|
+
if (Array.isArray(audios)) {
|
|
315
|
+
for (let x = 0; x < audios.length; x++) {
|
|
316
|
+
const
|
|
317
|
+
audio = audios[x];
|
|
318
|
+
|
|
319
|
+
createMapping(`${prefix}${audio}`, audio, voBatch);
|
|
320
|
+
}
|
|
321
|
+
} else {
|
|
322
|
+
Object.keys(audios).forEach((key) => createMapping(`${prefix}${key}`, audios[key], voBatch));
|
|
323
|
+
}
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
for (let i = 0; i < voMapLength; i++) {
|
|
328
|
+
const
|
|
329
|
+
key = voMapKeys[i];
|
|
330
|
+
|
|
331
|
+
audioDefinition.audioMap[key] = createVO(voiceOverMap[key], animationMap, this.message, this.frameLength);
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
Promise.all([componentInit(typeof this.renderComponent === 'string' ? platypus.components[this.renderComponent] : this.renderComponent, animationDefinition), componentInit(AudioVO, audioDefinition)]).then(callback);
|
|
335
|
+
|
|
336
|
+
return true;
|
|
337
|
+
},
|
|
338
|
+
|
|
339
|
+
methods: {
|
|
340
|
+
destroy () {
|
|
341
|
+
platypus.game.voPlayer.off('lipsync', this.relayLipSyncEvents);
|
|
342
|
+
}
|
|
343
|
+
},
|
|
344
|
+
|
|
345
|
+
events: {
|
|
346
|
+
"play-voice-over-with-lip-sync": function (vo) {
|
|
347
|
+
this.owner.triggerEvent('play-voice-over', createVO(vo, this.animationMap, this.message, this.frameLength));
|
|
348
|
+
}
|
|
349
|
+
},
|
|
350
|
+
|
|
351
|
+
getAssetList: function (component, props, defaultProps) {
|
|
352
|
+
const
|
|
353
|
+
ss = component?.spriteSheet ?? props?.spriteSheet ?? defaultProps?.spriteSheet,
|
|
354
|
+
audioMap = component?.voiceOverMap ?? props?.voiceOverMap ?? defaultProps?.voiceOverMap,
|
|
355
|
+
voAssets = AudioVO.getAssetList({
|
|
356
|
+
audioMap
|
|
357
|
+
});
|
|
358
|
+
|
|
359
|
+
if (typeof ss === 'string') {
|
|
360
|
+
return [
|
|
361
|
+
...greenSlice(platypus.game.settings.spriteSheets[ss].images),
|
|
362
|
+
...voAssets
|
|
363
|
+
];
|
|
364
|
+
} else if (ss) {
|
|
365
|
+
return [
|
|
366
|
+
...greenSlice(ss.images),
|
|
367
|
+
...voAssets
|
|
368
|
+
];
|
|
369
|
+
} else {
|
|
370
|
+
return voAssets;
|
|
371
|
+
}
|
|
372
|
+
}
|
|
373
|
+
});
|