pixi-rainman-game-engine 0.3.40 → 0.3.42

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.
@@ -7,6 +7,7 @@ import { Color } from "pixi.js";
7
7
  export const defaultAppConfig = {
8
8
  gameWidth: 1920,
9
9
  gameHeight: 1080,
10
+ enableInfinityAutoplay: false,
10
11
  reinitMode: false,
11
12
  reinitSymbolIndexFix: 0,
12
13
  idleTimeout: 10000,
@@ -13,6 +13,7 @@ export type RequiredAppConfig = {
13
13
  export type OptionalAppConfig = {
14
14
  gameWidth: number;
15
15
  gameHeight: number;
16
+ enableInfinityAutoplay: boolean;
16
17
  reinitMode: boolean;
17
18
  reinitSymbolIndexFix: number;
18
19
  idleTimeout: number;
@@ -4,6 +4,7 @@ import i18next from "i18next";
4
4
  import { observer } from "mobx-react-lite";
5
5
  import React from "react";
6
6
  import { COMPONENTS } from "../../../components";
7
+ import { RainMan } from "../../../Rainman";
7
8
  import { UI_ITEMS } from "../../../utils";
8
9
  import { useStores } from "../../hooks";
9
10
  import { CloseModalButton } from "../CloseModalButton";
@@ -11,11 +12,18 @@ import { RichLimitingSlider } from "../RichLimitingSlider";
11
12
  import { SpeedButtonWithHeader, SwitchWithHeader } from "../SwitchWithHeader";
12
13
  const initialAutoSpinCount = 10;
13
14
  const availableAutoSpinLimits = [10, 20, 50, 70, 100];
15
+ const infiniteAutoSpinCount = 1000000;
14
16
  export const AutoplaySettings = observer(() => {
15
17
  const { settingStore } = useStores();
18
+ const infinityAutoplayEnabled = RainMan.config.enableInfinityAutoplay;
16
19
  const updateLimitsBounds = (newLimit) => {
20
+ settingStore.setInfinitySpinsEnabled(false);
17
21
  setAutoSpinsCount(newLimit);
18
22
  };
23
+ const enableInfinityAutoplay = () => {
24
+ settingStore.setInfinitySpinsEnabled(true);
25
+ setAutoSpinsCount(infiniteAutoSpinCount);
26
+ };
19
27
  const { singleWinExceeds, balancedIncreased, balancedDecreased, howManyAutoSpinsLeft, infinitySpinsEnabled, setSingleWinExceeds, setBalancedIncreased, setBalancedDecreased, setAutoSpinsCount, resetAutoplaySettings } = settingStore;
20
28
  return (<div className="autoplay-settings">
21
29
  <CloseModalButton layerId={UI_ITEMS.autoplay}/>
@@ -23,9 +31,14 @@ export const AutoplaySettings = observer(() => {
23
31
  <h2>{i18next.t("autoPlay")}</h2>
24
32
  </div>
25
33
  <div className="autoplay-settings__stop-limits">
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))}>
34
+ {availableAutoSpinLimits.map((limiter, index) => (<button key={`${limiter}${index}`} value={limiter} className={`autoplay-settings__limit ${!infinitySpinsEnabled && limiter === howManyAutoSpinsLeft
35
+ ? "autoplay-settings__limit--active"
36
+ : ""} middle`} onClick={(event) => updateLimitsBounds(Number(event.currentTarget.value))}>
27
37
  <p>{limiter}</p>
28
38
  </button>))}
39
+ {infinityAutoplayEnabled && (<button value="infinity" className={`autoplay-settings__limit ${infinitySpinsEnabled ? "autoplay-settings__limit--active" : ""} right`} onClick={enableInfinityAutoplay}>
40
+ <p>∞</p>
41
+ </button>)}
29
42
  </div>
30
43
  <div className="separator"/>
31
44
  <div className="autoplay-settings__title">
@@ -5,13 +5,13 @@ export const SpeedButtonSwitch = observer(() => {
5
5
  const speedLevelMapper = {
6
6
  slow: "turtle",
7
7
  normal: "rabbit",
8
- fast: "cheetah",
8
+ fast: "cheetah"
9
9
  };
10
10
  const { settingStore } = useStores();
11
11
  const { currentSpeed } = settingStore;
12
12
  return (<label className="switch turtle-switch speed-level-id">
13
13
  <span className="turtle-switch-round-box"/>
14
- <img className="turtle-switch__icon" src={`assets/settings/speed-${speedLevelMapper[currentSpeed]}.png`}/>
14
+ <img className="turtle-switch__icon" src={`assets/settings/speed-${speedLevelMapper[currentSpeed]}.webp`}/>
15
15
  <div className="dot-container">
16
16
  {Array.from({ length: 3 }).map((_, i) => (<div key={i} className={`white-dot ${i < settingStore.indexOfSpeedLevel ? "white-dot--active" : ""}`}/>))}
17
17
  </div>
@@ -36,7 +36,7 @@
36
36
  bottom: 5px;
37
37
  width: 30px;
38
38
  height: 30px;
39
- background-image: url("/assets/settings/sound-icon.png");
39
+ background-image: url("/assets/settings/sound-icon.webp");
40
40
  background-position: center;
41
41
  background-size: contain;
42
42
  background-repeat: no-repeat;
@@ -16,6 +16,7 @@ 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
20
  constructor();
20
21
  /**
21
22
  * Function for loading sounds into pixi
@@ -37,7 +38,7 @@ export declare class SoundManagerInstance implements SpeedAdapterInterface<Sound
37
38
  * @returns {void}
38
39
  */
39
40
  stopPlayingFxSounds(): void;
40
- stopSoundsForPageHide(): void;
41
+ pauseSoundsForPageHide(): void;
41
42
  /**
42
43
  * Function for resuming and pausing sound in game
43
44
  * @public
@@ -76,6 +77,12 @@ export declare class SoundManagerInstance implements SpeedAdapterInterface<Sound
76
77
  * @returns {void}
77
78
  */
78
79
  restoreAmbientPlayback(): void;
80
+ /**
81
+ * Restores sounds paused because of page visibility change.
82
+ * @public
83
+ * @returns {void}
84
+ */
85
+ restorePlaybackAfterPageHide(): void;
79
86
  /**
80
87
  * Function for pausing all sounds in game
81
88
  * If sound is not playing, it will be skipped
@@ -1,7 +1,12 @@
1
1
  import { Sound, sound } from "@pixi/sound";
2
2
  import { RainMan } from "../../Rainman";
3
3
  import { SoundTracks } from "./types";
4
- const soundsToLoop = [SoundTracks.mysteryFxLoop, SoundTracks.music, SoundTracks.freeSpinsMusic];
4
+ const soundsToLoop = [
5
+ SoundTracks.mysteryFxLoop,
6
+ SoundTracks.music,
7
+ SoundTracks.freeSpinsMusic,
8
+ SoundTracks.cardDrawing
9
+ ];
5
10
  export const pixiSoundContext = sound.context;
6
11
  /**
7
12
  * Class for managing sound in game, uses the pixi sound library
@@ -26,6 +31,7 @@ export class SoundManagerInstance {
26
31
  };
27
32
  musicCatalogue = new Map();
28
33
  currentAmbientTrack = null;
34
+ pausedSoundsForPageHide = new Set();
29
35
  constructor() {
30
36
  this.overrides = this.speedLevelMapper.slow;
31
37
  }
@@ -89,19 +95,18 @@ export class SoundManagerInstance {
89
95
  sound.stop();
90
96
  }
91
97
  }
92
- stopSoundsForPageHide() {
93
- const music = this.getSound(SoundTracks.music);
94
- const freeSpinsMusic = this.getSound(SoundTracks.freeSpinsMusic);
95
- const isMusicPlaying = Boolean(music?.isPlaying && !music.paused);
96
- const isFreeSpinsMusicPlaying = Boolean(freeSpinsMusic?.isPlaying && !freeSpinsMusic.paused);
97
- if (isFreeSpinsMusicPlaying) {
98
- this.currentAmbientTrack = SoundTracks.freeSpinsMusic;
99
- }
100
- else if (isMusicPlaying) {
101
- this.currentAmbientTrack = SoundTracks.music;
102
- }
103
- this.stopAmbientMusic();
104
- this.stopPlayingFxSounds();
98
+ pauseSoundsForPageHide() {
99
+ this.pausedSoundsForPageHide.clear();
100
+ this.musicCatalogue.forEach((sound, soundName) => {
101
+ if (!sound.isPlaying || sound.paused) {
102
+ return;
103
+ }
104
+ if (soundName === SoundTracks.freeSpinsMusic || soundName === SoundTracks.music) {
105
+ this.currentAmbientTrack = soundName;
106
+ }
107
+ this.pausedSoundsForPageHide.add(soundName);
108
+ sound.pause();
109
+ });
105
110
  }
106
111
  /**
107
112
  * Function for resuming and pausing sound in game
@@ -148,7 +153,8 @@ export class SoundManagerInstance {
148
153
  sound.play({
149
154
  singleInstance: true,
150
155
  loop: soundsToLoop.includes(soundName),
151
- volume: this.getVolumeLevel(soundName)
156
+ volume: this.getVolumeLevel(soundName),
157
+ speed: soundName === "cardDrawing" ? 0.8 : 1
152
158
  });
153
159
  }
154
160
  /**
@@ -225,6 +231,32 @@ export class SoundManagerInstance {
225
231
  (RainMan.settingsStore.isFreeSpinsStarted ? SoundTracks.freeSpinsMusic : SoundTracks.music);
226
232
  this.play(ambientTrack);
227
233
  }
234
+ /**
235
+ * Restores sounds paused because of page visibility change.
236
+ * @public
237
+ * @returns {void}
238
+ */
239
+ restorePlaybackAfterPageHide() {
240
+ if (!RainMan.settingsStore.isSoundStarted) {
241
+ this.pausedSoundsForPageHide.clear();
242
+ return;
243
+ }
244
+ if (!this.pausedSoundsForPageHide.size) {
245
+ this.restoreAmbientPlayback();
246
+ return;
247
+ }
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);
258
+ });
259
+ }
228
260
  /**
229
261
  * Function for pausing all sounds in game
230
262
  * If sound is not playing, it will be skipped
@@ -311,7 +343,7 @@ export class SoundManagerInstance {
311
343
  if (!isAudioContextRunning) {
312
344
  return false;
313
345
  }
314
- this.restoreAmbientPlayback();
346
+ this.restorePlaybackAfterPageHide();
315
347
  return true;
316
348
  }
317
349
  /**
@@ -64,6 +64,10 @@ export interface SoundTracks {
64
64
  rouletteStop: "rouletteStop";
65
65
  mysterySymbol: "mysterySymbol";
66
66
  jackpotWin: "jackpotWin";
67
+ cardWin: "cardWin";
68
+ cardLose: "cardLose";
69
+ cardDrawing: "cardDrawing";
70
+ cardCollect: "cardCollect";
67
71
  }
68
72
  export type SoundTrack = SoundTracks[keyof SoundTracks];
69
73
  /**
@@ -61,5 +61,9 @@ export const SoundTracks = {
61
61
  rouletteStart: "rouletteStart",
62
62
  rouletteLoop: "rouletteLoop",
63
63
  rouletteStop: "rouletteStop",
64
- jackpotWin: "jackpotWin"
64
+ jackpotWin: "jackpotWin",
65
+ cardWin: "cardWin",
66
+ cardLose: "cardLose",
67
+ cardDrawing: "cardDrawing",
68
+ cardCollect: "cardCollect"
65
69
  };
@@ -563,6 +563,10 @@ export class AbstractMainContainer extends Container {
563
563
  this.messageBox.visible = true;
564
564
  RainMan.settingsStore.isGambleGameActive = false;
565
565
  this._controller.updateGambleValues(RainMan.settingsStore.gambleGameWin);
566
+ if (RainMan.settingsStore.gambleGameWin > 0) {
567
+ RainMan.settingsStore.useTakeAction?.();
568
+ }
569
+ RainMan.settingsStore.disableSpecialButtons();
566
570
  this.gambleGame = null;
567
571
  }
568
572
  this.hideOverlay();
@@ -1,6 +1,7 @@
1
1
  import i18next from "i18next";
2
2
  import { Container, Sprite, Text, Texture } from "pixi.js";
3
3
  import { Spine } from "pixi-spine";
4
+ import { SoundManager } from "../../application";
4
5
  import { COLOR_BUTTONS_CENTER_OFFSET, COLOR_BUTTONS_SCALE_X, gamblesTextStyle } from "../../constants";
5
6
  import { RainMan } from "../../Rainman";
6
7
  import { getDeviceOrientation, getSpineData, globalMinRatio } from "../../utils";
@@ -60,6 +61,7 @@ export class GambleGame extends Container {
60
61
  this.addChildrenOnce();
61
62
  this.bindButtonsOnce();
62
63
  this.resize();
64
+ SoundManager.play("cardDrawing");
63
65
  }
64
66
  configureButtons() {
65
67
  this.collectButton.eventMode = "static";
@@ -86,8 +88,11 @@ export class GambleGame extends Container {
86
88
  this.redButton.setOnClick(() => this.handleGamblePlay("red"));
87
89
  this.blackButton.setOnClick(() => this.handleGamblePlay("black"));
88
90
  this.collectButton.setOnClick(() => {
91
+ SoundManager.stop("cardDrawing");
92
+ SoundManager.play("music");
89
93
  RainMan.settingsStore.gambleGameWin = this.lastWinAmount;
90
94
  RainMan.componentRegistry.get(BUTTONS.gamble).flipDisabledFlag(true);
95
+ SoundManager.play("cardCollect");
91
96
  this.resolver(this.balanceValue);
92
97
  });
93
98
  }
@@ -170,6 +175,8 @@ export class GambleGame extends Container {
170
175
  if (this.lastWinAmount === 0) {
171
176
  return;
172
177
  }
178
+ SoundManager.stop("cardDrawing");
179
+ this.collectButton.flipDisabledFlag(true);
173
180
  this.redButton.flipDisabledFlag(true);
174
181
  this.blackButton.flipDisabledFlag(true);
175
182
  this.card.state.addAnimation(0, "wait", true, 0);
@@ -178,6 +185,12 @@ export class GambleGame extends Container {
178
185
  this.card.state.clearTracks();
179
186
  this.card.state.clearListeners();
180
187
  this.card.state.addAnimation(0, gambleData.winning_card, false, 0);
188
+ if (gambleData.win_amount > 0) {
189
+ SoundManager.play("cardWin");
190
+ }
191
+ else {
192
+ SoundManager.play("cardLose");
193
+ }
181
194
  this.card.state.addListener({ complete: () => resolve() });
182
195
  });
183
196
  if (gambleData.win_amount > 0) {
@@ -186,6 +199,8 @@ export class GambleGame extends Container {
186
199
  this.blackButton.flipDisabledFlag(false);
187
200
  this.redButton.eventMode = "static";
188
201
  this.blackButton.eventMode = "static";
202
+ SoundManager.play("cardDrawing");
203
+ this.collectButton.flipDisabledFlag(false);
189
204
  }
190
205
  this.card.state.addAnimation(0, "wait2", true, 0);
191
206
  this.lastWinAmount = gambleData.win_amount;
@@ -197,8 +212,10 @@ export class GambleGame extends Container {
197
212
  this.card.state.addAnimation(0, "idle", true, 0);
198
213
  await this.loseDoubleAndWinFields();
199
214
  window.setTimeout(() => {
215
+ SoundManager.stop("cardDrawing");
216
+ SoundManager.play("music");
200
217
  this.closeGame();
201
- }, 2000);
218
+ }, 500);
202
219
  }
203
220
  }
204
221
  /**
@@ -95,6 +95,16 @@ export declare abstract class AbstractColumnsContainer extends Container impleme
95
95
  * @returns {Promise<void>}
96
96
  */
97
97
  configBlindSpin(finalSymbolReels: SymbolReels, afterSpinResolver: (value: void | PromiseLike<void>) => void, indexesOfReelsToExtendSpinningTime: number[]): Promise<void>;
98
+ /**
99
+ * This function is responsible for setting symbols in single column
100
+ * @public
101
+ * @param {SymbolReels} finalSymbolReels final symbols in columns
102
+ * @param {(value: void | PromiseLike<void>) => void} afterSpinResolver resolver for after spin
103
+ * @param {number} columnIndex index of column to configure
104
+ * @param {boolean} shouldExtendSpinningTime determines whether to extend spinning time for the column
105
+ * @returns {Promise<void>}
106
+ */
107
+ configBlindSpinForColumn(finalSymbolReels: SymbolReels, afterSpinResolver: (value: void | PromiseLike<void>) => void, columnIndex: number, shouldExtendSpinningTime?: boolean): Promise<void>;
98
108
  /**
99
109
  * This cancels the timeout, removes the stored delayed action and invalidates
100
110
  * any older timeout callback that might still try to execute later.
@@ -227,4 +237,12 @@ export declare abstract class AbstractColumnsContainer extends Container impleme
227
237
  * @returns {PlayableAction[]} - array of playable actions
228
238
  */
229
239
  protected prepareConfiguringReelsEnd(finalSymbolReels: SymbolReels): PlayableAction[];
240
+ /**
241
+ * Function for preparing configuring for single reel
242
+ * @protected
243
+ * @param {SymbolReels} finalSymbolReels - final symbol reels
244
+ * @param {number} columnIndex - column index
245
+ * @returns {PlayableAction} - playable action
246
+ */
247
+ protected prepareConfiguringReelsEndForIndex(finalSymbolReels: SymbolReels, columnIndex: number): PlayableAction;
230
248
  }
@@ -187,6 +187,63 @@ export class AbstractColumnsContainer extends Container {
187
187
  resolve();
188
188
  });
189
189
  }
190
+ /**
191
+ * This function is responsible for setting symbols in single column
192
+ * @public
193
+ * @param {SymbolReels} finalSymbolReels final symbols in columns
194
+ * @param {(value: void | PromiseLike<void>) => void} afterSpinResolver resolver for after spin
195
+ * @param {number} columnIndex index of column to configure
196
+ * @param {boolean} shouldExtendSpinningTime determines whether to extend spinning time for the column
197
+ * @returns {Promise<void>}
198
+ */
199
+ async configBlindSpinForColumn(finalSymbolReels, afterSpinResolver, columnIndex, shouldExtendSpinningTime = false) {
200
+ return new Promise(async (resolve) => {
201
+ this.clearPendingMysteryDelay();
202
+ this.mysteryFxGeneration = 0;
203
+ this.skipRemainingMysteryFxs = false;
204
+ const frame = RainMan.componentRegistry.get(AbstractFrame.registryName);
205
+ const column = this.allColumns[columnIndex];
206
+ if (column === undefined) {
207
+ afterSpinResolver();
208
+ resolve();
209
+ return;
210
+ }
211
+ const configureSpinAction = this.prepareConfiguringReelsEndForIndex(finalSymbolReels, columnIndex);
212
+ if (this.quickReelsStop ||
213
+ this.skipRemainingMysteryFxs ||
214
+ RainMan.settingsStore.currentSpeed === SPEED_LEVELS.fast) {
215
+ await configureSpinAction();
216
+ SoundManager.play(SoundTracks.allRollStops);
217
+ }
218
+ else if (shouldExtendSpinningTime) {
219
+ this.currentColumn = column;
220
+ column.adaptToSpeed(RainMan.settingsStore.currentSpeed);
221
+ frame.invokeMysteryFx(column.x, column.y);
222
+ this.skipScatterDelay = configureSpinAction;
223
+ await column.speedUpForMysterySpin(100, 1500);
224
+ if (this.quickReelsStop || this.skipRemainingMysteryFxs) {
225
+ await column.changeVelocity(Speed.STOP, 0, frame);
226
+ }
227
+ else {
228
+ if (!RainMan.settingsStore.isAutoplayEnabled ||
229
+ (RainMan.settingsStore.isAutoplayEnabled && !this.quickReelsStop)) {
230
+ this.scheduleMysteryDelay(configureSpinAction, RainMan.config.durationOfActions.scatterDelayerTime);
231
+ }
232
+ await column.changeVelocity(Speed.STOP, RainMan.config.durationOfActions.scatterSpinningTime, frame);
233
+ }
234
+ frame.destroyMysteryFx();
235
+ }
236
+ else {
237
+ await configureSpinAction();
238
+ }
239
+ await column.getStopPromise();
240
+ this.clearPendingMysteryDelay();
241
+ this.mysteryFxGeneration = 0;
242
+ this.skipRemainingMysteryFxs = false;
243
+ afterSpinResolver();
244
+ resolve();
245
+ });
246
+ }
190
247
  /**
191
248
  * This cancels the timeout, removes the stored delayed action and invalidates
192
249
  * any older timeout callback that might still try to execute later.
@@ -441,4 +498,20 @@ export class AbstractColumnsContainer extends Container {
441
498
  this.currentColumn = null;
442
499
  });
443
500
  }
501
+ /**
502
+ * Function for preparing configuring for single reel
503
+ * @protected
504
+ * @param {SymbolReels} finalSymbolReels - final symbol reels
505
+ * @param {number} columnIndex - column index
506
+ * @returns {PlayableAction} - playable action
507
+ */
508
+ prepareConfiguringReelsEndForIndex(finalSymbolReels, columnIndex) {
509
+ const column = this.allColumns[columnIndex];
510
+ return async () => {
511
+ this.currentColumn = column;
512
+ column.configBlindSpin(finalSymbolReels[columnIndex]);
513
+ await column.getTriggersPromise();
514
+ this.currentColumn = null;
515
+ };
516
+ }
444
517
  }
@@ -34,6 +34,8 @@ export declare class MessageBox extends Container {
34
34
  private possibleWinShown;
35
35
  private incentiveMessageShown;
36
36
  private winLineIndicatorShown;
37
+ private winLineIndicatorXOffset;
38
+ private winLineIndicatorYOffset;
37
39
  constructor();
38
40
  /**
39
41
  * Updates the incentive text displayed in the message box.
@@ -84,6 +86,15 @@ export declare class MessageBox extends Container {
84
86
  * @returns {void}
85
87
  */
86
88
  hideWinLineIndicator(): void;
89
+ /**
90
+ * Sets the offset of the win line indicator along the x and y axes. Each time winLineIndicator is shown,
91
+ * the indicator is positioned at the current offset.
92
+ * @public
93
+ * @param {number} xOffset - The amount to move the win line indicator along the x-axis.
94
+ * @param {number} yOffset - The amount to move the win line indicator along the y-axis.
95
+ * @returns {void}
96
+ */
97
+ setWinWinLineIndicatorOffset(xOffset?: number, yOffset?: number): void;
87
98
  /**
88
99
  * Function for resizing message box
89
100
  * It sets the scale of the message box to the global minimum ratio.
@@ -39,6 +39,8 @@ export class MessageBox extends Container {
39
39
  possibleWinShown = false;
40
40
  incentiveMessageShown = false;
41
41
  winLineIndicatorShown = false;
42
+ winLineIndicatorXOffset = 0;
43
+ winLineIndicatorYOffset = 0;
42
44
  constructor() {
43
45
  super();
44
46
  this.name = MessageBox.registryName;
@@ -165,6 +167,18 @@ export class MessageBox extends Container {
165
167
  this.winLineIndicator = undefined;
166
168
  }
167
169
  }
170
+ /**
171
+ * Sets the offset of the win line indicator along the x and y axes. Each time winLineIndicator is shown,
172
+ * the indicator is positioned at the current offset.
173
+ * @public
174
+ * @param {number} xOffset - The amount to move the win line indicator along the x-axis.
175
+ * @param {number} yOffset - The amount to move the win line indicator along the y-axis.
176
+ * @returns {void}
177
+ */
178
+ setWinWinLineIndicatorOffset(xOffset, yOffset) {
179
+ this.winLineIndicatorXOffset += xOffset ?? 0;
180
+ this.winLineIndicatorYOffset += yOffset ?? 0;
181
+ }
168
182
  /**
169
183
  * Function for resizing message box
170
184
  * It sets the scale of the message box to the global minimum ratio.
@@ -334,6 +348,12 @@ export class MessageBox extends Container {
334
348
  };
335
349
  this.winLineIndicator = new WinLineIndicator(winLineTextStyle, getImageForSymbolId, message, symbolIds);
336
350
  this.winLineIndicator.y = 50;
351
+ if (this.winLineIndicatorXOffset) {
352
+ this.winLineIndicator.x = this.winLineIndicatorXOffset;
353
+ }
354
+ if (this.winLineIndicatorYOffset) {
355
+ this.winLineIndicator.y = this.winLineIndicatorYOffset;
356
+ }
337
357
  this.winLineIndicatorShown = true;
338
358
  this.centerTextOnX(this.winLineIndicator);
339
359
  this.addChild(this.winLineIndicator);
@@ -74,6 +74,7 @@ export interface MiniGameInterface {
74
74
  };
75
75
  extra_data: ExtraData;
76
76
  win_amount: number;
77
+ round_win_amount: number;
77
78
  wins: Record<string, RawWin>;
78
79
  matrix: {
79
80
  height: number;
@@ -311,6 +311,7 @@ export class AbstractController {
311
311
  if (RainMan.componentRegistry.has(BUTTONS.gamble)) {
312
312
  this.buttonsEventManager.initGambleButton(() => {
313
313
  RainMan.settingsStore.isGambleGameAvailable = false;
314
+ SoundManager.stopAll();
314
315
  this.mainContainer.invokeGambleGame();
315
316
  }, !RainMan.settingsStore.isGambleGameAvailable ||
316
317
  RainMan.settingsStore.isPlayingAnyAction ||
@@ -322,7 +323,6 @@ export class AbstractController {
322
323
  if (RainMan.componentRegistry.has(BUTTONS.take)) {
323
324
  this.buttonsEventManager.initTakeButton(() => {
324
325
  RainMan.settingsStore.useTakeAction?.();
325
- this.uiController.currentBalance = RainMan.settingsStore.balanceAfterSpin;
326
326
  RainMan.settingsStore.isTakeActionAvailable = false;
327
327
  RainMan.settingsStore.isGambleGameAvailable = false;
328
328
  if (RainMan.componentRegistry.has(BUTTONS.gamble)) {
@@ -842,6 +842,13 @@ export class AbstractController {
842
842
  this.uiController.totalNumberOfFreeSpins += this.getBoughtNumberOfFreeSpins();
843
843
  RainMan.componentRegistry.getIfExists(FreeSpinButton.registryName)?.setInteractivity(false);
844
844
  RainMan.settingsStore.setIsFreeSpinBought(false);
845
+ if (this.speedLevelBeforeFreeSpins === null) {
846
+ this.speedLevelBeforeFreeSpins = RainMan.settingsStore.currentSpeed;
847
+ }
848
+ RainMan.settingsStore.setIsTemporarySpeed(false);
849
+ RainMan.componentRegistry.setSpeedLevel(SPEED_LEVELS.slow);
850
+ this.uiController.updateShownSpeedLevel(SPEED_LEVELS.slow);
851
+ this.disableSpeedButton(false);
845
852
  }
846
853
  /**
847
854
  * This function is responsible for getting the number of free spins bought
@@ -904,6 +911,7 @@ export class AbstractController {
904
911
  logInfo("Take action can't be performed");
905
912
  return;
906
913
  }
914
+ this.shouldPlayNextSpin = false;
907
915
  if (this.currentlyPlayedAction !== undefined) {
908
916
  this.changeGamePhase(gamePhases.STOPPING_HANDLING_ACTIONS);
909
917
  this.stopPlayingSymbolsStreak();
@@ -914,6 +922,7 @@ export class AbstractController {
914
922
  if (takeData.status.code === 200) {
915
923
  RainMan.settingsStore.isTakeActionAvailable = false;
916
924
  logInfo(`💰 Round ${takeData.round_number} ended with balance:`, takeData.balance);
925
+ await this.uiController.animateCurrentBalance(takeData.balance);
917
926
  }
918
927
  else {
919
928
  RainMan.settingsStore.isTakeActionAvailable = true;
@@ -1219,7 +1228,9 @@ export class AbstractController {
1219
1228
  }
1220
1229
  await this.performActionsAfterSpin();
1221
1230
  this.freeSpinsWin = gambleWinAmount > 0 ? gambleWinAmount : null;
1222
- this.gambleGameEnabler(gambleWinAmount);
1231
+ if (!RainMan.globals.isSuperBonusGameEnabled) {
1232
+ this.gambleGameEnabler(gambleWinAmount);
1233
+ }
1223
1234
  if (RainMan.settingsStore.isAutoplayEnabled ||
1224
1235
  (RainMan.settingsStore.howManyFreeSpinsLeft !== 0 && this.shouldAutoplayFreeSpins)) {
1225
1236
  this.handleAutoplayLogic();
@@ -1237,6 +1248,7 @@ export class AbstractController {
1237
1248
  if (this.speedLevelBeforeFreeSpins !== null &&
1238
1249
  !RainMan.settingsStore.isFreeSpinsPlayEnabled &&
1239
1250
  !RainMan.globals.isSuperBonusGameEnabled) {
1251
+ RainMan.settingsStore.setIsTemporarySpeed(false);
1240
1252
  RainMan.componentRegistry.setSpeedLevel(this.speedLevelBeforeFreeSpins);
1241
1253
  this.uiController.updateShownSpeedLevel(this.speedLevelBeforeFreeSpins);
1242
1254
  this.speedLevelBeforeFreeSpins = null;
@@ -189,6 +189,13 @@ export declare class UiController {
189
189
  * @returns {Promise<void>}
190
190
  */
191
191
  updateDisplayedBalance(amount: number): Promise<void>;
192
+ /**
193
+ * Function for animating current balance to a final value.
194
+ * @public
195
+ * @param {number} finalBalance - final balance to display
196
+ * @returns {Promise<void>}
197
+ */
198
+ animateCurrentBalance(finalBalance: number): Promise<void>;
192
199
  /**
193
200
  * Function for resetting win amount
194
201
  * @public
@@ -282,6 +282,23 @@ export class UiController {
282
282
  async updateDisplayedBalance(amount) {
283
283
  this.promisesToSetBalances.push(RainMan.componentRegistry.get(COMPONENTS.creditText).update(amount));
284
284
  }
285
+ /**
286
+ * Function for animating current balance to a final value.
287
+ * @public
288
+ * @param {number} finalBalance - final balance to display
289
+ * @returns {Promise<void>}
290
+ */
291
+ async animateCurrentBalance(finalBalance) {
292
+ const balanceDiff = ceilToDecimal(finalBalance - this._currentBalance);
293
+ this._currentBalance = finalBalance;
294
+ if (balanceDiff === 0) {
295
+ this.setDisplayedBalance();
296
+ return;
297
+ }
298
+ const animationPromise = RainMan.componentRegistry.get(COMPONENTS.creditText).update(balanceDiff);
299
+ this.promisesToSetBalances.push(animationPromise);
300
+ await animationPromise;
301
+ }
285
302
  /**
286
303
  * Function for resetting win amount
287
304
  * @public
@@ -24,7 +24,7 @@ export class AbstractLoadingContainer extends Container {
24
24
  this.name = "loadingContainer";
25
25
  this.x = 0;
26
26
  this.y = 0;
27
- this.loaderContainer = new SpriteLoadingContainer("./assets/images/materials/texture.png");
27
+ this.loaderContainer = new SpriteLoadingContainer("./assets/images/materials/texture.webp");
28
28
  this.presentationFinishedPromise = new Promise((resolve) => {
29
29
  this.resolvePresentationFinished = resolve;
30
30
  });
@@ -113,6 +113,12 @@ export declare class SettingsStore {
113
113
  * @returns {void}
114
114
  */
115
115
  disableSpecialButtons(): void;
116
+ /**
117
+ * Enables special buttons like gamble and take.
118
+ * @public
119
+ * @returns {void}
120
+ */
121
+ enableSpecialButtons(): void;
116
122
  /**
117
123
  * Swaps the bet change disabled flag.
118
124
  * @public
@@ -338,6 +344,13 @@ export declare class SettingsStore {
338
344
  * @returns {void}
339
345
  */
340
346
  setAutoSpinsCount(count: number): void;
347
+ /**
348
+ * Setter for the infinite autoplay flag
349
+ * @public
350
+ * @param {boolean} newState new state for infinite autoplay
351
+ * @returns {void}
352
+ */
353
+ setInfinitySpinsEnabled(newState: boolean): void;
341
354
  /**
342
355
  * Setter for the autoplay enabled flag
343
356
  * @public
@@ -79,6 +79,7 @@ export class SettingsStore {
79
79
  initialAutoplaySettingsState = {
80
80
  onAnyWin: false,
81
81
  howManyAutoSpinsLeft: INITIAL_AUTO_SPIN_COUNT,
82
+ infinitySpinsEnabled: false,
82
83
  singleWinExceeds: 0,
83
84
  balancedIncreased: 0,
84
85
  balancedDecreased: 0,
@@ -185,6 +186,21 @@ export class SettingsStore {
185
186
  RainMan.componentRegistry.get(BUTTONS.take).flipDisabledFlag(true);
186
187
  }
187
188
  }
189
+ /**
190
+ * Enables special buttons like gamble and take.
191
+ * @public
192
+ * @returns {void}
193
+ */
194
+ enableSpecialButtons() {
195
+ if (RainMan.componentRegistry.has(BUTTONS.gamble)) {
196
+ RainMan.settingsStore.isGambleGameAvailable = true;
197
+ RainMan.componentRegistry.get(BUTTONS.gamble).flipDisabledFlag(false);
198
+ }
199
+ if (RainMan.componentRegistry.has(BUTTONS.take)) {
200
+ RainMan.settingsStore.isTakeActionAvailable = true;
201
+ RainMan.componentRegistry.get(BUTTONS.take).flipDisabledFlag(false);
202
+ }
203
+ }
188
204
  /**
189
205
  * Swaps the bet change disabled flag.
190
206
  * @public
@@ -576,6 +592,15 @@ export class SettingsStore {
576
592
  setAutoSpinsCount(count) {
577
593
  this.howManyAutoSpinsLeft = count;
578
594
  }
595
+ /**
596
+ * Setter for the infinite autoplay flag
597
+ * @public
598
+ * @param {boolean} newState new state for infinite autoplay
599
+ * @returns {void}
600
+ */
601
+ setInfinitySpinsEnabled(newState) {
602
+ this.infinitySpinsEnabled = newState;
603
+ }
579
604
  /**
580
605
  * Setter for the autoplay enabled flag
581
606
  * @public
@@ -713,6 +738,7 @@ export class SettingsStore {
713
738
  resetAutoplaySettings() {
714
739
  this.onAnyWin = this.initialAutoplaySettingsState.onAnyWin;
715
740
  this.howManyAutoSpinsLeft = this.initialAutoplaySettingsState.howManyAutoSpinsLeft;
741
+ this.infinitySpinsEnabled = this.initialAutoplaySettingsState.infinitySpinsEnabled;
716
742
  this.singleWinExceeds = this.initialAutoplaySettingsState.singleWinExceeds;
717
743
  this.balancedIncreased = this.initialAutoplaySettingsState.balancedIncreased;
718
744
  this.balancedDecreased = this.initialAutoplaySettingsState.balancedDecreased;
@@ -24,7 +24,7 @@ const pauseAudio = () => {
24
24
  shouldResumeAudioOnInterruption = false;
25
25
  removeInterruptedAudioResumeListeners();
26
26
  if (RainMan.settingsStore.isSoundEnabled()) {
27
- SoundManager.stopSoundsForPageHide();
27
+ SoundManager.pauseSoundsForPageHide();
28
28
  }
29
29
  };
30
30
  /**
@@ -84,6 +84,6 @@ export const loadSoundsOnTabChange = () => {
84
84
  sound.disableAutoPause = true;
85
85
  document.addEventListener("visibilitychange", handleVisibilityChange);
86
86
  window.addEventListener("pagehide", () => pauseAudio());
87
- window.addEventListener("pageshow", () => interruptedAudioResume());
88
- window.addEventListener("focus", () => interruptedAudioResume());
87
+ window.addEventListener("pageshow", () => resumeAudio());
88
+ window.addEventListener("focus", () => resumeAudio());
89
89
  };
@@ -25,6 +25,8 @@ export declare class TexturedText extends UpdatableValueComponent {
25
25
  * @returns {void}
26
26
  */
27
27
  randomizeSet(value: number, _possibleValues: number[]): void;
28
+ adjustTextureSpriteLength(): void;
29
+ set(newValue: number): void;
28
30
  /**
29
31
  * Plays the animation for the text element
30
32
  * @public
@@ -18,6 +18,7 @@ export class TexturedText extends UpdatableValueComponent {
18
18
  this.registryName = registryName;
19
19
  this.valueTextComponent.style = { ...this.valueTextComponent.style, fontSize: 120, padding: 50 };
20
20
  this.valueTextComponent.anchor.set(0.5, 0.5);
21
+ this.updateFontSize();
21
22
  if (textureName) {
22
23
  this.textureSprite = new TilingSprite(getTexture(textureName), this.valueTextComponent.width, this.valueTextComponent.height);
23
24
  this.textureSprite.scale.set(1.1);
@@ -48,6 +49,17 @@ export class TexturedText extends UpdatableValueComponent {
48
49
  this.valueTextComponent.style.fontSize = -10 * new MoneyText(value).toString().length + this.baseFontSize;
49
50
  super.update(value, 1);
50
51
  }
52
+ adjustTextureSpriteLength() {
53
+ if (!this.textureSprite) {
54
+ return;
55
+ }
56
+ this.textureSprite.width = this.valueTextComponent.width;
57
+ this.textureSprite.height = this.valueTextComponent.height;
58
+ }
59
+ set(newValue) {
60
+ super.set(newValue);
61
+ this.adjustTextureSpriteLength();
62
+ }
51
63
  /**
52
64
  * Plays the animation for the text element
53
65
  * @public
@@ -89,5 +101,6 @@ export class TexturedText extends UpdatableValueComponent {
89
101
  */
90
102
  updateFontSize() {
91
103
  this.valueTextComponent.style.fontSize = -10 * super.value.length + this.baseFontSize;
104
+ this.adjustTextureSpriteLength();
92
105
  }
93
106
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pixi-rainman-game-engine",
3
- "version": "0.3.40",
3
+ "version": "0.3.42",
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",