@next2d/display 1.14.20

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.
@@ -0,0 +1,940 @@
1
+ import { Sprite } from "./Sprite";
2
+ import { FrameLabel } from "./FrameLabel";
3
+ import { Event } from "@next2d/events";
4
+ import { Sound } from "@next2d/media";
5
+ import { $setCurrentLoaderInfo, $currentPlayer, $rendererWorker } from "@next2d/util";
6
+ import { $Array, $getArray, $getMap, $isNaN, $Math } from "@next2d/share";
7
+ /**
8
+ * MovieClip クラスは、Sprite、DisplayObjectContainer、InteractiveObject、DisplayObject
9
+ * および EventDispatcher クラスを継承します。
10
+ * MovieClip オブジェクトには、Sprite オブジェクトとは違ってタイムラインがあります。
11
+ * タイムラインの再生ヘッドが停止されても、その MovieClip オブジェクトの子 MovieClip オブジェクトの再生ヘッドは停止しません。
12
+ *
13
+ * The MovieClip class inherits from the following classes: Sprite, DisplayObjectContainer,
14
+ * InteractiveObject, DisplayObject, and EventDispatcher.
15
+ * Unlike the Sprite object, a MovieClip object has a timeline.
16
+ * When the playback head of the timeline is stopped,
17
+ * the playback head of the child MovieClip object of that MovieClip object will not be stopped.
18
+ *
19
+ * @class
20
+ * @memberOf next2d.display
21
+ * @extends Sprite
22
+ */
23
+ export class MovieClip extends Sprite {
24
+ /**
25
+ * @constructor
26
+ * @public
27
+ */
28
+ constructor() {
29
+ super();
30
+ /**
31
+ * @type {boolean}
32
+ * @default false
33
+ * @private
34
+ */
35
+ this._$stopFlag = false;
36
+ /**
37
+ * @type {boolean}
38
+ * @default true
39
+ * @private
40
+ */
41
+ this._$canAction = true;
42
+ /**
43
+ * @type {boolean}
44
+ * @default false
45
+ * @private
46
+ */
47
+ this._$childRemove = false;
48
+ /**
49
+ * @type {boolean}
50
+ * @default true
51
+ * @private
52
+ */
53
+ this._$canSound = true;
54
+ /**
55
+ * @type {boolean}
56
+ * @default false
57
+ * @private
58
+ */
59
+ this._$actionProcess = false;
60
+ /**
61
+ * @type {Map}
62
+ * @private
63
+ */
64
+ this._$actions = $getMap();
65
+ /**
66
+ * @type {Map}
67
+ * @private
68
+ */
69
+ this._$frameCache = $getMap();
70
+ /**
71
+ * @type {Map}
72
+ * @default null
73
+ * @private
74
+ */
75
+ this._$labels = null;
76
+ /**
77
+ * @type {Map}
78
+ * @private
79
+ */
80
+ this._$sounds = $getMap();
81
+ /**
82
+ * @type {number}
83
+ * @default 0
84
+ * @private
85
+ */
86
+ this._$actionOffset = 0;
87
+ /**
88
+ * @type {number}
89
+ * @default 0
90
+ * @private
91
+ */
92
+ this._$actionLimit = 0;
93
+ /**
94
+ * @type {number}
95
+ * @default 1
96
+ * @private
97
+ */
98
+ this._$currentFrame = 1;
99
+ /**
100
+ * @type {number}
101
+ * @default 1
102
+ * @private
103
+ */
104
+ this._$totalFrames = 1;
105
+ /**
106
+ * @type {boolean}
107
+ * @default false
108
+ * @private
109
+ */
110
+ this._$isPlaying = false;
111
+ /**
112
+ * @type {LoopConfig}
113
+ * @default null
114
+ * @private
115
+ */
116
+ this._$loopConfig = null;
117
+ /**
118
+ * @type {number}
119
+ * @default 0
120
+ * @private
121
+ */
122
+ this._$tweenFrame = 0;
123
+ }
124
+ /**
125
+ * @description 指定されたクラスのストリングを返します。
126
+ * Returns the string representation of the specified class.
127
+ *
128
+ * @return {string}
129
+ * @default [class MovieClip]
130
+ * @method
131
+ * @static
132
+ */
133
+ static toString() {
134
+ return "[class MovieClip]";
135
+ }
136
+ /**
137
+ * @description 指定されたクラスの空間名を返します。
138
+ * Returns the space name of the specified class.
139
+ *
140
+ * @return {string}
141
+ * @default next2d.display.MovieClip
142
+ * @const
143
+ * @static
144
+ */
145
+ static get namespace() {
146
+ return "next2d.display.MovieClip";
147
+ }
148
+ /**
149
+ * @description 指定されたオブジェクトのストリングを返します。
150
+ * Returns the string representation of the specified object.
151
+ *
152
+ * @return {string}
153
+ * @default [object MovieClip]
154
+ * @method
155
+ * @public
156
+ */
157
+ toString() {
158
+ return "[object MovieClip]";
159
+ }
160
+ /**
161
+ * @description 指定されたオブジェクトの空間名を返します。
162
+ * Returns the space name of the specified object.
163
+ *
164
+ * @return {string}
165
+ * @default next2d.display.MovieClip
166
+ * @const
167
+ * @public
168
+ */
169
+ get namespace() {
170
+ return "next2d.display.MovieClip";
171
+ }
172
+ /**
173
+ * @description MovieClip インスタンスのタイムライン内の再生ヘッドが置かれているフレームの番号を示します。
174
+ * Specifies the number of the frame in which the playhead is located
175
+ * in the timeline of the MovieClip instance.
176
+ *
177
+ * @member {number}
178
+ * @default 1
179
+ * @readonly
180
+ * @public
181
+ */
182
+ get currentFrame() {
183
+ return this._$currentFrame;
184
+ }
185
+ /**
186
+ * @description MovieClip インスタンスのタイムライン内の現在のフレームにあるラベルです。
187
+ * The label at the current frame in the timeline of the MovieClip instance.
188
+ *
189
+ * @member {FrameLabel|null}
190
+ * @readonly
191
+ * @public
192
+ */
193
+ get currentFrameLabel() {
194
+ if (!this._$labels) {
195
+ return null;
196
+ }
197
+ const frame = this._$currentFrame;
198
+ if (!this._$labels.has(frame)) {
199
+ return null;
200
+ }
201
+ return this._$labels.get(frame) || null;
202
+ }
203
+ /**
204
+ * @description 現在のシーンの FrameLabel オブジェクトの配列を返します。
205
+ * Returns an array of FrameLabel objects from the current scene.
206
+ *
207
+ * @member {array|null}
208
+ * @readonly
209
+ * @public
210
+ */
211
+ get currentLabels() {
212
+ return !this._$labels || !this._$labels.size
213
+ ? null
214
+ : $Array.from(this._$labels.values());
215
+ }
216
+ /**
217
+ * @description ムービークリップが現在再生されているかどうかを示すブール値です。
218
+ * A Boolean value that indicates whether a movie clip is curently playing.
219
+ *
220
+ * @member {boolean}
221
+ * @default false
222
+ * @readonly
223
+ * @public
224
+ */
225
+ get isPlaying() {
226
+ return this._$isPlaying;
227
+ }
228
+ /**
229
+ * @description MovieClip インスタンス内のフレーム総数です。
230
+ * The total number of frames in the MovieClip instance.
231
+ *
232
+ * @member {number}
233
+ * @default 1
234
+ * @readonly
235
+ * @public
236
+ */
237
+ get totalFrames() {
238
+ return this._$totalFrames;
239
+ }
240
+ /**
241
+ * @description MovieClipのフレームヘッダーの移動方法の設定オブジェクトを返します。
242
+ * Returns a configuration object for how MovieClip's frame headers are moved.
243
+ *
244
+ * @member {object}
245
+ * @default null
246
+ * @public
247
+ */
248
+ get loopConfig() {
249
+ if (this._$loopConfig) {
250
+ return this._$loopConfig;
251
+ }
252
+ const place = this._$getPlaceObject();
253
+ if (!place || !place.loop) {
254
+ return null;
255
+ }
256
+ if (this._$tweenFrame) {
257
+ this._$changePlace = this._$tweenFrame !== this._$parent._$currentFrame;
258
+ this._$tweenFrame = 0;
259
+ }
260
+ if (place.loop.tweenFrame) {
261
+ this._$tweenFrame = place.loop.tweenFrame;
262
+ }
263
+ return place.loop;
264
+ }
265
+ set loopConfig(loop_config) {
266
+ this._$loopConfig = loop_config;
267
+ if (loop_config) {
268
+ loop_config.frame = this._$startFrame;
269
+ this._$loopConfig = loop_config;
270
+ this._$currentFrame = this._$getLoopFrame(loop_config);
271
+ }
272
+ }
273
+ /**
274
+ * @description 指定されたフレームで SWF ファイルの再生を開始します。
275
+ * Starts playing the SWF file at the specified frame.
276
+ *
277
+ * @param {number|string} frame
278
+ * @return {void}
279
+ * @method
280
+ * @public
281
+ */
282
+ gotoAndPlay(frame) {
283
+ this.play();
284
+ this._$goToFrame(frame);
285
+ }
286
+ /**
287
+ * @description このムービークリップの指定されたフレームに再生ヘッドを送り、そこで停止させます。
288
+ * Brings the playhead to the specified frame
289
+ * of the movie clip and stops it there.
290
+ *
291
+ * @param {number|string} frame
292
+ * @return {void}
293
+ * @method
294
+ * @public
295
+ */
296
+ gotoAndStop(frame) {
297
+ this.stop();
298
+ this._$goToFrame(frame);
299
+ }
300
+ /**
301
+ * @description 次のフレームに再生ヘッドを送り、停止します。
302
+ * Sends the playhead to the next frame and stops it.
303
+ *
304
+ * @return {void}
305
+ * @method
306
+ * @public
307
+ */
308
+ nextFrame() {
309
+ this.stop();
310
+ if (this._$totalFrames > this._$currentFrame) {
311
+ this._$goToFrame(this._$currentFrame + 1);
312
+ }
313
+ }
314
+ /**
315
+ * @description ムービークリップのタイムライン内で再生ヘッドを移動します。
316
+ * Moves the playhead in the timeline of the movie clip.
317
+ *
318
+ * @return {void}
319
+ * @method
320
+ * @public
321
+ */
322
+ play() {
323
+ this._$stopFlag = false;
324
+ this._$isPlaying = true;
325
+ this._$updateState();
326
+ }
327
+ /**
328
+ * @description 直前のフレームに再生ヘッドを戻し、停止します。
329
+ * Sends the playhead to the previous frame and stops it.
330
+ *
331
+ * @return {void}
332
+ * @method
333
+ * @public
334
+ */
335
+ prevFrame() {
336
+ const frame = this._$currentFrame - 1;
337
+ if (frame) {
338
+ this.stop();
339
+ this._$goToFrame(frame);
340
+ }
341
+ }
342
+ /**
343
+ * @description ムービークリップ内の再生ヘッドを停止します。
344
+ * Stops the playhead in the movie clip.
345
+ *
346
+ * @return {void}
347
+ * @method
348
+ * @public
349
+ */
350
+ stop() {
351
+ this._$stopFlag = true;
352
+ this._$isPlaying = false;
353
+ }
354
+ /**
355
+ * @description タイムラインに対して動的にLabelを追加できます。
356
+ * Labels can be added dynamically to the timeline.
357
+ *
358
+ * @example <caption>Example1 usage of addFrameLabel.</caption>
359
+ * // case 1
360
+ * const {MovieClip, FrameLabel} = next2d.display;
361
+ * const movieClip = new MovieClip();
362
+ * movieClip.addFrameLabel(new FrameLabel(1, "start"));
363
+ *
364
+ * @param {FrameLabel} frame_label
365
+ * @return {void}
366
+ * @public
367
+ */
368
+ addFrameLabel(frame_label) {
369
+ if (!this._$labels) {
370
+ this._$labels = $getMap();
371
+ }
372
+ this._$labels.set(frame_label.frame, frame_label);
373
+ }
374
+ /**
375
+ * @description 指定のフレームのアクションを追加できます
376
+ * You can add an action for a given frame.
377
+ *
378
+ * @example <caption>Example1 usage of addFrameScript.</caption>
379
+ * // case 1
380
+ * const {MovieClip} = next2d.display;
381
+ * const movieClip = new MovieClip();
382
+ * movieClip.addFrameScript(1 , function ()
383
+ * {
384
+ * this.stop();
385
+ * });
386
+ *
387
+ * @example <caption>Example3 usage of addFrameScript.</caption>
388
+ * // case 2
389
+ * const {MovieClip} = next2d.display;
390
+ * const movieClip = new MovieClip();
391
+ * movieClip.addFrameScript(1, method_1, 2, method_2, 10, method_10);
392
+ *
393
+ * @return {void}
394
+ * @method
395
+ * @public
396
+ */
397
+ addFrameScript(...args) {
398
+ for (let idx = 0; idx < args.length; idx += 2) {
399
+ const value = args[idx];
400
+ let frame = +value;
401
+ if ($isNaN(frame)) {
402
+ frame = this._$getFrameForLabel(`${value}`);
403
+ }
404
+ const script = args[idx + 1];
405
+ if (script && frame && this._$totalFrames >= frame) {
406
+ this._$addAction(frame, script);
407
+ }
408
+ // end action add
409
+ if (frame === this._$currentFrame) {
410
+ // set action position
411
+ const player = $currentPlayer();
412
+ player._$actionOffset = player._$actions.length;
413
+ // execute action stack
414
+ this._$canAction = true;
415
+ this._$setAction();
416
+ // adjustment
417
+ if (player._$actionOffset !== player._$actions.length) {
418
+ // marge
419
+ const actions = player._$actions.splice(0, player._$actionOffset);
420
+ player._$actions.push(...player._$actions, ...actions);
421
+ // reset
422
+ player._$actionOffset = 0;
423
+ }
424
+ }
425
+ }
426
+ }
427
+ /**
428
+ * @param {string} name
429
+ * @return {number}
430
+ * @private
431
+ */
432
+ _$getFrameForLabel(name) {
433
+ if (!this._$labels) {
434
+ return 0;
435
+ }
436
+ for (const [frame, frameLabel] of this._$labels) {
437
+ if (frameLabel.name === name) {
438
+ return frame;
439
+ }
440
+ }
441
+ return 0;
442
+ }
443
+ /**
444
+ * @param {number} frame
445
+ * @param {function} script
446
+ * @return {void}
447
+ * @method
448
+ * @private
449
+ */
450
+ _$addAction(frame, script) {
451
+ if (frame) {
452
+ if (!this._$actions.has(frame)) {
453
+ this._$actions.set(frame, $getArray());
454
+ }
455
+ const actions = this._$actions.get(frame);
456
+ if (actions) {
457
+ actions.push(script);
458
+ }
459
+ }
460
+ }
461
+ /**
462
+ * @return {void}
463
+ * @private
464
+ */
465
+ _$setAction() {
466
+ // added event
467
+ this._$executeAddedEvent();
468
+ if (this._$canAction) {
469
+ const frame = this._$currentFrame;
470
+ // frame label event
471
+ if (this._$labels && this._$labels.has(frame)) {
472
+ const frameLabel = this._$labels.get(frame);
473
+ if (frameLabel && frameLabel.willTrigger(Event.FRAME_LABEL)) {
474
+ frameLabel.dispatchEvent(new Event(Event.FRAME_LABEL));
475
+ }
476
+ }
477
+ // add action queue
478
+ if (this._$actions.size && this._$actions.has(frame)) {
479
+ const player = $currentPlayer();
480
+ const index = player._$actions.indexOf(this);
481
+ if (index === -1) {
482
+ player._$actions.push(this);
483
+ }
484
+ }
485
+ }
486
+ }
487
+ /**
488
+ * @param {number|string} value
489
+ * @return {void}
490
+ * @private
491
+ */
492
+ _$goToFrame(value) {
493
+ let frame = +value;
494
+ if ($isNaN(frame)) {
495
+ frame = this._$getFrameForLabel(`${value}`);
496
+ }
497
+ if (frame < 1) {
498
+ frame = 1;
499
+ }
500
+ // over
501
+ if (frame > this._$totalFrames) {
502
+ this._$currentFrame = this._$totalFrames;
503
+ this._$clearChildren();
504
+ // flag off
505
+ this._$canAction = false;
506
+ this._$wait = false;
507
+ return;
508
+ }
509
+ const player = $currentPlayer();
510
+ switch (true) {
511
+ case frame !== this._$currentFrame:
512
+ {
513
+ // flag off
514
+ this._$wait = false;
515
+ const currentFrame = this._$currentFrame;
516
+ if (this._$actionProcess) {
517
+ this._$frameCache.set("nextFrame", frame);
518
+ this._$frameCache.set("stopFlag", this._$stopFlag);
519
+ this._$frameCache.set("isPlaying", this._$isPlaying);
520
+ }
521
+ // setup
522
+ this._$currentFrame = frame;
523
+ this._$clearChildren();
524
+ // set action position
525
+ player._$actionOffset = player._$actions.length;
526
+ const position = player._$actionOffset
527
+ ? player._$actions.indexOf(this)
528
+ : -1;
529
+ this._$canAction = true;
530
+ this._$prepareActions();
531
+ // adjustment
532
+ if (player._$actionOffset
533
+ && player._$actionOffset !== player._$actions.length) {
534
+ // marge
535
+ const actions = player._$actions.splice(0, player._$actionOffset);
536
+ player._$actions.push(...player._$actions, ...actions);
537
+ // reset
538
+ player._$actionOffset = 0;
539
+ }
540
+ if (!this._$actionProcess && (position > -1 || !player._$actionOffset)) {
541
+ while (player._$actions.length) {
542
+ if (player._$actions.length === position) {
543
+ break;
544
+ }
545
+ // target object
546
+ const mc = player._$actions.pop();
547
+ if (!mc) {
548
+ continue;
549
+ }
550
+ mc._$canAction = false;
551
+ mc._$actionOffset = 0;
552
+ mc._$actionLimit = 0;
553
+ if (mc._$actionProcess && mc._$frameCache.size) {
554
+ mc._$currentFrame = mc._$frameCache.get("nextFrame");
555
+ mc._$clearChildren();
556
+ mc._$stopFlag = mc._$frameCache.get("stopFlag");
557
+ mc._$isPlaying = mc._$frameCache.get("isPlaying");
558
+ mc._$frameCache.clear();
559
+ }
560
+ const frame = mc._$currentFrame;
561
+ if (!mc._$actions.has(frame)) {
562
+ continue;
563
+ }
564
+ const actions = mc._$actions.get(frame);
565
+ if (!actions) {
566
+ continue;
567
+ }
568
+ for (let idx = 0; idx < actions.length; ++idx) {
569
+ try {
570
+ $setCurrentLoaderInfo(mc._$loaderInfo);
571
+ actions[idx].apply(mc);
572
+ }
573
+ catch (e) {
574
+ mc.stop();
575
+ }
576
+ }
577
+ }
578
+ }
579
+ if (this._$actionProcess) {
580
+ this._$currentFrame = currentFrame;
581
+ this._$clearChildren();
582
+ }
583
+ }
584
+ break;
585
+ case !this._$actionProcess && player._$actions.indexOf(this) > -1:
586
+ {
587
+ if (!this._$actionLimit) {
588
+ break;
589
+ }
590
+ // flag off
591
+ this._$wait = false;
592
+ const myActions = player._$actions.splice(this._$actionOffset, this._$actionLimit);
593
+ while (myActions.length) {
594
+ const mc = myActions.pop();
595
+ if (!mc) {
596
+ continue;
597
+ }
598
+ // target reset
599
+ mc._$canAction = false;
600
+ mc._$actionOffset = 0;
601
+ mc._$actionLimit = 0;
602
+ const frame = mc._$currentFrame;
603
+ if (!mc._$actions.has(frame)) {
604
+ continue;
605
+ }
606
+ const actions = mc._$actions.get(frame);
607
+ if (!actions) {
608
+ continue;
609
+ }
610
+ for (let idx = 0; idx < actions.length; ++idx) {
611
+ try {
612
+ $setCurrentLoaderInfo(mc._$loaderInfo);
613
+ // @ts-ignore
614
+ actions[idx].apply(mc);
615
+ }
616
+ catch (e) {
617
+ mc.stop();
618
+ }
619
+ }
620
+ }
621
+ }
622
+ break;
623
+ default:
624
+ break;
625
+ }
626
+ $setCurrentLoaderInfo(null);
627
+ // set sound
628
+ if (this._$canSound && this._$sounds.size
629
+ && this._$sounds.has(this._$currentFrame)
630
+ && !player._$sounds.has(this._$instanceId)) {
631
+ player._$sounds.set(this._$instanceId, this);
632
+ }
633
+ }
634
+ /**
635
+ * @return {void}
636
+ * @method
637
+ * @private
638
+ */
639
+ _$prepareActions() {
640
+ // draw flag
641
+ this._$wait = false;
642
+ const children = this._$needsChildren
643
+ ? this._$getChildren()
644
+ : this._$children;
645
+ for (let idx = children.length - 1; idx > -1; --idx) {
646
+ children[idx]._$prepareActions();
647
+ }
648
+ this._$setAction();
649
+ }
650
+ /**
651
+ * @return {boolean}
652
+ * @method
653
+ * @private
654
+ */
655
+ _$nextFrame() {
656
+ let isNext = this._$needsChildren;
657
+ switch (true) {
658
+ case this._$wait:
659
+ isNext = true;
660
+ this._$wait = false;
661
+ break;
662
+ case this._$stopFlag:
663
+ case this._$totalFrames === 1:
664
+ break;
665
+ default:
666
+ {
667
+ isNext = true;
668
+ // action on
669
+ this._$canAction = true;
670
+ // sound on
671
+ this._$canSound = true;
672
+ const loopConfig = this.loopConfig;
673
+ if (!loopConfig) {
674
+ // next frame point
675
+ ++this._$currentFrame;
676
+ if (this._$currentFrame > this._$totalFrames) {
677
+ this._$currentFrame = 1;
678
+ }
679
+ }
680
+ else {
681
+ const totalFrames = loopConfig.end
682
+ ? loopConfig.end
683
+ : this._$totalFrames;
684
+ switch (loopConfig.type) {
685
+ case 0: // REPEAT
686
+ if (this._$changePlace) {
687
+ this._$currentFrame = loopConfig.start;
688
+ }
689
+ else {
690
+ ++this._$currentFrame;
691
+ if (this._$currentFrame > totalFrames) {
692
+ this._$currentFrame = loopConfig.start;
693
+ }
694
+ }
695
+ break;
696
+ case 1: // NO_REPEAT
697
+ if (this._$changePlace) {
698
+ this._$currentFrame = loopConfig.start;
699
+ }
700
+ else {
701
+ ++this._$currentFrame;
702
+ if (this._$currentFrame > totalFrames) {
703
+ this._$currentFrame = totalFrames;
704
+ isNext = false;
705
+ // action on
706
+ this._$canAction = false;
707
+ // sound on
708
+ this._$canSound = false;
709
+ }
710
+ }
711
+ break;
712
+ case 2: // FIXED
713
+ if (this._$changePlace) {
714
+ this._$currentFrame = loopConfig.start;
715
+ }
716
+ else {
717
+ isNext = false;
718
+ // action on
719
+ this._$canAction = false;
720
+ // sound on
721
+ this._$canSound = false;
722
+ }
723
+ break;
724
+ case 3: // NO_REPEAT_REVERSAL
725
+ if (this._$changePlace) {
726
+ this._$currentFrame = totalFrames;
727
+ }
728
+ else {
729
+ --this._$currentFrame;
730
+ if (loopConfig.start > this._$currentFrame) {
731
+ this._$currentFrame = loopConfig.start;
732
+ isNext = false;
733
+ // action on
734
+ this._$canAction = false;
735
+ // sound on
736
+ this._$canSound = false;
737
+ }
738
+ }
739
+ break;
740
+ case 4: // REPEAT_REVERSAL
741
+ if (this._$changePlace) {
742
+ this._$currentFrame = totalFrames;
743
+ }
744
+ else {
745
+ --this._$currentFrame;
746
+ if (loopConfig.start > this._$currentFrame) {
747
+ this._$currentFrame = totalFrames;
748
+ }
749
+ }
750
+ break;
751
+ }
752
+ }
753
+ // clear
754
+ if (isNext) {
755
+ this._$clearChildren();
756
+ }
757
+ // set sound
758
+ if (this._$canSound && this._$sounds.size
759
+ && this._$sounds.has(this._$currentFrame)) {
760
+ const player = $currentPlayer();
761
+ if (!player._$sounds.has(this._$instanceId)) {
762
+ player._$sounds.set(this._$instanceId, this);
763
+ }
764
+ }
765
+ }
766
+ break;
767
+ }
768
+ const children = this._$needsChildren
769
+ ? this._$getChildren()
770
+ : this._$children;
771
+ for (let idx = children.length - 1; idx > -1; --idx) {
772
+ const child = children[idx];
773
+ if (!child._$isNext) {
774
+ continue;
775
+ }
776
+ if (isNext) {
777
+ child._$nextFrame();
778
+ }
779
+ else {
780
+ isNext = child._$nextFrame();
781
+ }
782
+ }
783
+ // frame action
784
+ this._$setAction();
785
+ this._$isNext = isNext;
786
+ if (!this._$posted && $rendererWorker) {
787
+ this._$postProperty();
788
+ }
789
+ return this._$isNext;
790
+ }
791
+ /**
792
+ * @param {LoopConfig} loop_config
793
+ * @return {number}
794
+ * @method
795
+ * @private
796
+ */
797
+ _$getLoopFrame(loop_config) {
798
+ const parent = this._$parent;
799
+ const length = parent._$currentFrame - loop_config.frame;
800
+ let frame = 1;
801
+ switch (loop_config.type) {
802
+ case 0: // REPEAT
803
+ {
804
+ const totalFrame = loop_config.end
805
+ ? loop_config.end
806
+ : this._$totalFrames;
807
+ frame = loop_config.start;
808
+ for (let idx = 0; idx < length; ++idx) {
809
+ ++frame;
810
+ if (frame > totalFrame) {
811
+ frame = loop_config.start;
812
+ }
813
+ }
814
+ }
815
+ break;
816
+ case 1: // NO_REPEAT
817
+ {
818
+ const totalFrame = loop_config.end
819
+ ? loop_config.end
820
+ : this._$totalFrames;
821
+ frame = $Math.min(totalFrame, loop_config.start + length);
822
+ }
823
+ break;
824
+ case 2: // FIXED
825
+ frame = loop_config.start;
826
+ break;
827
+ case 3: // NO_REPEAT_REVERSAL
828
+ frame = loop_config.end
829
+ ? loop_config.end
830
+ : this._$totalFrames;
831
+ frame = $Math.max(loop_config.start, frame - length);
832
+ break;
833
+ case 4: // REPEAT_REVERSAL
834
+ {
835
+ const totalFrame = loop_config.end
836
+ ? loop_config.end
837
+ : this._$totalFrames;
838
+ frame = totalFrame;
839
+ for (let idx = 0; idx < length; ++idx) {
840
+ --frame;
841
+ if (loop_config.start > frame) {
842
+ frame = totalFrame;
843
+ }
844
+ }
845
+ }
846
+ break;
847
+ }
848
+ return frame;
849
+ }
850
+ /**
851
+ * @param {object} character
852
+ * @return {void}
853
+ * @method
854
+ * @private
855
+ */
856
+ _$buildCharacter(character) {
857
+ if (character.sounds) {
858
+ for (let idx = 0; idx < character.sounds.length; ++idx) {
859
+ const object = character.sounds[idx];
860
+ const sounds = $getArray();
861
+ for (let idx = 0; idx < object.sound.length; ++idx) {
862
+ const sound = new Sound();
863
+ sound._$build(object.sound[idx], this);
864
+ sounds.push(sound);
865
+ }
866
+ this._$sounds.set(object.frame, sounds);
867
+ }
868
+ }
869
+ if (character.actions) {
870
+ for (let idx = 0; idx < character.actions.length; ++idx) {
871
+ const object = character.actions[idx];
872
+ if (!object.script) {
873
+ object.script = Function(object.action);
874
+ }
875
+ this._$addAction(object.frame, object.script);
876
+ }
877
+ }
878
+ if (character.labels) {
879
+ for (let idx = 0; idx < character.labels.length; ++idx) {
880
+ const label = character.labels[idx];
881
+ this.addFrameLabel(new FrameLabel(label.name, label.frame));
882
+ }
883
+ }
884
+ this._$totalFrames = character.totalFrame || 1;
885
+ }
886
+ /**
887
+ * @param {object} character
888
+ * @return {void}
889
+ * @method
890
+ * @private
891
+ */
892
+ _$sync(character) {
893
+ super._$sync(character);
894
+ this._$buildCharacter(character);
895
+ }
896
+ /**
897
+ * @param {object} tag
898
+ * @param {DisplayObjectContainer} parent
899
+ * @return {object}
900
+ * @method
901
+ * @private
902
+ */
903
+ _$build(tag, parent) {
904
+ const character = super._$build(tag, parent);
905
+ this._$buildCharacter(character);
906
+ return character;
907
+ }
908
+ /**
909
+ * @return {void}
910
+ * @method
911
+ * @private
912
+ */
913
+ _$soundPlay() {
914
+ if (!this._$sounds.has(this._$currentFrame)) {
915
+ return;
916
+ }
917
+ const sounds = this._$sounds.get(this._$currentFrame);
918
+ if (sounds.length) {
919
+ // set SoundTransform
920
+ let soundTransform = this._$soundTransform;
921
+ let parent = this._$parent;
922
+ while (parent) {
923
+ if (parent._$soundTransform) {
924
+ soundTransform = parent._$soundTransform;
925
+ }
926
+ parent = parent._$parent;
927
+ }
928
+ // play sound
929
+ for (let idx = 0; idx < sounds.length; ++idx) {
930
+ const sound = sounds[idx];
931
+ if (soundTransform) {
932
+ sound.loopCount = soundTransform.loop ? 0xffffff : 0;
933
+ sound.volume = soundTransform.volume;
934
+ }
935
+ sound.play();
936
+ }
937
+ }
938
+ this._$canSound = false;
939
+ }
940
+ }