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.
@@ -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, { useEffect, useState } from "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
- useEffect(() => {
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 left ${infinitySpinsEnabled ? "autoplay-settings__limit--infinity" : ""}`} onClick={() => {
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
- this.currentColumn = this.allColumns[index];
158
- this.currentColumn.adaptToSpeed(RainMan.settingsStore.currentSpeed);
159
- frame.invokeMysteryFx(this.currentColumn.x, this.currentColumn.y);
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 this.currentColumn.speedUpForMysterySpin(100, 1500);
162
+ await mysteryColumn.speedUpForMysterySpin(100, 1500);
162
163
  if (this.quickReelsStop || this.skipRemainingMysteryFxs) {
163
- await this.currentColumn.changeVelocity(Speed.STOP, 0, frame);
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 this.currentColumn.changeVelocity(Speed.STOP, RainMan.config.durationOfActions.scatterSpinningTime, frame);
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
- this.playSoundDependingOnAmount(amount, allSymbols);
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
- this.playStreakSound(streakSymbols);
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
- if (!isSpinning) {
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
- this.columnsContainer.clearQuickReelsStop();
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 { Background, Logo, LogoStatic } from "../components";
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"], // gradient
28
+ fill: ["#FCD784", "#AE6119", "#FEE391"],
21
29
  stroke: "#4a1850",
22
30
  dropShadowColor: "#000000"
23
31
  });
24
- RainMan.app.renderer.on("resize", () => this.resize()); // arrow fn to pass this context to resize
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.loaderContainer.updateProgress(progress);
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 sprite;
11
+ private progressBar;
12
+ private progressBarMask;
13
+ private progressBorderMask;
14
+ private progressBorderShadow;
12
15
  private text;
13
- private rectangle;
14
- private errorBackground;
16
+ progressBorder: Sprite;
17
+ private progress;
18
+ private targetProgress;
19
+ private completionPromiseResolver;
20
+ private completionPromise;
15
21
  private readonly loadingStatusContainerHeight;
16
- private readonly loadingStatusContainerWidth;
17
- private readonly errorBackgroundPaddingWidth;
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
- sprite;
12
+ progressBar;
13
+ progressBarMask;
14
+ progressBorderMask;
15
+ progressBorderShadow;
12
16
  text;
13
- rectangle;
14
- errorBackground;
15
- loadingStatusContainerHeight = 40;
16
- loadingStatusContainerWidth = 150;
17
- errorBackgroundPaddingWidth = 20;
18
- errorBackgroundPaddingHeight = 10;
19
- loadingStatusWidth = this.loadingStatusContainerWidth - 20;
20
- loadingStatusHeight = this.loadingStatusContainerHeight - 10;
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.rectangle = new Graphics();
31
- this.errorBackground = null;
32
- this.sprite = Sprite.from(Texture.from(texture));
33
- this.rectangle.lineTextureStyle({ width: 5, texture: this.sprite.texture });
34
- this.rectangle.beginFill(0x650a5a, 0.25);
35
- this.rectangle.drawRoundedRect(0, 0, this.loadingStatusContainerWidth, this.loadingStatusContainerHeight, 8);
36
- this.rectangle.endFill();
37
- this.sprite.width = 0;
38
- this.sprite.height = this.loadingStatusHeight;
39
- this.sprite.tint = 0xfcd784;
40
- this.addChild(this.rectangle);
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(`Loaded: ${0}%`, loaderTextStyle);
53
- this.text.anchor.set(0, 0.5);
54
- this.reposition();
55
- this.addChild(this.sprite);
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
- this.text.text = `Loaded: ${(progress * 100).toFixed(2)}%`;
66
- this.sprite.width = this.loadingStatusWidth * progress;
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.rectangle);
88
- this.removeChild(this.sprite);
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
- this.rectangle._accessibleActive;
106
- this.rectangle.y = RainMan.app.screen.height / 2;
107
- this.rectangle.x = RainMan.app.screen.width / 2 - this.width / 2 - 5;
108
- if (this.errorBackground) {
109
- this.errorBackground.y = RainMan.app.screen.height / 2;
110
- this.errorBackground.x = RainMan.app.screen.width / 2 - this.errorBackground.width / 2;
111
- this.text.x = this.errorBackground.x + this.errorBackgroundPaddingWidth / 2;
112
- this.text.y = this.errorBackground.y + this.text.height / 2 + this.errorBackgroundPaddingHeight / 2;
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.sprite.y = this.rectangle.y + 5;
116
- this.sprite.x = this.rectangle.x + 10;
117
- this.text.x = RainMan.app.screen.width / 2 - this.text.width / 2 - 15;
118
- this.text.y = RainMan.app.screen.height / 2 + 70;
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.infinitySpinsEnabled || this.isFreeSpinsPlayEnabled) {
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
- * This function is required for setting up the sound while changing the tab
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
- * This function using the visibilitychange event to resume sounds
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 resumeAudioContext = () => {
10
- if (RainMan.settingsStore.isSoundEnabled() && document.hidden) {
11
- SoundManager.stopAll();
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() && !document.hidden) {
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
- * This function is required for setting up the sound while changing the tab
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", resumeAudioContext);
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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pixi-rainman-game-engine",
3
- "version": "0.3.31",
3
+ "version": "0.3.33-beta.0",
4
4
  "description": "This repository contains all of the mechanics that used in rainman games.",
5
5
  "main": "./dist/index.js",
6
6
  "module": "./dist/index.js",