pixi-rainman-game-engine 0.3.43-c → 0.3.44-beta

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.
@@ -49,7 +49,8 @@ export class ComponentRegistry {
49
49
  this.speedLevelHandler.setSpeed(newSpeedLevel);
50
50
  RainMan.settingsStore.setSpeedLevel(this.speedLevelHandler.currentSpeed);
51
51
  this.updateAllSpeedAdaptable(RainMan.settingsStore.currentSpeed);
52
- this.get(SpeedControlButton.registryName).forceTextureUpdate();
52
+ this.getIfExists(SpeedControlButton.registryName)?.forceTextureUpdate();
53
+ this.getIfExists(SpeedControlButtonMobile.registryName)?.forceTextureUpdate();
53
54
  }
54
55
  /**
55
56
  * Setting the next available speed level
@@ -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
+ isCurrentAnimationPastProgressThreshold?: () => 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;
@@ -154,6 +154,7 @@ export declare class ButtonsEventManager {
154
154
  * @returns {void}
155
155
  */
156
156
  private handleModalInvocation;
157
+ private closeAllModals;
157
158
  /**
158
159
  * Function for closing all other modals except the one with given name
159
160
  * @param {string} activeLayerName - name of the layer that should remain open
@@ -62,6 +62,7 @@ export class ButtonsEventManager {
62
62
  RainMan.settingsStore.handleModalInvocation = this.handleModalInvocation.bind(this);
63
63
  RainMan.settingsStore.enableButtons = this.enableButtons.bind(this);
64
64
  RainMan.settingsStore.closeOtherModals = this.closeOtherModals.bind(this);
65
+ RainMan.settingsStore.closeAllModals = this.closeAllModals.bind(this);
65
66
  RainMan.settingsStore.closeCurrentlyOpenModal = this.closeCurrentlyOpenModal.bind(this);
66
67
  }
67
68
  /**
@@ -359,6 +360,14 @@ export class ButtonsEventManager {
359
360
  }
360
361
  }
361
362
  }
363
+ closeAllModals() {
364
+ for (const itemId of allUiItems) {
365
+ const layer = window.document.getElementById(itemId);
366
+ if (layer !== null && layer.style.display === "flex") {
367
+ layer.style.display = "none";
368
+ }
369
+ }
370
+ }
362
371
  /**
363
372
  * Function for closing all other modals except the one with given name
364
373
  * @param {string} activeLayerName - name of the layer that should remain open
@@ -320,6 +320,7 @@ export declare abstract class AbstractMainContainer extends Container {
320
320
  * @returns {Promise<void>} - A promise that resolves when the gamble game is finished.
321
321
  */
322
322
  invokeGambleGame(): Promise<void>;
323
+ private destroyDisplayContainer;
323
324
  /**
324
325
  * Function for creating the under frame panel.
325
326
  * This panel is used in mobile portrait mode to display additional information.
@@ -243,7 +243,7 @@ export class AbstractMainContainer extends Container {
243
243
  await this._freeSpinPlate.onStart();
244
244
  });
245
245
  if (this._freeSpinPlate !== null) {
246
- this.removeChild(this._freeSpinPlate);
246
+ this.destroyDisplayContainer(this._freeSpinPlate);
247
247
  this._freeSpinPlate = null;
248
248
  }
249
249
  this.hideOverlay();
@@ -252,6 +252,7 @@ export class AbstractMainContainer extends Container {
252
252
  invokeBuyFreeSpinPlate() {
253
253
  const refreshButton = RainMan.componentRegistry.get(RefreshButton.registryName);
254
254
  refreshButton.flipDisabledFlag(true);
255
+ RainMan.settingsStore.closeAllModals?.();
255
256
  this._buyFreeSpinsPlate = this.createBuyFreeSpinPlate();
256
257
  if (!this._buyFreeSpinsPlate) {
257
258
  refreshButton.flipDisabledFlag(false);
@@ -303,7 +304,7 @@ export class AbstractMainContainer extends Container {
303
304
  this.addChild(this._freeSpinSummary);
304
305
  });
305
306
  if (this._freeSpinSummary !== null) {
306
- this.removeChild(this._freeSpinSummary);
307
+ this.destroyDisplayContainer(this._freeSpinSummary);
307
308
  this._freeSpinSummary = null;
308
309
  }
309
310
  this.hideOverlay();
@@ -320,7 +321,7 @@ export class AbstractMainContainer extends Container {
320
321
  return;
321
322
  }
322
323
  this.freeSpinSummary.onClick();
323
- this.removeChild(this.freeSpinSummary);
324
+ this.destroyDisplayContainer(this.freeSpinSummary);
324
325
  this._freeSpinSummary = null;
325
326
  this.hideOverlay();
326
327
  }
@@ -570,7 +571,7 @@ export class AbstractMainContainer extends Container {
570
571
  this.addChild(this.gambleGame);
571
572
  });
572
573
  if (this.gambleGame) {
573
- this.removeChild(this.gambleGame);
574
+ this.destroyDisplayContainer(this.gambleGame);
574
575
  this.messageBox.visible = true;
575
576
  RainMan.settingsStore.isGambleGameActive = false;
576
577
  this._controller.updateGambleValues(RainMan.settingsStore.gambleGameWin);
@@ -582,6 +583,15 @@ export class AbstractMainContainer extends Container {
582
583
  }
583
584
  this.hideOverlay();
584
585
  }
586
+ destroyDisplayContainer(container) {
587
+ if (container.destroyed) {
588
+ return;
589
+ }
590
+ if (container.parent) {
591
+ container.parent.removeChild(container);
592
+ }
593
+ container.destroy({ children: true });
594
+ }
585
595
  /**
586
596
  * Function for creating the under frame panel.
587
597
  * This panel is used in mobile portrait mode to display additional information.
@@ -108,13 +108,13 @@ export class MiddleButtons extends ResponsiveContainer {
108
108
  }
109
109
  if (this.landscape) {
110
110
  this.betButton.x = width / 2;
111
- this.betButton.y = -20;
111
+ this.betButton.y = -25;
112
112
  this.autoplayButton.x = width / 2;
113
- this.autoplayButton.y = height + 30;
113
+ this.autoplayButton.y = height + 35;
114
114
  }
115
115
  else {
116
116
  this.autoplayButton.x = -width;
117
- this.autoplayButton.y = 50;
117
+ this.autoplayButton.y = 0;
118
118
  this.betButton.x = width;
119
119
  this.betButton.y = height;
120
120
  }
@@ -68,7 +68,16 @@ export class AnimatedNumber extends Container {
68
68
  * @returns {void}
69
69
  */
70
70
  removeDigits() {
71
- this.digits.forEach((digit) => this.removeChild(digit));
71
+ this.digits.forEach((digit) => {
72
+ if (digit.destroyed) {
73
+ return;
74
+ }
75
+ digit.state.clearListeners();
76
+ if (digit.parent) {
77
+ digit.parent.removeChild(digit);
78
+ }
79
+ digit.destroy({ children: true });
80
+ });
72
81
  this.digits = [];
73
82
  }
74
83
  /**
@@ -91,6 +91,7 @@ export declare abstract class AbstractSymbolBase extends Container implements Sp
91
91
  * @returns {Promise<void>}
92
92
  */
93
93
  handleMultiplierAnimation(multiplier: number): Promise<void>;
94
+ private destroySpine;
94
95
  /**
95
96
  * Function for playing the symbol animation
96
97
  * @public
@@ -98,6 +99,7 @@ export declare abstract class AbstractSymbolBase extends Container implements Sp
98
99
  * @returns {Promise<void>} - A promise that resolves when the animation is complete
99
100
  */
100
101
  play(suffix?: string): Promise<void>;
102
+ isCurrentAnimationPastProgressThreshold(): boolean;
101
103
  /**
102
104
  * Function for stopping the symbol animation.
103
105
  * @public
@@ -31,10 +31,10 @@ export class AbstractSymbolBase extends Container {
31
31
  this._id = symbolId;
32
32
  this.spine = new Spine(this.getResourceForSymbolId(this._id));
33
33
  this.spine.parentLayer = AnimationLayer;
34
- this.symbolImage = new Sprite(getTexture(this.getImage(this._id)));
35
- this.setSymbol(symbolId);
36
34
  this.spine.x = 0;
37
35
  this.spine.y = 0;
36
+ this.symbolImage = new Sprite(getTexture(this.getImage(this._id)));
37
+ this.setSymbol(symbolId);
38
38
  this.eventMode = "static";
39
39
  const symbolRadius = 150;
40
40
  this.hitArea = new Rectangle(-symbolRadius, -symbolRadius, 2 * symbolRadius, 2 * symbolRadius);
@@ -109,6 +109,7 @@ export class AbstractSymbolBase extends Container {
109
109
  return () => {
110
110
  this.swapSymbol(symbolId);
111
111
  return new Promise((resolve) => {
112
+ this.destroySpine(this.spine);
112
113
  this.spine = new Spine(getSpineData(specialResourceName));
113
114
  this.endOfSwapAnimationPromise = resolve;
114
115
  switch (this.animationSpeedMultiplier) {
@@ -143,10 +144,11 @@ export class AbstractSymbolBase extends Container {
143
144
  */
144
145
  switchSymbolBaseDisplayedObject(option, suffix = "") {
145
146
  if (option === "spine") {
146
- this.removeChild(this.spine);
147
- this.spine.x = this.symbolImage.x;
148
- this.spine.y = this.symbolImage.y;
147
+ const { x, y } = this.symbolImage;
148
+ this.destroySpine(this.spine);
149
149
  this.spine = new Spine(this.getResourceForSymbolId(this._id));
150
+ this.spine.x = x;
151
+ this.spine.y = y;
150
152
  const animationName = this.getAnimationNameForSymbol(this._id, suffix);
151
153
  this.spine.parentLayer = AnimationLayer;
152
154
  this.spine.state.setAnimation(0, animationName, false);
@@ -156,14 +158,15 @@ export class AbstractSymbolBase extends Container {
156
158
  this.removeChild(this.symbolImage);
157
159
  return;
158
160
  }
161
+ const { x, y } = this.symbolImage;
159
162
  this.removeChild(this.symbolImage);
160
163
  this.symbolImage.destroy({ children: true });
161
164
  this.symbolImage = new Sprite(getTexture(this.getImage(this._id, suffix)));
162
165
  this.symbolImage.anchor.set(0.5, 0.5);
163
- this.symbolImage.x = this.spine.x;
164
- this.symbolImage.y = this.spine.y;
166
+ this.symbolImage.x = this.spine.destroyed ? x : this.spine.x;
167
+ this.symbolImage.y = this.spine.destroyed ? y : this.spine.y;
165
168
  this.addChild(this.symbolImage);
166
- this.removeChild(this.spine);
169
+ this.destroySpine(this.spine);
167
170
  return;
168
171
  }
169
172
  /**
@@ -182,6 +185,7 @@ export class AbstractSymbolBase extends Container {
182
185
  }
183
186
  else {
184
187
  logError(`$ Multiplier animation 'x${multiplier}' does not exist for symbol ID: ${this._id}`);
188
+ symbolMultiplier.destroy({ children: true });
185
189
  return;
186
190
  }
187
191
  this.addChild(symbolMultiplier);
@@ -194,6 +198,17 @@ export class AbstractSymbolBase extends Container {
194
198
  });
195
199
  });
196
200
  this.removeChild(symbolMultiplier);
201
+ symbolMultiplier.destroy({ children: true });
202
+ }
203
+ destroySpine(spine) {
204
+ if (spine.destroyed) {
205
+ return;
206
+ }
207
+ spine.state.clearListeners();
208
+ if (spine.parent) {
209
+ spine.parent.removeChild(spine);
210
+ }
211
+ spine.destroy({ children: true });
197
212
  }
198
213
  /**
199
214
  * Function for playing the symbol animation
@@ -214,6 +229,18 @@ export class AbstractSymbolBase extends Container {
214
229
  });
215
230
  });
216
231
  }
232
+ isCurrentAnimationPastProgressThreshold() {
233
+ const entry = this.spine.state.tracks[0];
234
+ if (!entry) {
235
+ return false;
236
+ }
237
+ const trackTime = entry.trackTime ?? 0;
238
+ const animationEnd = entry.animationEnd ?? 0;
239
+ if (animationEnd <= 0) {
240
+ return false;
241
+ }
242
+ return trackTime >= animationEnd / 3;
243
+ }
217
244
  /**
218
245
  * Function for stopping the symbol animation.
219
246
  * @public
@@ -57,6 +57,9 @@ export declare abstract class AbstractController<T> {
57
57
  shouldStopMobileSpin: boolean;
58
58
  disableMessageBoxChange: boolean;
59
59
  shouldClearQuickReelsStopAfterSpin: boolean;
60
+ protected firstAnimationTarget: {
61
+ isCurrentAnimationPastProgressThreshold?: () => boolean;
62
+ } | null;
60
63
  freeSpinsWin: Nullable<number>;
61
64
  protected resolveBigWin?: () => void;
62
65
  protected resolveMysteryWin?: () => void;
@@ -57,6 +57,7 @@ export class AbstractController {
57
57
  shouldStopMobileSpin = false;
58
58
  disableMessageBoxChange = false;
59
59
  shouldClearQuickReelsStopAfterSpin = false;
60
+ firstAnimationTarget = null;
60
61
  freeSpinsWin = null;
61
62
  resolveBigWin;
62
63
  resolveMysteryWin;
@@ -769,7 +770,7 @@ export class AbstractController {
769
770
  return;
770
771
  }
771
772
  if (this.gamePhase === gamePhases.HANDLING_ACTIONS) {
772
- this.shouldPlayNextSpin = true;
773
+ this.shouldPlayNextSpin = !RainMan.settingsStore.isFreeSpinsPlayEnabled;
773
774
  }
774
775
  if (this.mainContainer.mysteryWinVisibleFor) {
775
776
  RainMan.componentRegistry.get(COMPONENTS.mysteryWin).stopUpdate();
@@ -916,7 +917,7 @@ export class AbstractController {
916
917
  async useTakeAction() {
917
918
  if (!RainMan.settingsStore.isTakeActionAvailable ||
918
919
  RainMan.settingsStore.roundNumber === 0 ||
919
- this._lastWinAmount === 0) {
920
+ (this._lastWinAmount === 0 && !RainMan.settingsStore.isAfterFreeSpinsSummary)) {
920
921
  logInfo("Take action can't be performed");
921
922
  return;
922
923
  }
@@ -1168,6 +1169,8 @@ export class AbstractController {
1168
1169
  this.uiController.limitWinAmount(spinLogic.winTotalAmount);
1169
1170
  }
1170
1171
  this.composeWinActionsQueue(spinLogic);
1172
+ this.firstAnimationTarget =
1173
+ this.winActionsQueue.find((action) => action.animationTarget)?.animationTarget ?? null;
1171
1174
  if (spinLogic.winTotalAmount === 0 && this.invokeFreeSpinPlateAfterWin) {
1172
1175
  this.uiController.messageBox.hideCurrentWinText();
1173
1176
  }
@@ -1321,6 +1324,8 @@ export class AbstractController {
1321
1324
  * @returns {Promise<void>} - promise that resolves when free spin plate is invoked
1322
1325
  */
1323
1326
  async handleFreeSpinPlateInvocation(numberOfFreeSpins, isAdditionalFreeSpin = false) {
1327
+ RainMan.componentRegistry.getIfExists(SpeedControlButtonMobile.registryName)?.flipDisabledFlag(true);
1328
+ RainMan.componentRegistry.getIfExists(SpeedControlButton.registryName)?.flipDisabledFlag(true);
1324
1329
  await this.buttonsEventManager.disableButtonsForAction(async () => {
1325
1330
  await this.mainContainer.invokeFreeSpinPlate(numberOfFreeSpins ?? RainMan.settingsStore.totalNumberOfFreeSpins, isAdditionalFreeSpin);
1326
1331
  });
@@ -1337,9 +1342,12 @@ export class AbstractController {
1337
1342
  * @returns {Promise<void>} - promise that resolves when free spin summary plate is invoked
1338
1343
  */
1339
1344
  async handleFreeSpinSummaryPlateInvocation() {
1345
+ this.shouldPlayNextSpin = false;
1340
1346
  await this.buttonsEventManager.disableButtonsForAction(async () => {
1341
1347
  this.uiController.messageBox.hideFreeSpinText();
1342
1348
  this.uiController.messageBox.hideCurrentWinText();
1349
+ RainMan.componentRegistry.getIfExists(SpeedControlButtonMobile.registryName)?.flipDisabledFlag(true);
1350
+ RainMan.componentRegistry.getIfExists(SpeedControlButton.registryName)?.flipDisabledFlag(true);
1343
1351
  await this.mainContainer.invokeFreeSpinSummary(this.uiController.totalNumberOfFreeSpins, this.uiController.totalFreeSpinWinAmount);
1344
1352
  this.uiController.totalNumberOfFreeSpins = 0;
1345
1353
  this.uiController.resetTotalFreeSpinWinAmount();
@@ -63,6 +63,7 @@ export declare class SettingsStore {
63
63
  updateVolumeButtonTexture?: () => void;
64
64
  handleModalInvocation?: (layerId: string) => void;
65
65
  closeOtherModals?: (layerId: string) => void;
66
+ closeAllModals?: () => void;
66
67
  closeCurrentlyOpenModal?: () => void;
67
68
  handleInvokeFreeSpinPlate?: (freeSpinAmount: number, isAdditionalFreeSpin: boolean) => Promise<void>;
68
69
  handleBuyFreeSpinPopupActivation?: () => void;
@@ -69,6 +69,7 @@ export class SettingsStore {
69
69
  updateVolumeButtonTexture;
70
70
  handleModalInvocation;
71
71
  closeOtherModals;
72
+ closeAllModals;
72
73
  closeCurrentlyOpenModal;
73
74
  handleInvokeFreeSpinPlate;
74
75
  handleBuyFreeSpinPopupActivation;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pixi-rainman-game-engine",
3
- "version": "0.3.43c",
3
+ "version": "0.3.44-beta",
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",