pixi-rainman-game-engine 0.3.31 → 0.3.33-beta.0
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/dist/SettingsUI/components/AutoplaySettings/index.jsx +5 -62
- package/dist/application/SoundManager/SoundManager.d.ts +12 -0
- package/dist/application/SoundManager/SoundManager.js +31 -6
- package/dist/components/frame/AbstractColumnsContainer.js +11 -8
- package/dist/controllers/AbstractController.d.ts +2 -0
- package/dist/controllers/AbstractController.js +32 -12
- package/dist/loading/AbstractLoadingContainer.d.ts +20 -2
- package/dist/loading/AbstractLoadingContainer.js +78 -4
- package/dist/loading/SpriteLoadingContainer.d.ts +20 -9
- package/dist/loading/SpriteLoadingContainer.js +138 -45
- package/dist/stores/SettingsStore.d.ts +1 -6
- package/dist/stores/SettingsStore.js +2 -16
- package/dist/utils/common/sound.d.ts +1 -2
- package/dist/utils/common/sound.js +60 -10
- package/package.json +1 -1
|
@@ -2,87 +2,30 @@ import "react-custom-scroll/dist/customScroll.css";
|
|
|
2
2
|
import "./autoplaySettings.css";
|
|
3
3
|
import i18next from "i18next";
|
|
4
4
|
import { observer } from "mobx-react-lite";
|
|
5
|
-
import React
|
|
5
|
+
import React from "react";
|
|
6
6
|
import { COMPONENTS } from "../../../components";
|
|
7
7
|
import { UI_ITEMS } from "../../../utils";
|
|
8
8
|
import { useStores } from "../../hooks";
|
|
9
9
|
import { CloseModalButton } from "../CloseModalButton";
|
|
10
10
|
import { RichLimitingSlider } from "../RichLimitingSlider";
|
|
11
11
|
import { SpeedButtonWithHeader, SwitchWithHeader } from "../SwitchWithHeader";
|
|
12
|
+
const initialAutoSpinCount = 10;
|
|
13
|
+
const availableAutoSpinLimits = [10, 20, 50, 70, 100];
|
|
12
14
|
export const AutoplaySettings = observer(() => {
|
|
13
|
-
const [limitNumbers, setLimitNumbers] = useState([0]);
|
|
14
|
-
const initialAutoSpinCount = 50;
|
|
15
|
-
const availableAutoSpinLimits = [10, 20, 50, 100, 150, 200, 250, 300, 350, 400, 500, 600, 700, 800, 900, 1000];
|
|
16
15
|
const { settingStore } = useStores();
|
|
17
|
-
const calculateLimitBounds = (indexOfSelectedLimit) => {
|
|
18
|
-
let bottomLimitIndex = indexOfSelectedLimit - 2;
|
|
19
|
-
let topLimitIndex = indexOfSelectedLimit + 1;
|
|
20
|
-
if (bottomLimitIndex < 0) {
|
|
21
|
-
bottomLimitIndex = availableAutoSpinLimits.length - -bottomLimitIndex;
|
|
22
|
-
}
|
|
23
|
-
if (topLimitIndex === availableAutoSpinLimits.length) {
|
|
24
|
-
topLimitIndex = 0;
|
|
25
|
-
}
|
|
26
|
-
return {
|
|
27
|
-
bottomLimitIndex,
|
|
28
|
-
topLimitIndex
|
|
29
|
-
};
|
|
30
|
-
};
|
|
31
16
|
const updateLimitsBounds = (newLimit) => {
|
|
32
17
|
setAutoSpinsCount(newLimit);
|
|
33
|
-
const indexOfSelectedSpinsLimit = availableAutoSpinLimits.findIndex((limitCount) => limitCount === newLimit);
|
|
34
|
-
if (indexOfSelectedSpinsLimit === -1) {
|
|
35
|
-
}
|
|
36
|
-
const { bottomLimitIndex, topLimitIndex } = calculateLimitBounds(indexOfSelectedSpinsLimit);
|
|
37
|
-
const limitNumbersIndexes = [];
|
|
38
|
-
for (let index = bottomLimitIndex; index !== topLimitIndex + 1; index++) {
|
|
39
|
-
if (index === availableAutoSpinLimits.length) {
|
|
40
|
-
index = 0;
|
|
41
|
-
}
|
|
42
|
-
limitNumbersIndexes.push(availableAutoSpinLimits[index]);
|
|
43
|
-
if (limitNumbersIndexes.length === 4) {
|
|
44
|
-
break;
|
|
45
|
-
}
|
|
46
|
-
}
|
|
47
|
-
setLimitNumbers(limitNumbersIndexes);
|
|
48
18
|
};
|
|
49
|
-
|
|
50
|
-
updateLimitsBounds(initialAutoSpinCount);
|
|
51
|
-
}, []);
|
|
52
|
-
const { singleWinExceeds, balancedIncreased, balancedDecreased, howManyAutoSpinsLeft, infinitySpinsEnabled, setSingleWinExceeds, setBalancedIncreased, setBalancedDecreased, setAutoSpinsCount, flipInfinitySpinsFlag, resetAutoplaySettings } = settingStore;
|
|
19
|
+
const { singleWinExceeds, balancedIncreased, balancedDecreased, howManyAutoSpinsLeft, infinitySpinsEnabled, setSingleWinExceeds, setBalancedIncreased, setBalancedDecreased, setAutoSpinsCount, resetAutoplaySettings } = settingStore;
|
|
53
20
|
return (<div className="autoplay-settings">
|
|
54
21
|
<CloseModalButton layerId={UI_ITEMS.autoplay}/>
|
|
55
22
|
<div className="autoplay-settings__title">
|
|
56
23
|
<h2>{i18next.t("autoPlay")}</h2>
|
|
57
24
|
</div>
|
|
58
25
|
<div className="autoplay-settings__stop-limits">
|
|
59
|
-
<button className={`autoplay-settings__limit
|
|
60
|
-
let indexOfSelectedSpinsLimit = availableAutoSpinLimits.findIndex((limitCount) => limitCount === howManyAutoSpinsLeft);
|
|
61
|
-
indexOfSelectedSpinsLimit -= 1;
|
|
62
|
-
if (indexOfSelectedSpinsLimit === -1) {
|
|
63
|
-
indexOfSelectedSpinsLimit = availableAutoSpinLimits.length - 1;
|
|
64
|
-
}
|
|
65
|
-
updateLimitsBounds(availableAutoSpinLimits[indexOfSelectedSpinsLimit]);
|
|
66
|
-
}}>
|
|
67
|
-
<p>-</p>
|
|
68
|
-
</button>
|
|
69
|
-
{limitNumbers.map((limiter, index) => (<button key={`${limiter}${index}`} value={limiter} className={`autoplay-settings__limit ${limiter === howManyAutoSpinsLeft ? "autoplay-settings__limit--active" : ""} middle ${infinitySpinsEnabled ? "autoplay-settings__limit--infinity" : ""}`} disabled={settingStore.infinitySpinsEnabled} onClick={(event) => updateLimitsBounds(Number(event.currentTarget.value))}>
|
|
26
|
+
{availableAutoSpinLimits.map((limiter, index) => (<button key={`${limiter}${index}`} value={limiter} className={`autoplay-settings__limit ${limiter === howManyAutoSpinsLeft ? "autoplay-settings__limit--active" : ""} middle ${infinitySpinsEnabled ? "autoplay-settings__limit--infinity" : ""}`} disabled={settingStore.infinitySpinsEnabled} onClick={(event) => updateLimitsBounds(Number(event.currentTarget.value))}>
|
|
70
27
|
<p>{limiter}</p>
|
|
71
28
|
</button>))}
|
|
72
|
-
<button className={`autoplay-settings__limit right ${infinitySpinsEnabled ? "autoplay-settings__limit--infinity" : ""}`} onClick={() => {
|
|
73
|
-
let indexOfSelectedSpinsLimit = availableAutoSpinLimits.findIndex((limitCount) => limitCount === howManyAutoSpinsLeft);
|
|
74
|
-
indexOfSelectedSpinsLimit += 1;
|
|
75
|
-
if (indexOfSelectedSpinsLimit === availableAutoSpinLimits.length) {
|
|
76
|
-
indexOfSelectedSpinsLimit = 0;
|
|
77
|
-
}
|
|
78
|
-
const newAutoSpinsLimitNumber = availableAutoSpinLimits[indexOfSelectedSpinsLimit];
|
|
79
|
-
updateLimitsBounds(newAutoSpinsLimitNumber);
|
|
80
|
-
}}>
|
|
81
|
-
<p>+</p>
|
|
82
|
-
</button>
|
|
83
|
-
<button className={`autoplay-settings__limit circle ${infinitySpinsEnabled ? "autoplay-settings__limit--active" : ""}`} onClick={() => flipInfinitySpinsFlag()}>
|
|
84
|
-
<p>∞</p>
|
|
85
|
-
</button>
|
|
86
29
|
</div>
|
|
87
30
|
<div className="separator"/>
|
|
88
31
|
<div className="autoplay-settings__title">
|
|
@@ -64,6 +64,12 @@ export declare class SoundManagerInstance implements SpeedAdapterInterface<Sound
|
|
|
64
64
|
* @returns {void}
|
|
65
65
|
*/
|
|
66
66
|
resumeAll(): void;
|
|
67
|
+
/**
|
|
68
|
+
* Restores currently active ambient loop if browser dropped it while page was inactive.
|
|
69
|
+
* @public
|
|
70
|
+
* @returns {void}
|
|
71
|
+
*/
|
|
72
|
+
restoreAmbientPlayback(): void;
|
|
67
73
|
/**
|
|
68
74
|
* Function for pausing all sounds in game
|
|
69
75
|
* If sound is not playing, it will be skipped
|
|
@@ -112,5 +118,11 @@ export declare class SoundManagerInstance implements SpeedAdapterInterface<Sound
|
|
|
112
118
|
* @returns {void}
|
|
113
119
|
*/
|
|
114
120
|
resumeOnFocus(): Promise<void>;
|
|
121
|
+
/**
|
|
122
|
+
* Returns information if audio context is ready to play sounds.
|
|
123
|
+
* @public
|
|
124
|
+
* @returns {boolean} True when audio context is in running state.
|
|
125
|
+
*/
|
|
126
|
+
isAudioContextRunning(): boolean;
|
|
115
127
|
}
|
|
116
128
|
export declare const SoundManager: SoundManagerInstance;
|
|
@@ -170,6 +170,28 @@ export class SoundManagerInstance {
|
|
|
170
170
|
}
|
|
171
171
|
});
|
|
172
172
|
}
|
|
173
|
+
/**
|
|
174
|
+
* Restores currently active ambient loop if browser dropped it while page was inactive.
|
|
175
|
+
* @public
|
|
176
|
+
* @returns {void}
|
|
177
|
+
*/
|
|
178
|
+
restoreAmbientPlayback() {
|
|
179
|
+
if (!RainMan.settingsStore.shouldPlayAmbientMusic) {
|
|
180
|
+
return;
|
|
181
|
+
}
|
|
182
|
+
const ambientTrack = RainMan.settingsStore.isFreeSpinsPlayEnabled
|
|
183
|
+
? SoundTracks.freeSpinsMusic
|
|
184
|
+
: SoundTracks.music;
|
|
185
|
+
const ambientMusic = this.musicCatalogue.get(ambientTrack);
|
|
186
|
+
if (!ambientMusic || ambientMusic.isPlaying) {
|
|
187
|
+
return;
|
|
188
|
+
}
|
|
189
|
+
if (ambientMusic.paused) {
|
|
190
|
+
ambientMusic.resume();
|
|
191
|
+
return;
|
|
192
|
+
}
|
|
193
|
+
this.play(ambientTrack);
|
|
194
|
+
}
|
|
173
195
|
/**
|
|
174
196
|
* Function for pausing all sounds in game
|
|
175
197
|
* If sound is not playing, it will be skipped
|
|
@@ -240,13 +262,16 @@ export class SoundManagerInstance {
|
|
|
240
262
|
catch (error) {
|
|
241
263
|
console.error("Could not resume audio");
|
|
242
264
|
}
|
|
243
|
-
const ambientMusic = this.musicCatalogue.get(RainMan.settingsStore.isFreeSpinsPlayEnabled ? "freeSpinsMusic" : "music");
|
|
244
|
-
if (ambientMusic !== undefined &&
|
|
245
|
-
ambientMusic.isPlaying !== true &&
|
|
246
|
-
RainMan.settingsStore.shouldPlayAmbientMusic) {
|
|
247
|
-
ambientMusic.resume();
|
|
248
|
-
}
|
|
249
265
|
}
|
|
266
|
+
this.restoreAmbientPlayback();
|
|
267
|
+
}
|
|
268
|
+
/**
|
|
269
|
+
* Returns information if audio context is ready to play sounds.
|
|
270
|
+
* @public
|
|
271
|
+
* @returns {boolean} True when audio context is in running state.
|
|
272
|
+
*/
|
|
273
|
+
isAudioContextRunning() {
|
|
274
|
+
return sound.context.audioContext.state === "running";
|
|
250
275
|
}
|
|
251
276
|
}
|
|
252
277
|
export const SoundManager = SoundManagerInstance.getInstance();
|
|
@@ -154,20 +154,21 @@ export class AbstractColumnsContainer extends Container {
|
|
|
154
154
|
const configureSpinAction = configuredBlindSpinsActions.shift();
|
|
155
155
|
if (configureSpinAction !== undefined) {
|
|
156
156
|
if (indexesOfReelsToExtendSpinningTime.includes(index)) {
|
|
157
|
-
|
|
158
|
-
this.currentColumn
|
|
159
|
-
|
|
157
|
+
const mysteryColumn = this.allColumns[index];
|
|
158
|
+
this.currentColumn = mysteryColumn;
|
|
159
|
+
mysteryColumn.adaptToSpeed(RainMan.settingsStore.currentSpeed);
|
|
160
|
+
frame.invokeMysteryFx(mysteryColumn.x, mysteryColumn.y);
|
|
160
161
|
this.skipScatterDelay = configureSpinAction;
|
|
161
|
-
await
|
|
162
|
+
await mysteryColumn.speedUpForMysterySpin(100, 1500);
|
|
162
163
|
if (this.quickReelsStop || this.skipRemainingMysteryFxs) {
|
|
163
|
-
await
|
|
164
|
+
await mysteryColumn.changeVelocity(Speed.STOP, 0, frame);
|
|
164
165
|
}
|
|
165
166
|
else {
|
|
166
167
|
if (!RainMan.settingsStore.isAutoplayEnabled ||
|
|
167
168
|
(RainMan.settingsStore.isAutoplayEnabled && !this.quickReelsStop)) {
|
|
168
169
|
this.scheduleMysteryDelay(configureSpinAction, RainMan.config.durationOfActions.scatterDelayerTime);
|
|
169
170
|
}
|
|
170
|
-
await
|
|
171
|
+
await mysteryColumn.changeVelocity(Speed.STOP, RainMan.config.durationOfActions.scatterSpinningTime, frame);
|
|
171
172
|
}
|
|
172
173
|
frame.destroyMysteryFx();
|
|
173
174
|
}
|
|
@@ -257,7 +258,8 @@ export class AbstractColumnsContainer extends Container {
|
|
|
257
258
|
columnsToPlay.forEach((column, index) => {
|
|
258
259
|
column.forEach((symbolIndex) => promises.push(this.getColumnAtPosition(index).playSymbolAtPosition(symbolIndex, true)));
|
|
259
260
|
});
|
|
260
|
-
|
|
261
|
+
if (!RainMan.settingsStore.isLoopingWinActionQueue)
|
|
262
|
+
this.playSoundDependingOnAmount(amount, allSymbols);
|
|
261
263
|
await Promise.allSettled(promises);
|
|
262
264
|
}
|
|
263
265
|
/**
|
|
@@ -342,7 +344,8 @@ export class AbstractColumnsContainer extends Container {
|
|
|
342
344
|
}
|
|
343
345
|
reelIndex++;
|
|
344
346
|
}
|
|
345
|
-
|
|
347
|
+
if (!RainMan.settingsStore.isLoopingWinActionQueue)
|
|
348
|
+
this.playStreakSound(streakSymbols);
|
|
346
349
|
await Promise.allSettled(promises);
|
|
347
350
|
}
|
|
348
351
|
/**
|
|
@@ -58,6 +58,7 @@ export declare abstract class AbstractController<T> {
|
|
|
58
58
|
protected buttonHeldSince: number;
|
|
59
59
|
shouldStopMobileSpin: boolean;
|
|
60
60
|
disableMessageBoxChange: boolean;
|
|
61
|
+
shouldClearQuickReelsStopAfterSpin: boolean;
|
|
61
62
|
protected resolveBigWin?: () => void;
|
|
62
63
|
protected resolveMysteryWin?: () => void;
|
|
63
64
|
protected resolveBonusWin?: () => void;
|
|
@@ -384,6 +385,7 @@ export declare abstract class AbstractController<T> {
|
|
|
384
385
|
* @returns {Promise<void>}
|
|
385
386
|
*/
|
|
386
387
|
private spin;
|
|
388
|
+
private cleanupDeferredQuickStop;
|
|
387
389
|
/**
|
|
388
390
|
* Function for invoking free spin plate, invoked in mainContainer
|
|
389
391
|
* ```ts
|
|
@@ -58,6 +58,7 @@ export class AbstractController {
|
|
|
58
58
|
buttonHeldSince = 0;
|
|
59
59
|
shouldStopMobileSpin = false;
|
|
60
60
|
disableMessageBoxChange = false;
|
|
61
|
+
shouldClearQuickReelsStopAfterSpin = false;
|
|
61
62
|
resolveBigWin;
|
|
62
63
|
resolveMysteryWin;
|
|
63
64
|
resolveBonusWin;
|
|
@@ -191,6 +192,10 @@ export class AbstractController {
|
|
|
191
192
|
keyboardEvent.preventDefault();
|
|
192
193
|
this.buttonHeldSince = 0;
|
|
193
194
|
this.columnsContainer.clearQuickReelsStop();
|
|
195
|
+
if (this.gamePhase === gamePhases.HANDLING_ACTIONS ||
|
|
196
|
+
this.gamePhase === gamePhases.STOPPING_HANDLING_ACTIONS) {
|
|
197
|
+
this.shouldPlayNextSpin = false;
|
|
198
|
+
}
|
|
194
199
|
}
|
|
195
200
|
});
|
|
196
201
|
window.addEventListener("blur", () => {
|
|
@@ -430,25 +435,26 @@ export class AbstractController {
|
|
|
430
435
|
initSpinButton() {
|
|
431
436
|
const refreshButton = RainMan.componentRegistry.get(RefreshButton.registryName);
|
|
432
437
|
this.clearMobileSpinTimeout = () => {
|
|
433
|
-
this.buttonHeldSince = 0;
|
|
434
438
|
const isSpinning = this.gamePhase === gamePhases.SPINNING ||
|
|
435
439
|
this.gamePhase === gamePhases.CONFIGURING_SPIN ||
|
|
436
440
|
this.gamePhase === gamePhases.DATA_FETCH;
|
|
437
|
-
|
|
438
|
-
this.columnsContainer.clearQuickReelsStop();
|
|
439
|
-
}
|
|
441
|
+
this.buttonHeldSince = 0;
|
|
440
442
|
if (this.mobileSpinTimeout) {
|
|
441
443
|
clearInterval(this.mobileSpinTimeout);
|
|
442
|
-
this.quickStopController.setQuickStopCounterLock(false);
|
|
443
444
|
this.mobileSpinTimeout = null;
|
|
444
|
-
if (!isSpinning) {
|
|
445
|
-
this.columnsContainer.clearQuickReelsStop();
|
|
446
|
-
}
|
|
447
445
|
}
|
|
448
446
|
if (this.mobileSpinDelay) {
|
|
449
447
|
clearTimeout(this.mobileSpinDelay);
|
|
450
448
|
this.mobileSpinDelay = null;
|
|
451
449
|
}
|
|
450
|
+
if (isSpinning) {
|
|
451
|
+
this.shouldClearQuickReelsStopAfterSpin = true;
|
|
452
|
+
}
|
|
453
|
+
else {
|
|
454
|
+
this.columnsContainer.clearQuickReelsStop();
|
|
455
|
+
this.quickStopController.setQuickStopCounterLock(false);
|
|
456
|
+
this.shouldClearQuickReelsStopAfterSpin = false;
|
|
457
|
+
}
|
|
452
458
|
};
|
|
453
459
|
refreshButton.setOnClick(() => {
|
|
454
460
|
this.handleSpinLogic();
|
|
@@ -726,10 +732,10 @@ export class AbstractController {
|
|
|
726
732
|
this.handleDisablingButtons(true);
|
|
727
733
|
RainMan.globals.shouldShowModals = false;
|
|
728
734
|
RainMan.settingsStore.closeCurrentlyOpenModal?.();
|
|
729
|
-
const realTime = Date.now() - this.buttonHeldSince;
|
|
730
|
-
if (realTime <= 500) {
|
|
731
|
-
|
|
732
|
-
}
|
|
735
|
+
// const realTime = Date.now() - this.buttonHeldSince;
|
|
736
|
+
// if (realTime <= 500) {
|
|
737
|
+
// this.columnsContainer.clearQuickReelsStop();
|
|
738
|
+
// }
|
|
733
739
|
if (this.mainContainer.freeSpinPlate) {
|
|
734
740
|
if (this.mainContainer.freeSpinPlate.shownAt === null ||
|
|
735
741
|
performance.now() - this.mainContainer.freeSpinPlate.shownAt <
|
|
@@ -810,6 +816,7 @@ export class AbstractController {
|
|
|
810
816
|
}
|
|
811
817
|
this.prepareBoughtFreeSpins();
|
|
812
818
|
if (RainMan.settingsStore.isAutoplayEnabled && !this.spinning) {
|
|
819
|
+
this.cleanupDeferredQuickStop();
|
|
813
820
|
this.changeGamePhase(gamePhases.IDLE);
|
|
814
821
|
this.spin();
|
|
815
822
|
return;
|
|
@@ -819,6 +826,7 @@ export class AbstractController {
|
|
|
819
826
|
this.changeGamePhase(gamePhases.IDLE);
|
|
820
827
|
}
|
|
821
828
|
if (this.gamePhase === gamePhases.IDLE || this.gamePhase === gamePhases.HANDLING_ACTIONS) {
|
|
829
|
+
this.cleanupDeferredQuickStop();
|
|
822
830
|
this.spin();
|
|
823
831
|
}
|
|
824
832
|
this.spinThrottlerTimeout = setTimeout(() => (this.spinThrottlerTimeout = null), 100);
|
|
@@ -1252,6 +1260,14 @@ export class AbstractController {
|
|
|
1252
1260
|
}, 0);
|
|
1253
1261
|
}
|
|
1254
1262
|
}
|
|
1263
|
+
cleanupDeferredQuickStop() {
|
|
1264
|
+
if (!this.shouldClearQuickReelsStopAfterSpin) {
|
|
1265
|
+
return;
|
|
1266
|
+
}
|
|
1267
|
+
this.columnsContainer.clearQuickReelsStop();
|
|
1268
|
+
this.quickStopController.setQuickStopCounterLock(false);
|
|
1269
|
+
this.shouldClearQuickReelsStopAfterSpin = false;
|
|
1270
|
+
}
|
|
1255
1271
|
/**
|
|
1256
1272
|
* Function for invoking free spin plate, invoked in mainContainer
|
|
1257
1273
|
* ```ts
|
|
@@ -1357,6 +1373,8 @@ export class AbstractController {
|
|
|
1357
1373
|
stopPlayingSymbolsStreak() {
|
|
1358
1374
|
this.columnsContainer.stopPlayingSymbolsInColumns();
|
|
1359
1375
|
this.frame.stopPlayingWinLineAnimations();
|
|
1376
|
+
this.frame.hideMysteryFx();
|
|
1377
|
+
this.frame.destroyMysteryFx();
|
|
1360
1378
|
}
|
|
1361
1379
|
/**
|
|
1362
1380
|
* Function for stopping playing symbols transformation
|
|
@@ -1621,8 +1639,10 @@ export class AbstractController {
|
|
|
1621
1639
|
return;
|
|
1622
1640
|
}
|
|
1623
1641
|
this.isLoopingWinActionsQueue = true;
|
|
1642
|
+
RainMan.settingsStore.isLoopingWinActionQueue = true;
|
|
1624
1643
|
await this.loopWinActionsQueue();
|
|
1625
1644
|
this.isLoopingWinActionsQueue = false;
|
|
1645
|
+
RainMan.settingsStore.isLoopingWinActionQueue = false;
|
|
1626
1646
|
}
|
|
1627
1647
|
/**
|
|
1628
1648
|
* Function for looping win actions queue, after initial play
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { Container } from "pixi.js";
|
|
2
|
-
import {
|
|
2
|
+
import { Spine } from "pixi-spine";
|
|
3
|
+
import { Logo, LogoStatic } from "../components";
|
|
3
4
|
import { SpriteLoadingContainer } from "./SpriteLoadingContainer";
|
|
4
5
|
/**
|
|
5
6
|
* Class represents the container with loading logic inside
|
|
@@ -9,9 +10,15 @@ import { SpriteLoadingContainer } from "./SpriteLoadingContainer";
|
|
|
9
10
|
* @augments {Container}
|
|
10
11
|
*/
|
|
11
12
|
export declare abstract class AbstractLoadingContainer extends Container {
|
|
12
|
-
protected abstract background: Background;
|
|
13
13
|
protected logo: Logo | LogoStatic | null;
|
|
14
14
|
loaderContainer: SpriteLoadingContainer;
|
|
15
|
+
protected initAnimation: Spine | null;
|
|
16
|
+
protected initLogo: Spine | null;
|
|
17
|
+
private canUpdateProgress;
|
|
18
|
+
private pendingProgress;
|
|
19
|
+
private introAnimationFinished;
|
|
20
|
+
private readonly presentationFinishedPromise;
|
|
21
|
+
private resolvePresentationFinished;
|
|
15
22
|
protected constructor();
|
|
16
23
|
/**
|
|
17
24
|
* Function for resizing loading container elements
|
|
@@ -32,4 +39,15 @@ export declare abstract class AbstractLoadingContainer extends Container {
|
|
|
32
39
|
* @returns {void}
|
|
33
40
|
*/
|
|
34
41
|
onUpdate(progress: number): void;
|
|
42
|
+
/**
|
|
43
|
+
* Runs the standardized loading intro sequence.
|
|
44
|
+
* The intro spine plays "start" once, while the logo spine starts with "start"
|
|
45
|
+
* and switches to looping "idle" after the intro completes.
|
|
46
|
+
* @protected
|
|
47
|
+
* @returns {void}
|
|
48
|
+
*/
|
|
49
|
+
protected setupInitAnimations(): void;
|
|
50
|
+
waitForPresentationToFinish(): Promise<void>;
|
|
51
|
+
scaleInitSpineToScreenWidth(): void;
|
|
52
|
+
private tryResolvePresentationFinished;
|
|
35
53
|
}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { Container } from "pixi.js";
|
|
2
2
|
import { RainMan } from "../Rainman";
|
|
3
|
+
import { getDeviceOrientation, globalMinRatio } from "../utils";
|
|
3
4
|
import { SpriteLoadingContainer } from "./SpriteLoadingContainer";
|
|
4
5
|
/**
|
|
5
6
|
* Class represents the container with loading logic inside
|
|
@@ -11,17 +12,27 @@ import { SpriteLoadingContainer } from "./SpriteLoadingContainer";
|
|
|
11
12
|
export class AbstractLoadingContainer extends Container {
|
|
12
13
|
logo = null;
|
|
13
14
|
loaderContainer;
|
|
15
|
+
initAnimation = null;
|
|
16
|
+
initLogo = null;
|
|
17
|
+
canUpdateProgress = true;
|
|
18
|
+
pendingProgress = 0;
|
|
19
|
+
introAnimationFinished = false;
|
|
20
|
+
presentationFinishedPromise;
|
|
21
|
+
resolvePresentationFinished = null;
|
|
14
22
|
constructor() {
|
|
15
23
|
super();
|
|
16
24
|
this.name = "loadingContainer";
|
|
17
25
|
this.x = 0;
|
|
18
26
|
this.y = 0;
|
|
19
27
|
this.loaderContainer = new SpriteLoadingContainer("./assets/images/materials/texture.png", {
|
|
20
|
-
fill: ["#FCD784", "#AE6119", "#FEE391"],
|
|
28
|
+
fill: ["#FCD784", "#AE6119", "#FEE391"],
|
|
21
29
|
stroke: "#4a1850",
|
|
22
30
|
dropShadowColor: "#000000"
|
|
23
31
|
});
|
|
24
|
-
|
|
32
|
+
this.presentationFinishedPromise = new Promise((resolve) => {
|
|
33
|
+
this.resolvePresentationFinished = resolve;
|
|
34
|
+
});
|
|
35
|
+
RainMan.app.renderer.on("resize", () => this.resize());
|
|
25
36
|
}
|
|
26
37
|
/**
|
|
27
38
|
* Function for resizing loading container elements
|
|
@@ -29,9 +40,17 @@ export class AbstractLoadingContainer extends Container {
|
|
|
29
40
|
* @returns {void}
|
|
30
41
|
*/
|
|
31
42
|
resize() {
|
|
32
|
-
this.background.resize();
|
|
33
43
|
this.logo?.resize();
|
|
34
44
|
this.loaderContainer.resize();
|
|
45
|
+
const mobilePortraitModifier = getDeviceOrientation() === "mobile-portrait" ? 0.06 : 0.13;
|
|
46
|
+
if (this.initAnimation && this.initLogo) {
|
|
47
|
+
this.scaleInitSpineToScreenWidth();
|
|
48
|
+
this.initAnimation.x = RainMan.app.screen.width / 2;
|
|
49
|
+
this.initAnimation.y =
|
|
50
|
+
this.loaderContainer.progressBorder.y - RainMan.app.screen.height * mobilePortraitModifier;
|
|
51
|
+
this.initLogo.x = this.initAnimation.x;
|
|
52
|
+
this.initLogo.y = this.initAnimation.y;
|
|
53
|
+
}
|
|
35
54
|
}
|
|
36
55
|
/**
|
|
37
56
|
* Function for removing loading container elements
|
|
@@ -39,6 +58,7 @@ export class AbstractLoadingContainer extends Container {
|
|
|
39
58
|
* @returns {void}
|
|
40
59
|
*/
|
|
41
60
|
remove() {
|
|
61
|
+
this.loaderContainer.remove();
|
|
42
62
|
RainMan.app.renderer.removeAllListeners("resize");
|
|
43
63
|
}
|
|
44
64
|
/**
|
|
@@ -48,6 +68,60 @@ export class AbstractLoadingContainer extends Container {
|
|
|
48
68
|
* @returns {void}
|
|
49
69
|
*/
|
|
50
70
|
onUpdate(progress) {
|
|
51
|
-
this.
|
|
71
|
+
this.pendingProgress = progress;
|
|
72
|
+
if (this.canUpdateProgress) {
|
|
73
|
+
this.loaderContainer.updateProgress(progress);
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* Runs the standardized loading intro sequence.
|
|
78
|
+
* The intro spine plays "start" once, while the logo spine starts with "start"
|
|
79
|
+
* and switches to looping "idle" after the intro completes.
|
|
80
|
+
* @protected
|
|
81
|
+
* @returns {void}
|
|
82
|
+
*/
|
|
83
|
+
setupInitAnimations() {
|
|
84
|
+
if (!this.initAnimation || !this.initLogo) {
|
|
85
|
+
return;
|
|
86
|
+
}
|
|
87
|
+
this.canUpdateProgress = false;
|
|
88
|
+
this.initAnimation.state.setAnimation(0, "start", false);
|
|
89
|
+
this.initLogo.state.setAnimation(0, "start", false);
|
|
90
|
+
this.initLogo.state.addAnimation(0, "idle", true, 0);
|
|
91
|
+
const initAnimationListener = {
|
|
92
|
+
complete: () => {
|
|
93
|
+
this.canUpdateProgress = true;
|
|
94
|
+
this.introAnimationFinished = true;
|
|
95
|
+
this.loaderContainer.updateProgress(this.pendingProgress);
|
|
96
|
+
this.tryResolvePresentationFinished();
|
|
97
|
+
this.initAnimation?.state.removeListener(initAnimationListener);
|
|
98
|
+
}
|
|
99
|
+
};
|
|
100
|
+
this.initAnimation.state.addListener(initAnimationListener);
|
|
101
|
+
}
|
|
102
|
+
waitForPresentationToFinish() {
|
|
103
|
+
return this.presentationFinishedPromise;
|
|
104
|
+
}
|
|
105
|
+
scaleInitSpineToScreenWidth() {
|
|
106
|
+
if (getDeviceOrientation() === "mobile-portrait") {
|
|
107
|
+
this.initAnimation?.scale.set(0.5 * globalMinRatio());
|
|
108
|
+
this.initLogo?.scale.set(0.5 * globalMinRatio());
|
|
109
|
+
}
|
|
110
|
+
else {
|
|
111
|
+
this.initAnimation?.scale.set(0.8 * globalMinRatio());
|
|
112
|
+
this.initLogo?.scale.set(0.8 * globalMinRatio());
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
tryResolvePresentationFinished() {
|
|
116
|
+
if (!this.introAnimationFinished || !this.resolvePresentationFinished) {
|
|
117
|
+
return;
|
|
118
|
+
}
|
|
119
|
+
void this.loaderContainer.waitForCompletion().then(() => {
|
|
120
|
+
if (!this.resolvePresentationFinished) {
|
|
121
|
+
return;
|
|
122
|
+
}
|
|
123
|
+
this.resolvePresentationFinished();
|
|
124
|
+
this.resolvePresentationFinished = null;
|
|
125
|
+
});
|
|
52
126
|
}
|
|
53
127
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Container, ITextStyle } from "pixi.js";
|
|
1
|
+
import { Container, ITextStyle, Sprite } from "pixi.js";
|
|
2
2
|
import { ProgressiveLoader } from "./ProgressiveLoader";
|
|
3
3
|
/**
|
|
4
4
|
* This class contains the loading process to pixi application
|
|
@@ -8,16 +8,19 @@ import { ProgressiveLoader } from "./ProgressiveLoader";
|
|
|
8
8
|
* @implements {ProgressiveLoader}
|
|
9
9
|
*/
|
|
10
10
|
export declare class SpriteLoadingContainer extends Container implements ProgressiveLoader {
|
|
11
|
-
private
|
|
11
|
+
private progressBar;
|
|
12
|
+
private progressBarMask;
|
|
13
|
+
private progressBorderMask;
|
|
14
|
+
private progressBorderShadow;
|
|
12
15
|
private text;
|
|
13
|
-
|
|
14
|
-
private
|
|
16
|
+
progressBorder: Sprite;
|
|
17
|
+
private progress;
|
|
18
|
+
private targetProgress;
|
|
19
|
+
private completionPromiseResolver;
|
|
20
|
+
private completionPromise;
|
|
15
21
|
private readonly loadingStatusContainerHeight;
|
|
16
|
-
private readonly
|
|
17
|
-
private readonly
|
|
18
|
-
private readonly errorBackgroundPaddingHeight;
|
|
19
|
-
private readonly loadingStatusWidth;
|
|
20
|
-
private readonly loadingStatusHeight;
|
|
22
|
+
private readonly progressBorderWidth;
|
|
23
|
+
private readonly minimumFullProgressDuration;
|
|
21
24
|
/**
|
|
22
25
|
* Constructor for SpriteLoadingContainer
|
|
23
26
|
* @public
|
|
@@ -26,6 +29,7 @@ export declare class SpriteLoadingContainer extends Container implements Progres
|
|
|
26
29
|
* @returns {void}
|
|
27
30
|
*/
|
|
28
31
|
constructor(texture: string, textOptions: Partial<ITextStyle>);
|
|
32
|
+
waitForCompletion(): Promise<void>;
|
|
29
33
|
/**
|
|
30
34
|
* Function for updating loading progress
|
|
31
35
|
* @public
|
|
@@ -41,11 +45,13 @@ export declare class SpriteLoadingContainer extends Container implements Progres
|
|
|
41
45
|
*/
|
|
42
46
|
setError(errorMessage: string): void;
|
|
43
47
|
removeProgressBar(): void;
|
|
48
|
+
remove(): void;
|
|
44
49
|
/**
|
|
45
50
|
* Function for resizing loading container elements
|
|
46
51
|
* @public
|
|
47
52
|
* @returns {void}
|
|
48
53
|
*/
|
|
54
|
+
protected recreateProgressBar(): void;
|
|
49
55
|
resize(): void;
|
|
50
56
|
/**
|
|
51
57
|
* Function for repositioning loading container elements
|
|
@@ -53,4 +59,9 @@ export declare class SpriteLoadingContainer extends Container implements Progres
|
|
|
53
59
|
* @returns {void}
|
|
54
60
|
*/
|
|
55
61
|
reposition(): void;
|
|
62
|
+
private getLoadingStatusContainerWidth;
|
|
63
|
+
private updateProgressMask;
|
|
64
|
+
private animateProgress;
|
|
65
|
+
private isComplete;
|
|
66
|
+
private resolveCompletionIfReady;
|
|
56
67
|
}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { Container, Graphics, Sprite, Text, TextStyle, Texture } from "pixi.js";
|
|
2
2
|
import { RainMan } from "../Rainman";
|
|
3
|
+
import { getDeviceOrientation } from "../utils/common/deviceOrientation";
|
|
3
4
|
/**
|
|
4
5
|
* This class contains the loading process to pixi application
|
|
5
6
|
* @class SpriteLoadingContainer
|
|
@@ -8,16 +9,19 @@ import { RainMan } from "../Rainman";
|
|
|
8
9
|
* @implements {ProgressiveLoader}
|
|
9
10
|
*/
|
|
10
11
|
export class SpriteLoadingContainer extends Container {
|
|
11
|
-
|
|
12
|
+
progressBar;
|
|
13
|
+
progressBarMask;
|
|
14
|
+
progressBorderMask;
|
|
15
|
+
progressBorderShadow;
|
|
12
16
|
text;
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
17
|
+
progressBorder;
|
|
18
|
+
progress = 0;
|
|
19
|
+
targetProgress = 0;
|
|
20
|
+
completionPromiseResolver = null;
|
|
21
|
+
completionPromise = null;
|
|
22
|
+
loadingStatusContainerHeight = 10;
|
|
23
|
+
progressBorderWidth = 2;
|
|
24
|
+
minimumFullProgressDuration = 250;
|
|
21
25
|
/**
|
|
22
26
|
* Constructor for SpriteLoadingContainer
|
|
23
27
|
* @public
|
|
@@ -27,17 +31,23 @@ export class SpriteLoadingContainer extends Container {
|
|
|
27
31
|
*/
|
|
28
32
|
constructor(texture, textOptions) {
|
|
29
33
|
super();
|
|
30
|
-
this.
|
|
31
|
-
this.
|
|
32
|
-
this.
|
|
33
|
-
this.
|
|
34
|
-
this.
|
|
35
|
-
this.
|
|
36
|
-
this.
|
|
37
|
-
this.
|
|
38
|
-
this.
|
|
39
|
-
this.
|
|
40
|
-
this.
|
|
34
|
+
this.progressBarMask = new Graphics();
|
|
35
|
+
this.progressBorderMask = new Graphics();
|
|
36
|
+
this.progressBar = Sprite.from(Texture.from(texture));
|
|
37
|
+
this.progressBorder = Sprite.from(this.progressBar.texture);
|
|
38
|
+
this.progressBorderShadow = Sprite.from(this.progressBorder.texture);
|
|
39
|
+
this.progressBorderShadow.tint = 0x000000;
|
|
40
|
+
this.progressBorderShadow.alpha = 0.1;
|
|
41
|
+
this.progressBorder.mask = this.progressBorderMask;
|
|
42
|
+
this.progressBorderMask.renderable = false;
|
|
43
|
+
this.recreateProgressBar();
|
|
44
|
+
this.progressBorderShadow.width = this.getLoadingStatusContainerWidth();
|
|
45
|
+
this.progressBorderShadow.height = this.loadingStatusContainerHeight;
|
|
46
|
+
this.progressBorder.width = this.getLoadingStatusContainerWidth();
|
|
47
|
+
this.progressBorder.height = this.loadingStatusContainerHeight;
|
|
48
|
+
this.progressBar.width = this.getLoadingStatusContainerWidth();
|
|
49
|
+
this.progressBar.height = this.loadingStatusContainerHeight;
|
|
50
|
+
this.progressBar.mask = this.progressBarMask;
|
|
41
51
|
const loaderTextStyle = new TextStyle({
|
|
42
52
|
fontFamily: RainMan.config.fontFace,
|
|
43
53
|
fontSize: 24,
|
|
@@ -49,11 +59,27 @@ export class SpriteLoadingContainer extends Container {
|
|
|
49
59
|
align: "center",
|
|
50
60
|
...textOptions
|
|
51
61
|
});
|
|
52
|
-
this.text = new Text(
|
|
53
|
-
this.text.anchor.set(0, 0.5);
|
|
54
|
-
this.
|
|
55
|
-
this.addChild(this.
|
|
62
|
+
this.text = new Text("", loaderTextStyle);
|
|
63
|
+
this.text.anchor.set(0.5, 0.5);
|
|
64
|
+
this.addChild(this.progressBorderShadow);
|
|
65
|
+
this.addChild(this.progressBorderMask);
|
|
66
|
+
this.addChild(this.progressBorder);
|
|
67
|
+
this.addChild(this.progressBarMask);
|
|
68
|
+
this.addChild(this.progressBar);
|
|
56
69
|
this.addChild(this.text);
|
|
70
|
+
this.reposition();
|
|
71
|
+
RainMan.app.ticker.add(this.animateProgress, this);
|
|
72
|
+
}
|
|
73
|
+
waitForCompletion() {
|
|
74
|
+
if (this.isComplete()) {
|
|
75
|
+
return Promise.resolve();
|
|
76
|
+
}
|
|
77
|
+
if (!this.completionPromise) {
|
|
78
|
+
this.completionPromise = new Promise((resolve) => {
|
|
79
|
+
this.completionPromiseResolver = resolve;
|
|
80
|
+
});
|
|
81
|
+
}
|
|
82
|
+
return this.completionPromise;
|
|
57
83
|
}
|
|
58
84
|
/**
|
|
59
85
|
* Function for updating loading progress
|
|
@@ -62,8 +88,12 @@ export class SpriteLoadingContainer extends Container {
|
|
|
62
88
|
* @returns {void}
|
|
63
89
|
*/
|
|
64
90
|
updateProgress(progress) {
|
|
65
|
-
|
|
66
|
-
|
|
91
|
+
const normalizedProgress = Math.max(0, Math.min(progress, 1));
|
|
92
|
+
if (normalizedProgress < this.progress) {
|
|
93
|
+
this.progress = normalizedProgress;
|
|
94
|
+
}
|
|
95
|
+
this.targetProgress = normalizedProgress;
|
|
96
|
+
this.updateProgressMask();
|
|
67
97
|
}
|
|
68
98
|
/**
|
|
69
99
|
* Function for setting error text
|
|
@@ -72,27 +102,42 @@ export class SpriteLoadingContainer extends Container {
|
|
|
72
102
|
* @returns {void}
|
|
73
103
|
*/
|
|
74
104
|
setError(errorMessage) {
|
|
75
|
-
this.errorBackground = new Graphics();
|
|
76
105
|
this.removeProgressBar();
|
|
77
106
|
this.text.text = errorMessage[0].toUpperCase() + errorMessage.substring(1);
|
|
78
107
|
this.text.style.fill = "0xffffff";
|
|
79
|
-
this.errorBackground.beginFill(0x000000, 0.8);
|
|
80
|
-
this.errorBackground.drawRoundedRect(0, 0, this.text.width + this.errorBackgroundPaddingWidth, this.text.height + this.errorBackgroundPaddingHeight, 8);
|
|
81
|
-
this.errorBackground.endFill();
|
|
82
|
-
this.addChild(this.errorBackground);
|
|
83
108
|
this.addChild(this.text);
|
|
84
109
|
this.reposition();
|
|
85
110
|
}
|
|
86
111
|
removeProgressBar() {
|
|
87
|
-
this.removeChild(this.
|
|
88
|
-
this.removeChild(this.
|
|
112
|
+
this.removeChild(this.progressBorderShadow);
|
|
113
|
+
this.removeChild(this.progressBorderMask);
|
|
114
|
+
this.removeChild(this.progressBorder);
|
|
115
|
+
this.removeChild(this.progressBarMask);
|
|
116
|
+
this.removeChild(this.progressBar);
|
|
89
117
|
this.removeChild(this.text);
|
|
90
118
|
}
|
|
119
|
+
remove() {
|
|
120
|
+
RainMan.app.ticker.remove(this.animateProgress, this);
|
|
121
|
+
}
|
|
91
122
|
/**
|
|
92
123
|
* Function for resizing loading container elements
|
|
93
124
|
* @public
|
|
94
125
|
* @returns {void}
|
|
95
126
|
*/
|
|
127
|
+
recreateProgressBar() {
|
|
128
|
+
const loadingStatusContainerWidth = this.getLoadingStatusContainerWidth() + 1;
|
|
129
|
+
this.progressBorderShadow.width = loadingStatusContainerWidth;
|
|
130
|
+
this.progressBorderShadow.height = this.loadingStatusContainerHeight;
|
|
131
|
+
this.progressBorder.width = loadingStatusContainerWidth;
|
|
132
|
+
this.progressBorder.height = this.loadingStatusContainerHeight;
|
|
133
|
+
this.progressBorderMask.clear();
|
|
134
|
+
this.progressBorderMask.beginFill(0xffffff);
|
|
135
|
+
this.progressBorderMask.drawRoundedRect(0, 0, loadingStatusContainerWidth, this.loadingStatusContainerHeight, 8);
|
|
136
|
+
this.progressBorderMask.beginHole();
|
|
137
|
+
this.progressBorderMask.drawRoundedRect(this.progressBorderWidth / 2, this.progressBorderWidth / 2, loadingStatusContainerWidth - this.progressBorderWidth, this.loadingStatusContainerHeight - this.progressBorderWidth, 7);
|
|
138
|
+
this.progressBorderMask.endHole();
|
|
139
|
+
this.progressBorderMask.endFill();
|
|
140
|
+
}
|
|
96
141
|
resize() {
|
|
97
142
|
this.reposition();
|
|
98
143
|
}
|
|
@@ -102,20 +147,68 @@ export class SpriteLoadingContainer extends Container {
|
|
|
102
147
|
* @returns {void}
|
|
103
148
|
*/
|
|
104
149
|
reposition() {
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
150
|
+
const startY = getDeviceOrientation().includes("mobile")
|
|
151
|
+
? RainMan.app.screen.height / 2 + this.progressBar.height * 3
|
|
152
|
+
: RainMan.app.screen.height / 2 + 100;
|
|
153
|
+
const loadingStatusContainerWidth = this.getLoadingStatusContainerWidth();
|
|
154
|
+
this.recreateProgressBar();
|
|
155
|
+
this.progressBorderShadow.width = loadingStatusContainerWidth + 2;
|
|
156
|
+
this.progressBorderShadow.height = this.loadingStatusContainerHeight;
|
|
157
|
+
this.progressBorder.width = loadingStatusContainerWidth + 2;
|
|
158
|
+
this.progressBorder.height = this.loadingStatusContainerHeight;
|
|
159
|
+
this.progressBar.width = loadingStatusContainerWidth;
|
|
160
|
+
this.progressBar.height = this.loadingStatusContainerHeight;
|
|
161
|
+
this.progressBorderShadow.x = RainMan.app.screen.width / 2 - loadingStatusContainerWidth / 2;
|
|
162
|
+
this.progressBorderShadow.y = startY + 1;
|
|
163
|
+
this.progressBorder.x = RainMan.app.screen.width / 2 - loadingStatusContainerWidth / 2;
|
|
164
|
+
this.progressBorder.y = startY;
|
|
165
|
+
this.progressBorderMask.x = this.progressBorder.x;
|
|
166
|
+
this.progressBorderMask.y = this.progressBorder.y;
|
|
167
|
+
if (this.text.text !== "") {
|
|
168
|
+
this.text.x = RainMan.app.screen.width / 2;
|
|
169
|
+
this.text.y = RainMan.app.screen.height / 2;
|
|
113
170
|
}
|
|
114
171
|
else {
|
|
115
|
-
this.
|
|
116
|
-
this.
|
|
117
|
-
this.
|
|
118
|
-
this.
|
|
172
|
+
this.progressBar.y = this.progressBorder.y;
|
|
173
|
+
this.progressBar.x = this.progressBorder.x;
|
|
174
|
+
this.progressBarMask.y = this.progressBar.y;
|
|
175
|
+
this.progressBarMask.x = this.progressBar.x;
|
|
176
|
+
this.text.x = RainMan.app.screen.width / 2;
|
|
177
|
+
this.text.y = startY + this.text.height;
|
|
178
|
+
this.updateProgressMask();
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
getLoadingStatusContainerWidth() {
|
|
182
|
+
return getDeviceOrientation() === "mobile-portrait"
|
|
183
|
+
? RainMan.app.screen.width * 0.5
|
|
184
|
+
: RainMan.app.screen.width * 0.35;
|
|
185
|
+
}
|
|
186
|
+
updateProgressMask() {
|
|
187
|
+
const loadingStatusContainerWidth = this.getLoadingStatusContainerWidth();
|
|
188
|
+
this.progressBarMask.clear();
|
|
189
|
+
this.progressBarMask.beginFill(0xffffff);
|
|
190
|
+
this.progressBarMask.drawRoundedRect(0, 0, loadingStatusContainerWidth * this.progress, this.loadingStatusContainerHeight, 8);
|
|
191
|
+
this.progressBarMask.endFill();
|
|
192
|
+
}
|
|
193
|
+
animateProgress() {
|
|
194
|
+
if (this.progress >= this.targetProgress) {
|
|
195
|
+
this.resolveCompletionIfReady();
|
|
196
|
+
return;
|
|
197
|
+
}
|
|
198
|
+
const maxStep = RainMan.app.ticker.deltaMS / this.minimumFullProgressDuration;
|
|
199
|
+
this.progress = Math.min(this.progress + maxStep, this.targetProgress);
|
|
200
|
+
this.updateProgressMask();
|
|
201
|
+
this.resolveCompletionIfReady();
|
|
202
|
+
}
|
|
203
|
+
isComplete() {
|
|
204
|
+
return this.targetProgress === 1 && this.progress >= this.targetProgress;
|
|
205
|
+
}
|
|
206
|
+
resolveCompletionIfReady() {
|
|
207
|
+
if (!this.isComplete() || !this.completionPromiseResolver) {
|
|
208
|
+
return;
|
|
119
209
|
}
|
|
210
|
+
this.completionPromiseResolver();
|
|
211
|
+
this.completionPromiseResolver = null;
|
|
212
|
+
this.completionPromise = null;
|
|
120
213
|
}
|
|
121
214
|
}
|
|
@@ -26,6 +26,7 @@ export declare class SettingsStore {
|
|
|
26
26
|
isAutoplayEnabled: boolean;
|
|
27
27
|
isLastFreeSpin: boolean;
|
|
28
28
|
isTakeActionAvailable: boolean;
|
|
29
|
+
isLoopingWinActionQueue: boolean;
|
|
29
30
|
infinitySpinsEnabled: boolean;
|
|
30
31
|
roundNumber: number;
|
|
31
32
|
reelSetId: number;
|
|
@@ -327,12 +328,6 @@ export declare class SettingsStore {
|
|
|
327
328
|
* @returns {void}
|
|
328
329
|
*/
|
|
329
330
|
setAutoSpinsCount(count: number): void;
|
|
330
|
-
/**
|
|
331
|
-
* Flip the infinity spins flag
|
|
332
|
-
* @public
|
|
333
|
-
* @returns {void}
|
|
334
|
-
*/
|
|
335
|
-
flipInfinitySpinsFlag(): void;
|
|
336
331
|
/**
|
|
337
332
|
* Setter for the autoplay enabled flag
|
|
338
333
|
* @public
|
|
@@ -30,6 +30,7 @@ export class SettingsStore {
|
|
|
30
30
|
isAutoplayEnabled = false;
|
|
31
31
|
isLastFreeSpin = false;
|
|
32
32
|
isTakeActionAvailable = true;
|
|
33
|
+
isLoopingWinActionQueue = false;
|
|
33
34
|
infinitySpinsEnabled = false;
|
|
34
35
|
roundNumber = 0;
|
|
35
36
|
reelSetId = 0;
|
|
@@ -72,7 +73,6 @@ export class SettingsStore {
|
|
|
72
73
|
enableButtons;
|
|
73
74
|
useTakeAction;
|
|
74
75
|
initialAutoplaySettingsState = {
|
|
75
|
-
infinitySpinsEnabled: false,
|
|
76
76
|
onAnyWin: false,
|
|
77
77
|
howManyAutoSpinsLeft: INITIAL_AUTO_SPIN_COUNT,
|
|
78
78
|
singleWinExceeds: 0,
|
|
@@ -108,7 +108,6 @@ export class SettingsStore {
|
|
|
108
108
|
resetDataForSymbolPaytablePopup: action.bound,
|
|
109
109
|
decreaseAutoSpinsCount: action.bound,
|
|
110
110
|
setAutoSpinsCount: action.bound,
|
|
111
|
-
flipInfinitySpinsFlag: action.bound,
|
|
112
111
|
resetAutoplaySettings: action.bound,
|
|
113
112
|
setAutoPlayEnabledFlag: action.bound,
|
|
114
113
|
setNumberOfFreeSpin: action.bound,
|
|
@@ -541,7 +540,7 @@ export class SettingsStore {
|
|
|
541
540
|
* @returns {void}
|
|
542
541
|
*/
|
|
543
542
|
decreaseAutoSpinsCount() {
|
|
544
|
-
if (this.
|
|
543
|
+
if (this.isFreeSpinsPlayEnabled) {
|
|
545
544
|
return;
|
|
546
545
|
}
|
|
547
546
|
this.howManyAutoSpinsLeft -= 1;
|
|
@@ -553,20 +552,8 @@ export class SettingsStore {
|
|
|
553
552
|
* @returns {void}
|
|
554
553
|
*/
|
|
555
554
|
setAutoSpinsCount(count) {
|
|
556
|
-
if (this.infinitySpinsEnabled) {
|
|
557
|
-
this.setAutoSpinsCount(-1);
|
|
558
|
-
this.flipInfinitySpinsFlag();
|
|
559
|
-
}
|
|
560
555
|
this.howManyAutoSpinsLeft = count;
|
|
561
556
|
}
|
|
562
|
-
/**
|
|
563
|
-
* Flip the infinity spins flag
|
|
564
|
-
* @public
|
|
565
|
-
* @returns {void}
|
|
566
|
-
*/
|
|
567
|
-
flipInfinitySpinsFlag() {
|
|
568
|
-
this.infinitySpinsEnabled = !this.infinitySpinsEnabled;
|
|
569
|
-
}
|
|
570
557
|
/**
|
|
571
558
|
* Setter for the autoplay enabled flag
|
|
572
559
|
* @public
|
|
@@ -702,7 +689,6 @@ export class SettingsStore {
|
|
|
702
689
|
* @returns {void}
|
|
703
690
|
*/
|
|
704
691
|
resetAutoplaySettings() {
|
|
705
|
-
this.infinitySpinsEnabled = this.initialAutoplaySettingsState.infinitySpinsEnabled;
|
|
706
692
|
this.onAnyWin = this.initialAutoplaySettingsState.onAnyWin;
|
|
707
693
|
this.howManyAutoSpinsLeft = this.initialAutoplaySettingsState.howManyAutoSpinsLeft;
|
|
708
694
|
this.singleWinExceeds = this.initialAutoplaySettingsState.singleWinExceeds;
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
3
|
-
* This is using the visibilitychange event
|
|
2
|
+
* Sets up audio states for tab switches and iOS app interruptions.
|
|
4
3
|
* @returns {void}
|
|
5
4
|
*/
|
|
6
5
|
export declare const loadSoundsOnTabChange: () => void;
|
|
@@ -1,26 +1,76 @@
|
|
|
1
1
|
import { SoundManager } from "../../application";
|
|
2
2
|
import { RainMan } from "../../Rainman";
|
|
3
|
+
let shouldResumeAudioOnInterruption = false;
|
|
4
|
+
const removeInterruptedAudioResumeListeners = () => {
|
|
5
|
+
window.removeEventListener("pointerdown", resumeAudioOnInterruption);
|
|
6
|
+
window.removeEventListener("touchend", resumeAudioOnInterruption);
|
|
7
|
+
};
|
|
8
|
+
const interruptedAudioResume = () => {
|
|
9
|
+
if (shouldResumeAudioOnInterruption) {
|
|
10
|
+
return;
|
|
11
|
+
}
|
|
12
|
+
shouldResumeAudioOnInterruption = true;
|
|
13
|
+
window.addEventListener("pointerdown", resumeAudioOnInterruption, { once: true });
|
|
14
|
+
window.addEventListener("touchend", resumeAudioOnInterruption, { once: true, passive: true });
|
|
15
|
+
};
|
|
3
16
|
/**
|
|
4
|
-
*
|
|
5
|
-
* If any time the sounds was enable than this will be resumed
|
|
6
|
-
* If the sounds was disabled than this will be not triggered
|
|
17
|
+
* Pauses audio when the page goes to the background.
|
|
7
18
|
* @returns {void}
|
|
8
19
|
*/
|
|
9
|
-
const
|
|
10
|
-
|
|
11
|
-
|
|
20
|
+
const pauseAudio = () => {
|
|
21
|
+
shouldResumeAudioOnInterruption = false;
|
|
22
|
+
removeInterruptedAudioResumeListeners();
|
|
23
|
+
if (RainMan.settingsStore.isSoundEnabled()) {
|
|
24
|
+
SoundManager.pauseAll();
|
|
25
|
+
}
|
|
26
|
+
};
|
|
27
|
+
/**
|
|
28
|
+
* Restores audio when the page becomes active again.
|
|
29
|
+
* @returns {Promise<void>}
|
|
30
|
+
*/
|
|
31
|
+
const resumeAudio = async () => {
|
|
32
|
+
if (document.hidden || !RainMan.settingsStore.isSoundEnabled()) {
|
|
12
33
|
return;
|
|
13
34
|
}
|
|
14
|
-
if (RainMan.settingsStore.isSoundEnabled()
|
|
35
|
+
if (RainMan.settingsStore.isSoundEnabled()) {
|
|
36
|
+
await SoundManager.resumeOnFocus();
|
|
15
37
|
SoundManager.resumeAll();
|
|
38
|
+
}
|
|
39
|
+
if (SoundManager.isAudioContextRunning()) {
|
|
40
|
+
shouldResumeAudioOnInterruption = false;
|
|
41
|
+
removeInterruptedAudioResumeListeners();
|
|
42
|
+
return;
|
|
43
|
+
}
|
|
44
|
+
interruptedAudioResume();
|
|
45
|
+
};
|
|
46
|
+
/**
|
|
47
|
+
* Restore audio on the next user action if browser blocked it earlier.
|
|
48
|
+
* @returns {void}
|
|
49
|
+
*/
|
|
50
|
+
function resumeAudioOnInterruption() {
|
|
51
|
+
shouldResumeAudioOnInterruption = false;
|
|
52
|
+
removeInterruptedAudioResumeListeners();
|
|
53
|
+
resumeAudio();
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* Check if visibility is changed to pause or restore audio.
|
|
57
|
+
* @returns {void}
|
|
58
|
+
*/
|
|
59
|
+
const handleVisibilityChange = () => {
|
|
60
|
+
if (document.hidden) {
|
|
61
|
+
pauseAudio();
|
|
16
62
|
return;
|
|
17
63
|
}
|
|
64
|
+
resumeAudio();
|
|
18
65
|
};
|
|
19
66
|
/**
|
|
20
|
-
*
|
|
21
|
-
* This is using the visibilitychange event
|
|
67
|
+
* Sets up audio states for tab switches and iOS app interruptions.
|
|
22
68
|
* @returns {void}
|
|
23
69
|
*/
|
|
24
70
|
export const loadSoundsOnTabChange = () => {
|
|
25
|
-
document.addEventListener("visibilitychange",
|
|
71
|
+
document.addEventListener("visibilitychange", handleVisibilityChange);
|
|
72
|
+
window.addEventListener("pagehide", pauseAudio);
|
|
73
|
+
window.addEventListener("blur", pauseAudio);
|
|
74
|
+
window.addEventListener("pageshow", () => resumeAudio());
|
|
75
|
+
window.addEventListener("focus", () => resumeAudio());
|
|
26
76
|
};
|
package/package.json
CHANGED