pixi-rainman-game-engine 0.3.3 → 0.3.4

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.
@@ -150,7 +150,10 @@ export class ComponentRegistry {
150
150
  */
151
151
  updateTemporaryReelSpeed(speedLevel) {
152
152
  this.speedAdaptableComponents.forEach((speedAdaptableComponent) => {
153
- if (!(speedAdaptableComponent instanceof AbstractSymbolsColumn)) {
153
+ if (speedAdaptableComponent instanceof AbstractSymbolsColumn) {
154
+ speedAdaptableComponent.adaptToSpeedWithoutSymbols(speedLevel);
155
+ }
156
+ else {
154
157
  speedAdaptableComponent.adaptToSpeed(speedLevel);
155
158
  }
156
159
  });
@@ -263,15 +263,20 @@ export class ButtonsEventManager {
263
263
  */
264
264
  initAutoplayButtonEvent() {
265
265
  RainMan.componentRegistry.get(AutoplayButton.registryName).setOnClick(() => {
266
- if (!RainMan.settingsStore.isModalsAvailable() || RainMan.settingsStore.isPlayingAnyAction) {
266
+ if (!RainMan.settingsStore.isAutoplayEnabled && !RainMan.settingsStore.isModalsAvailable()) {
267
267
  return;
268
268
  }
269
269
  if (RainMan.settingsStore.isAutoplayEnabled) {
270
270
  RainMan.settingsStore.resetAutoplaySettings();
271
271
  RainMan.componentRegistry.get(MessageBox.registryName).hideAutoSpinText();
272
272
  RainMan.componentRegistry.get(AutoplayButton.registryName).activate(false);
273
- RainMan.componentRegistry.get(BUTTONS.plus).flipDisabledFlag(false);
274
- RainMan.componentRegistry.get(BUTTONS.neg).flipDisabledFlag(false);
273
+ if (!RainMan.settingsStore.isPlayingAnyAction) {
274
+ RainMan.componentRegistry.get(BUTTONS.neg).flipDisabledFlag(false);
275
+ RainMan.componentRegistry.get(BUTTONS.plus).flipDisabledFlag(false);
276
+ }
277
+ else {
278
+ RainMan.componentRegistry.get(BUTTONS.autoplay).flipDisabledFlag(true);
279
+ }
275
280
  return;
276
281
  }
277
282
  this.handleModalInvocation(UI_ITEMS.autoplay);
@@ -141,11 +141,11 @@ export class SoundManagerInstance {
141
141
  getVolumeLevel(soundName) {
142
142
  switch (soundName) {
143
143
  case SoundTracks.music:
144
- return (RainMan.settingsStore.volumeLevel / 100) * 0.5;
144
+ return Number((RainMan.settingsStore.volumeLevel / 100) * 0.5);
145
145
  case SoundTracks.freeSpinsMusic:
146
- return (RainMan.settingsStore.volumeLevel / 100) * 0.95;
146
+ return Number((RainMan.settingsStore.volumeLevel / 100) * 0.95);
147
147
  default:
148
- return RainMan.settingsStore.volumeLevel / 100;
148
+ return Number(RainMan.settingsStore.volumeLevel / 100);
149
149
  }
150
150
  }
151
151
  /**
@@ -216,6 +216,7 @@ export class AbstractMainContainer extends Container {
216
216
  */
217
217
  async invokeFreeSpinPlate(freeSpinCount, isAdditionalFreeSpin = false) {
218
218
  const refreshButton = RainMan.componentRegistry.get(RefreshButton.registryName);
219
+ RainMan.settingsStore.setIsAfterFreeSpinsSummary(true);
219
220
  refreshButton.flipDisabledFlag(true);
220
221
  await new Promise(async (resolve) => {
221
222
  this._freeSpinPlate = this.createFreeSpinPlate(resolve, freeSpinCount, isAdditionalFreeSpin);
@@ -268,6 +269,7 @@ export class AbstractMainContainer extends Container {
268
269
  }
269
270
  this.hideOverlay();
270
271
  refreshButton.flipDisabledFlag(false);
272
+ RainMan.settingsStore.setIsAfterFreeSpinsSummary(false);
271
273
  }
272
274
  /**
273
275
  * Function for hiding free spin summary
@@ -24,6 +24,7 @@ export declare abstract class AbstractColumnsContainer extends Container impleme
24
24
  protected playLineSoundOnEachStreak: boolean;
25
25
  protected abstract mapSymbolToSound: Record<string, SoundTrack>;
26
26
  protected currentPlayingSoundName: Nullable<SoundTrack>;
27
+ mysterySymbolsCount: number;
27
28
  protected abstract maskCoordinates: {
28
29
  top: number;
29
30
  bottom: number;
@@ -31,6 +32,19 @@ export declare abstract class AbstractColumnsContainer extends Container impleme
31
32
  right: number;
32
33
  };
33
34
  protected constructor();
35
+ /**
36
+ * Function for resetting mysterySymbolsCount
37
+ * @public
38
+ * @returns {void}
39
+ */
40
+ resetMysterySymbolsCount(): void;
41
+ /**
42
+ * Function for updating mysterySymbolsCount
43
+ * @public
44
+ * @param {number} count count of new symbols that appeared in column
45
+ * @returns {void}
46
+ */
47
+ addMysterySymbols(count: number): number;
34
48
  /**
35
49
  * Function for initializing columns container
36
50
  * @protected
@@ -25,6 +25,7 @@ export class AbstractColumnsContainer extends Container {
25
25
  quickReelsStop = false;
26
26
  playLineSoundOnEachStreak = false;
27
27
  currentPlayingSoundName = null;
28
+ mysterySymbolsCount = 0;
28
29
  constructor() {
29
30
  super();
30
31
  this.x = 0;
@@ -32,6 +33,24 @@ export class AbstractColumnsContainer extends Container {
32
33
  this.name = AbstractColumnsContainer.registryName;
33
34
  this.parentLayer = FrameLayer;
34
35
  }
36
+ /**
37
+ * Function for resetting mysterySymbolsCount
38
+ * @public
39
+ * @returns {void}
40
+ */
41
+ resetMysterySymbolsCount() {
42
+ this.mysterySymbolsCount = 0;
43
+ }
44
+ /**
45
+ * Function for updating mysterySymbolsCount
46
+ * @public
47
+ * @param {number} count count of new symbols that appeared in column
48
+ * @returns {void}
49
+ */
50
+ addMysterySymbols(count) {
51
+ this.mysterySymbolsCount += count;
52
+ return this.mysterySymbolsCount;
53
+ }
35
54
  /**
36
55
  * Function for initializing columns container
37
56
  * @protected
@@ -92,6 +92,13 @@ export declare abstract class AbstractSymbolsColumn extends Container implements
92
92
  * @returns {void}
93
93
  */
94
94
  adaptToSpeed(speedLevel: SpeedLevel): void;
95
+ /**
96
+ * Function for adapting the column to current speed level, omitting symbol speed.
97
+ * @public
98
+ * @param {SpeedLevel} speedLevel desired speed level
99
+ * @param speedLevel
100
+ */
101
+ adaptToSpeedWithoutSymbols(speedLevel: SpeedLevel): void;
95
102
  /**
96
103
  * Function for changing the velocity of the column and speed it up
97
104
  * This is used in games e.g. Book of Wizards and Mystery Book
@@ -259,6 +266,12 @@ export declare abstract class AbstractSymbolsColumn extends Container implements
259
266
  * @returns {void}
260
267
  */
261
268
  private ensureTopOffset;
269
+ /**
270
+ * Getter for column number
271
+ * @private
272
+ * @returns {number} column number
273
+ */
274
+ private getColumNumber;
262
275
  /**
263
276
  * Swaps the face of the given symbol with another symbol.
264
277
  * @private
@@ -100,6 +100,15 @@ export class AbstractSymbolsColumn extends Container {
100
100
  this.velocityMultiplier = this.speedLevelMapper[speedLevel];
101
101
  this.symbols.forEach((symbol) => symbol.updateTimeScaleOfSpine(this.velocityMultiplier));
102
102
  }
103
+ /**
104
+ * Function for adapting the column to current speed level, omitting symbol speed.
105
+ * @public
106
+ * @param {SpeedLevel} speedLevel desired speed level
107
+ * @param speedLevel
108
+ */
109
+ adaptToSpeedWithoutSymbols(speedLevel) {
110
+ this.velocityMultiplier = this.speedLevelMapper[speedLevel];
111
+ }
103
112
  /**
104
113
  * Function for changing the velocity of the column and speed it up
105
114
  * This is used in games e.g. Book of Wizards and Mystery Book
@@ -476,15 +485,16 @@ export class AbstractSymbolsColumn extends Container {
476
485
  * @returns {boolean} true if special symbols are present false otherwise
477
486
  */
478
487
  shouldPlaySpecialRollStopSound() {
479
- return (Array.from({ length: RainMan.config.numberOfColumns }, (_, i) => i)
480
- .map((i) => {
481
- const column = this.columnsContainer.getColumnAtPosition(i);
482
- if (column !== this)
483
- return null;
484
- return column.symbols.slice(0, RainMan.config.numberOfRows);
485
- })
486
- .flat()
487
- .filter((symbol) => symbol && symbol.id === this.bonusSymbol).length >= 1);
488
+ const currentColumnSymbols = this.symbols
489
+ .slice(0, RainMan.config.numberOfRows)
490
+ .filter((symbol) => symbol && symbol.id === this.bonusSymbol).length;
491
+ const remainingReels = RainMan.config.numberOfColumns - this.getColumNumber();
492
+ if (currentColumnSymbols === 0) {
493
+ return false;
494
+ }
495
+ const totalAfterThisReel = this.columnsContainer.addMysterySymbols(currentColumnSymbols);
496
+ const potentialMax = this.columnsContainer.mysterySymbolsCount + remainingReels;
497
+ return totalAfterThisReel >= 3 || potentialMax >= 3;
488
498
  }
489
499
  /**
490
500
  * Function for determining if the mystery stop sound should be played.
@@ -561,6 +571,27 @@ export class AbstractSymbolsColumn extends Container {
561
571
  // calculate and add error
562
572
  index[0].y += calculateError(index[0].y, index[1].y);
563
573
  }
574
+ /**
575
+ * Getter for column number
576
+ * @private
577
+ * @returns {number} column number
578
+ */
579
+ getColumNumber() {
580
+ switch (this._id) {
581
+ case "A":
582
+ return 1;
583
+ case "B":
584
+ return 2;
585
+ case "C":
586
+ return 3;
587
+ case "D":
588
+ return 4;
589
+ case "E":
590
+ return 5;
591
+ default:
592
+ return 0;
593
+ }
594
+ }
564
595
  /**
565
596
  * Swaps the face of the given symbol with another symbol.
566
597
  * @private
@@ -25,6 +25,7 @@ export declare abstract class AbstractController<T> {
25
25
  protected spinning: boolean;
26
26
  protected roundNumber: number;
27
27
  protected winActionsQueue: DescribedPlayableAction<T>[];
28
+ protected firstStreakInRound: DescribedPlayableAction<T> | null;
28
29
  protected spinThrottlerTimeout: Nullable<NodeJS.Timeout>;
29
30
  protected mobileSpinTimeout: Nullable<NodeJS.Timeout>;
30
31
  protected isSpinThrottled: boolean;
@@ -506,6 +507,7 @@ export declare abstract class AbstractController<T> {
506
507
  * @returns {Promise<void>} - promise that resolves when all actions are played
507
508
  */
508
509
  protected abstract playWinActionQueue(): Promise<void>;
510
+ protected playFirstStreak(): Promise<void>;
509
511
  /**
510
512
  * Function will turn off loop while `RainMan.settingsStore.isAutoplayEnabled` is `true`
511
513
  * @protected
@@ -4,7 +4,7 @@ import { ApiConfig, SoundManager, SoundTracks, SPEED_LEVELS } from "../applicati
4
4
  import { AbstractFrame, AutoplayButton, BUTTONS, COMPONENTS, FreeSpinButton, MessageBox, RefreshButton, SpeedControlButton } from "../components";
5
5
  import { gamePhases, PossibleWins } from "../constants";
6
6
  import { RainMan } from "../Rainman";
7
- import { ceilToDecimal, debugSpinData, debugSymbolsMatchConfiguration, getDeviceOrientation, hideLayerIfPresent, hidePaytable, logGroup, logGroupEnd, logInfo, logWarn, UI_ITEMS } from "../utils";
7
+ import { ceilToDecimal, debugSpinData, debugSymbolsMatchConfiguration, getDeviceOrientation, hideLayerIfPresent, hidePaytable, logGroup, logGroupEnd, logInfo, logWarn, UI_ITEMS, wait } from "../utils";
8
8
  import { QuickStopController } from "./QuickStopController";
9
9
  import { UiController } from "./UiController";
10
10
  /**
@@ -24,6 +24,7 @@ export class AbstractController {
24
24
  spinning = false;
25
25
  roundNumber = 0;
26
26
  winActionsQueue = [];
27
+ firstStreakInRound = null;
27
28
  spinThrottlerTimeout = null;
28
29
  mobileSpinTimeout = null;
29
30
  isSpinThrottled = false;
@@ -183,6 +184,9 @@ export class AbstractController {
183
184
  RainMan.componentRegistry.get(BUTTONS.neg)
184
185
  ];
185
186
  buttonsToChange.forEach((button) => button.flipDisabledFlag(shouldBeDisabled));
187
+ if (!RainMan.settingsStore.isPlayingAnyAction && !RainMan.settingsStore.isAutoplayEnabled) {
188
+ RainMan.componentRegistry.get(AutoplayButton.registryName).flipDisabledFlag(true);
189
+ }
186
190
  }
187
191
  /**
188
192
  * Function for disabling autoplay button
@@ -886,6 +890,7 @@ export class AbstractController {
886
890
  this.mainContainer.destroyBigWin();
887
891
  this.changeGamePhase(gamePhases.SPINNING);
888
892
  this.uiController.resetWinAmount();
893
+ this.firstStreakInRound = null;
889
894
  await this.onSpinningStart();
890
895
  RainMan.settingsStore.disableSpecialButtons();
891
896
  this.columnsContainer.clearQuickReelsStop();
@@ -943,8 +948,14 @@ export class AbstractController {
943
948
  this.uiController.messageBox.hideCurrentWinText();
944
949
  }
945
950
  RainMan.settingsStore.setIsPlayingAnyAction(true);
951
+ if (this.firstStreakInRound) {
952
+ await this.playFirstStreak();
953
+ await this.countBigWin();
954
+ }
946
955
  await this.playWinActionQueue();
947
- await this.countBigWin();
956
+ if (!this.firstStreakInRound) {
957
+ await this.countBigWin();
958
+ }
948
959
  await this.handleLoopingWinActionQueue();
949
960
  this.clearAndDestroyActions();
950
961
  debugSymbolsMatchConfiguration(spinLogic.finalBlindsConfiguration);
@@ -1006,6 +1017,7 @@ export class AbstractController {
1006
1017
  if (RainMan.settingsStore.isTemporarySpeed) {
1007
1018
  RainMan.settingsStore.setIsTemporarySpeed(false);
1008
1019
  }
1020
+ this.columnsContainer.resetMysterySymbolsCount();
1009
1021
  RainMan.settingsStore.setModalsAvailability(true);
1010
1022
  RainMan.globals.shouldShowModals = true;
1011
1023
  RainMan.settingsStore.setIsPlayingAnyAction(false);
@@ -1129,7 +1141,7 @@ export class AbstractController {
1129
1141
  * @returns {Promise<void>} - promise that resolves when big win is displayed
1130
1142
  */
1131
1143
  async countBigWin(customAmount) {
1132
- if (RainMan.config.disableBigWinOnBonus) {
1144
+ if (RainMan.config.disableBigWinOnBonus || RainMan.settingsStore.disableBigWin) {
1133
1145
  return;
1134
1146
  }
1135
1147
  if (this.uiController.recentWin < RainMan.config.constants.bigWinMultiplier * RainMan.settingsStore.bet &&
@@ -1241,10 +1253,10 @@ export class AbstractController {
1241
1253
  this.mainContainer.destroyMysteryWin();
1242
1254
  this.resolveMysteryWin();
1243
1255
  this.resolveMysteryWin = undefined;
1244
- this.updateUiValues(winAmount);
1245
1256
  }, RainMan.config.durationOfActions.mysteryPhaseTimeout /
1246
1257
  (RainMan.settingsStore.currentSpeed === SPEED_LEVELS.fast ? 2 : 1));
1247
1258
  });
1259
+ this.updateUiValues(winAmount);
1248
1260
  }
1249
1261
  /**
1250
1262
  * Parser for streaks and reels quantity
@@ -1360,6 +1372,23 @@ export class AbstractController {
1360
1372
  updateGambleValues(value) {
1361
1373
  this.uiController.setDisplayedCurrentWinGamble(value);
1362
1374
  }
1375
+ async playFirstStreak() {
1376
+ if (!this.firstStreakInRound) {
1377
+ return;
1378
+ }
1379
+ this.updateUiValues(this.lastWinAmount);
1380
+ this.handleDisablingButtons(true);
1381
+ this.uiController.messageBox.hideIncentiveText();
1382
+ this.isPlayingUnskippableStreak = true;
1383
+ if (this.gamePhase === gamePhases.STOPPING_HANDLING_ACTIONS)
1384
+ return;
1385
+ this.currentlyPlayedAction = this.firstStreakInRound.action();
1386
+ this.currentlyPlayedActionType = this.firstStreakInRound.type;
1387
+ await this.currentlyPlayedAction;
1388
+ this.uiController.messageBox.hideWinLineIndicator();
1389
+ this.isPlayingUnskippableStreak = false;
1390
+ await wait(100);
1391
+ }
1363
1392
  /**
1364
1393
  * Function will turn off loop while `RainMan.settingsStore.isAutoplayEnabled` is `true`
1365
1394
  * @protected
@@ -46,6 +46,8 @@ export declare class SettingsStore {
46
46
  currentSpeed: SpeedLevel;
47
47
  isPlayingAnyAction: boolean;
48
48
  isTemporarySpeed: boolean;
49
+ disableBigWin: boolean;
50
+ isAfterFreeSpinsSummary: boolean;
49
51
  setSpeedPopupVisibility?: (visibility: "flex" | "none") => void;
50
52
  popupPaytableRepositionCallback?: () => void;
51
53
  updateVolumeButtonTexture?: () => void;
@@ -133,6 +135,13 @@ export declare class SettingsStore {
133
135
  * @returns {void}
134
136
  */
135
137
  setBatteryFlag(flag: boolean): void;
138
+ /**
139
+ * Sets the isAfterFreeSpinsSummary flag
140
+ * @public
141
+ * @param {boolean} state = The new state for isAfterFreeSpinsSummary flag.
142
+ * @returns {void}
143
+ */
144
+ setIsAfterFreeSpinsSummary(state: boolean): void;
136
145
  /**
137
146
  * Resets animation timescale for all spines `allSpines` in {@link ResumableContainer}, {@link SpineWithResumableContainer}
138
147
  * @public
@@ -44,12 +44,14 @@ export class SettingsStore {
44
44
  howManyFreeSpinsLeft = 0;
45
45
  howManyAutoSpinsLeft = INITIAL_AUTO_SPIN_COUNT;
46
46
  freeSpinBuyTable = {};
47
- volumeLevel = getFromLocalStorage(LOCAL_STORAGE.volumeLevel);
47
+ volumeLevel = Number(getFromLocalStorage(LOCAL_STORAGE.volumeLevel, 100));
48
48
  popupPaytablePosition;
49
49
  popupPaytableData;
50
50
  currentSpeed = "slow";
51
51
  isPlayingAnyAction = false;
52
52
  isTemporarySpeed = false;
53
+ disableBigWin = false;
54
+ isAfterFreeSpinsSummary = false;
53
55
  setSpeedPopupVisibility;
54
56
  popupPaytableRepositionCallback;
55
57
  updateVolumeButtonTexture;
@@ -134,7 +136,7 @@ export class SettingsStore {
134
136
  * @returns {boolean} true if there is a free spins number greater than 0
135
137
  */
136
138
  get isFreeSpinsPlayEnabled() {
137
- return this.howManyFreeSpinsLeft > 0 || this.isLastFreeSpin;
139
+ return this.howManyFreeSpinsLeft > 0 || this.isLastFreeSpin || this.isAfterFreeSpinsSummary;
138
140
  }
139
141
  /**
140
142
  * Adds a cumulative win amount.
@@ -220,6 +222,15 @@ export class SettingsStore {
220
222
  this.refreshBatterySaver();
221
223
  setToLocalStorage(LOCAL_STORAGE.batterySaver, flag);
222
224
  }
225
+ /**
226
+ * Sets the isAfterFreeSpinsSummary flag
227
+ * @public
228
+ * @param {boolean} state = The new state for isAfterFreeSpinsSummary flag.
229
+ * @returns {void}
230
+ */
231
+ setIsAfterFreeSpinsSummary(state) {
232
+ this.isAfterFreeSpinsSummary = state;
233
+ }
223
234
  /**
224
235
  * Resets animation timescale for all spines `allSpines` in {@link ResumableContainer}, {@link SpineWithResumableContainer}
225
236
  * @public
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pixi-rainman-game-engine",
3
- "version": "0.3.3",
3
+ "version": "0.3.4",
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",