pixi-rainman-game-engine 0.3.25 → 0.3.27

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.
@@ -37,6 +37,7 @@ export class RainMan {
37
37
  isSuperBonusGameEnabled: false
38
38
  };
39
39
  this.initLocalStorageSettings();
40
+ RainMan.settingsStore.setHiResolutionFlag(false);
40
41
  }
41
42
  /**
42
43
  * Method for genereting a token or getting existing token for the game session.
@@ -63,5 +64,6 @@ export class RainMan {
63
64
  setToLocalStorage(LOCAL_STORAGE.isSoundEnabled, true);
64
65
  setToLocalStorage(LOCAL_STORAGE.isSoundFxEnabled, true);
65
66
  setToLocalStorage(LOCAL_STORAGE.isAmbientMusicEnabled, true);
67
+ setToLocalStorage(LOCAL_STORAGE.hiResolution, false);
66
68
  }
67
69
  }
@@ -0,0 +1,62 @@
1
+ import { Container, Sprite, Text, TextStyle, Texture } from "pixi.js";
2
+ import { Logo } from "../common";
3
+ import { IntroContinueButton } from "./IntroContinueButton";
4
+ import { IntroDotButton } from "./IntroDotButton";
5
+ import { IntroPageData } from "./IntroPageData";
6
+ /**
7
+ * Abstract intro screen shown between loading and gameplay.
8
+ * Each game subclasses this and provides assets + page definitions as properties.
9
+ * @abstract
10
+ * @class AbstractIntroScreen
11
+ * @typedef {AbstractIntroScreen}
12
+ * @augments {Container}
13
+ */
14
+ export declare abstract class AbstractIntroScreen extends Container {
15
+ static shouldSkip(): boolean;
16
+ protected abstract background: Sprite;
17
+ protected abstract backgroundPortrait: Sprite;
18
+ protected abstract checkboxLabelText: string;
19
+ protected abstract checkboxTextStyle: TextStyle;
20
+ protected abstract checkboxTextures: {
21
+ frame: Texture;
22
+ tick: Texture;
23
+ };
24
+ protected abstract logo: Logo;
25
+ protected abstract pages: IntroPageData[];
26
+ protected abstract pageTextStyle: TextStyle;
27
+ protected checkboxLabel: Text;
28
+ protected checkboxSprite: Sprite;
29
+ protected checkboxTick: Sprite;
30
+ protected continueButton: IntroContinueButton;
31
+ protected dots: IntroDotButton[];
32
+ protected frameImage: Sprite;
33
+ protected pageImage: Sprite;
34
+ protected pageText: Text;
35
+ private currentPage;
36
+ private isChecked;
37
+ private isAnimating;
38
+ private fadeTween?;
39
+ private readonly FADE_DURATION;
40
+ private readonly onContinueCallback;
41
+ private readonly resizeHandler;
42
+ protected constructor(onContinue: () => void);
43
+ init(): void;
44
+ resize(): void;
45
+ protected abstract positionContinueButton(): void;
46
+ protected abstract positionDots(): void;
47
+ protected abstract positionText(): void;
48
+ protected abstract positionCheckbox(): void;
49
+ protected abstract positionLogo(): void;
50
+ protected abstract getPageImageScale(): {
51
+ vertical: number;
52
+ horizontal: number;
53
+ };
54
+ protected abstract positionPageImage(): void;
55
+ protected showPage(index: number): void;
56
+ private updateFade;
57
+ private cleanup;
58
+ private createDots;
59
+ private navigatePage;
60
+ private handleContinue;
61
+ private toggleCheckbox;
62
+ }
@@ -0,0 +1,192 @@
1
+ import * as TWEEN from "@tweenjs/tween.js";
2
+ import { Container, Sprite, Text, Texture, Ticker } from "pixi.js";
3
+ import { UXLayer } from "../../layers";
4
+ import { getFromLocalStorage, LOCAL_STORAGE, setToLocalStorage } from "../../localStorage";
5
+ import { RainMan } from "../../Rainman";
6
+ import { getDeviceOrientation, getTexture, scaleToParent } from "../../utils";
7
+ import { IntroContinueButton } from "./IntroContinueButton";
8
+ import { IntroDotButton } from "./IntroDotButton";
9
+ /**
10
+ * Abstract intro screen shown between loading and gameplay.
11
+ * Each game subclasses this and provides assets + page definitions as properties.
12
+ * @abstract
13
+ * @class AbstractIntroScreen
14
+ * @typedef {AbstractIntroScreen}
15
+ * @augments {Container}
16
+ */
17
+ export class AbstractIntroScreen extends Container {
18
+ static shouldSkip() {
19
+ return getFromLocalStorage(LOCAL_STORAGE.introScreenHidden, false);
20
+ }
21
+ checkboxLabel;
22
+ checkboxSprite;
23
+ checkboxTick;
24
+ continueButton;
25
+ dots = [];
26
+ frameImage;
27
+ pageImage;
28
+ pageText;
29
+ currentPage = 0;
30
+ isChecked = false;
31
+ isAnimating = false;
32
+ fadeTween;
33
+ FADE_DURATION = 200;
34
+ onContinueCallback;
35
+ resizeHandler = () => this.resize();
36
+ constructor(onContinue) {
37
+ super();
38
+ this.onContinueCallback = onContinue;
39
+ this.parentLayer = UXLayer;
40
+ this.eventMode = "static";
41
+ }
42
+ init() {
43
+ this.frameImage = new Sprite();
44
+ this.pageImage = new Sprite();
45
+ this.pageText = new Text("", this.pageTextStyle);
46
+ this.pageText.anchor.set(0.5);
47
+ this.continueButton = new IntroContinueButton();
48
+ this.continueButton.setOnClick(() => {
49
+ this.handleContinue();
50
+ });
51
+ this.createDots();
52
+ this.checkboxSprite = new Sprite(this.checkboxTextures.frame);
53
+ this.checkboxSprite.eventMode = "static";
54
+ this.checkboxSprite.cursor = "pointer";
55
+ this.checkboxSprite.anchor.set(0.5);
56
+ this.checkboxSprite.on("pointerdown", () => this.toggleCheckbox());
57
+ this.checkboxTick = new Sprite(this.checkboxTextures.tick);
58
+ this.checkboxTick.visible = false;
59
+ this.checkboxTick.eventMode = "static";
60
+ this.checkboxTick.cursor = "pointer";
61
+ this.checkboxTick.anchor.set(0.5);
62
+ this.checkboxTick.on("pointerdown", () => this.toggleCheckbox());
63
+ this.checkboxLabel = new Text(this.checkboxLabelText, this.checkboxTextStyle);
64
+ this.checkboxLabel.eventMode = "static";
65
+ this.checkboxLabel.cursor = "pointer";
66
+ this.checkboxLabel.anchor.set(0.5);
67
+ this.checkboxLabel.on("pointerdown", () => this.toggleCheckbox());
68
+ Ticker.shared.add(this.updateFade, this);
69
+ this.showPage(0);
70
+ RainMan.app.renderer.on("resize", this.resizeHandler);
71
+ this.resize();
72
+ }
73
+ resize() {
74
+ this.fadeTween?.stop();
75
+ this.fadeTween = undefined;
76
+ this.isAnimating = false;
77
+ const orientation = getDeviceOrientation();
78
+ const isPortrait = orientation === "mobile-portrait";
79
+ this.removeChildren();
80
+ const bg = isPortrait ? this.backgroundPortrait : this.background;
81
+ bg.width = RainMan.app.screen.width;
82
+ bg.height = RainMan.app.screen.height;
83
+ this.addChild(bg);
84
+ this.frameImage.texture = getTexture(isPortrait ? "intro-frame-mobile.png" : "intro-frame.png");
85
+ this.showPage(this.currentPage);
86
+ this.pageImage.alpha = 1;
87
+ this.pageText.alpha = 1;
88
+ const pageImageScale = this.getPageImageScale();
89
+ const dimensions = scaleToParent(this.pageImage.texture.width, this.pageImage.texture.height, RainMan.app.screen.width * pageImageScale.horizontal, RainMan.app.screen.height * pageImageScale.vertical);
90
+ this.pageImage.width = dimensions.width;
91
+ this.pageImage.height = dimensions.height;
92
+ const scale = this.pageImage.scale.x;
93
+ this.continueButton.scale.set(scale);
94
+ this.pageText.scale.set(scale);
95
+ this.checkboxSprite.scale.set(scale);
96
+ this.checkboxTick.scale.set(scale);
97
+ this.checkboxLabel.scale.set(scale);
98
+ this.dots.forEach((dot) => dot.scale.set(scale));
99
+ this.pageImage.x = RainMan.app.screen.width / 2 - this.pageImage.width / 2;
100
+ this.pageImage.y = RainMan.app.screen.height / 2 - this.pageImage.height / 2;
101
+ this.addChild(this.frameImage);
102
+ this.addChild(this.frameImage);
103
+ this.addChild(this.pageImage);
104
+ this.addChild(this.pageText);
105
+ this.addChild(this.continueButton);
106
+ this.addChild(this.checkboxSprite);
107
+ this.addChild(this.checkboxTick);
108
+ this.addChild(this.checkboxLabel);
109
+ this.dots.forEach((dot) => this.addChild(dot));
110
+ this.positionPageImage();
111
+ this.positionLogo();
112
+ this.positionDots();
113
+ this.positionCheckbox();
114
+ this.positionContinueButton();
115
+ this.positionText();
116
+ this.frameImage.x = this.pageImage.x;
117
+ this.frameImage.y = this.pageImage.y;
118
+ this.frameImage.width = this.pageImage.width;
119
+ this.frameImage.height = this.pageImage.height;
120
+ this.frameImage.x = this.pageImage.x;
121
+ this.frameImage.y = this.pageImage.y;
122
+ this.frameImage.width = this.pageImage.width;
123
+ this.frameImage.height = this.pageImage.height;
124
+ }
125
+ showPage(index) {
126
+ if (index < 0 || index >= this.pages.length)
127
+ return;
128
+ this.currentPage = index;
129
+ const page = this.pages[index];
130
+ const isPortrait = getDeviceOrientation() === "mobile-portrait";
131
+ this.pageImage.texture = Texture.from(isPortrait ? page.imageMobile : page.imageDesktop);
132
+ this.pageText.text = page.text;
133
+ for (let i = 0; i < this.dots.length; i++) {
134
+ this.dots[i].setActive(i === index);
135
+ }
136
+ }
137
+ updateFade() {
138
+ this.fadeTween?.update();
139
+ }
140
+ cleanup() {
141
+ Ticker.shared.remove(this.updateFade, this);
142
+ Ticker.shared.remove(this.updateFade, this);
143
+ RainMan.app.renderer.off("resize", this.resizeHandler);
144
+ this.destroy({ children: true });
145
+ }
146
+ createDots() {
147
+ this.dots = [];
148
+ for (let i = 0; i < this.pages.length; i++) {
149
+ const dot = new IntroDotButton(i);
150
+ dot.setOnClick(() => this.navigatePage(i));
151
+ dot.setOnClick(() => this.navigatePage(i));
152
+ this.dots.push(dot);
153
+ }
154
+ }
155
+ navigatePage(index) {
156
+ if (this.isAnimating || index === this.currentPage)
157
+ return;
158
+ this.isAnimating = true;
159
+ this.fadeTween?.stop();
160
+ this.fadeTween = new TWEEN.Tween({ alpha: this.pageImage.alpha })
161
+ .to({ alpha: 0 }, this.FADE_DURATION)
162
+ .onUpdate((obj) => {
163
+ this.pageImage.alpha = obj.alpha;
164
+ this.pageText.alpha = obj.alpha;
165
+ })
166
+ .onComplete(() => {
167
+ this.showPage(index);
168
+ this.fadeTween = new TWEEN.Tween({ alpha: 0 })
169
+ .to({ alpha: 1 }, this.FADE_DURATION)
170
+ .onUpdate((obj) => {
171
+ this.pageImage.alpha = obj.alpha;
172
+ this.pageText.alpha = obj.alpha;
173
+ })
174
+ .onComplete(() => {
175
+ this.isAnimating = false;
176
+ })
177
+ .start();
178
+ })
179
+ .start();
180
+ }
181
+ handleContinue() {
182
+ if (this.isChecked) {
183
+ setToLocalStorage(LOCAL_STORAGE.introScreenHidden, true);
184
+ }
185
+ this.cleanup();
186
+ this.onContinueCallback();
187
+ }
188
+ toggleCheckbox() {
189
+ this.isChecked = !this.isChecked;
190
+ this.checkboxTick.visible = this.isChecked;
191
+ }
192
+ }
@@ -0,0 +1,13 @@
1
+ import { BaseButton } from "../buttons/BaseButton";
2
+ /**
3
+ * Button for starting game after intro screen.
4
+ * It needs following textures:
5
+ * - normal - `intro-continue-n.png`
6
+ * - hover - `intro-continue-h.png`
7
+ * - clicked - `intro-continue-c.png`
8
+ * @augments {BaseButton}
9
+ */
10
+ export declare class IntroContinueButton extends BaseButton {
11
+ static registryName: string;
12
+ constructor();
13
+ }
@@ -0,0 +1,21 @@
1
+ import { getTexture } from "../../utils";
2
+ import { BaseButton, ButtonStates } from "../buttons/BaseButton";
3
+ /**
4
+ * Button for starting game after intro screen.
5
+ * It needs following textures:
6
+ * - normal - `intro-continue-n.png`
7
+ * - hover - `intro-continue-h.png`
8
+ * - clicked - `intro-continue-c.png`
9
+ * @augments {BaseButton}
10
+ */
11
+ export class IntroContinueButton extends BaseButton {
12
+ static registryName = "introContinueButton";
13
+ constructor() {
14
+ const textureMap = {
15
+ [ButtonStates.NORMAL]: getTexture("intro-continue-n.png"),
16
+ [ButtonStates.OVER]: getTexture("intro-continue-h.png"),
17
+ [ButtonStates.CLICKED]: getTexture("intro-continue-c.png")
18
+ };
19
+ super(IntroContinueButton.registryName, textureMap);
20
+ }
21
+ }
@@ -0,0 +1,15 @@
1
+ import { BaseButton } from "../buttons/BaseButton";
2
+ /**
3
+ * Dot button for intro screen page navigation.
4
+ * Textures:
5
+ * - normal - `dot-off-n.png`
6
+ * - hover - `dot-off-h.png`
7
+ * - clicked - `dot-off-c.png`
8
+ * - active (current page) - `dot-on.png`
9
+ * @augments {BaseButton}
10
+ */
11
+ export declare class IntroDotButton extends BaseButton {
12
+ static registryName: string;
13
+ constructor(index: number);
14
+ setActive(active: boolean): void;
15
+ }
@@ -0,0 +1,30 @@
1
+ import { getTexture } from "../../utils";
2
+ import { BaseButton, ButtonStates } from "../buttons/BaseButton";
3
+ /**
4
+ * Dot button for intro screen page navigation.
5
+ * Textures:
6
+ * - normal - `dot-off-n.png`
7
+ * - hover - `dot-off-h.png`
8
+ * - clicked - `dot-off-c.png`
9
+ * - active (current page) - `dot-on.png`
10
+ * @augments {BaseButton}
11
+ */
12
+ export class IntroDotButton extends BaseButton {
13
+ static registryName = "introDotButton";
14
+ constructor(index) {
15
+ const textureMap = {
16
+ [ButtonStates.NORMAL]: getTexture("dot-off-n.png"),
17
+ [ButtonStates.OVER]: getTexture("dot-off-h.png"),
18
+ [ButtonStates.CLICKED]: getTexture("dot-off-c.png"),
19
+ [ButtonStates.ACTIVE]: getTexture("dot-on.png"),
20
+ [ButtonStates.ACTIVE_OVER]: getTexture("dot-on.png"),
21
+ [ButtonStates.ACTIVE_CLICKED]: getTexture("dot-on.png")
22
+ };
23
+ super(`${IntroDotButton.registryName}-${index}`, textureMap);
24
+ this.anchor.set(0.5);
25
+ this.cursor = "pointer";
26
+ }
27
+ setActive(active) {
28
+ this.activate(active);
29
+ }
30
+ }
@@ -0,0 +1,8 @@
1
+ /**
2
+ * Data for a single page in the intro screen.
3
+ */
4
+ export type IntroPageData = {
5
+ imageDesktop: string;
6
+ imageMobile: string;
7
+ text: string;
8
+ };
@@ -0,0 +1,4 @@
1
+ export * from "./AbstractIntroScreen";
2
+ export * from "./IntroContinueButton";
3
+ export * from "./IntroDotButton";
4
+ export * from "./IntroPageData";
@@ -0,0 +1,4 @@
1
+ export * from "./AbstractIntroScreen";
2
+ export * from "./IntroContinueButton";
3
+ export * from "./IntroDotButton";
4
+ export * from "./IntroPageData";
@@ -93,6 +93,7 @@ export class AbstractColumnsContainer extends Container {
93
93
  */
94
94
  enableQuickReelsStop() {
95
95
  this.quickReelsStop = true;
96
+ RainMan.settingsStore.isQuickSpinsActive = true;
96
97
  RainMan.settingsStore.setIsTemporarySpeed(true);
97
98
  RainMan.componentRegistry.setTemporarySpeedLevel(SPEED_LEVELS.fast);
98
99
  this.skipScatterDelay?.();
@@ -109,6 +110,7 @@ export class AbstractColumnsContainer extends Container {
109
110
  */
110
111
  clearQuickReelsStop() {
111
112
  this.quickReelsStop = false;
113
+ RainMan.settingsStore.isQuickSpinsActive = false;
112
114
  RainMan.componentRegistry.revertTemporarySpeedLevel();
113
115
  }
114
116
  /**
@@ -138,7 +140,6 @@ export class AbstractColumnsContainer extends Container {
138
140
  await Promise.all(configuredBlindSpinsActions.map(async (action) => await action()));
139
141
  configuredBlindSpinsActions.length = 0;
140
142
  SoundManager.play(SoundTracks.allRollStops);
141
- this.clearQuickReelsStop();
142
143
  break;
143
144
  }
144
145
  const configureSpinAction = configuredBlindSpinsActions.shift();
@@ -1,4 +1,5 @@
1
1
  export * from "./AbstractFreeSpinContainer";
2
+ export * from "./AbstractIntroScreen";
2
3
  export * from "./AbstractMainContainer";
3
4
  export * from "./buttons";
4
5
  export * from "./common";
@@ -1,4 +1,5 @@
1
1
  export * from "./AbstractFreeSpinContainer";
2
+ export * from "./AbstractIntroScreen";
2
3
  export * from "./AbstractMainContainer";
3
4
  export * from "./buttons";
4
5
  export * from "./common";
@@ -53,6 +53,9 @@ export declare abstract class AbstractController<T> {
53
53
  protected shouldDisableBetButtons: boolean;
54
54
  protected wasAutoplayEnabledBeforeFreeSpin: boolean;
55
55
  protected setSlowerSpeedForFreeSpins: boolean;
56
+ protected quickReelsCounter: number;
57
+ protected buttonPressed: boolean;
58
+ protected buttonHeldSince: number;
56
59
  shouldStopMobileSpin: boolean;
57
60
  disableMessageBoxChange: boolean;
58
61
  protected resolveBigWin?: () => void;
@@ -53,6 +53,9 @@ export class AbstractController {
53
53
  shouldDisableBetButtons = false;
54
54
  wasAutoplayEnabledBeforeFreeSpin = false;
55
55
  setSlowerSpeedForFreeSpins = false;
56
+ quickReelsCounter = 0;
57
+ buttonPressed = false;
58
+ buttonHeldSince = 0;
56
59
  shouldStopMobileSpin = false;
57
60
  disableMessageBoxChange = false;
58
61
  resolveBigWin;
@@ -164,6 +167,13 @@ export class AbstractController {
164
167
  window.addEventListener("keydown", (keyboardEvent) => {
165
168
  if (keyboardEvent.code === "Space" || keyboardEvent.code === "Enter") {
166
169
  keyboardEvent.preventDefault();
170
+ if (this.buttonHeldSince === 0) {
171
+ this.buttonHeldSince = Date.now();
172
+ }
173
+ const buttonHold = Date.now() - this.buttonHeldSince;
174
+ if (buttonHold >= 500) {
175
+ this.columnsContainer.enableQuickReelsStop();
176
+ }
167
177
  if (RainMan.settingsStore.isGambleGameActive || RainMan.settingsStore.isSpeedModalShown) {
168
178
  return;
169
179
  }
@@ -174,6 +184,17 @@ export class AbstractController {
174
184
  this.handleSpinLogic();
175
185
  }
176
186
  });
187
+ window.addEventListener("keyup", (keyboardEvent) => {
188
+ if (keyboardEvent.code === "Space" || keyboardEvent.code === "Enter") {
189
+ keyboardEvent.preventDefault();
190
+ this.buttonHeldSince = 0;
191
+ this.columnsContainer.clearQuickReelsStop();
192
+ }
193
+ });
194
+ window.addEventListener("blur", () => {
195
+ this.buttonHeldSince = 0;
196
+ this.columnsContainer.clearQuickReelsStop();
197
+ });
177
198
  }
178
199
  /**
179
200
  * This function sets up a listener for resize event
@@ -359,7 +380,7 @@ export class AbstractController {
359
380
  if (availableFreeSpins > 0) {
360
381
  this._lastWinAmount = this.initData.round_win_amount;
361
382
  }
362
- if (spinLogic.winTotalAmount !== 0 && spinLogic.winTotalAmount) {
383
+ if (spinLogic.winTotalAmount !== 0 && spinLogic.winTotalAmount !== undefined) {
363
384
  this.uiController.recentWin = ceilToDecimal(spinLogic.winTotalAmount);
364
385
  this.uiController.totalFreeSpinWinAmount = this._lastWinAmount;
365
386
  }
@@ -407,6 +428,8 @@ export class AbstractController {
407
428
  initSpinButton() {
408
429
  const refreshButton = RainMan.componentRegistry.get(RefreshButton.registryName);
409
430
  this.clearMobileSpinTimeout = () => {
431
+ this.buttonHeldSince = 0;
432
+ this.columnsContainer.clearQuickReelsStop();
410
433
  if (this.mobileSpinTimeout) {
411
434
  clearInterval(this.mobileSpinTimeout);
412
435
  this.quickStopController.setQuickStopCounterLock(false);
@@ -450,25 +473,30 @@ export class AbstractController {
450
473
  this.clearMobileSpinTimeout();
451
474
  });
452
475
  refreshButton.on("touchstart", () => {
453
- this.mobileSpinDelay = setTimeout(() => {
454
- this.mobileSpinTimeout = setInterval(() => {
455
- this.handleDisablingButtons(true);
456
- this.quickStopController.setQuickStopCounterLock(true);
457
- this.summaryStop = this.skipFreeSpinSummary ? false : this.invokeFreeSpinSummaryPlateAfterWin;
458
- if (this.invokeFreeSpinPlateAfterWin ||
459
- this.summaryStop ||
460
- this.shouldStopMobileSpin ||
461
- this.resolveBigWin !== undefined ||
462
- this.resolveSuperBonusWin !== undefined ||
463
- this.resolveMysteryWin !== undefined) {
464
- if (this.clearMobileSpinTimeout) {
465
- this.clearMobileSpinTimeout();
466
- }
467
- return;
476
+ if (this.buttonHeldSince === 0) {
477
+ this.buttonHeldSince = Date.now();
478
+ }
479
+ this.mobileSpinTimeout = setInterval(() => {
480
+ const buttonHold = Date.now() - this.buttonHeldSince;
481
+ if (buttonHold >= 500) {
482
+ this.columnsContainer.enableQuickReelsStop();
483
+ }
484
+ this.handleDisablingButtons(true);
485
+ this.quickStopController.setQuickStopCounterLock(true);
486
+ this.summaryStop = this.skipFreeSpinSummary ? false : this.invokeFreeSpinSummaryPlateAfterWin;
487
+ if (this.invokeFreeSpinPlateAfterWin ||
488
+ this.summaryStop ||
489
+ this.shouldStopMobileSpin ||
490
+ this.resolveBigWin !== undefined ||
491
+ this.resolveSuperBonusWin !== undefined ||
492
+ this.resolveMysteryWin !== undefined) {
493
+ if (this.clearMobileSpinTimeout) {
494
+ this.clearMobileSpinTimeout();
468
495
  }
469
- this.handleSpinLogic();
470
- }, RainMan.config.durationOfActions.mobileSpinTimeout);
471
- }, 500);
496
+ return;
497
+ }
498
+ this.handleSpinLogic();
499
+ }, RainMan.config.durationOfActions.mobileSpinTimeout);
472
500
  });
473
501
  }
474
502
  /**
@@ -672,7 +700,10 @@ export class AbstractController {
672
700
  this.handleDisablingButtons(true);
673
701
  RainMan.globals.shouldShowModals = false;
674
702
  RainMan.settingsStore.closeCurrentlyOpenModal?.();
675
- this.columnsContainer.clearQuickReelsStop();
703
+ const realTime = Date.now() - this.buttonHeldSince;
704
+ if (realTime <= 500) {
705
+ this.columnsContainer.clearQuickReelsStop();
706
+ }
676
707
  if (this.mainContainer.freeSpinPlate) {
677
708
  if (this.mainContainer.freeSpinPlate.shownAt === null ||
678
709
  performance.now() - this.mainContainer.freeSpinPlate.shownAt <
@@ -729,6 +760,7 @@ export class AbstractController {
729
760
  * @returns {void}
730
761
  */
731
762
  throttledSpin() {
763
+ this.quickReelsCounter += 1;
732
764
  this.columnsContainer.setInteractivityForSymbols(false);
733
765
  this.handleDisablingButtons(true);
734
766
  RainMan.globals.shouldShowModals = false;
@@ -741,7 +773,6 @@ export class AbstractController {
741
773
  if (this.gamePhase === gamePhases.DATA_FETCH ||
742
774
  this.gamePhase === gamePhases.SPINNING ||
743
775
  this.gamePhase === gamePhases.CONFIGURING_SPIN) {
744
- this.columnsContainer.enableQuickReelsStop();
745
776
  this.isSpinThrottled = true;
746
777
  this.quickStopController.handleResetCounter(this.spinThrottlerTimeout !== null);
747
778
  this.quickStopController.handleInvokingSpeedPopup();
@@ -762,10 +793,6 @@ export class AbstractController {
762
793
  this.mainContainer.destroyBigWin();
763
794
  this.changeGamePhase(gamePhases.IDLE);
764
795
  }
765
- if (this.spinThrottlerTimeout !== null && !RainMan.settingsStore.isFreeSpinsPlayEnabled) {
766
- this.columnsContainer.clearQuickReelsStop();
767
- return;
768
- }
769
796
  if (this.gamePhase === gamePhases.IDLE || this.gamePhase === gamePhases.HANDLING_ACTIONS) {
770
797
  this.spin();
771
798
  }
@@ -1025,7 +1052,6 @@ export class AbstractController {
1025
1052
  await this.onSpinningStart();
1026
1053
  hideVolumeModal();
1027
1054
  RainMan.settingsStore.disableSpecialButtons();
1028
- this.columnsContainer.clearQuickReelsStop();
1029
1055
  this.columnsContainer.blindSpin();
1030
1056
  this.clearAndDestroyActions();
1031
1057
  const { spinLogic } = await this.fetchAndPrepareSpinData();
@@ -9,6 +9,7 @@ export declare const LOCAL_STORAGE: {
9
9
  readonly isSoundEnabled: "isSoundEnabledLocalStorage";
10
10
  readonly isSoundFxEnabled: "isSoundFxEnabledLocalStorage";
11
11
  readonly isAmbientMusicEnabled: "isAmbientMusicEnabledLocalStorage";
12
+ readonly introScreenHidden: "introScreenHiddenLocalStorage";
12
13
  };
13
14
  /**
14
15
  * Type for local storage keys
@@ -8,7 +8,8 @@ export const LOCAL_STORAGE = {
8
8
  volumeLevel: "volumeLevelLocalStorage",
9
9
  isSoundEnabled: "isSoundEnabledLocalStorage",
10
10
  isSoundFxEnabled: "isSoundFxEnabledLocalStorage",
11
- isAmbientMusicEnabled: "isAmbientMusicEnabledLocalStorage"
11
+ isAmbientMusicEnabled: "isAmbientMusicEnabledLocalStorage",
12
+ introScreenHidden: "introScreenHiddenLocalStorage"
12
13
  };
13
14
  /**
14
15
  * Get a value from local storage
@@ -12,6 +12,7 @@ export declare class SettingsStore {
12
12
  static instance: SettingsStore | null;
13
13
  betChangeDisabled: boolean;
14
14
  disableKeyboardShortcuts: boolean;
15
+ isQuickSpinsActive: boolean;
15
16
  bet: number;
16
17
  betText: string;
17
18
  shouldPlayAmbientMusic: boolean;
@@ -16,6 +16,7 @@ export class SettingsStore {
16
16
  static instance = null;
17
17
  betChangeDisabled = false;
18
18
  disableKeyboardShortcuts = false;
19
+ isQuickSpinsActive = false;
19
20
  bet = 0;
20
21
  betText = "";
21
22
  shouldPlayAmbientMusic = true;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pixi-rainman-game-engine",
3
- "version": "0.3.25",
3
+ "version": "0.3.27",
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",