pixi-rainman-game-engine 0.3.34 → 0.3.36-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.
@@ -10,5 +10,8 @@ export declare class DescribedPlayableAction<T> {
10
10
  winAmount: Nullable<number>;
11
11
  skipLooping: boolean;
12
12
  isFirstStreak: boolean;
13
+ animationTarget: {
14
+ isCurrentAnimationPastHalf?: () => boolean;
15
+ } | null;
13
16
  constructor(type: T, action: PlayableAction, winAmount?: Nullable<number>, skipLooping?: boolean);
14
17
  }
@@ -9,6 +9,7 @@ export class DescribedPlayableAction {
9
9
  winAmount;
10
10
  skipLooping;
11
11
  isFirstStreak = false;
12
+ animationTarget = null;
12
13
  constructor(type, action, winAmount = null, skipLooping = false) {
13
14
  this.type = type;
14
15
  this.action = action;
@@ -13,7 +13,7 @@ export const CloseModalButton = observer(({ layerId, afterClose }) => {
13
13
  settingStore.setSpeedPopupVisibility?.("none");
14
14
  settingStore.isSpeedModalShown = false;
15
15
  }
16
- if (isBetDisabled) {
16
+ if (!isBetDisabled) {
17
17
  settingStore.enableButtons?.();
18
18
  }
19
19
  afterClose?.();
@@ -117,7 +117,7 @@ export declare class SoundManagerInstance implements SpeedAdapterInterface<Sound
117
117
  * @public
118
118
  * @returns {void}
119
119
  */
120
- resumeOnFocus(): Promise<void>;
120
+ resumeOnFocus(): Promise<boolean>;
121
121
  /**
122
122
  * Returns information if audio context is ready to play sounds.
123
123
  * @public
@@ -186,10 +186,7 @@ export class SoundManagerInstance {
186
186
  if (!ambientMusic || ambientMusic.isPlaying) {
187
187
  return;
188
188
  }
189
- if (ambientMusic.paused) {
190
- ambientMusic.resume();
191
- return;
192
- }
189
+ ambientMusic.stop();
193
190
  this.play(ambientTrack);
194
191
  }
195
192
  /**
@@ -260,10 +257,15 @@ export class SoundManagerInstance {
260
257
  await sound.context.audioContext.resume();
261
258
  }
262
259
  catch (error) {
263
- console.error("Could not resume audio");
260
+ console.error("Could not resume audio", error);
264
261
  }
265
262
  }
263
+ if (!this.isAudioContextRunning()) {
264
+ return false;
265
+ }
266
+ sound.resumeAll();
266
267
  this.restoreAmbientPlayback();
268
+ return true;
267
269
  }
268
270
  /**
269
271
  * Returns information if audio context is ready to play sounds.
@@ -893,9 +893,6 @@ export class AbstractMainContainer extends Container {
893
893
  this.mysteryBonusContainer.resize();
894
894
  this.addChild(this.mysteryBonusContainer);
895
895
  }
896
- if (this.gambleGame) {
897
- this.gambleGame.resize();
898
- }
899
896
  if (this.bottomPanel) {
900
897
  this.bottomPanel.scale.set(resizingHelper);
901
898
  this.bottomPanel.width = RainMan.app.screen.width;
@@ -915,6 +912,10 @@ export class AbstractMainContainer extends Container {
915
912
  this.addChild(this._buyFreeSpinsPlate);
916
913
  this._buyFreeSpinsPlate.resize();
917
914
  }
915
+ if (this.gambleGame) {
916
+ this.gambleGame.resize();
917
+ this.addChild(this.gambleGame);
918
+ }
918
919
  RainMan.settingsStore.popupPaytableRepositionCallback?.();
919
920
  }
920
921
  /**
@@ -26,6 +26,11 @@ export declare class GambleGame extends Container {
26
26
  private readonly previousGamblesContainer;
27
27
  private previousGambles;
28
28
  constructor(cardSpineName: string, resolver: (newBalance: number) => void, lastWinAmount: number, balanceValue: number);
29
+ private configureButtons;
30
+ private addChildrenOnce;
31
+ private bindButtonsOnce;
32
+ private bringButtonsToFront;
33
+ private restoreButtonEventModes;
29
34
  /**
30
35
  * Function for showing previous gamble
31
36
  * @private
@@ -72,4 +77,7 @@ export declare class GambleGame extends Container {
72
77
  * @returns {void}
73
78
  */
74
79
  private initChildPositions;
80
+ private layoutPc;
81
+ private layoutMobilePortrait;
82
+ private layoutMobileLandscape;
75
83
  }
@@ -3,7 +3,7 @@ import { Container, Sprite, Text, Texture } from "pixi.js";
3
3
  import { Spine } from "pixi-spine";
4
4
  import { COLOR_BUTTONS_CENTER_OFFSET, COLOR_BUTTONS_SCALE_X, gamblesTextStyle } from "../../constants";
5
5
  import { RainMan } from "../../Rainman";
6
- import { getSpineData, globalMinRatio } from "../../utils";
6
+ import { getDeviceOrientation, getSpineData, globalMinRatio } from "../../utils";
7
7
  import { BlackButton, BUTTONS, CollectButton, RedButton } from "../buttons";
8
8
  import { GambleUpdatableText } from "../updatable";
9
9
  /**
@@ -37,7 +37,10 @@ export class GambleGame extends Container {
37
37
  this.resolver = resolver;
38
38
  this.lastWinAmount = lastWinAmount;
39
39
  this.balanceValue = balanceValue;
40
+ this.x = 0;
41
+ this.y = 0;
40
42
  this.eventMode = "static";
43
+ this.interactiveChildren = true;
41
44
  this.background = new Sprite(Texture.from("black-bg.png"));
42
45
  this.frame = new Sprite(Texture.from("frame-v2.png"));
43
46
  this.previousGamblesText = new Text(i18next.t("previousCards"), gamblesTextStyle());
@@ -53,6 +56,33 @@ export class GambleGame extends Container {
53
56
  this.card.state.addAnimation(0, "wait2", true, 0);
54
57
  this.currentWin = new GambleUpdatableText(GambleGame.gambleCurrentWinRegistryName, i18next.t("currentWinGamble"), this.lastWinAmount, 1);
55
58
  this.gambleWin = new GambleUpdatableText(GambleGame.gambleWinRegistryName, i18next.t("gambleToWin"), this.lastWinAmount * 2, 2);
59
+ this.configureButtons();
60
+ this.addChildrenOnce();
61
+ this.bindButtonsOnce();
62
+ this.resize();
63
+ }
64
+ configureButtons() {
65
+ this.collectButton.eventMode = "static";
66
+ this.redButton.eventMode = "static";
67
+ this.blackButton.eventMode = "static";
68
+ this.collectButton.cursor = "pointer";
69
+ this.redButton.cursor = "pointer";
70
+ this.blackButton.cursor = "pointer";
71
+ }
72
+ addChildrenOnce() {
73
+ this.addChild(this.background);
74
+ this.addChild(this.frame);
75
+ this.addChild(this.card);
76
+ this.addChild(this.previousGamblesText);
77
+ this.addChild(this.previousGamblesContainer);
78
+ this.addChild(this.currentWin);
79
+ this.addChild(this.gambleWin);
80
+ this.addChild(this.collectText);
81
+ this.addChild(this.collectButton);
82
+ this.addChild(this.redButton);
83
+ this.addChild(this.blackButton);
84
+ }
85
+ bindButtonsOnce() {
56
86
  this.redButton.setOnClick(() => this.handleGamblePlay("red"));
57
87
  this.blackButton.setOnClick(() => this.handleGamblePlay("black"));
58
88
  this.collectButton.setOnClick(() => {
@@ -60,7 +90,22 @@ export class GambleGame extends Container {
60
90
  RainMan.componentRegistry.get(BUTTONS.gamble).flipDisabledFlag(true);
61
91
  this.resolver(this.balanceValue);
62
92
  });
63
- this.resize();
93
+ }
94
+ bringButtonsToFront() {
95
+ this.addChild(this.collectButton);
96
+ this.addChild(this.redButton);
97
+ this.addChild(this.blackButton);
98
+ }
99
+ restoreButtonEventModes() {
100
+ if (!this.collectButton.disabled) {
101
+ this.collectButton.eventMode = "static";
102
+ }
103
+ if (!this.redButton.disabled) {
104
+ this.redButton.eventMode = "static";
105
+ }
106
+ if (!this.blackButton.disabled) {
107
+ this.blackButton.eventMode = "static";
108
+ }
64
109
  }
65
110
  /**
66
111
  * Function for showing previous gamble
@@ -70,11 +115,15 @@ export class GambleGame extends Container {
70
115
  */
71
116
  showPreviousGambles(previousGambles) {
72
117
  const { width } = RainMan.app.screen;
73
- this.previousGambles.forEach((gambleCard) => this.previousGamblesContainer.removeChild(gambleCard));
118
+ this.previousGambles.forEach((gambleCard) => {
119
+ this.previousGamblesContainer.removeChild(gambleCard);
120
+ gambleCard.destroy();
121
+ });
74
122
  this.previousGambles = previousGambles.map((gambleCard, index) => {
75
123
  const card = new Sprite(Texture.from(`${gambleCard}-small.png`));
76
- card.scale.set(0.45);
124
+ getDeviceOrientation() === "mobile-landscape" ? card.scale.set(0.3) : card.scale.set(0.45);
77
125
  card.x = index * (card.width + 10);
126
+ card.y = 0;
78
127
  this.previousGamblesContainer.addChild(card);
79
128
  return card;
80
129
  });
@@ -125,7 +174,6 @@ export class GambleGame extends Container {
125
174
  this.blackButton.flipDisabledFlag(true);
126
175
  this.card.state.addAnimation(0, "wait", true, 0);
127
176
  const gambleData = await window.connectionWrapper.fetchGambleData(RainMan.settingsStore.roundNumber, this.lastWinAmount, choice);
128
- this.balanceValue = gambleData.balance + gambleData.win_amount;
129
177
  this.balanceValue = gambleData.balanceAfterSpin;
130
178
  await new Promise((resolve) => {
131
179
  this.card.state.clearTracks();
@@ -137,20 +185,21 @@ export class GambleGame extends Container {
137
185
  await this.updateDoubleAndWinFields(Math.max(gambleData.win_amount, 0));
138
186
  this.redButton.flipDisabledFlag(false);
139
187
  this.blackButton.flipDisabledFlag(false);
188
+ this.redButton.eventMode = "static";
189
+ this.blackButton.eventMode = "static";
140
190
  }
141
191
  this.card.state.addAnimation(0, "wait2", true, 0);
142
192
  this.lastWinAmount = gambleData.win_amount;
143
193
  this.showPreviousGambles(gambleData.previous_gambles);
194
+ this.bringButtonsToFront();
144
195
  if (gambleData.win_amount === 0) {
145
196
  this.redButton.flipDisabledFlag(true);
146
197
  this.blackButton.flipDisabledFlag(true);
147
198
  this.card.state.addAnimation(0, "idle", true, 0);
148
- Promise.all([
149
- this.loseDoubleAndWinFields(),
150
- setTimeout(() => {
151
- this.closeGame();
152
- }, 2000)
153
- ]);
199
+ await this.loseDoubleAndWinFields();
200
+ window.setTimeout(() => {
201
+ this.closeGame();
202
+ }, 2000);
154
203
  }
155
204
  }
156
205
  /**
@@ -162,68 +211,121 @@ export class GambleGame extends Container {
162
211
  const { width, height } = RainMan.app.screen;
163
212
  this.x = 0;
164
213
  this.y = 0;
165
- this.width = width;
166
- this.height = height;
167
- this.initChildPositions();
214
+ this.scale.set(1);
215
+ this.initChildPositions(width, height);
216
+ this.restoreButtonEventModes();
217
+ this.bringButtonsToFront();
168
218
  }
169
219
  /**
170
220
  * Function for initializing child positions
171
221
  * @private
172
222
  * @returns {void}
173
223
  */
174
- initChildPositions() {
175
- this.removeChild(this.background);
176
- this.removeChild(this.frame);
177
- this.removeChild(this.previousGamblesText);
178
- this.removeChild(this.collectButton);
179
- this.removeChild(this.collectText);
180
- this.removeChild(this.redButton);
181
- this.removeChild(this.blackButton);
182
- this.removeChild(this.card);
183
- this.removeChild(this.currentWin);
184
- this.removeChild(this.gambleWin);
185
- this.removeChild(this.previousGamblesContainer);
186
- const { width, height } = RainMan.app.screen;
224
+ initChildPositions(width, height) {
225
+ const ratio = globalMinRatio();
226
+ const deviceOrientation = getDeviceOrientation();
227
+ const centerX = width / 2;
228
+ this.background.x = 0;
229
+ this.background.y = 0;
187
230
  this.background.width = width;
188
231
  this.background.height = height;
232
+ this.frame.x = 0;
233
+ this.frame.y = 0;
189
234
  this.frame.width = width;
190
235
  this.frame.height = height;
191
- const CENTER = RainMan.app.screen.width / 2;
192
236
  const previousGambles = window.connectionWrapper.getLastGambleResponse()?.previous_gambles || [];
193
- this.previousGamblesText.x = CENTER - this.previousGamblesText.width / 2;
237
+ this.previousGamblesText.x = centerX - this.previousGamblesText.width / 2;
194
238
  this.previousGamblesText.y = 20;
239
+ if (deviceOrientation === "mobile-landscape") {
240
+ this.previousGamblesText.scale.set(0.38);
241
+ this.previousGamblesText.y = 6;
242
+ }
243
+ else {
244
+ this.previousGamblesText.scale.set(1);
245
+ this.previousGamblesText.y = 20;
246
+ }
247
+ this.previousGamblesText.x = centerX - this.previousGamblesText.width / 2;
195
248
  this.showPreviousGambles(previousGambles);
196
- this.card.scale.set(globalMinRatio() * 0.8);
197
- this.card.x = CENTER;
198
- this.card.y = height / 2;
199
- this.collectText.x = CENTER;
249
+ if (deviceOrientation === "pc") {
250
+ this.layoutPc(width, height, ratio);
251
+ return;
252
+ }
253
+ if (deviceOrientation === "mobile-portrait") {
254
+ this.layoutMobilePortrait(width, height, ratio);
255
+ return;
256
+ }
257
+ this.layoutMobileLandscape(width, height, ratio);
258
+ }
259
+ layoutPc(width, height, ratio) {
260
+ const centerX = width / 2;
261
+ const centerY = height / 2;
262
+ this.card.scale.set(ratio * 0.8);
263
+ this.card.x = centerX;
264
+ this.card.y = centerY;
265
+ this.collectText.x = centerX;
200
266
  this.collectText.y = height - this.collectText.height;
201
- this.collectButton.scale.set(globalMinRatio() * 0.6);
202
- this.collectButton.x = CENTER;
267
+ this.collectButton.scale.set(ratio * 0.6);
268
+ this.collectButton.x = centerX;
203
269
  this.collectButton.y = this.collectText.y - this.collectButton.height - 30;
204
- this.redButton.scale.set(globalMinRatio() * COLOR_BUTTONS_SCALE_X);
205
- const COLOR_BUTTONS_Y = this.card.y - this.redButton.height / 2;
206
- this.redButton.x = CENTER - COLOR_BUTTONS_CENTER_OFFSET - this.redButton.width;
207
- this.redButton.y = COLOR_BUTTONS_Y;
208
- this.blackButton.scale.set(globalMinRatio() * COLOR_BUTTONS_SCALE_X);
209
- this.blackButton.x = CENTER + COLOR_BUTTONS_CENTER_OFFSET;
210
- this.blackButton.y = COLOR_BUTTONS_Y;
211
- this.currentWin.scale.set(globalMinRatio());
212
- this.currentWin.y = height - this.currentWin.height;
270
+ this.redButton.scale.set(ratio * COLOR_BUTTONS_SCALE_X);
271
+ this.blackButton.scale.set(ratio * COLOR_BUTTONS_SCALE_X);
272
+ this.redButton.x = centerX - COLOR_BUTTONS_CENTER_OFFSET - this.redButton.width;
273
+ this.redButton.y = this.card.y - this.redButton.height / 2;
274
+ this.blackButton.x = centerX + COLOR_BUTTONS_CENTER_OFFSET;
275
+ this.blackButton.y = this.card.y - this.redButton.height / 2;
276
+ this.currentWin.scale.set(ratio);
277
+ this.gambleWin.scale.set(ratio);
213
278
  this.currentWin.x = width / 4;
214
- this.gambleWin.scale.set(globalMinRatio());
215
- this.gambleWin.y = height - this.gambleWin.height;
279
+ this.currentWin.y = height - this.currentWin.height;
216
280
  this.gambleWin.x = (width * 3) / 4;
217
- this.addChild(this.background);
218
- this.addChild(this.frame);
219
- this.addChild(this.previousGamblesText);
220
- this.addChild(this.collectButton);
221
- this.addChild(this.collectText);
222
- this.addChild(this.redButton);
223
- this.addChild(this.blackButton);
224
- this.addChild(this.card);
225
- this.addChild(this.currentWin);
226
- this.addChild(this.gambleWin);
227
- this.addChild(this.previousGamblesContainer);
281
+ this.gambleWin.y = height - this.gambleWin.height;
282
+ }
283
+ layoutMobilePortrait(width, height, ratio) {
284
+ const centerX = width / 2;
285
+ this.card.scale.set(ratio * 0.65);
286
+ this.card.x = centerX;
287
+ this.card.y = height * 0.4;
288
+ this.collectText.x = centerX;
289
+ this.collectText.y = height - this.collectText.height;
290
+ this.collectButton.scale.set(ratio * 0.6);
291
+ this.collectButton.x = centerX;
292
+ this.collectButton.y = this.collectText.y - this.collectButton.height - 30;
293
+ this.redButton.scale.set(ratio * 0.7);
294
+ this.blackButton.scale.set(ratio * 0.7);
295
+ this.redButton.x = centerX - this.redButton.width - 20;
296
+ this.redButton.y = this.card.y + this.card.height / 2 + 30;
297
+ this.blackButton.x = centerX + 20;
298
+ this.blackButton.y = this.card.y + this.card.height / 2 + 30;
299
+ this.currentWin.scale.set(ratio);
300
+ this.gambleWin.scale.set(ratio);
301
+ this.currentWin.x = width * 0.25;
302
+ this.currentWin.y = this.collectButton.y - this.currentWin.height - 20;
303
+ this.gambleWin.x = width * 0.75;
304
+ this.gambleWin.y = this.collectButton.y - this.currentWin.height - 20;
305
+ }
306
+ layoutMobileLandscape(width, height, ratio) {
307
+ const centerX = width / 2;
308
+ const centerY = height / 2;
309
+ this.card.scale.set(ratio * 0.72);
310
+ this.card.x = centerX;
311
+ this.card.y = centerY - 18;
312
+ this.collectText.scale.set(0.55);
313
+ this.collectText.x = centerX;
314
+ this.collectText.y = height - 28;
315
+ this.collectButton.scale.set(ratio * 0.65);
316
+ this.collectButton.x = centerX;
317
+ this.collectButton.y = this.collectText.y - this.collectButton.height - 16;
318
+ this.redButton.scale.set(ratio * COLOR_BUTTONS_SCALE_X * 1.18);
319
+ this.blackButton.scale.set(ratio * COLOR_BUTTONS_SCALE_X * 1.18);
320
+ this.redButton.x = centerX - COLOR_BUTTONS_CENTER_OFFSET * 0.68 - this.redButton.width;
321
+ this.redButton.y = this.card.y - this.redButton.height / 2;
322
+ this.blackButton.x = centerX + COLOR_BUTTONS_CENTER_OFFSET * 0.68;
323
+ this.blackButton.y = this.card.y - this.redButton.height / 2;
324
+ this.currentWin.scale.set(ratio * 1.45);
325
+ this.gambleWin.scale.set(ratio * 1.45);
326
+ this.currentWin.x = width * 0.24;
327
+ this.currentWin.y = height - this.currentWin.height / 2 - 8;
328
+ this.gambleWin.x = width * 0.8;
329
+ this.gambleWin.y = height - this.gambleWin.height / 2 - 8;
228
330
  }
229
331
  }
@@ -98,6 +98,7 @@ export declare abstract class AbstractSymbolBase extends Container implements Sp
98
98
  * @returns {Promise<void>} - A promise that resolves when the animation is complete
99
99
  */
100
100
  play(suffix?: string): Promise<void>;
101
+ isCurrentAnimationPastHalf(): boolean;
101
102
  /**
102
103
  * Function for stopping the symbol animation.
103
104
  * @public
@@ -214,6 +214,18 @@ export class AbstractSymbolBase extends Container {
214
214
  });
215
215
  });
216
216
  }
217
+ isCurrentAnimationPastHalf() {
218
+ const entry = this.spine.state.tracks[0];
219
+ if (!entry) {
220
+ return false;
221
+ }
222
+ const trackTime = entry.trackTime ?? 0;
223
+ const animationEnd = entry.animationEnd ?? 0;
224
+ if (animationEnd <= 0) {
225
+ return false;
226
+ }
227
+ return trackTime >= animationEnd / 3;
228
+ }
217
229
  /**
218
230
  * Function for stopping the symbol animation.
219
231
  * @public
@@ -22,6 +22,7 @@ export declare abstract class UpdatableValueComponent extends Container implemen
22
22
  protected tween: TWEEN.Tween<{
23
23
  value: number;
24
24
  }> | null;
25
+ private readonly animateTick;
25
26
  limitValue: number | null;
26
27
  protected countingDivider: number;
27
28
  readonly speedLevelMapper: SpeedLevelDirectory<number>;
@@ -23,6 +23,7 @@ export class UpdatableValueComponent extends Container {
23
23
  valueTextComponent;
24
24
  shouldBeUpdated = false;
25
25
  tween = null;
26
+ animateTick = () => this.animate();
26
27
  limitValue = null;
27
28
  countingDivider;
28
29
  speedLevelMapper = {
@@ -68,7 +69,8 @@ export class UpdatableValueComponent extends Container {
68
69
  }
69
70
  async update(newValue, time = RainMan.config.durationOfActions.updatableTextCountingDuration) {
70
71
  this.finalValue = newValue;
71
- Ticker.shared.add(() => this.animate());
72
+ Ticker.shared.remove(this.animateTick);
73
+ Ticker.shared.add(this.animateTick);
72
74
  await this.initAnimation(newValue, time / this.countingDivider);
73
75
  }
74
76
  /**
@@ -78,7 +80,8 @@ export class UpdatableValueComponent extends Container {
78
80
  * @returns {Promise<void>} A promise that resolves when the update is complete.
79
81
  */
80
82
  async loseUpdate(time = RainMan.config.durationOfActions.updatableTextCountingDuration) {
81
- Ticker.shared.add(() => this.animate());
83
+ Ticker.shared.remove(this.animateTick);
84
+ Ticker.shared.add(this.animateTick);
82
85
  await this.initAnimation(0, time / this.countingDivider, true);
83
86
  }
84
87
  /**
@@ -162,7 +165,7 @@ export class UpdatableValueComponent extends Container {
162
165
  if (!this.shouldBeUpdated) {
163
166
  this.tween?.stop();
164
167
  this.tween = null;
165
- Ticker.shared.remove(() => this.animate());
168
+ Ticker.shared.remove(this.animateTick);
166
169
  return;
167
170
  }
168
171
  this.changeCurrentWin(value);
@@ -188,11 +191,10 @@ export class UpdatableValueComponent extends Container {
188
191
  * @returns {void}
189
192
  */
190
193
  setResultAndClearTween(finalValue) {
191
- if (this.shouldBeUpdated)
192
- return;
194
+ this.shouldBeUpdated = false;
193
195
  this.tween?.stop();
194
196
  this.tween = null;
195
- Ticker.shared.remove(() => this.animate());
197
+ Ticker.shared.remove(this.animateTick);
196
198
  this.changeCurrentWin(finalValue);
197
199
  }
198
200
  /**
@@ -33,6 +33,9 @@ export type EggWin = BaseWin & {
33
33
  export type GoldWin = BaseWin & {
34
34
  type: typeof PossibleWins.gold;
35
35
  };
36
+ export type PearlWin = BaseWin & {
37
+ type: typeof PossibleWins.pearl;
38
+ };
36
39
  export type BigWin = BaseWin & {
37
40
  type: typeof PossibleWins.bigWin;
38
41
  };
@@ -60,7 +63,7 @@ export type JackpotWin = BaseWin & {
60
63
  export type CoinWin = BaseWin & {
61
64
  type: typeof PossibleWins.coin;
62
65
  };
63
- export type Win = StreakWin | FreeSpinWin | GambleWin | MysteryWin | EggWin | GoldWin | BigWin | BonusWin | SuperBonusWin | ScatterWin | RouletteWin | JackpotWin | CoinWin | MultiWin | JokerWin;
66
+ export type Win = StreakWin | FreeSpinWin | GambleWin | MysteryWin | EggWin | GoldWin | BigWin | PearlWin | BonusWin | SuperBonusWin | ScatterWin | RouletteWin | JackpotWin | CoinWin | MultiWin | JokerWin;
64
67
  /**
65
68
  * Type represents data that come from backend
66
69
  * @typedef {WinDetails}
@@ -20,4 +20,5 @@ export declare const PossibleWins: {
20
20
  readonly superBonus: "super-bonus";
21
21
  readonly joker: "joker";
22
22
  readonly multi: "multi";
23
+ readonly pearl: "pearl";
23
24
  };
@@ -19,5 +19,6 @@ export const PossibleWins = {
19
19
  bonus: "bonus",
20
20
  superBonus: "super-bonus",
21
21
  joker: "joker",
22
- multi: "multi"
22
+ multi: "multi",
23
+ pearl: "pearl"
23
24
  };
@@ -59,6 +59,9 @@ export declare abstract class AbstractController<T> {
59
59
  shouldStopMobileSpin: boolean;
60
60
  disableMessageBoxChange: boolean;
61
61
  shouldClearQuickReelsStopAfterSpin: boolean;
62
+ protected firstAnimationTarget: {
63
+ isCurrentAnimationPastHalf?: () => boolean;
64
+ } | null;
62
65
  protected resolveBigWin?: () => void;
63
66
  protected resolveMysteryWin?: () => void;
64
67
  protected resolveBonusWin?: () => void;
@@ -59,6 +59,7 @@ export class AbstractController {
59
59
  shouldStopMobileSpin = false;
60
60
  disableMessageBoxChange = false;
61
61
  shouldClearQuickReelsStopAfterSpin = false;
62
+ firstAnimationTarget = null;
62
63
  resolveBigWin;
63
64
  resolveMysteryWin;
64
65
  resolveBonusWin;
@@ -387,6 +388,7 @@ export class AbstractController {
387
388
  const availableFreeSpins = spinLogic.availableFreeSpins;
388
389
  if (availableFreeSpins > 0) {
389
390
  this._lastWinAmount = this.initData.round_win_amount;
391
+ this.uiController.totalFreeSpinWinAmount = ceilToDecimal(this.initData.round_win_amount);
390
392
  }
391
393
  if (spinLogic.winTotalAmount !== 0 && spinLogic.winTotalAmount !== undefined) {
392
394
  this.uiController.recentWin = ceilToDecimal(spinLogic.winTotalAmount);
@@ -836,6 +838,11 @@ export class AbstractController {
836
838
  if (!RainMan.settingsStore.isFreeSpinsBought) {
837
839
  return;
838
840
  }
841
+ const boughtRoundNumber = RainMan.settingsStore.buyFreeSpinsData?.getRawData().round_number;
842
+ if (boughtRoundNumber !== undefined) {
843
+ this.roundNumber = boughtRoundNumber;
844
+ RainMan.settingsStore.setRoundNumber(boughtRoundNumber);
845
+ }
839
846
  this.uiController.totalNumberOfFreeSpins += this.getBoughtNumberOfFreeSpins();
840
847
  RainMan.componentRegistry.getIfExists(FreeSpinButton.registryName)?.setInteractivity(false);
841
848
  RainMan.settingsStore.setIsFreeSpinBought(false);
@@ -846,7 +853,8 @@ export class AbstractController {
846
853
  * @returns {number} - number of free spins bought
847
854
  */
848
855
  getBoughtNumberOfFreeSpins() {
849
- return RainMan.settingsStore.buyFreeSpinsData?.getRawData().extra_data.available_free_spins || 0;
856
+ return (RainMan.settingsStore.buyFreeSpinsData?.getRawData()?.extra_data?.available_free_spins ??
857
+ RainMan.settingsStore.freeSpinNumberBought);
850
858
  }
851
859
  /**
852
860
  * Function for autoplay spin
@@ -1021,10 +1029,14 @@ export class AbstractController {
1021
1029
  */
1022
1030
  gambleGameEnabler(winAmount) {
1023
1031
  if (RainMan.componentRegistry.has(BUTTONS.gamble) && RainMan.settingsStore.isGambleGameAvailable) {
1024
- RainMan.componentRegistry.get(BUTTONS.gamble).flipDisabledFlag(winAmount === 0);
1032
+ RainMan.componentRegistry
1033
+ .get(BUTTONS.gamble)
1034
+ .flipDisabledFlag(winAmount === 0 || RainMan.settingsStore.isAutoplayEnabled);
1025
1035
  }
1026
1036
  if (RainMan.componentRegistry.has(BUTTONS.take) && RainMan.settingsStore.isTakeActionAvailable) {
1027
- RainMan.componentRegistry.get(BUTTONS.take).flipDisabledFlag(winAmount === 0);
1037
+ RainMan.componentRegistry
1038
+ .get(BUTTONS.take)
1039
+ .flipDisabledFlag(winAmount === 0 || RainMan.settingsStore.isAutoplayEnabled);
1028
1040
  }
1029
1041
  }
1030
1042
  /**
@@ -1144,6 +1156,8 @@ export class AbstractController {
1144
1156
  this.uiController.limitWinAmount(spinLogic.winTotalAmount);
1145
1157
  }
1146
1158
  this.composeWinActionsQueue(spinLogic);
1159
+ this.firstAnimationTarget =
1160
+ this.winActionsQueue.find((action) => action.animationTarget)?.animationTarget ?? null;
1147
1161
  if (spinLogic.winTotalAmount === 0 && this.invokeFreeSpinPlateAfterWin) {
1148
1162
  this.uiController.messageBox.hideCurrentWinText();
1149
1163
  }
@@ -1194,7 +1208,9 @@ export class AbstractController {
1194
1208
  this.uiController.messageBox.hideCurrentWinText();
1195
1209
  this.uiController.messageBox.hideAutoSpinText();
1196
1210
  await this.handleFreeSpinPlateInvocation(freeSpinAward, isAdditionalFreeSpins || additionalFreeSpinsOnLastSpin);
1197
- this.uiController.currentBalance = RainMan.settingsStore.balanceAfterSpin;
1211
+ if (!isAdditionalFreeSpins && !additionalFreeSpinsOnLastSpin) {
1212
+ this.uiController.currentBalance = RainMan.settingsStore.balanceAfterSpin;
1213
+ }
1198
1214
  this.uiController.triggerUiElementsUpdate();
1199
1215
  this.uiController.messageBox.showFreeSpinText();
1200
1216
  }
@@ -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
  */
@@ -216,7 +216,6 @@ export class UiController {
216
216
  }
217
217
  /**
218
218
  * Shows game pays in a bottom panel, if exists
219
- * @param amount
220
219
  * @public
221
220
  * @returns {void}
222
221
  */
@@ -136,7 +136,6 @@ export declare class SettingsStore {
136
136
  /**
137
137
  * Sets the free spins price table.
138
138
  * @public
139
- * @param freeSpinsBuyOptions
140
139
  * @param {Record<number, number>} freeSpinsPriceTable table with free spins prices
141
140
  * @returns {void}
142
141
  */
@@ -151,7 +150,6 @@ export declare class SettingsStore {
151
150
  /**
152
151
  * Sets the free spins price table.
153
152
  * @public
154
- * @param buyTypes
155
153
  * @param {Record<number, number>} jokerBuyOption table with free spins prices
156
154
  * @returns {void}
157
155
  */
@@ -218,7 +218,6 @@ export class SettingsStore {
218
218
  /**
219
219
  * Sets the free spins price table.
220
220
  * @public
221
- * @param freeSpinsBuyOptions
222
221
  * @param {Record<number, number>} freeSpinsPriceTable table with free spins prices
223
222
  * @returns {void}
224
223
  */
@@ -237,7 +236,6 @@ export class SettingsStore {
237
236
  /**
238
237
  * Sets the free spins price table.
239
238
  * @public
240
- * @param buyTypes
241
239
  * @param {Record<number, number>} jokerBuyOption table with free spins prices
242
240
  * @returns {void}
243
241
  */
@@ -1,6 +1,8 @@
1
+ import { sound } from "@pixi/sound";
1
2
  import { SoundManager } from "../../application";
2
3
  import { RainMan } from "../../Rainman";
3
4
  let shouldResumeAudioOnInterruption = false;
5
+ let isResumingAudio = false;
4
6
  const removeInterruptedAudioResumeListeners = () => {
5
7
  window.removeEventListener("pointerdown", resumeAudioOnInterruption);
6
8
  window.removeEventListener("touchend", resumeAudioOnInterruption);
@@ -18,6 +20,7 @@ const interruptedAudioResume = () => {
18
20
  * @returns {void}
19
21
  */
20
22
  const pauseAudio = () => {
23
+ isResumingAudio = false;
21
24
  shouldResumeAudioOnInterruption = false;
22
25
  removeInterruptedAudioResumeListeners();
23
26
  if (RainMan.settingsStore.isSoundEnabled()) {
@@ -32,16 +35,29 @@ const resumeAudio = async () => {
32
35
  if (document.hidden || !RainMan.settingsStore.isSoundEnabled()) {
33
36
  return;
34
37
  }
35
- if (RainMan.settingsStore.isSoundEnabled()) {
36
- await SoundManager.resumeOnFocus();
37
- SoundManager.resumeAll();
38
- }
39
- if (SoundManager.isAudioContextRunning()) {
40
- shouldResumeAudioOnInterruption = false;
41
- removeInterruptedAudioResumeListeners();
38
+ if (isResumingAudio) {
42
39
  return;
43
40
  }
44
- interruptedAudioResume();
41
+ isResumingAudio = true;
42
+ try {
43
+ if (RainMan.settingsStore.isSoundEnabled()) {
44
+ const hasResumedAudioContext = await SoundManager.resumeOnFocus();
45
+ if (!hasResumedAudioContext) {
46
+ interruptedAudioResume();
47
+ return;
48
+ }
49
+ SoundManager.resumeAll();
50
+ }
51
+ if (SoundManager.isAudioContextRunning()) {
52
+ shouldResumeAudioOnInterruption = false;
53
+ removeInterruptedAudioResumeListeners();
54
+ return;
55
+ }
56
+ interruptedAudioResume();
57
+ }
58
+ finally {
59
+ isResumingAudio = false;
60
+ }
45
61
  };
46
62
  /**
47
63
  * Restore audio on the next user action if browser blocked it earlier.
@@ -61,16 +77,16 @@ const handleVisibilityChange = () => {
61
77
  pauseAudio();
62
78
  return;
63
79
  }
64
- resumeAudio();
80
+ interruptedAudioResume();
65
81
  };
66
82
  /**
67
83
  * Sets up audio states for tab switches and iOS app interruptions.
68
84
  * @returns {void}
69
85
  */
70
86
  export const loadSoundsOnTabChange = () => {
87
+ sound.disableAutoPause = true;
71
88
  document.addEventListener("visibilitychange", handleVisibilityChange);
72
- window.addEventListener("pagehide", pauseAudio);
73
- window.addEventListener("blur", pauseAudio);
74
- window.addEventListener("pageshow", () => resumeAudio());
75
- window.addEventListener("focus", () => resumeAudio());
89
+ window.addEventListener("pagehide", () => pauseAudio());
90
+ window.addEventListener("pageshow", () => interruptedAudioResume());
91
+ window.addEventListener("focus", () => interruptedAudioResume());
76
92
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pixi-rainman-game-engine",
3
- "version": "0.3.34",
3
+ "version": "0.3.36-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",