pixi-rainman-game-engine 0.3.28 → 0.3.29

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.
@@ -209,7 +209,7 @@ export class ButtonsEventManager {
209
209
  initFreeSpinModal() {
210
210
  RainMan.componentRegistry
211
211
  .getIfExists(FreeSpinButton.registryName)
212
- ?.setOnClick(() => this.handleModalInvocation(UI_ITEMS.freeSpin));
212
+ ?.setOnClick(() => RainMan.settingsStore.handleBuyFreeSpinPopupActivation?.());
213
213
  }
214
214
  /**
215
215
  * Method for initializing volume button
@@ -0,0 +1,44 @@
1
+ import { Container, Sprite } from "pixi.js";
2
+ import { BuyFreeSpinOption } from "SettingsUI/hooks";
3
+ import { AcceptDeclineButton } from "./AcceptDeclineButton";
4
+ export type BuyFreeSpinConfig = {
5
+ options: BuyFreeSpinOption[];
6
+ };
7
+ export type BuyFreeSpinState = "selection" | "confirmation";
8
+ export declare abstract class AbstractBuyFreeSpinsPlate extends Container {
9
+ private state;
10
+ protected selectedOption?: BuyFreeSpinOption;
11
+ protected readonly config: BuyFreeSpinConfig;
12
+ protected selectionPlate?: Sprite;
13
+ protected closeButton?: AcceptDeclineButton;
14
+ protected confirmationPlate?: Sprite;
15
+ protected acceptButton?: AcceptDeclineButton;
16
+ protected declineButton?: AcceptDeclineButton;
17
+ protected selectionPlateTexture: string;
18
+ protected confirmationPlateTexture: string;
19
+ private readonly onContinueCallback;
20
+ private readonly resizeHandler;
21
+ private handleClose;
22
+ protected constructor(onContinue: () => void, config: BuyFreeSpinConfig);
23
+ init(): void;
24
+ private createElements;
25
+ resize(): void;
26
+ private showSelectionScreen;
27
+ private showConfirmationScreen;
28
+ protected abstract getPlateScale(): {
29
+ vertical: number;
30
+ horizontal: number;
31
+ };
32
+ protected abstract createSelectionContent(): void;
33
+ protected abstract createConfirmationContent(): void;
34
+ protected abstract showSelectionContent(): void;
35
+ protected abstract showConfirmationContent(option: BuyFreeSpinOption): void;
36
+ protected abstract positionSelectionScreen(): void;
37
+ protected abstract positionConfirmationScreen(): void;
38
+ protected onCleanup(): void;
39
+ protected onBuyButtonClick(option: BuyFreeSpinOption): void;
40
+ private onAcceptClick;
41
+ private renderState;
42
+ private onDeclineClick;
43
+ private cleanup;
44
+ }
@@ -0,0 +1,181 @@
1
+ import { Container, Sprite, Texture } from "pixi.js";
2
+ import { UXLayer } from "../../layers";
3
+ import { RainMan } from "../../Rainman";
4
+ import { scaleToParent } from "../../utils";
5
+ import { AcceptDeclineButton } from "./AcceptDeclineButton";
6
+ export class AbstractBuyFreeSpinsPlate extends Container {
7
+ state = "selection";
8
+ selectedOption;
9
+ config;
10
+ selectionPlate;
11
+ closeButton;
12
+ confirmationPlate;
13
+ acceptButton;
14
+ declineButton;
15
+ selectionPlateTexture = "selection-freespin-frame";
16
+ confirmationPlateTexture = "confirmation-freespin-frame";
17
+ onContinueCallback;
18
+ resizeHandler = () => this.resize();
19
+ handleClose = () => {
20
+ this.cleanup();
21
+ this.onContinueCallback();
22
+ RainMan.settingsStore.disableKeyboardShortcuts = false;
23
+ };
24
+ constructor(onContinue, config) {
25
+ super();
26
+ RainMan.settingsStore.disableKeyboardShortcuts = true;
27
+ this.onContinueCallback = onContinue;
28
+ this.config = config;
29
+ this.parentLayer = UXLayer;
30
+ this.eventMode = "static";
31
+ if (config.options.length === 1) {
32
+ this.state = "confirmation";
33
+ this.selectedOption = config.options[0];
34
+ }
35
+ }
36
+ init() {
37
+ this.createElements();
38
+ this.renderState();
39
+ RainMan.app.renderer.on("resize", this.resizeHandler);
40
+ this.resize();
41
+ }
42
+ createElements() {
43
+ const numOptions = this.config.options.length;
44
+ this.selectionPlate = new Sprite(Texture.from(this.selectionPlateTexture));
45
+ this.selectionPlate.anchor.set(0.5);
46
+ if (numOptions > 1) {
47
+ this.closeButton = new AcceptDeclineButton(false);
48
+ this.closeButton.setOnClick(() => this.handleClose());
49
+ this.closeButton.anchor.set(0.5);
50
+ }
51
+ this.confirmationPlate = new Sprite(Texture.from(this.confirmationPlateTexture));
52
+ this.confirmationPlate.anchor.set(0.5);
53
+ this.acceptButton = new AcceptDeclineButton(true);
54
+ this.acceptButton.setOnClick(() => this.onAcceptClick(this.selectedOption));
55
+ this.acceptButton.anchor.set(0.5);
56
+ this.declineButton = new AcceptDeclineButton(false);
57
+ this.declineButton.setOnClick(() => this.onDeclineClick());
58
+ this.declineButton.anchor.set(0.5);
59
+ this.createSelectionContent();
60
+ this.createConfirmationContent();
61
+ }
62
+ resize() {
63
+ const { width, height } = RainMan.app.screen;
64
+ if (this.state === "selection") {
65
+ if (!this.selectionPlate)
66
+ return;
67
+ const plateScale = this.getPlateScale();
68
+ const texW = this.selectionPlate.texture.width;
69
+ const texH = this.selectionPlate.texture.height;
70
+ const targetW = width * plateScale.horizontal;
71
+ const targetH = height * plateScale.vertical;
72
+ const dims = scaleToParent(texW, texH, targetW, targetH);
73
+ const scaleX = dims.width / texW;
74
+ const scaleY = dims.height / texH;
75
+ const scale = Math.min(scaleX, scaleY);
76
+ this.scale.set(scale);
77
+ this.position.set(width / 2, height / 2);
78
+ this.positionSelectionScreen();
79
+ return;
80
+ }
81
+ if (this.state === "confirmation") {
82
+ if (!this.confirmationPlate)
83
+ return;
84
+ const plateScale = this.getPlateScale();
85
+ const texW = this.confirmationPlate.texture.width;
86
+ const texH = this.confirmationPlate.texture.height;
87
+ const targetW = RainMan.app.screen.width * plateScale.horizontal;
88
+ const targetH = RainMan.app.screen.height * plateScale.vertical;
89
+ const dims = scaleToParent(texW, texH, targetW, targetH);
90
+ const scaleX = dims.width / texW;
91
+ const scaleY = dims.height / texH;
92
+ const scale = Math.min(scaleX, scaleY);
93
+ this.scale.set(scale);
94
+ this.position.set(width / 2, height / 2);
95
+ this.positionConfirmationScreen();
96
+ }
97
+ this.parentLayer = UXLayer;
98
+ }
99
+ showSelectionScreen() {
100
+ if (!this.selectionPlate)
101
+ return;
102
+ this.selectionPlate.visible = true;
103
+ this.selectionPlate.alpha = 1;
104
+ this.addChild(this.selectionPlate);
105
+ this.showSelectionContent();
106
+ if (this.closeButton) {
107
+ this.addChild(this.closeButton);
108
+ }
109
+ this.positionSelectionScreen();
110
+ }
111
+ showConfirmationScreen() {
112
+ if (!this.confirmationPlate)
113
+ return;
114
+ if (!this.selectedOption) {
115
+ this.selectedOption = this.config.options[0];
116
+ }
117
+ this.addChild(this.confirmationPlate);
118
+ this.showConfirmationContent(this.selectedOption);
119
+ if (this.acceptButton) {
120
+ this.addChild(this.acceptButton);
121
+ }
122
+ if (this.declineButton) {
123
+ this.addChild(this.declineButton);
124
+ }
125
+ this.positionConfirmationScreen();
126
+ }
127
+ onCleanup() { }
128
+ onBuyButtonClick(option) {
129
+ this.selectedOption = option;
130
+ this.state = "confirmation";
131
+ this.renderState();
132
+ this.resize();
133
+ }
134
+ async onAcceptClick(option) {
135
+ const roundNumber = RainMan.settingsStore.roundNumber + 1;
136
+ try {
137
+ const data = await window.connectionWrapper.fetchFreeSpinData(roundNumber, option.amount, option.price);
138
+ const code = data.getRawData()?.status?.code;
139
+ if (code === 400 || code === 406) {
140
+ return;
141
+ }
142
+ RainMan.settingsStore.setIsFreeSpinBought(true, data);
143
+ RainMan.settingsStore.setFreeSpinNumberBought(option.amount);
144
+ RainMan.globals.updateFreeSpinShown?.(option.amount);
145
+ RainMan.settingsStore.setRoundNumber(roundNumber);
146
+ this.cleanup();
147
+ this.onContinueCallback();
148
+ RainMan.settingsStore.handleModalInvocation?.("freeSpin");
149
+ RainMan.settingsStore.handleInvokeFreeSpinPlate?.(option.amount, false);
150
+ RainMan.settingsStore.disableKeyboardShortcuts = false;
151
+ }
152
+ catch (err) {
153
+ console.error(err);
154
+ RainMan.settingsStore.setIsFreeSpinBought(false);
155
+ RainMan.settingsStore.disableKeyboardShortcuts = false;
156
+ }
157
+ }
158
+ renderState() {
159
+ this.removeChildren();
160
+ if (this.state === "selection") {
161
+ this.showSelectionScreen();
162
+ return;
163
+ }
164
+ this.showConfirmationScreen();
165
+ }
166
+ onDeclineClick() {
167
+ if (this.config.options.length === 1) {
168
+ this.handleClose();
169
+ return;
170
+ }
171
+ this.state = "selection";
172
+ this.selectedOption = undefined;
173
+ this.renderState();
174
+ this.resize();
175
+ }
176
+ cleanup() {
177
+ RainMan.app.renderer.off("resize", this.resizeHandler);
178
+ this.onCleanup();
179
+ this.destroy({ children: true });
180
+ }
181
+ }
@@ -0,0 +1,14 @@
1
+ import { BaseButton } from "../buttons/BaseButton";
2
+ /**
3
+ * Button for accepting or declining number of free spins to buy.
4
+ * It needs following textures:
5
+ * - normal - `v-n.png` for accept, `x-n.png` for decline
6
+ * - hover - `v-h.png` for accept, `x-h.png` for decline
7
+ * - clicked - `v-c.png` for accept, `x-c.png` for decline
8
+ * @param {boolean} isAcceptButton desides which texture to use
9
+ * @augments {BaseButton}
10
+ */
11
+ export declare class AcceptDeclineButton extends BaseButton {
12
+ static registryName: string;
13
+ constructor(isAcceptButton?: boolean);
14
+ }
@@ -0,0 +1,28 @@
1
+ import { getTexture } from "../../utils";
2
+ import { BaseButton, ButtonStates } from "../buttons/BaseButton";
3
+ /**
4
+ * Button for accepting or declining number of free spins to buy.
5
+ * It needs following textures:
6
+ * - normal - `v-n.png` for accept, `x-n.png` for decline
7
+ * - hover - `v-h.png` for accept, `x-h.png` for decline
8
+ * - clicked - `v-c.png` for accept, `x-c.png` for decline
9
+ * @param {boolean} isAcceptButton desides which texture to use
10
+ * @augments {BaseButton}
11
+ */
12
+ export class AcceptDeclineButton extends BaseButton {
13
+ static registryName = "acceptDeclineButton";
14
+ constructor(isAcceptButton) {
15
+ const textureMap = isAcceptButton
16
+ ? {
17
+ [ButtonStates.NORMAL]: getTexture("v-n.png"),
18
+ [ButtonStates.OVER]: getTexture("v-h.png"),
19
+ [ButtonStates.CLICKED]: getTexture("v-c.png")
20
+ }
21
+ : {
22
+ [ButtonStates.NORMAL]: getTexture("x-n.png"),
23
+ [ButtonStates.OVER]: getTexture("x-h.png"),
24
+ [ButtonStates.CLICKED]: getTexture("x-c.png")
25
+ };
26
+ super(AcceptDeclineButton.registryName, textureMap);
27
+ }
28
+ }
@@ -0,0 +1,14 @@
1
+ import { BaseButton } from "../buttons/BaseButton";
2
+ /**
3
+ * Button for selecting number of freespins to buy
4
+ * It needs following textures:
5
+ * - normal - `buttonTextureName-n.png`
6
+ * - hover - `buttonTextureName-h.png`
7
+ * - clicked - `buttonTextureName-c.png`
8
+ * @param {string} buttonTextureName name of a texture to use
9
+ * @augments {BaseButton}
10
+ */
11
+ export declare class BuyFreeSpinButton extends BaseButton {
12
+ static registryName: string;
13
+ constructor(buttonTextureName: string);
14
+ }
@@ -0,0 +1,22 @@
1
+ import { getTexture } from "../../utils";
2
+ import { BaseButton, ButtonStates } from "../buttons/BaseButton";
3
+ /**
4
+ * Button for selecting number of freespins to buy
5
+ * It needs following textures:
6
+ * - normal - `buttonTextureName-n.png`
7
+ * - hover - `buttonTextureName-h.png`
8
+ * - clicked - `buttonTextureName-c.png`
9
+ * @param {string} buttonTextureName name of a texture to use
10
+ * @augments {BaseButton}
11
+ */
12
+ export class BuyFreeSpinButton extends BaseButton {
13
+ static registryName = "buyFreeSpinButton";
14
+ constructor(buttonTextureName) {
15
+ const textureMap = {
16
+ [ButtonStates.NORMAL]: getTexture(`${buttonTextureName}-n.png`),
17
+ [ButtonStates.OVER]: getTexture(`${buttonTextureName}-h.png`),
18
+ [ButtonStates.CLICKED]: getTexture(`${buttonTextureName}-c.png`)
19
+ };
20
+ super(BuyFreeSpinButton.registryName, textureMap);
21
+ }
22
+ }
@@ -0,0 +1,3 @@
1
+ export * from "./AbstractBuyFreeSpinsPlate";
2
+ export * from "./AcceptDeclineButton";
3
+ export * from "./BuyFreeSpinButton";
@@ -0,0 +1,3 @@
1
+ export * from "./AbstractBuyFreeSpinsPlate";
2
+ export * from "./AcceptDeclineButton";
3
+ export * from "./BuyFreeSpinButton";
@@ -1,3 +1,4 @@
1
+ import { AbstractBuyFreeSpinsPlate } from "components/AbstractBuyFreeSpins/AbstractBuyFreeSpinsPlate";
1
2
  import { Container, Graphics, Sprite } from "pixi.js";
2
3
  import { ButtonsEventManager, WinTypeId } from "../../application";
3
4
  import { AbstractController } from "../../controllers";
@@ -54,6 +55,7 @@ export declare abstract class AbstractMainContainer extends Container {
54
55
  protected mysteryBonusContainer: Nullable<UpdatableSpineContainer>;
55
56
  protected resolveMysteryBonusContainer?: PromiseResolverType;
56
57
  protected _freeSpinPlate: Nullable<AbstractFreeSpinContainer>;
58
+ protected _buyFreeSpinsPlate: Nullable<AbstractBuyFreeSpinsPlate>;
57
59
  protected _freeSpinSummary: Nullable<AbstractFreeSpinContainer>;
58
60
  private _bigWinInvokedAt;
59
61
  private _mysteryWinInvokedAt;
@@ -173,6 +175,9 @@ export declare abstract class AbstractMainContainer extends Container {
173
175
  * @returns {Promise<void>}
174
176
  */
175
177
  invokeFreeSpinPlate(freeSpinCount: number, isAdditionalFreeSpin?: boolean, isMoneyReward?: boolean): Promise<void>;
178
+ invokeBuyFreeSpinPlate(): void;
179
+ protected closeBuyFreeSpinPlate(): void;
180
+ protected createBuyFreeSpinPlate(): AbstractBuyFreeSpinsPlate | null;
176
181
  /**
177
182
  * Function for creating free spin summary
178
183
  * @protected
@@ -47,6 +47,7 @@ export class AbstractMainContainer extends Container {
47
47
  mysteryBonusContainer = null;
48
48
  resolveMysteryBonusContainer = undefined;
49
49
  _freeSpinPlate = null;
50
+ _buyFreeSpinsPlate = null;
50
51
  _freeSpinSummary = null;
51
52
  _bigWinInvokedAt = null;
52
53
  _mysteryWinInvokedAt = null;
@@ -60,6 +61,7 @@ export class AbstractMainContainer extends Container {
60
61
  this.messageBox = new MessageBox();
61
62
  SoundManager.setPlayStatus(SoundTracks.music, RainMan.settingsStore.shouldPlayAmbientMusic);
62
63
  RainMan.settingsStore.setHandleInvokeFreeSpinPlate(this.invokeFreeSpinPlate.bind(this));
64
+ RainMan.settingsStore.setHandleBuyFreeSpinPopupActivatio(this.invokeBuyFreeSpinPlate.bind(this));
63
65
  RainMan.app.renderer.on("resize", () => this.resize());
64
66
  }
65
67
  /**
@@ -236,6 +238,30 @@ export class AbstractMainContainer extends Container {
236
238
  this.hideOverlay();
237
239
  refreshButton.flipDisabledFlag(false);
238
240
  }
241
+ invokeBuyFreeSpinPlate() {
242
+ const refreshButton = RainMan.componentRegistry.get(RefreshButton.registryName);
243
+ refreshButton.flipDisabledFlag(true);
244
+ this._buyFreeSpinsPlate = this.createBuyFreeSpinPlate();
245
+ if (!this._buyFreeSpinsPlate) {
246
+ refreshButton.flipDisabledFlag(false);
247
+ return;
248
+ }
249
+ this._buyFreeSpinsPlate.init();
250
+ this._buyFreeSpinsPlate.resize();
251
+ this.showOverlay();
252
+ this.addChild(this._buyFreeSpinsPlate);
253
+ }
254
+ closeBuyFreeSpinPlate() {
255
+ this.removeChild(this._buyFreeSpinsPlate);
256
+ this._buyFreeSpinsPlate.destroy({ children: true });
257
+ this._buyFreeSpinsPlate = null;
258
+ this.hideOverlay();
259
+ const refreshButton = RainMan.componentRegistry.get(RefreshButton.registryName);
260
+ refreshButton.flipDisabledFlag(false);
261
+ }
262
+ createBuyFreeSpinPlate() {
263
+ return null;
264
+ }
239
265
  /**
240
266
  * Function for creating free spin summary
241
267
  * @protected
@@ -884,6 +910,11 @@ export class AbstractMainContainer extends Container {
884
910
  this.addChild(this.freeSpinSummary);
885
911
  this.freeSpinSummary.resize();
886
912
  }
913
+ if (this._buyFreeSpinsPlate) {
914
+ this.removeChild(this._buyFreeSpinsPlate);
915
+ this.addChild(this._buyFreeSpinsPlate);
916
+ this._buyFreeSpinsPlate.resize();
917
+ }
887
918
  RainMan.settingsStore.popupPaytableRepositionCallback?.();
888
919
  }
889
920
  /**
@@ -60,6 +60,9 @@
60
60
  "doubleTo": "DOUBLE TO",
61
61
  "collect": "COLLECT",
62
62
  "totalBet": "Bet",
63
+ "buyFreeSpinTitle": "BUY",
64
+ "freeSpinsAmount": "{{amount}}",
65
+ "freeSpinText": "FREE SPINS",
63
66
  "buyFreeSpins": "Buy Free Spins",
64
67
  "possibleWin": "POSSIBLE WIN: ",
65
68
  "gamePaysText": "GAME PAYS: "
@@ -1,3 +1,4 @@
1
+ export * from "./AbstractBuyFreeSpins";
1
2
  export * from "./AbstractFreeSpinContainer";
2
3
  export * from "./AbstractIntroScreen";
3
4
  export * from "./AbstractMainContainer";
@@ -1,3 +1,4 @@
1
+ export * from "./AbstractBuyFreeSpins";
1
2
  export * from "./AbstractFreeSpinContainer";
2
3
  export * from "./AbstractIntroScreen";
3
4
  export * from "./AbstractMainContainer";
@@ -429,12 +429,19 @@ export class AbstractController {
429
429
  const refreshButton = RainMan.componentRegistry.get(RefreshButton.registryName);
430
430
  this.clearMobileSpinTimeout = () => {
431
431
  this.buttonHeldSince = 0;
432
- this.columnsContainer.clearQuickReelsStop();
432
+ const isSpinning = this.gamePhase === gamePhases.SPINNING ||
433
+ this.gamePhase === gamePhases.CONFIGURING_SPIN ||
434
+ this.gamePhase === gamePhases.DATA_FETCH;
435
+ if (!isSpinning) {
436
+ this.columnsContainer.clearQuickReelsStop();
437
+ }
433
438
  if (this.mobileSpinTimeout) {
434
439
  clearInterval(this.mobileSpinTimeout);
435
440
  this.quickStopController.setQuickStopCounterLock(false);
436
441
  this.mobileSpinTimeout = null;
437
- this.columnsContainer.clearQuickReelsStop();
442
+ if (!isSpinning) {
443
+ this.columnsContainer.clearQuickReelsStop();
444
+ }
438
445
  }
439
446
  if (this.mobileSpinDelay) {
440
447
  clearTimeout(this.mobileSpinDelay);
@@ -62,6 +62,7 @@ export declare class SettingsStore {
62
62
  closeOtherModals?: (layerId: string) => void;
63
63
  closeCurrentlyOpenModal?: () => void;
64
64
  handleInvokeFreeSpinPlate?: (freeSpinAmount: number, isAdditionalFreeSpin: boolean) => Promise<void>;
65
+ handleBuyFreeSpinPopupActivation?: () => void;
65
66
  increaseBet?: () => void;
66
67
  decreaseBet?: () => void;
67
68
  enableButtons?: () => void;
@@ -387,6 +388,13 @@ export declare class SettingsStore {
387
388
  * @returns {void}
388
389
  */
389
390
  setHandleInvokeFreeSpinPlate(handleInvokeFreeSpinPlate: (freeSpinAmount: number, isAdditionalFreeSpin: boolean) => Promise<void>): void;
391
+ /**
392
+ * Set the callback for invoking the free spin plate
393
+ * @param handleBuyFreeSpinPopupActivation
394
+ * @public
395
+ * @returns {void}
396
+ */
397
+ setHandleBuyFreeSpinPopupActivatio(handleBuyFreeSpinPopupActivation: () => void): void;
390
398
  /**
391
399
  * Reset the number of free spins bought
392
400
  * @public
@@ -66,6 +66,7 @@ export class SettingsStore {
66
66
  closeOtherModals;
67
67
  closeCurrentlyOpenModal;
68
68
  handleInvokeFreeSpinPlate;
69
+ handleBuyFreeSpinPopupActivation;
69
70
  increaseBet;
70
71
  decreaseBet;
71
72
  enableButtons;
@@ -641,6 +642,15 @@ export class SettingsStore {
641
642
  setHandleInvokeFreeSpinPlate(handleInvokeFreeSpinPlate) {
642
643
  this.handleInvokeFreeSpinPlate = handleInvokeFreeSpinPlate;
643
644
  }
645
+ /**
646
+ * Set the callback for invoking the free spin plate
647
+ * @param handleBuyFreeSpinPopupActivation
648
+ * @public
649
+ * @returns {void}
650
+ */
651
+ setHandleBuyFreeSpinPopupActivatio(handleBuyFreeSpinPopupActivation) {
652
+ this.handleBuyFreeSpinPopupActivation = handleBuyFreeSpinPopupActivation;
653
+ }
644
654
  /**
645
655
  * Reset the number of free spins bought
646
656
  * @public
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pixi-rainman-game-engine",
3
- "version": "0.3.28",
3
+ "version": "0.3.29",
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",