pixi-rainman-game-engine 0.3.42 → 0.3.43-b

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.
@@ -1,5 +1,5 @@
1
1
  import { isIncentiveComponentInterface, isSpeedAdapterInterface, SpeedLevelHandler, TimedIncentiveController } from "../application";
2
- import { AbstractSymbolsColumn, SpeedControlButton } from "../components";
2
+ import { AbstractSymbolsColumn, SpeedControlButton, SpeedControlButtonMobile } from "../components";
3
3
  import { RainMan } from "../Rainman";
4
4
  import { openFullscreen } from "../utils";
5
5
  /**
@@ -60,7 +60,8 @@ export class ComponentRegistry {
60
60
  this.speedLevelHandler.nextSpeed();
61
61
  RainMan.settingsStore.setSpeedLevel(this.speedLevelHandler.currentSpeed);
62
62
  this.updateAllSpeedAdaptable(RainMan.settingsStore.currentSpeed);
63
- this.get(SpeedControlButton.registryName).forceTextureUpdate();
63
+ this.getIfExists(SpeedControlButton.registryName)?.forceTextureUpdate();
64
+ this.getIfExists(SpeedControlButtonMobile.registryName)?.forceTextureUpdate();
64
65
  }
65
66
  /**
66
67
  * Getter for current speed level
@@ -1,4 +1,4 @@
1
- import { AbstractColumnsContainer, AbstractFrame, AbstractInnerFrame, AutoplayButton, Background, BUTTONS, COMPONENTS, FreeSpinButton, GambleButton, InfoButton, MessageBox, MoreButton, NegButton, PlusButton, RefreshButton, SpeedControlButton, StylefulUpdatableText, TakeButton, UpdatableTextComponent, UpdatableValueComponent, VolumeButton } from "../components";
1
+ import { AbstractColumnsContainer, AbstractFrame, AbstractInnerFrame, AutoplayButton, Background, BUTTONS, COMPONENTS, FreeSpinButton, GambleButton, InfoButton, MessageBox, MoreButton, NegButton, PlusButton, RefreshButton, SpeedControlButton, SpeedControlButtonMobile, StylefulUpdatableText, TakeButton, UpdatableTextComponent, UpdatableValueComponent, VolumeButton } from "../components";
2
2
  /**
3
3
  * RegistryMap is a map of all components that are registered in the ComponentRegistry.
4
4
  * It is used to type-check the components that are registered in the ComponentRegistry.
@@ -11,6 +11,7 @@ export interface RegistryMap {
11
11
  [MessageBox.gamePaysTextRegistryName]: UpdatableTextComponent;
12
12
  [MessageBox.registryName]: MessageBox;
13
13
  [SpeedControlButton.registryName]: SpeedControlButton;
14
+ [SpeedControlButtonMobile.registryName]: SpeedControlButtonMobile;
14
15
  [Background.registryName]: Background;
15
16
  [VolumeButton.registryName]: VolumeButton;
16
17
  [AutoplayButton.registryName]: AutoplayButton;
@@ -1 +1 @@
1
- import { AbstractColumnsContainer, AbstractFrame, AbstractInnerFrame, AutoplayButton, Background, BUTTONS, COMPONENTS, FreeSpinButton, InfoButton, MessageBox, MoreButton, NegButton, PlusButton, RefreshButton, SpeedControlButton, VolumeButton } from "../components";
1
+ import { AbstractColumnsContainer, AbstractFrame, AbstractInnerFrame, AutoplayButton, Background, BUTTONS, COMPONENTS, FreeSpinButton, InfoButton, MessageBox, MoreButton, NegButton, PlusButton, RefreshButton, SpeedControlButton, SpeedControlButtonMobile, VolumeButton } from "../components";
@@ -16,8 +16,11 @@ export declare class SoundManagerInstance implements SpeedAdapterInterface<Sound
16
16
  readonly speedLevelMapper: SpeedLevelDirectory<SoundOverrides>;
17
17
  private musicCatalogue;
18
18
  private currentAmbientTrack;
19
- private pausedSoundsForPageHide;
19
+ private currentAmbientOffset;
20
+ private loadedSoundAssets;
21
+ private audioContextRecoveryPromise;
20
22
  constructor();
23
+ private loadSound;
21
24
  /**
22
25
  * Function for loading sounds into pixi
23
26
  * @public
@@ -62,7 +65,19 @@ export declare class SoundManagerInstance implements SpeedAdapterInterface<Sound
62
65
  getVolumeLevel(soundName: SoundTrack): number;
63
66
  private isAmbientTrack;
64
67
  private getOppositeAmbientTrack;
68
+ private getCurrentAmbientTrack;
65
69
  private getSound;
70
+ private getAmbientPlaybackOffset;
71
+ private trackAmbientPlaybackProgress;
72
+ /**
73
+ * Saves active ambient position before the browser interrupts WebAudio playback.
74
+ * @public
75
+ * @returns {void}
76
+ */
77
+ captureAmbientPlaybackPositionForPageHide(): void;
78
+ private isIOSWebAudioRuntime;
79
+ private recreatePixiSoundContext;
80
+ private recreatePixiSoundContextOnce;
66
81
  stopAmbientMusic(): void;
67
82
  /**
68
83
  * Function for resuming all sounds in game
@@ -78,11 +93,18 @@ export declare class SoundManagerInstance implements SpeedAdapterInterface<Sound
78
93
  */
79
94
  restoreAmbientPlayback(): void;
80
95
  /**
81
- * Restores sounds paused because of page visibility change.
82
- * @public
96
+ * Recreates the active ambient WebAudio source after iOS app interruptions.
97
+ * Existing Pixi instances can report isPlaying=true even when the native audio pipeline is silent.
98
+ * @private
83
99
  * @returns {void}
84
100
  */
85
- restorePlaybackAfterPageHide(): void;
101
+ private restartAmbientPlaybackAfterInterruption;
102
+ /**
103
+ * Restores ambient playback after page visibility change or iOS app interruption.
104
+ * @public
105
+ * @returns {boolean} True when audio context is ready after recovery.
106
+ */
107
+ restorePlaybackAfterPageHide(): Promise<boolean>;
86
108
  /**
87
109
  * Function for pausing all sounds in game
88
110
  * If sound is not playing, it will be skipped
@@ -128,13 +150,13 @@ export declare class SoundManagerInstance implements SpeedAdapterInterface<Sound
128
150
  /**
129
151
  * Function that resumes sounds and music when game is back in focus.
130
152
  * @public
131
- * @returns {void}
153
+ * @returns {Promise<boolean>} True when audio context is running.
132
154
  */
133
155
  resumeAudioContext(): Promise<boolean>;
134
156
  /**
135
157
  * Function that resumes sounds and music when game is back in focus.
136
158
  * @public
137
- * @returns {void}
159
+ * @returns {Promise<boolean>} True when audio context and ambient recovery are ready.
138
160
  */
139
161
  resumeOnFocus(): Promise<boolean>;
140
162
  /**
@@ -31,10 +31,25 @@ export class SoundManagerInstance {
31
31
  };
32
32
  musicCatalogue = new Map();
33
33
  currentAmbientTrack = null;
34
- pausedSoundsForPageHide = new Set();
34
+ currentAmbientOffset = 0;
35
+ loadedSoundAssets = {};
36
+ audioContextRecoveryPromise = null;
35
37
  constructor() {
36
38
  this.overrides = this.speedLevelMapper.slow;
37
39
  }
40
+ loadSound(soundName, url) {
41
+ return new Promise((resolve) => {
42
+ const sound = Sound.from({
43
+ url,
44
+ autoPlay: false,
45
+ preload: true,
46
+ volume: this.getVolumeLevel(soundName),
47
+ loaded: () => resolve()
48
+ });
49
+ sound.name = soundName;
50
+ this.musicCatalogue.set(soundName, sound);
51
+ });
52
+ }
38
53
  /**
39
54
  * Function for loading sounds into pixi
40
55
  * @public
@@ -42,22 +57,13 @@ export class SoundManagerInstance {
42
57
  * @returns {Promise<void>} - promise that resolves when all sounds are loaded
43
58
  */
44
59
  async loadSounds(assets) {
60
+ this.loadedSoundAssets = { ...assets };
45
61
  const loadingSoundsPromises = [];
46
62
  for (const [soundName, url] of Object.entries(assets)) {
47
63
  if (!url) {
48
64
  continue;
49
65
  }
50
- loadingSoundsPromises.push(new Promise((resolve) => {
51
- const sound = Sound.from({
52
- url,
53
- autoPlay: false,
54
- preload: true,
55
- volume: this.getVolumeLevel(soundName),
56
- loaded: () => resolve()
57
- });
58
- sound.name = soundName;
59
- this.musicCatalogue.set(soundName, sound);
60
- }));
66
+ loadingSoundsPromises.push(this.loadSound(soundName, url));
61
67
  }
62
68
  await Promise.allSettled(loadingSoundsPromises);
63
69
  }
@@ -96,7 +102,6 @@ export class SoundManagerInstance {
96
102
  }
97
103
  }
98
104
  pauseSoundsForPageHide() {
99
- this.pausedSoundsForPageHide.clear();
100
105
  this.musicCatalogue.forEach((sound, soundName) => {
101
106
  if (!sound.isPlaying || sound.paused) {
102
107
  return;
@@ -104,9 +109,8 @@ export class SoundManagerInstance {
104
109
  if (soundName === SoundTracks.freeSpinsMusic || soundName === SoundTracks.music) {
105
110
  this.currentAmbientTrack = soundName;
106
111
  }
107
- this.pausedSoundsForPageHide.add(soundName);
108
- sound.pause();
109
112
  });
113
+ sound.pauseAll();
110
114
  }
111
115
  /**
112
116
  * Function for resuming and pausing sound in game
@@ -150,12 +154,15 @@ export class SoundManagerInstance {
150
154
  return;
151
155
  }
152
156
  }
153
- sound.play({
157
+ const instance = sound.play({
154
158
  singleInstance: true,
155
159
  loop: soundsToLoop.includes(soundName),
156
160
  volume: this.getVolumeLevel(soundName),
157
161
  speed: soundName === "cardDrawing" ? 0.8 : 1
158
162
  });
163
+ if (this.isAmbientTrack(soundName) && !(instance instanceof Promise)) {
164
+ this.trackAmbientPlaybackProgress(soundName, sound, instance);
165
+ }
159
166
  }
160
167
  /**
161
168
  * Returns the volume level for a given sound track.
@@ -178,10 +185,86 @@ export class SoundManagerInstance {
178
185
  getOppositeAmbientTrack(soundName) {
179
186
  return soundName === SoundTracks.freeSpinsMusic ? SoundTracks.music : SoundTracks.freeSpinsMusic;
180
187
  }
188
+ getCurrentAmbientTrack() {
189
+ return (this.currentAmbientTrack ??
190
+ (RainMan.settingsStore.isFreeSpinsStarted ? SoundTracks.freeSpinsMusic : SoundTracks.music));
191
+ }
181
192
  getSound(soundName) {
182
193
  const overrideSoundName = this.overrides[soundName];
183
194
  return this.musicCatalogue.get(overrideSoundName ?? soundName);
184
195
  }
196
+ getAmbientPlaybackOffset(sound) {
197
+ if (!sound.isPlayable || !Number.isFinite(sound.duration) || sound.duration <= 0) {
198
+ return 0;
199
+ }
200
+ return this.currentAmbientOffset % sound.duration;
201
+ }
202
+ trackAmbientPlaybackProgress(soundName, sound, instance) {
203
+ if (!sound.isPlayable || sound.duration <= 0) {
204
+ return;
205
+ }
206
+ instance.on("progress", (progress) => {
207
+ if (this.currentAmbientTrack !== soundName || !Number.isFinite(progress)) {
208
+ return;
209
+ }
210
+ this.currentAmbientOffset = (progress * sound.duration) % sound.duration;
211
+ });
212
+ }
213
+ /**
214
+ * Saves active ambient position before the browser interrupts WebAudio playback.
215
+ * @public
216
+ * @returns {void}
217
+ */
218
+ captureAmbientPlaybackPositionForPageHide() {
219
+ if (!RainMan.settingsStore.isSoundStarted || !RainMan.settingsStore.shouldPlayAmbientMusic) {
220
+ return;
221
+ }
222
+ const ambientTrack = this.getCurrentAmbientTrack();
223
+ const ambientSound = this.getSound(ambientTrack);
224
+ const ambientInstance = ambientSound?.instances.find((instance) => Number.isFinite(instance.progress) && instance.progress > 0);
225
+ if (!ambientSound || !ambientInstance || !ambientSound.isPlayable || ambientSound.duration <= 0) {
226
+ return;
227
+ }
228
+ this.currentAmbientTrack = ambientTrack;
229
+ this.currentAmbientOffset = (ambientInstance.progress * ambientSound.duration) % ambientSound.duration;
230
+ }
231
+ isIOSWebAudioRuntime() {
232
+ const userAgent = navigator.userAgent;
233
+ const platform = navigator.platform;
234
+ return /iPad|iPhone|iPod/.test(userAgent) || (platform === "MacIntel" && navigator.maxTouchPoints > 1);
235
+ }
236
+ async recreatePixiSoundContext() {
237
+ if (this.audioContextRecoveryPromise) {
238
+ return this.audioContextRecoveryPromise;
239
+ }
240
+ this.audioContextRecoveryPromise = this.recreatePixiSoundContextOnce().finally(() => {
241
+ this.audioContextRecoveryPromise = null;
242
+ });
243
+ return this.audioContextRecoveryPromise;
244
+ }
245
+ async recreatePixiSoundContextOnce() {
246
+ if (!Object.keys(this.loadedSoundAssets).length) {
247
+ return;
248
+ }
249
+ this.musicCatalogue.forEach((sound) => sound.destroy());
250
+ this.musicCatalogue.clear();
251
+ try {
252
+ sound.context.destroy();
253
+ }
254
+ catch (error) {
255
+ console.error("Could not destroy Pixi sound context", error);
256
+ }
257
+ sound.init();
258
+ sound.disableAutoPause = false;
259
+ const loadingSoundsPromises = [];
260
+ for (const [soundName, url] of Object.entries(this.loadedSoundAssets)) {
261
+ if (!url) {
262
+ continue;
263
+ }
264
+ loadingSoundsPromises.push(this.loadSound(soundName, url));
265
+ }
266
+ await Promise.allSettled(loadingSoundsPromises);
267
+ }
185
268
  stopAmbientMusic() {
186
269
  this.stop(SoundTracks.music);
187
270
  this.stop(SoundTracks.freeSpinsMusic);
@@ -196,23 +279,8 @@ export class SoundManagerInstance {
196
279
  if (!RainMan.settingsStore.isSoundStarted) {
197
280
  return;
198
281
  }
199
- this.musicCatalogue.forEach((sound, soundName) => {
200
- if (sound.paused && sound.isPlaying && sound) {
201
- sound.resume();
202
- }
203
- if (RainMan.settingsStore.shouldPlayAmbientMusic &&
204
- !RainMan.settingsStore.isFreeSpinsPlayEnabled &&
205
- soundName === SoundTracks.music &&
206
- sound.paused) {
207
- sound.resume();
208
- }
209
- if (RainMan.settingsStore.shouldPlayAmbientMusic &&
210
- RainMan.settingsStore.isFreeSpinsPlayEnabled &&
211
- soundName === SoundTracks.freeSpinsMusic &&
212
- sound.paused) {
213
- sound.resume();
214
- }
215
- });
282
+ sound.resumeAll();
283
+ this.restoreAmbientPlayback();
216
284
  }
217
285
  /**
218
286
  * Restores currently active ambient loop if browser dropped it while page was inactive.
@@ -227,35 +295,61 @@ export class SoundManagerInstance {
227
295
  this.stopAmbientMusic();
228
296
  return;
229
297
  }
230
- const ambientTrack = this.currentAmbientTrack ??
231
- (RainMan.settingsStore.isFreeSpinsStarted ? SoundTracks.freeSpinsMusic : SoundTracks.music);
298
+ const ambientTrack = this.getCurrentAmbientTrack();
232
299
  this.play(ambientTrack);
233
300
  }
234
301
  /**
235
- * Restores sounds paused because of page visibility change.
236
- * @public
302
+ * Recreates the active ambient WebAudio source after iOS app interruptions.
303
+ * Existing Pixi instances can report isPlaying=true even when the native audio pipeline is silent.
304
+ * @private
237
305
  * @returns {void}
238
306
  */
239
- restorePlaybackAfterPageHide() {
307
+ restartAmbientPlaybackAfterInterruption() {
240
308
  if (!RainMan.settingsStore.isSoundStarted) {
241
- this.pausedSoundsForPageHide.clear();
242
309
  return;
243
310
  }
244
- if (!this.pausedSoundsForPageHide.size) {
245
- this.restoreAmbientPlayback();
311
+ if (!RainMan.settingsStore.shouldPlayAmbientMusic) {
312
+ this.stopAmbientMusic();
246
313
  return;
247
314
  }
248
- const pausedSounds = [...this.pausedSoundsForPageHide];
249
- this.pausedSoundsForPageHide.clear();
250
- pausedSounds.forEach((soundName) => {
251
- if (this.isAmbientTrack(soundName) && !RainMan.settingsStore.shouldPlayAmbientMusic) {
252
- return;
253
- }
254
- if (!this.isAmbientTrack(soundName) && !RainMan.settingsStore.shouldPlayFxSounds) {
255
- return;
256
- }
257
- this.resume(soundName);
315
+ const ambientTrack = this.getCurrentAmbientTrack();
316
+ const ambientSound = this.getSound(ambientTrack);
317
+ if (!ambientSound) {
318
+ return;
319
+ }
320
+ this.stop(this.getOppositeAmbientTrack(ambientTrack));
321
+ ambientSound.stop();
322
+ this.currentAmbientTrack = ambientTrack;
323
+ const instance = ambientSound.play({
324
+ singleInstance: true,
325
+ loop: soundsToLoop.includes(ambientTrack),
326
+ start: this.getAmbientPlaybackOffset(ambientSound),
327
+ volume: this.getVolumeLevel(ambientTrack),
328
+ speed: 1
258
329
  });
330
+ if (!(instance instanceof Promise)) {
331
+ this.trackAmbientPlaybackProgress(ambientTrack, ambientSound, instance);
332
+ }
333
+ }
334
+ /**
335
+ * Restores ambient playback after page visibility change or iOS app interruption.
336
+ * @public
337
+ * @returns {boolean} True when audio context is ready after recovery.
338
+ */
339
+ async restorePlaybackAfterPageHide() {
340
+ if (!RainMan.settingsStore.isSoundStarted) {
341
+ return false;
342
+ }
343
+ sound.resumeAll();
344
+ if (this.isIOSWebAudioRuntime()) {
345
+ await this.recreatePixiSoundContext();
346
+ }
347
+ const isAudioContextRunning = await this.resumeAudioContext();
348
+ if (!isAudioContextRunning) {
349
+ return false;
350
+ }
351
+ this.restartAmbientPlaybackAfterInterruption();
352
+ return true;
259
353
  }
260
354
  /**
261
355
  * Function for pausing all sounds in game
@@ -264,7 +358,7 @@ export class SoundManagerInstance {
264
358
  * @returns {void}
265
359
  */
266
360
  pauseAll() {
267
- this.musicCatalogue.forEach((sound) => sound.pause());
361
+ sound.pauseAll();
268
362
  }
269
363
  /**
270
364
  * Function for stopping all sounds in game
@@ -314,7 +408,7 @@ export class SoundManagerInstance {
314
408
  /**
315
409
  * Function that resumes sounds and music when game is back in focus.
316
410
  * @public
317
- * @returns {void}
411
+ * @returns {Promise<boolean>} True when audio context is running.
318
412
  */
319
413
  async resumeAudioContext() {
320
414
  const pixiSoundContext = sound.context;
@@ -333,7 +427,7 @@ export class SoundManagerInstance {
333
427
  /**
334
428
  * Function that resumes sounds and music when game is back in focus.
335
429
  * @public
336
- * @returns {void}
430
+ * @returns {Promise<boolean>} True when audio context and ambient recovery are ready.
337
431
  */
338
432
  async resumeOnFocus() {
339
433
  if (!RainMan.settingsStore.isSoundStarted) {
@@ -343,8 +437,7 @@ export class SoundManagerInstance {
343
437
  if (!isAudioContextRunning) {
344
438
  return false;
345
439
  }
346
- this.restorePlaybackAfterPageHide();
347
- return true;
440
+ return this.restorePlaybackAfterPageHide();
348
441
  }
349
442
  /**
350
443
  * Returns information if audio context is ready to play sounds.
@@ -6,7 +6,7 @@ import { RainMan } from "../../Rainman";
6
6
  import { Nullable, PromiseResolverType } from "../../utils";
7
7
  import { BigWinContainer, MysteryWinContainer, UpdatableSpineContainer } from "../../winComponents";
8
8
  import { AbstractFreeSpinContainer } from "../AbstractFreeSpinContainer";
9
- import { LeftButtons, LeftButtonsLandscape, LeftButtonsMobile, MiddleButtons, RightButtons, RightButtonsLandscape, RightButtonsMobile } from "../buttons";
9
+ import { LeftButtons, LeftButtonsLandscape, LeftButtonsMobile, MiddleButtons, RightButtons, RightButtonsLandscape, RightButtonsMobile, SpeedControlButtonMobile } from "../buttons";
10
10
  import { FreeSpinButton, Logo, ResponsiveContainer } from "../common";
11
11
  import { IResizableContainer } from "../common/IResizableContainer";
12
12
  import { AbstractFrame } from "../frame";
@@ -34,6 +34,7 @@ export declare abstract class AbstractMainContainer extends Container {
34
34
  static readonly registryName = "mainContainer";
35
35
  messageBox: MessageBox;
36
36
  protected bigWinContainer: Nullable<UpdatableSpineContainer> | BigWinContainer;
37
+ protected readonly mobileSpeedButton: SpeedControlButtonMobile;
37
38
  protected mysteryWinContainer: Nullable<UpdatableSpineContainer> | MysteryWinContainer;
38
39
  protected bonusWinContainer: Nullable<UpdatableSpineContainer>;
39
40
  protected superBonusWinContainer: Nullable<UpdatableSpineContainer>;
@@ -75,6 +76,7 @@ export declare abstract class AbstractMainContainer extends Container {
75
76
  * @returns {Promise<void>} - A promise that resolves when the animation is complete.
76
77
  */
77
78
  invokeMysteryWinPlate(_animationName: string, _popupAnimation: string, _winType: WinTypeId, _amount: number, _isAdditionalFreeSpins: boolean): Promise<void>;
79
+ protected attachMobileSpeedButton(parent: Container): void;
78
80
  /**
79
81
  * This function initializes the spines that should be paused when the battery saver is on
80
82
  * @protected
@@ -3,7 +3,7 @@ import { ButtonsEventManager, SoundManager, SoundTracks } from "../../applicatio
3
3
  import { AnimationLayer, BackgroundLayer, FrameLayer, UXLayer } from "../../layers";
4
4
  import { RainMan } from "../../Rainman";
5
5
  import { getDeviceOrientation, globalMinRatio } from "../../utils";
6
- import { BUTTONS, LeftButtons, LeftButtonsLandscape, LeftButtonsMobile, MiddleButtons, RefreshButton, RightButtons, RightButtonsLandscape, RightButtonsMobile } from "../buttons";
6
+ import { BUTTONS, LeftButtons, LeftButtonsLandscape, LeftButtonsMobile, MiddleButtons, RefreshButton, RightButtons, RightButtonsLandscape, RightButtonsMobile, SpeedControlButtonMobile } from "../buttons";
7
7
  import { GambleGame } from "../GambleGame";
8
8
  import { MessageBox } from "../messageBox";
9
9
  /**
@@ -29,6 +29,7 @@ export class AbstractMainContainer extends Container {
29
29
  static registryName = "mainContainer";
30
30
  messageBox;
31
31
  bigWinContainer = null;
32
+ mobileSpeedButton = new SpeedControlButtonMobile();
32
33
  mysteryWinContainer = null;
33
34
  bonusWinContainer = null;
34
35
  superBonusWinContainer = null;
@@ -59,6 +60,9 @@ export class AbstractMainContainer extends Container {
59
60
  this.name = AbstractMainContainer.registryName;
60
61
  this.buttonsEventManager = new ButtonsEventManager();
61
62
  this.messageBox = new MessageBox();
63
+ if (!RainMan.componentRegistry.has(SpeedControlButtonMobile.registryName)) {
64
+ RainMan.componentRegistry.add(this.mobileSpeedButton);
65
+ }
62
66
  SoundManager.setPlayStatus(SoundTracks.music, RainMan.settingsStore.shouldPlayAmbientMusic);
63
67
  RainMan.settingsStore.setHandleInvokeFreeSpinPlate(this.invokeFreeSpinPlate.bind(this));
64
68
  RainMan.settingsStore.setHandleBuyFreeSpinPopupActivatio(this.invokeBuyFreeSpinPlate.bind(this));
@@ -75,6 +79,13 @@ export class AbstractMainContainer extends Container {
75
79
  * @returns {Promise<void>} - A promise that resolves when the animation is complete.
76
80
  */
77
81
  async invokeMysteryWinPlate(_animationName, _popupAnimation, _winType, _amount, _isAdditionalFreeSpins) { }
82
+ attachMobileSpeedButton(parent) {
83
+ if (this.mobileSpeedButton.parent) {
84
+ this.mobileSpeedButton.parent.removeChild(this.mobileSpeedButton);
85
+ }
86
+ parent.addChild(this.mobileSpeedButton);
87
+ this.mobileSpeedButton.adaptToSpeed(RainMan.componentRegistry.getSpeedLevel());
88
+ }
78
89
  /**
79
90
  * This function initializes the spines that should be paused when the battery saver is on
80
91
  * @protected
@@ -771,7 +782,7 @@ export class AbstractMainContainer extends Container {
771
782
  * @returns {RightButtonsMobile} - Returns a new instance of RightButtonsMobile for mobile portrait orientation.
772
783
  */
773
784
  createRightButtonsMobile() {
774
- return new RightButtonsMobile();
785
+ return new RightButtonsMobile(this.mobileSpeedButton);
775
786
  }
776
787
  /**
777
788
  * Function for creating the left buttons for landscape orientation
@@ -795,7 +806,7 @@ export class AbstractMainContainer extends Container {
795
806
  * @returns {MiddleButtons} - Returns a new instance of MiddleButtons for portrait orientation.
796
807
  */
797
808
  createPortraitPlayButtons() {
798
- return new MiddleButtons();
809
+ return new MiddleButtons(false);
799
810
  }
800
811
  /**
801
812
  * Function for creating the landscape play buttons
@@ -803,7 +814,7 @@ export class AbstractMainContainer extends Container {
803
814
  * @returns {MiddleButtons} - Returns a new instance of MiddleButtons for landscape orientation.
804
815
  */
805
816
  createLandscapePlayButtons() {
806
- return new MiddleButtons(true);
817
+ return new MiddleButtons(true, this.mobileSpeedButton);
807
818
  }
808
819
  /**
809
820
  * This method initializes the components for the current screen ratio.
@@ -1,6 +1,6 @@
1
1
  import { Graphics } from "pixi.js";
2
2
  import { ResponsiveContainer } from "../../common";
3
- import { AutoplayButton, GambleButton, PlusButton, RefreshButton, TakeButton } from "../default";
3
+ import { AutoplayButton, GambleButton, PlusButton, RefreshButton, SpeedControlButtonMobile, TakeButton } from "../default";
4
4
  /**
5
5
  * Class for managing middle buttons in game (MOBILE ONLY).
6
6
  * The class uses {@link AutoplayButton}, {@link PlusButton}, {@link RefreshButton}.
@@ -8,13 +8,14 @@ import { AutoplayButton, GambleButton, PlusButton, RefreshButton, TakeButton } f
8
8
  */
9
9
  export declare class MiddleButtons extends ResponsiveContainer {
10
10
  private landscape;
11
+ private readonly speedButtonMobile?;
11
12
  protected readonly autoplayButton: AutoplayButton;
12
13
  protected readonly betButton: PlusButton;
13
14
  protected readonly refreshButton: RefreshButton;
14
15
  protected readonly positioningFrame: Graphics;
15
16
  protected readonly gambleButton: GambleButton;
16
17
  protected readonly takeButton: TakeButton;
17
- constructor(landscape?: boolean);
18
+ constructor(landscape?: boolean, speedButtonMobile?: SpeedControlButtonMobile | undefined);
18
19
  /**
19
20
  * Function for removing children from the registry
20
21
  * @public
@@ -11,26 +11,37 @@ import { REGISTRY } from "../registrynames";
11
11
  */
12
12
  export class MiddleButtons extends ResponsiveContainer {
13
13
  landscape;
14
+ speedButtonMobile;
14
15
  autoplayButton = new AutoplayButton(true);
15
16
  betButton = new PlusButton(true);
16
17
  refreshButton = new RefreshButton(true);
17
18
  positioningFrame = new Graphics();
18
19
  gambleButton = new GambleButton(true);
19
20
  takeButton = new TakeButton(true);
20
- constructor(landscape = false) {
21
+ constructor(landscape = false, speedButtonMobile) {
21
22
  super();
22
23
  this.landscape = landscape;
24
+ this.speedButtonMobile = speedButtonMobile;
23
25
  this.name = REGISTRY.middleButtons;
24
26
  this.parentLayer = UXLayer;
25
27
  const buttons = [this.autoplayButton, this.betButton, this.refreshButton];
26
28
  this.addChild(...buttons);
27
29
  RainMan.componentRegistry.addMany(buttons);
30
+ if (this.speedButtonMobile) {
31
+ this.speedButtonMobile.visible = true;
32
+ this.speedButtonMobile.scale.set(1);
33
+ this.speedButtonMobile.adaptToSpeed(RainMan.componentRegistry.getSpeedLevel());
34
+ this.addChild(this.speedButtonMobile);
35
+ }
28
36
  this.addGambleAndTakeButtons();
29
37
  this.positioningFrame.beginFill(0x000000, 0);
30
38
  this.positioningFrame.drawRect(0, 0, 800, 250);
31
39
  this.addChild(this.positioningFrame);
32
40
  this.refreshButton.scale.set(0.75);
33
41
  this.autoplayButton.scale.set(0.75);
42
+ if (this.speedButtonMobile) {
43
+ this.speedButtonMobile.scale.set(1);
44
+ }
34
45
  this.betButton.scale.set(0.75);
35
46
  this.takeButton.scale.set(0.75);
36
47
  this.gambleButton.scale.set(0.75);
@@ -63,7 +74,6 @@ export class MiddleButtons extends ResponsiveContainer {
63
74
  this.refreshButton,
64
75
  this.autoplayButton,
65
76
  this.betButton,
66
- this.positioningFrame,
67
77
  ...(this.gambleButton && RainMan.config.gambleGameEnabled ? [this.gambleButton] : []),
68
78
  ...(this.takeButton ? [this.takeButton] : [])
69
79
  ]);
@@ -92,15 +102,19 @@ export class MiddleButtons extends ResponsiveContainer {
92
102
  const { width, height } = this.positioningFrame;
93
103
  this.refreshButton.x = width / 2;
94
104
  this.refreshButton.y = height / 2;
105
+ if (this.speedButtonMobile) {
106
+ this.speedButtonMobile.x = width / 2;
107
+ this.speedButtonMobile.y = 150;
108
+ }
95
109
  if (this.landscape) {
96
110
  this.betButton.x = width / 2;
97
- this.betButton.y = 0;
111
+ this.betButton.y = -20;
98
112
  this.autoplayButton.x = width / 2;
99
- this.autoplayButton.y = height;
113
+ this.autoplayButton.y = height + 30;
100
114
  }
101
115
  else {
102
116
  this.autoplayButton.x = -width;
103
- this.autoplayButton.y = 0;
117
+ this.autoplayButton.y = 50;
104
118
  this.betButton.x = width;
105
119
  this.betButton.y = height;
106
120
  }
@@ -1,17 +1,17 @@
1
1
  import { ITextStyle } from "pixi.js";
2
2
  import { ResponsiveContainer } from "../../common";
3
3
  import { UpdatableTextComponent } from "../../updatable";
4
- import { InfoButton, NegButton, SpeedControlButton } from "../default";
4
+ import { InfoButton, NegButton, SpeedControlButtonMobile } from "../default";
5
5
  /**
6
6
  * Buttons for mobile version. It has a current bet and game information button.
7
7
  * @augments {ResponsiveContainer}
8
8
  */
9
9
  export declare class RightButtonsMobile extends ResponsiveContainer {
10
+ private readonly speedButtonMobile;
10
11
  protected readonly infoButton: InfoButton;
11
12
  protected readonly negButton: NegButton;
12
- protected readonly speedControlButton: SpeedControlButton;
13
13
  protected bet: UpdatableTextComponent;
14
- constructor();
14
+ constructor(speedButtonMobile: SpeedControlButtonMobile);
15
15
  /**
16
16
  * Function for removing children from the registry.
17
17
  * @public
@@ -3,7 +3,7 @@ import { UXLayer } from "../../../layers";
3
3
  import { RainMan } from "../../../Rainman";
4
4
  import { ResponsiveContainer } from "../../common";
5
5
  import { UPDATABLE_MODES, UpdatableTextComponent } from "../../updatable";
6
- import { InfoButton, NegButton, SpeedControlButton } from "../default";
6
+ import { InfoButton, NegButton } from "../default";
7
7
  import { COMPONENTS, REGISTRY } from "../registrynames";
8
8
  import { MOBILE_BUTTONS_GAP, MOBILE_PANEL_BUTTON_SCALE, MOBILE_X_OFFSET, MOBILE_Y_BOTTOM_OFFSET } from "./constants";
9
9
  /**
@@ -11,22 +11,25 @@ import { MOBILE_BUTTONS_GAP, MOBILE_PANEL_BUTTON_SCALE, MOBILE_X_OFFSET, MOBILE_
11
11
  * @augments {ResponsiveContainer}
12
12
  */
13
13
  export class RightButtonsMobile extends ResponsiveContainer {
14
+ speedButtonMobile;
14
15
  infoButton = new InfoButton(true);
15
16
  negButton = new NegButton();
16
- speedControlButton = new SpeedControlButton();
17
17
  bet = new UpdatableTextComponent(COMPONENTS.betText, i18next.t("bet"), UPDATABLE_MODES.money, RainMan.config.mobileFontSize);
18
- constructor() {
18
+ constructor(speedButtonMobile) {
19
19
  super(-1);
20
+ this.speedButtonMobile = speedButtonMobile;
20
21
  this.name = REGISTRY.rightButtons;
21
22
  this.parentLayer = UXLayer;
22
23
  this.negButton.visible = false;
23
- this.speedControlButton.visible = false;
24
24
  this.infoButton.anchor.set(1, 0.5);
25
25
  this.infoButton.scale.set(-MOBILE_PANEL_BUTTON_SCALE);
26
26
  this.bet.pivot.set(this.bet.width, this.bet.height / 2);
27
27
  this.bet.scale.set(-1);
28
- this.addChild(this.infoButton, this.bet);
29
- RainMan.componentRegistry.addMany([this.infoButton, this.bet, this.negButton, this.speedControlButton]);
28
+ this.speedButtonMobile.scale.set(-1);
29
+ this.speedButtonMobile.x = 100;
30
+ this.speedButtonMobile.y = 200;
31
+ this.addChild(this.infoButton, this.bet, this.speedButtonMobile);
32
+ RainMan.componentRegistry.addMany([this.infoButton, this.bet, this.negButton]);
30
33
  this.resize();
31
34
  }
32
35
  /**
@@ -35,7 +38,7 @@ export class RightButtonsMobile extends ResponsiveContainer {
35
38
  * @returns {void}
36
39
  */
37
40
  removeChildrenFromRegistry() {
38
- RainMan.componentRegistry.removeMany([this.infoButton, this.bet, this.negButton, this.speedControlButton]);
41
+ RainMan.componentRegistry.removeMany([this.infoButton, this.bet, this.negButton]);
39
42
  }
40
43
  /**
41
44
  * Function for repositioning components on mobile screens.
@@ -0,0 +1,22 @@
1
+ import { SpeedAdapterInterface, SpeedLevel, SpeedLevelDirectory } from "../../../application";
2
+ import { BaseButton, TextureMap } from "../BaseButton";
3
+ /**
4
+ * Button for speed control {@link SpeedLevel}
5
+ * It needs following textures:
6
+ * - turtle - `speed-turtle.png`
7
+ * - rabbit - `speed-rabbit.png`
8
+ * - cheetah - `speed-cheetah.png`
9
+ * @class
10
+ */
11
+ export declare class SpeedControlButtonMobile extends BaseButton implements SpeedAdapterInterface<TextureMap> {
12
+ static readonly registryName = "speedControlButtonMobile";
13
+ readonly speedLevelMapper: SpeedLevelDirectory<TextureMap>;
14
+ constructor();
15
+ /**
16
+ * Change button's texture according to speed level
17
+ * @public
18
+ * @param {SpeedLevel} speedLevel - new speed level
19
+ * @returns {void}
20
+ */
21
+ adaptToSpeed(speedLevel: SpeedLevel): void;
22
+ }
@@ -0,0 +1,51 @@
1
+ import { RainMan } from "../../../Rainman";
2
+ import { getTexture } from "../../../utils";
3
+ import { BaseButton, ButtonStates } from "../BaseButton";
4
+ const createTextureMap = (speedLevel) => {
5
+ return {
6
+ [ButtonStates.NORMAL]: getTexture(`${speedLevel}.png`),
7
+ [ButtonStates.CLICKED]: getTexture(`${speedLevel}-c.png`)
8
+ };
9
+ };
10
+ const createTextureMapForSpeed = (speedLevel) => {
11
+ const speedLevelMapper = {
12
+ slow: createTextureMap("turtle"),
13
+ normal: createTextureMap("rabbit"),
14
+ fast: createTextureMap("cheetah")
15
+ };
16
+ return speedLevelMapper[speedLevel];
17
+ };
18
+ /**
19
+ * Button for speed control {@link SpeedLevel}
20
+ * It needs following textures:
21
+ * - turtle - `speed-turtle.png`
22
+ * - rabbit - `speed-rabbit.png`
23
+ * - cheetah - `speed-cheetah.png`
24
+ * @class
25
+ */
26
+ export class SpeedControlButtonMobile extends BaseButton {
27
+ static registryName = "speedControlButtonMobile";
28
+ speedLevelMapper;
29
+ constructor() {
30
+ super(SpeedControlButtonMobile.registryName, createTextureMapForSpeed(RainMan.settingsStore.currentSpeed));
31
+ this.speedLevelMapper = {
32
+ slow: createTextureMap("turtle"),
33
+ normal: createTextureMap("rabbit"),
34
+ fast: createTextureMap("cheetah")
35
+ };
36
+ }
37
+ /**
38
+ * Change button's texture according to speed level
39
+ * @public
40
+ * @param {SpeedLevel} speedLevel - new speed level
41
+ * @returns {void}
42
+ */
43
+ adaptToSpeed(speedLevel) {
44
+ if (RainMan.settingsStore.isTemporarySpeed) {
45
+ return;
46
+ }
47
+ const textureMap = this.speedLevelMapper[speedLevel];
48
+ this.setTextureMap(textureMap);
49
+ this.texture = textureMap[ButtonStates.NORMAL];
50
+ }
51
+ }
@@ -9,6 +9,7 @@ export * from "./PlusButton";
9
9
  export * from "./RedButton";
10
10
  export * from "./RefreshButton";
11
11
  export * from "./SpeedControlButton";
12
+ export * from "./SpeedControlButtonMobile";
12
13
  export * from "./SpinConfirmationButton";
13
14
  export * from "./TakeButton";
14
15
  export * from "./VolumeButton";
@@ -9,6 +9,7 @@ export * from "./PlusButton";
9
9
  export * from "./RedButton";
10
10
  export * from "./RefreshButton";
11
11
  export * from "./SpeedControlButton";
12
+ export * from "./SpeedControlButtonMobile";
12
13
  export * from "./SpinConfirmationButton";
13
14
  export * from "./TakeButton";
14
15
  export * from "./VolumeButton";
@@ -11,6 +11,7 @@ export declare class ConnectionWrapper implements SubscribableConnectionWrapper
11
11
  private fetchInProgress;
12
12
  private lastFetchResponse;
13
13
  private lastGambleResponse;
14
+ private pendingSpinEndPromise;
14
15
  private subscribers;
15
16
  /**
16
17
  * Mapper function for data that comes from the server
@@ -17,6 +17,7 @@ export class ConnectionWrapper {
17
17
  fetchInProgress = false;
18
18
  lastFetchResponse = new Map();
19
19
  lastGambleResponse = null;
20
+ pendingSpinEndPromise = null;
20
21
  subscribers = [];
21
22
  /**
22
23
  * Mapper function for data that comes from the server
@@ -83,6 +84,7 @@ export class ConnectionWrapper {
83
84
  * @memberof ConnectionWrapper
84
85
  */
85
86
  async fetchInitData() {
87
+ this.pendingSpinEndPromise = null;
86
88
  this._initData = (await this.fetch({
87
89
  action: "init",
88
90
  reference: RainMan.config.gameInitReference,
@@ -177,6 +179,9 @@ export class ConnectionWrapper {
177
179
  if (typeof this.config?.token === "undefined") {
178
180
  throw new Error("Token was not defined");
179
181
  }
182
+ if (this.pendingSpinEndPromise !== null) {
183
+ await this.pendingSpinEndPromise;
184
+ }
180
185
  const data = await this.fetch({
181
186
  action: "spin",
182
187
  reference: RainMan.config.gameInitReference,
@@ -186,7 +191,14 @@ export class ConnectionWrapper {
186
191
  extra_data
187
192
  });
188
193
  this._messageHistory.push(data);
189
- this.sendEndOfRoundData(data.round_number);
194
+ const endPromise = this.sendEndOfRoundData(data.round_number);
195
+ let trackedEndPromise = null;
196
+ trackedEndPromise = endPromise.finally(() => {
197
+ if (this.pendingSpinEndPromise === trackedEndPromise) {
198
+ this.pendingSpinEndPromise = null;
199
+ }
200
+ });
201
+ this.pendingSpinEndPromise = trackedEndPromise;
190
202
  return new SpinData(this.config, data);
191
203
  }
192
204
  /**
@@ -1,7 +1,7 @@
1
1
  import i18next from "i18next";
2
2
  import { sample } from "lodash";
3
3
  import { ApiConfig, SoundManager, SoundTracks, SPEED_LEVELS } from "../application";
4
- import { AbstractFrame, AutoplayButton, BUTTONS, COMPONENTS, FreeSpinButton, MessageBox, RefreshButton, SpeedControlButton } from "../components";
4
+ import { AbstractFrame, AutoplayButton, BUTTONS, COMPONENTS, FreeSpinButton, MessageBox, RefreshButton, SpeedControlButton, SpeedControlButtonMobile } from "../components";
5
5
  import { SpinData } from "../connectivity";
6
6
  import { gamePhases, PossibleWins } from "../constants";
7
7
  import { RainMan } from "../Rainman";
@@ -256,7 +256,10 @@ export class AbstractController {
256
256
  * @returns {void}
257
257
  */
258
258
  disableSpeedButton(shouldBeDisabled) {
259
- RainMan.componentRegistry.get(SpeedControlButton.registryName).flipDisabledFlag(shouldBeDisabled);
259
+ RainMan.componentRegistry.getIfExists(SpeedControlButton.registryName)?.flipDisabledFlag(shouldBeDisabled);
260
+ RainMan.componentRegistry
261
+ .getIfExists(SpeedControlButtonMobile.registryName)
262
+ ?.flipDisabledFlag(shouldBeDisabled);
260
263
  }
261
264
  /**
262
265
  * Function for disabling information buttons
@@ -289,14 +292,11 @@ export class AbstractController {
289
292
  */
290
293
  handleDisablingButtons(disabled) {
291
294
  this.disableBetButtons(disabled ?? this.shouldBetButtonsBeDisabled);
292
- if (RainMan.settingsStore.isFreeSpinsPlayEnabled ||
293
- RainMan.globals.isSuperBonusGameEnabled ||
294
- RainMan.settingsStore.isAutoplayEnabled) {
295
- this.disableSpeedButton(false);
296
- }
297
- else {
298
- this.disableSpeedButton(disabled ?? this.shouldButtonsBeDisabled);
299
- }
295
+ const shouldSpeedBeDisabled = !RainMan.settingsStore.isFreeSpinsPlayEnabled &&
296
+ !RainMan.globals.isSuperBonusGameEnabled &&
297
+ !RainMan.settingsStore.isAutoplayEnabled &&
298
+ this.shouldButtonsBeDisabled;
299
+ this.disableSpeedButton(shouldSpeedBeDisabled);
300
300
  }
301
301
  /**
302
302
  * Function for initializing buttons
@@ -339,10 +339,12 @@ export class AbstractController {
339
339
  this.gamePhase === gamePhases.SPINNING ||
340
340
  this.gamePhase === gamePhases.GAME_SUMMARY);
341
341
  }
342
- RainMan.componentRegistry.get(SpeedControlButton.registryName).setOnClick(() => {
342
+ RainMan.componentRegistry.getIfExists(SpeedControlButton.registryName)?.setOnClick(() => {
343
343
  RainMan.settingsStore.closeCurrentlyOpenModal?.();
344
344
  RainMan.componentRegistry.nextSpeedLevel();
345
- this.uiController.messageBox.updateIncentiveText(sample(i18next.t("idleMessages", { returnObjects: true })[RainMan.componentRegistry.getSpeedLevel()]));
345
+ const speedLevel = RainMan.componentRegistry.getSpeedLevel();
346
+ this.uiController.updateShownSpeedLevel(speedLevel);
347
+ this.uiController.messageBox.updateIncentiveText(sample(i18next.t("idleMessages", { returnObjects: true })[speedLevel]));
346
348
  });
347
349
  }
348
350
  /**
@@ -365,6 +367,13 @@ export class AbstractController {
365
367
  }
366
368
  RainMan.settingsStore.handleModalInvocation?.(UI_ITEMS.bet);
367
369
  });
370
+ if (RainMan.componentRegistry.has(SpeedControlButtonMobile.registryName)) {
371
+ RainMan.componentRegistry.get(SpeedControlButtonMobile.registryName).setOnClick(() => {
372
+ RainMan.settingsStore.closeCurrentlyOpenModal?.();
373
+ RainMan.componentRegistry.nextSpeedLevel();
374
+ this.uiController.messageBox.updateIncentiveText(sample(i18next.t("idleMessages", { returnObjects: true })[RainMan.componentRegistry.getSpeedLevel()]));
375
+ });
376
+ }
368
377
  return;
369
378
  }
370
379
  RainMan.componentRegistry.get(BUTTONS.plus).setOnClick(() => {
@@ -149,7 +149,6 @@ export declare class UiController {
149
149
  updateShownPossibleWin(): void;
150
150
  /**
151
151
  * Shows game pays in a bottom panel, if exists
152
- * @param amount
153
152
  * @public
154
153
  * @returns {void}
155
154
  */
@@ -1,5 +1,5 @@
1
1
  import { round } from "lodash";
2
- import { COMPONENTS, MessageBox, SpeedControlButton } from "../components";
2
+ import { COMPONENTS, MessageBox, SpeedControlButton, SpeedControlButtonMobile } from "../components";
3
3
  import { RainMan } from "../Rainman";
4
4
  import { ceilToDecimal } from "../utils";
5
5
  /**
@@ -181,8 +181,12 @@ export class UiController {
181
181
  * @returns {void}
182
182
  */
183
183
  updateShownSpeedLevel(currentSpeed) {
184
- RainMan.componentRegistry.get(SpeedControlButton.registryName).adaptToSpeed(currentSpeed);
185
- RainMan.componentRegistry.get(SpeedControlButton.registryName).forceTextureUpdate();
184
+ const speedButton = RainMan.componentRegistry.getIfExists(SpeedControlButton.registryName);
185
+ const mobileSpeedButton = RainMan.componentRegistry.getIfExists(SpeedControlButtonMobile.registryName);
186
+ speedButton?.adaptToSpeed(currentSpeed);
187
+ speedButton?.forceTextureUpdate();
188
+ mobileSpeedButton?.adaptToSpeed(currentSpeed);
189
+ mobileSpeedButton?.forceTextureUpdate();
186
190
  }
187
191
  /**
188
192
  * Function for updating shown balance
@@ -216,7 +220,6 @@ export class UiController {
216
220
  }
217
221
  /**
218
222
  * Shows game pays in a bottom panel, if exists
219
- * @param amount
220
223
  * @public
221
224
  * @returns {void}
222
225
  */
@@ -16,15 +16,16 @@ const interruptedAudioResume = () => {
16
16
  window.addEventListener("touchend", resumeAudioOnInterruption, { once: true, passive: true });
17
17
  };
18
18
  /**
19
- * Pauses audio when the page goes to the background.
19
+ * Clears pending foreground recovery when the page goes to the background.
20
+ * Pixi handles the actual pause state through its WebAudio auto-pause flow.
20
21
  * @returns {void}
21
22
  */
22
- const pauseAudio = () => {
23
+ const prepareAudioForPageHide = () => {
23
24
  isResumingAudio = false;
24
25
  shouldResumeAudioOnInterruption = false;
25
26
  removeInterruptedAudioResumeListeners();
26
27
  if (RainMan.settingsStore.isSoundEnabled()) {
27
- SoundManager.pauseSoundsForPageHide();
28
+ SoundManager.captureAmbientPlaybackPositionForPageHide();
28
29
  }
29
30
  };
30
31
  /**
@@ -71,19 +72,19 @@ function resumeAudioOnInterruption() {
71
72
  */
72
73
  const handleVisibilityChange = () => {
73
74
  if (document.hidden) {
74
- pauseAudio();
75
+ prepareAudioForPageHide();
75
76
  return;
76
77
  }
77
- interruptedAudioResume();
78
+ resumeAudio();
78
79
  };
79
80
  /**
80
81
  * Sets up audio states for tab switches and iOS app interruptions.
81
82
  * @returns {void}
82
83
  */
83
84
  export const loadSoundsOnTabChange = () => {
84
- sound.disableAutoPause = true;
85
+ sound.disableAutoPause = false;
85
86
  document.addEventListener("visibilitychange", handleVisibilityChange);
86
- window.addEventListener("pagehide", () => pauseAudio());
87
+ window.addEventListener("pagehide", () => prepareAudioForPageHide());
87
88
  window.addEventListener("pageshow", () => resumeAudio());
88
89
  window.addEventListener("focus", () => resumeAudio());
89
90
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pixi-rainman-game-engine",
3
- "version": "0.3.42",
3
+ "version": "0.3.43b",
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",