pixi-rainman-game-engine 0.1.46 → 0.1.48

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.
Files changed (27) hide show
  1. package/dist/ComponentRegistry/ComponentRegistry.js +1 -4
  2. package/dist/SettingsUI/components/AutoplaySettings/index.jsx +3 -3
  3. package/dist/SettingsUI/components/VolumeSettings/index.jsx +4 -0
  4. package/dist/application/ButtonsEventManager/ButtonsEventManager.d.ts +3 -0
  5. package/dist/application/ButtonsEventManager/ButtonsEventManager.js +20 -0
  6. package/dist/application/SoundManager/SoundManager.d.ts +12 -0
  7. package/dist/application/SoundManager/SoundManager.js +24 -2
  8. package/dist/application/SpeedState/SpeedLevelHandler.d.ts +0 -1
  9. package/dist/application/SpeedState/SpeedLevelHandler.js +0 -1
  10. package/dist/components/AbstractMainContainer/AbstractMainContainer.d.ts +1 -0
  11. package/dist/components/AbstractMainContainer/AbstractMainContainer.js +3 -0
  12. package/dist/components/frame/AbstractColumnsContainer.d.ts +3 -2
  13. package/dist/components/frame/AbstractColumnsContainer.js +6 -3
  14. package/dist/components/frame/AbstractFrame.d.ts +3 -0
  15. package/dist/components/frame/AbstractFrame.js +15 -4
  16. package/dist/components/frame/types.d.ts +4 -0
  17. package/dist/components/frame/types.js +1 -0
  18. package/dist/components/symbols/AbstractSymbolBase.d.ts +6 -0
  19. package/dist/components/symbols/AbstractSymbolBase.js +36 -0
  20. package/dist/components/symbols/AbstractSymbolsColumn.d.ts +1 -0
  21. package/dist/components/symbols/AbstractSymbolsColumn.js +5 -3
  22. package/dist/constants/gamePhase.d.ts +2 -0
  23. package/dist/constants/gamePhase.js +3 -1
  24. package/dist/controllers/AbstractController.d.ts +4 -1
  25. package/dist/controllers/AbstractController.js +34 -8
  26. package/dist/utils/common/sound.js +7 -9
  27. package/package.json +1 -1
@@ -1,4 +1,4 @@
1
- import { isIncentiveComponentInterface, isSpeedAdapterInterface, SPEED_LEVELS, SpeedLevelHandler, TimedIncentiveController, } from "../application";
1
+ import { isIncentiveComponentInterface, isSpeedAdapterInterface, SpeedLevelHandler, TimedIncentiveController, } from "../application";
2
2
  import { SpeedControlButton } from "../components";
3
3
  import { RainMan } from "../Rainman";
4
4
  import { openFullscreen } from "../utils";
@@ -58,9 +58,6 @@ export class ComponentRegistry {
58
58
  */
59
59
  nextSpeedLevel() {
60
60
  this.speedLevelHandler.nextSpeed();
61
- if (this.speedLevelHandler.currentSpeed !== SPEED_LEVELS.slow) {
62
- RainMan.globals.disableSpeedPopup?.(false);
63
- }
64
61
  RainMan.settingsStore.setSpeedLevel(this.speedLevelHandler.currentSpeed);
65
62
  this.updateAllSpeedAdaptable(RainMan.settingsStore.currentSpeed);
66
63
  this.get(SpeedControlButton.registryName).forceTextureUpdate();
@@ -10,7 +10,7 @@ import { CloseModalButton } from "../CloseModalButton";
10
10
  import { RichLimitingSlider } from "../RichLimitingSlider";
11
11
  import { SpeedButtonWithHeader, SwitchWithHeader } from "../SwitchWithHeader";
12
12
  export const AutoplaySettings = observer(() => {
13
- const [limit_numbers, setLimitNumbers] = useState([0]);
13
+ const [limitNumbers, setLimitNumbers] = useState([0]);
14
14
  const initialAutoSpinCount = 50;
15
15
  const availableAutoSpinLimits = [10, 20, 50, 100, 150, 200, 250, 300, 350, 400, 500, 600, 700, 800, 900, 1000];
16
16
  const { settingStore } = useStores();
@@ -66,7 +66,7 @@ export const AutoplaySettings = observer(() => {
66
66
  }}>
67
67
  <p>-</p>
68
68
  </button>
69
- {limit_numbers.map((limiter, index) => (<button key={`${limiter}${index}`} value={limiter} className={`autoplay-settings__limit ${limiter === howManyAutoSpinsLeft ? "autoplay-settings__limit--active" : ""} middle ${infinitySpinsEnabled ? "autoplay-settings__limit--infinity" : ""}`} disabled={settingStore.infinitySpinsEnabled} onClick={(event) => updateLimitsBounds(Number(event.currentTarget.value))}>
69
+ {limitNumbers.map((limiter, index) => (<button key={`${limiter}${index}`} value={limiter} className={`autoplay-settings__limit ${limiter === howManyAutoSpinsLeft ? "autoplay-settings__limit--active" : ""} middle ${infinitySpinsEnabled ? "autoplay-settings__limit--infinity" : ""}`} disabled={settingStore.infinitySpinsEnabled} onClick={(event) => updateLimitsBounds(Number(event.currentTarget.value))}>
70
70
  <p>{limiter}</p>
71
71
  </button>))}
72
72
  <button className={`autoplay-settings__limit right ${infinitySpinsEnabled ? "autoplay-settings__limit--infinity" : ""}`} onClick={() => {
@@ -110,7 +110,7 @@ export const AutoplaySettings = observer(() => {
110
110
  }}>
111
111
  <p>{i18next.t("cancel")}</p>
112
112
  </button>
113
- <button id={COMPONENTS.autoSpinConfirm} className="autoplay-settings__confirmation-button">
113
+ <button id={COMPONENTS.autoSpinConfirm} onClick={() => settingStore.decreaseAutoSpinsCount()} className="autoplay-settings__confirmation-button">
114
114
  <p>{i18next.t("confirm")}</p>
115
115
  </button>
116
116
  </div>
@@ -3,6 +3,7 @@ import "./volume.css";
3
3
  import { observer } from "mobx-react-lite";
4
4
  import Slider from "rc-slider";
5
5
  import React from "react";
6
+ import { SoundManager } from "../../../application";
6
7
  import { RainMan } from "../../../Rainman";
7
8
  import { useStores } from "../../hooks";
8
9
  const onBeforeChange = () => {
@@ -13,6 +14,9 @@ const onChange = (newVolumeLevel) => {
13
14
  };
14
15
  const onAfterChange = (newVolumeLevel) => {
15
16
  RainMan.settingsStore.flipSoundsEnabled(newVolumeLevel);
17
+ if (RainMan.settingsStore.ambientMusicFlag) {
18
+ SoundManager.play("music");
19
+ }
16
20
  };
17
21
  export const VolumeSettings = observer(() => {
18
22
  const { settingStore } = useStores();
@@ -82,6 +82,8 @@ export declare class ButtonsEventManager {
82
82
  initFreeSpinModal(): void;
83
83
  /**
84
84
  * Method for initializing volume button
85
+ * This method changes texture of Volume button based on volume level
86
+ * If music was muted, it will be resumed
85
87
  *
86
88
  * @public
87
89
  */
@@ -150,4 +152,5 @@ export declare class ButtonsEventManager {
150
152
  * @param {string} layerName
151
153
  */
152
154
  private handleModalInvocation;
155
+ private checkAnyLayerIsPresent;
153
156
  }
@@ -1,5 +1,6 @@
1
1
  import i18n from "i18next";
2
2
  import { sample } from "lodash";
3
+ import { SoundManager, SoundTracks } from "../../application";
3
4
  import { AutoplayButton, Background, BUTTONS, COMPONENTS, FreeSpinButton, MessageBox, SpeedControlButton, VolumeButton, } from "../../components";
4
5
  import { RainMan } from "../../Rainman";
5
6
  import { allUiItems, hideLayerIfPresent, UI_ITEMS } from "../../utils";
@@ -74,6 +75,10 @@ export class ButtonsEventManager {
74
75
  if (this.settingsSpeedButton) {
75
76
  this.settingsSpeedButton.addEventListener("click", () => {
76
77
  RainMan.componentRegistry.nextSpeedLevel();
78
+ if (RainMan.settingsStore.currentSpeed === SPEED_LEVELS.fast ||
79
+ RainMan.settingsStore.currentSpeed === SPEED_LEVELS.normal) {
80
+ RainMan.globals.disableSpeedPopup?.(true);
81
+ }
77
82
  messageBox.updateIncentiveText(sample(i18n.t("idleMessages", { returnObjects: true })[RainMan.componentRegistry.getSpeedLevel()]));
78
83
  });
79
84
  }
@@ -179,6 +184,8 @@ export class ButtonsEventManager {
179
184
  }
180
185
  /**
181
186
  * Method for initializing volume button
187
+ * This method changes texture of Volume button based on volume level
188
+ * If music was muted, it will be resumed
182
189
  *
183
190
  * @public
184
191
  */
@@ -186,6 +193,9 @@ export class ButtonsEventManager {
186
193
  RainMan.settingsStore.setUpdateVolumeButtonTexture(() => RainMan.componentRegistry.get(VolumeButton.registryName).changeTextureDependOnVolume());
187
194
  RainMan.componentRegistry.get(VolumeButton.registryName).setOnClick(() => {
188
195
  RainMan.settingsStore.flipSoundsEnabled();
196
+ if (RainMan.settingsStore.ambientMusicFlag && RainMan.settingsStore.isSoundEnabled()) {
197
+ SoundManager.resume(SoundTracks.music);
198
+ }
189
199
  RainMan.componentRegistry.get(VolumeButton.registryName).changeTextureDependOnVolume();
190
200
  this.handleModalInvocation(UI_ITEMS.volume);
191
201
  });
@@ -316,6 +326,8 @@ export class ButtonsEventManager {
316
326
  * @param {string} layerName
317
327
  */
318
328
  handleModalInvocation(layerName) {
329
+ if (this.checkAnyLayerIsPresent(layerName))
330
+ return;
319
331
  const layer = window.document.getElementById(layerName);
320
332
  if (layer !== null) {
321
333
  if (layer.style.display !== "flex") {
@@ -328,4 +340,12 @@ export class ButtonsEventManager {
328
340
  }
329
341
  }
330
342
  }
343
+ checkAnyLayerIsPresent(layerName) {
344
+ return Object.values(UI_ITEMS).some((item) => {
345
+ if (item === layerName)
346
+ return false;
347
+ const layer = window.document.getElementById(item);
348
+ return layer !== null && layer.style.display === "flex";
349
+ });
350
+ }
331
351
  }
@@ -61,11 +61,23 @@ export declare class SoundManagerInstance implements SpeedAdapterInterface<Sound
61
61
  * @param {SoundTrack} soundName
62
62
  */
63
63
  pause(soundName: SoundTrack): void;
64
+ /**
65
+ * Function for resuming sound with given name
66
+ *
67
+ * @param {SoundTrack} soundName
68
+ */
69
+ resume(soundName: SoundTrack): void;
64
70
  /**
65
71
  * Function for adapting sound into the speed of animations
66
72
  *
67
73
  * @param {SpeedLevel} speedLevel
68
74
  */
69
75
  adaptToSpeed(speedLevel: SpeedLevel): void;
76
+ /**
77
+ * Function for stopping sound with given name
78
+ *
79
+ * @param {SoundTrack} soundName - name of the sound
80
+ */
81
+ stop(soundName: SoundTrack): void;
70
82
  }
71
83
  export declare const SoundManager: SoundManagerInstance;
@@ -93,14 +93,20 @@ export class SoundManagerInstance {
93
93
  return;
94
94
  if (soundName === "music" && sound.isPlaying && !RainMan.settingsStore.ambientMusicFlag)
95
95
  return;
96
- sound.play({ loop: soundsToLoop.includes(soundName) });
96
+ sound.play({ loop: soundsToLoop.includes(soundName), singleInstance: true });
97
97
  }
98
98
  /**
99
99
  * Function for resuming all sounds in game
100
100
  * If sound is not playing, it will be skipped
101
101
  */
102
102
  resumeAll() {
103
- this.musicCatalogue.forEach((sound) => sound.paused && sound.resume());
103
+ this.musicCatalogue.forEach((sound, soundName) => {
104
+ if (sound.paused && sound.isPlaying)
105
+ sound.resume();
106
+ if (soundName === SoundTracks.music && sound.paused) {
107
+ sound.resume();
108
+ }
109
+ });
104
110
  }
105
111
  /**
106
112
  * Function for pausing all sounds in game
@@ -117,6 +123,14 @@ export class SoundManagerInstance {
117
123
  pause(soundName) {
118
124
  this.musicCatalogue.get(soundName)?.pause();
119
125
  }
126
+ /**
127
+ * Function for resuming sound with given name
128
+ *
129
+ * @param {SoundTrack} soundName
130
+ */
131
+ resume(soundName) {
132
+ this.musicCatalogue.get(soundName)?.resume();
133
+ }
120
134
  /**
121
135
  * Function for adapting sound into the speed of animations
122
136
  *
@@ -125,5 +139,13 @@ export class SoundManagerInstance {
125
139
  adaptToSpeed(speedLevel) {
126
140
  this.overrides = this.speedLevelMapper[speedLevel];
127
141
  }
142
+ /**
143
+ * Function for stopping sound with given name
144
+ *
145
+ * @param {SoundTrack} soundName - name of the sound
146
+ */
147
+ stop(soundName) {
148
+ this.musicCatalogue.get(soundName)?.stop();
149
+ }
128
150
  }
129
151
  export const SoundManager = SoundManagerInstance.getInstance();
@@ -25,7 +25,6 @@ export declare class SpeedLevelHandler {
25
25
  * Setter for speed level
26
26
  *
27
27
  * @param newSpeedLevel
28
- * @returns
29
28
  */
30
29
  setSpeed(newSpeedLevel: SpeedLevel): void;
31
30
  /**
@@ -34,7 +34,6 @@ export class SpeedLevelHandler {
34
34
  * Setter for speed level
35
35
  *
36
36
  * @param newSpeedLevel
37
- * @returns
38
37
  */
39
38
  setSpeed(newSpeedLevel) {
40
39
  switch (newSpeedLevel) {
@@ -129,6 +129,7 @@ export declare abstract class AbstractMainContainer extends Container {
129
129
  protected showOverlay(): void;
130
130
  protected hideOverlay(): void;
131
131
  get bigWinVisibleFor(): number;
132
+ getFreeSpinButton(): typeof this.freeSpinButton;
132
133
  get mysteryWinVisibleFor(): number;
133
134
  get bonusWinVisibleFor(): number;
134
135
  get superBonusWinVisibleFor(): number;
@@ -203,6 +203,9 @@ export class AbstractMainContainer extends Container {
203
203
  get bigWinVisibleFor() {
204
204
  return this._bigWinInvokedAt == null ? 0 : performance.now() - this._bigWinInvokedAt;
205
205
  }
206
+ getFreeSpinButton() {
207
+ return this.freeSpinButton;
208
+ }
206
209
  get mysteryWinVisibleFor() {
207
210
  return this._mysteryWinInvokedAt == null ? 0 : performance.now() - this._mysteryWinInvokedAt;
208
211
  }
@@ -48,9 +48,9 @@ export declare abstract class AbstractColumnsContainer extends Container impleme
48
48
  * @returns {Promise<void>}
49
49
  */
50
50
  configBlindSpin(finalSymbolReels: SymbolReels, afterSpinResolver: (value: void | PromiseLike<void>) => void, indexesOfReelsToExtendSpinningTime: number[]): Promise<void>;
51
- playAllStreaks(streaks: StreakReelsIndexes[], amount: number): Promise<void>;
51
+ playAllStreaks(streaks: StreakReelsIndexes[], amount: number, allSymbols?: SymbolId[]): Promise<void>;
52
52
  resetPlayedSounds(): void;
53
- protected playSoundDependingOnAmount(amount: number): void;
53
+ protected playSoundDependingOnAmount(amount: number, _allSymbols?: SymbolId[]): void;
54
54
  protected playStreakSound(symbols: SymbolId[]): void;
55
55
  /**
56
56
  * Function for playing animation of streak
@@ -68,6 +68,7 @@ export declare abstract class AbstractColumnsContainer extends Container impleme
68
68
  animate(delta: number): void;
69
69
  abstract getColumnAtPosition(column: number): AbstractSymbolsColumn;
70
70
  stopPlayingSymbolsInColumns(): void;
71
+ stopPlayingSymbolsTransformationInColumns(): void;
71
72
  setInteractivityForSymbols(flag: boolean): void;
72
73
  handleDestroyTransformations(transformations: DropTransformationDetails[], skipAnimations?: boolean, isStandalone?: boolean, allowSoundInSymbolsColumn?: boolean): Promise<void>;
73
74
  protected createMask(): Graphics;
@@ -105,7 +105,7 @@ export class AbstractColumnsContainer extends Container {
105
105
  resolve();
106
106
  });
107
107
  }
108
- async playAllStreaks(streaks, amount) {
108
+ async playAllStreaks(streaks, amount, allSymbols = []) {
109
109
  const columnsToPlay = [];
110
110
  streaks.forEach((streak) => {
111
111
  streak.forEach((symbolIndex, index) => {
@@ -121,13 +121,13 @@ export class AbstractColumnsContainer extends Container {
121
121
  columnsToPlay.forEach((column, index) => {
122
122
  column.forEach((symbolIndex) => promises.push(this.getColumnAtPosition(index).playSymbolAtPosition(symbolIndex, true)));
123
123
  });
124
- this.playSoundDependingOnAmount(amount);
124
+ this.playSoundDependingOnAmount(amount, allSymbols);
125
125
  await Promise.allSettled(promises);
126
126
  }
127
127
  resetPlayedSounds() {
128
128
  this.playedSounds = [];
129
129
  }
130
- playSoundDependingOnAmount(amount) {
130
+ playSoundDependingOnAmount(amount, _allSymbols = []) {
131
131
  const bet = RainMan.settingsStore.bet;
132
132
  if (amount < WIN_MULTIPLIER_VALUES.LOW_WIN * bet) {
133
133
  SoundManager.play(SoundTracks.lowWin);
@@ -202,6 +202,9 @@ export class AbstractColumnsContainer extends Container {
202
202
  stopPlayingSymbolsInColumns() {
203
203
  this.allColumns.forEach((column) => column.stopAllPlayingSymbols());
204
204
  }
205
+ stopPlayingSymbolsTransformationInColumns() {
206
+ this.allColumns.forEach((column) => column.stopTransformationPlayingSymbols());
207
+ }
205
208
  setInteractivityForSymbols(flag) {
206
209
  this.allColumns.forEach((column) => column.setAllSymbolsInteractiveFlag(flag));
207
210
  }
@@ -4,6 +4,7 @@ import { Nullable } from "../../utils";
4
4
  import { ResumableContainer } from "../common";
5
5
  import { AbstractColumnsContainer } from "./AbstractColumnsContainer";
6
6
  import { AbstractInnerFrame } from "./AbstractInnerFrame";
7
+ import { MysteryFxCoordinates } from "./types";
7
8
  import { WinLineAnimation } from "./WinLineAnimation";
8
9
  /**
9
10
  * This class is representing all of elements in center frame like Columns Container, frame animation
@@ -30,12 +31,14 @@ export declare abstract class AbstractFrame extends ResumableContainer implement
30
31
  }>;
31
32
  protected abstract innerFrame: AbstractInnerFrame;
32
33
  protected winLineAnimations: WinLineAnimation[];
34
+ protected mysteryFxCoordinates: MysteryFxCoordinates;
33
35
  protected constructor(spineName: string);
34
36
  get columnsContainer(): AbstractColumnsContainer;
35
37
  adaptToSpeed(speedLevel: SpeedLevel): void;
36
38
  invokeMysteryFx(x: number, y: number): void;
37
39
  protected repositionMysteryFx(_x: number, _y: number): void;
38
40
  hideMysteryFx(): Promise<void>;
41
+ private resetMysteryFxCoordinates;
39
42
  destroyMysteryFx(): void;
40
43
  protected createWinLineAnimation(_animationName: string): WinLineAnimation | null;
41
44
  playAllWinLineAnimations(animationNames: string[]): Promise<void>;
@@ -22,6 +22,7 @@ export class AbstractFrame extends ResumableContainer {
22
22
  mysteryFxHighlight = null;
23
23
  mysteryFxConfig = null;
24
24
  winLineAnimations = [];
25
+ mysteryFxCoordinates = { x: 0, y: 0 };
25
26
  constructor(spineName) {
26
27
  super();
27
28
  this.name = AbstractFrame.registryName;
@@ -37,17 +38,20 @@ export class AbstractFrame extends ResumableContainer {
37
38
  invokeMysteryFx(x, y) {
38
39
  if (!this.mysteryFxConfig)
39
40
  return;
41
+ if (this.mysteryFxHighlight)
42
+ this.removeChild(this.mysteryFxHighlight);
43
+ this.mysteryFxCoordinates = { x, y };
40
44
  const spineName = this.mysteryFxConfig.mobileSpineName && getDeviceOrientation() === "mobile-portrait"
41
45
  ? this.mysteryFxConfig.mobileSpineName
42
46
  : this.mysteryFxConfig.spineName;
43
47
  this.mysteryFxHighlight = new Spine(getSpineData(spineName));
44
48
  this.repositionMysteryFx(x, y);
45
- this.addChild(this.mysteryFxHighlight);
46
49
  SoundManager.play(SoundTracks.mysteryFxLoop);
47
50
  this.mysteryFxHighlight.state.setAnimation(0, this.mysteryFxConfig.startAnimationName || this.mysteryFxConfig.loopAnimationName, false);
48
51
  this.mysteryFxHighlight.state.addListener({
49
52
  complete: () => this.mysteryFxHighlight?.state.setAnimation(0, this.mysteryFxConfig.loopAnimationName, true),
50
53
  });
54
+ this.addChild(this.mysteryFxHighlight);
51
55
  }
52
56
  repositionMysteryFx(_x, _y) { }
53
57
  async hideMysteryFx() {
@@ -59,8 +63,9 @@ export class AbstractFrame extends ResumableContainer {
59
63
  return resolve();
60
64
  }
61
65
  if (this.mysteryFxHighlight) {
62
- SoundManager.pause(SoundTracks.mysteryFxLoop);
66
+ SoundManager.stop(SoundTracks.mysteryFxLoop);
63
67
  SoundManager.play(SoundTracks.mysteryFxEnd);
68
+ this.resetMysteryFxCoordinates();
64
69
  this.mysteryFxHighlight.state.setAnimation(0, this.mysteryFxConfig.endAnimationName, false);
65
70
  this.mysteryFxHighlight.state.addListener({
66
71
  complete: () => resolve(),
@@ -68,6 +73,9 @@ export class AbstractFrame extends ResumableContainer {
68
73
  }
69
74
  });
70
75
  }
76
+ resetMysteryFxCoordinates() {
77
+ this.mysteryFxCoordinates = { x: 0, y: 0 };
78
+ }
71
79
  destroyMysteryFx() {
72
80
  if (this.mysteryFxHighlight) {
73
81
  SoundManager.pause(SoundTracks.mysteryFxLoop);
@@ -80,12 +88,12 @@ export class AbstractFrame extends ResumableContainer {
80
88
  return null;
81
89
  }
82
90
  async playAllWinLineAnimations(animationNames) {
83
- await Promise.all(animationNames.map((animationName) => this.playWinLineAnimation(animationName)));
91
+ await Promise.allSettled(animationNames.map((animationName) => this.playWinLineAnimation(animationName)));
84
92
  }
85
93
  async playWinLineAnimation(animationName) {
86
94
  const winLineAnimation = this.createWinLineAnimation(animationName);
87
95
  if (winLineAnimation === null) {
88
- logError(`Function createWinLineAnimation in Frame is not implemented`);
96
+ logError("Function createWinLineAnimation in Frame is not implemented!");
89
97
  return;
90
98
  }
91
99
  this.winLineAnimations.push(winLineAnimation);
@@ -112,6 +120,8 @@ export class AbstractFrame extends ResumableContainer {
112
120
  this.addChild(this.innerFrame);
113
121
  }
114
122
  resize() {
123
+ if (this.mysteryFxHighlight)
124
+ this.removeChild(this.mysteryFxHighlight);
115
125
  this.changeFrameForOrientation();
116
126
  const expectedScreenDimensions = {
117
127
  width: this.transformedWidth(),
@@ -122,6 +132,7 @@ export class AbstractFrame extends ResumableContainer {
122
132
  this.height = dimensions.height;
123
133
  this.width = dimensions.width;
124
134
  this.addChild(this.innerFrame);
135
+ this.repositionMysteryFx(this.mysteryFxCoordinates.x, this.mysteryFxCoordinates.y);
125
136
  if (this.mysteryFxHighlight)
126
137
  this.addChild(this.mysteryFxHighlight);
127
138
  this.winLineAnimations.forEach((winLineAnimation) => this.addChild(winLineAnimation));
@@ -0,0 +1,4 @@
1
+ export type MysteryFxCoordinates = {
2
+ x: number;
3
+ y: number;
4
+ };
@@ -0,0 +1 @@
1
+ export {};
@@ -18,6 +18,7 @@ export declare abstract class AbstractSymbolBase extends Container {
18
18
  protected animationSpeedMultiplier: number;
19
19
  private endOfMultiplierAnimationPromise;
20
20
  protected endOfSymbolAnimationPromise: ((value: void | PromiseLike<void>) => void) | undefined;
21
+ protected endOfSwapAnimationPromise: ((value: void | PromiseLike<void>) => void) | undefined;
21
22
  protected popSymbolAnimationPromise: ((value: void | PromiseLike<void>) => void) | undefined;
22
23
  protected abstract paytableIgnoredSymbols: SymbolId[];
23
24
  private _id;
@@ -25,6 +26,9 @@ export declare abstract class AbstractSymbolBase extends Container {
25
26
  get id(): SymbolId;
26
27
  setInteractiveFlag(flag: boolean): void;
27
28
  updateTimeScaleOfSpine(scale?: number): void;
29
+ protected changeSpecialFaceForSymbol(symbolId: SymbolId): string;
30
+ swapSymbolsToSpecials(symbolId: SymbolId, specialResourceName: string, spineScale: number): () => Promise<void>;
31
+ protected animateForSpecialSwap(animationName: string, specialResourceName: string, symbolId: SymbolId, spineScale: number): () => Promise<void>;
28
32
  switchSymbolBaseDisplayedObject(option: "sprite" | "spine", suffix?: string): void;
29
33
  handleMultiplierAnimation(multiplier: number): Promise<void>;
30
34
  play(suffix?: string): Promise<void>;
@@ -34,6 +38,8 @@ export declare abstract class AbstractSymbolBase extends Container {
34
38
  swapSymbol(symbolId: SymbolId): void;
35
39
  private setupClickListener;
36
40
  protected setSymbol(symbolId: SymbolId, shouldSwitchSprite?: boolean): void;
41
+ stopTransformationPlaying(): void;
42
+ protected resolveTransformationsPromise(): void;
37
43
  protected resolveAndDestroyPlayingPromise(): void;
38
44
  protected abstract getResourceForSymbolId(symbolId: SymbolId): LoadersSpineData;
39
45
  protected abstract getAnimationNameForSymbol(symbolId: SymbolId, suffix?: string): string;
@@ -20,6 +20,7 @@ export class AbstractSymbolBase extends Container {
20
20
  animationSpeedMultiplier = defaultSpeedConfig.animationDefaultSpeedScale;
21
21
  endOfMultiplierAnimationPromise;
22
22
  endOfSymbolAnimationPromise;
23
+ endOfSwapAnimationPromise;
23
24
  popSymbolAnimationPromise;
24
25
  _id;
25
26
  constructor(parent, symbolId, multiplierSpineName = null) {
@@ -48,6 +49,31 @@ export class AbstractSymbolBase extends Container {
48
49
  updateTimeScaleOfSpine(scale = 0) {
49
50
  this.animationSpeedMultiplier = scale;
50
51
  }
52
+ changeSpecialFaceForSymbol(symbolId) {
53
+ return `efx_${symbolId.toLowerCase()}`;
54
+ }
55
+ swapSymbolsToSpecials(symbolId, specialResourceName, spineScale) {
56
+ const animation = this.changeSpecialFaceForSymbol(symbolId);
57
+ return this.animateForSpecialSwap(animation, specialResourceName, symbolId, spineScale);
58
+ }
59
+ animateForSpecialSwap(animationName, specialResourceName, symbolId, spineScale) {
60
+ return () => {
61
+ this.swapSymbol(symbolId);
62
+ return new Promise((resolve) => {
63
+ this.spine = new Spine(getSpineData(specialResourceName));
64
+ this.endOfSwapAnimationPromise = resolve;
65
+ this.spine.state.timeScale = (this.animationSpeedMultiplier = 1) ? 1.5 : this.animationSpeedMultiplier;
66
+ this.spine.state.setAnimation(0, animationName, false);
67
+ this.spine.scale.set(spineScale);
68
+ this.addChild(this.spine);
69
+ this.spine.state.addListener({
70
+ complete: () => {
71
+ resolve();
72
+ },
73
+ });
74
+ });
75
+ };
76
+ }
51
77
  switchSymbolBaseDisplayedObject(option, suffix = "") {
52
78
  if (option === "spine") {
53
79
  this.removeChild(this.spine);
@@ -145,6 +171,16 @@ export class AbstractSymbolBase extends Container {
145
171
  if (shouldSwitchSprite)
146
172
  this.switchSymbolBaseDisplayedObject("sprite");
147
173
  }
174
+ stopTransformationPlaying() {
175
+ this.setSymbol(this._id);
176
+ this.resolveTransformationsPromise();
177
+ }
178
+ resolveTransformationsPromise() {
179
+ if (this.endOfSwapAnimationPromise) {
180
+ this.endOfSwapAnimationPromise();
181
+ this.endOfSwapAnimationPromise = undefined;
182
+ }
183
+ }
148
184
  resolveAndDestroyPlayingPromise() {
149
185
  if (this.endOfMultiplierAnimationPromise) {
150
186
  this.endOfMultiplierAnimationPromise();
@@ -60,6 +60,7 @@ export declare abstract class AbstractSymbolsColumn extends Container implements
60
60
  getTriggersPromise(): Promise<void>;
61
61
  getStopPromise(): Promise<void>;
62
62
  stopAllPlayingSymbols(): void;
63
+ stopTransformationPlayingSymbols(): void;
63
64
  getSymbolAtPosition(position: number): AbstractSymbolBase;
64
65
  findSymbols(symbolId: SymbolId): Array<AbstractSymbolBase>;
65
66
  playSymbolAtPosition(position: number, playSymbol: boolean, multiplier?: number, suffix?: string): Promise<void>;
@@ -81,7 +81,7 @@ export class AbstractSymbolsColumn extends Container {
81
81
  }
82
82
  async changeVelocity(velocity, duration, frame) {
83
83
  await new Promise((resolve) => {
84
- this.tween = new TWEEN.Tween({ velocity: this.velocity })
84
+ this.tween = new TWEEN.Tween({ velocity: velocity })
85
85
  .onUpdate((value) => {
86
86
  this.velocity = value.velocity;
87
87
  })
@@ -91,8 +91,7 @@ export class AbstractSymbolsColumn extends Container {
91
91
  .onStop(async () => {
92
92
  this.tween = null;
93
93
  if (frame) {
94
- if (!this.skipHidingMysteryFx)
95
- await frame.hideMysteryFx();
94
+ await frame.hideMysteryFx();
96
95
  frame.destroyMysteryFx();
97
96
  }
98
97
  this.skipHidingMysteryFx = false;
@@ -181,6 +180,9 @@ export class AbstractSymbolsColumn extends Container {
181
180
  stopAllPlayingSymbols() {
182
181
  this.symbols.forEach((symbol) => symbol.stopPlaying());
183
182
  }
183
+ stopTransformationPlayingSymbols() {
184
+ this.symbols.forEach((symbol) => symbol.stopTransformationPlaying());
185
+ }
184
186
  getSymbolAtPosition(position) {
185
187
  return this.symbols[position];
186
188
  }
@@ -10,6 +10,8 @@ export declare const gamePhases: {
10
10
  readonly STOPPING_HANDLING_ACTIONS: "stoppingHandlingActions";
11
11
  readonly DATA_FETCH: "dataFetch";
12
12
  readonly BIG_WIN_INVOKED: "bigWinInvoked";
13
+ readonly SUPER_BONUS_WIN_INVOKED: "superBonusWinInvoked";
14
+ readonly BONUS_WIN_INVOKED: "bonusWinInvoked";
13
15
  readonly GAME_SUMMARY: "gameSummary";
14
16
  };
15
17
  export type GamePhase = (typeof gamePhases)[keyof typeof gamePhases];
@@ -10,5 +10,7 @@ export const gamePhases = {
10
10
  STOPPING_HANDLING_ACTIONS: "stoppingHandlingActions",
11
11
  DATA_FETCH: "dataFetch",
12
12
  BIG_WIN_INVOKED: "bigWinInvoked",
13
- GAME_SUMMARY: "gameSummary"
13
+ SUPER_BONUS_WIN_INVOKED: "superBonusWinInvoked",
14
+ BONUS_WIN_INVOKED: "bonusWinInvoked",
15
+ GAME_SUMMARY: "gameSummary",
14
16
  };
@@ -43,11 +43,12 @@ export declare abstract class AbstractController<T> {
43
43
  protected resolveMysteryWin?: () => void;
44
44
  protected resolveBonusWin?: () => void;
45
45
  protected resolveSuperBonusWin?: () => void;
46
- protected mysteryFxSymbol: SymbolId | null;
46
+ protected mysteryFxSymbol: SymbolId | SymbolId[] | null;
47
47
  protected mysteryFxSymbolMustFormWinningLine: boolean;
48
48
  protected mysteryWinSymbol: Nullable<SymbolId>;
49
49
  private componentRegistry;
50
50
  protected skipFreeSpinSummary: boolean;
51
+ protected scatterCountForFx: number;
51
52
  protected _lastWinAmount: number;
52
53
  protected constructor(buttonsEventManager: ButtonsEventManager, mainContainer: AbstractMainContainer, connection: SubscribableConnectionWrapper);
53
54
  init(): void;
@@ -60,6 +61,7 @@ export declare abstract class AbstractController<T> {
60
61
  disableBetButtons(shouldBeDisabled: boolean): void;
61
62
  private disableSpeedButton;
62
63
  private disableInformationButtons;
64
+ private disableFreeSpinButton;
63
65
  protected handleDisablingButtons(disabled?: boolean): void;
64
66
  private initButtons;
65
67
  private initBetButtons;
@@ -152,6 +154,7 @@ export declare abstract class AbstractController<T> {
152
154
  private lockLogicWhileSpinning;
153
155
  private unlockLogicAfterSpinning;
154
156
  protected stopPlayingSymbolsStreak(): void;
157
+ protected stopPlayingSymbolsTransformation(): void;
155
158
  protected countBigWin(customAmount?: number): Promise<void>;
156
159
  protected countBonusWin(winAmount?: number): Promise<void>;
157
160
  protected countSuperBonusWin(winAmount?: number): Promise<void>;
@@ -47,6 +47,7 @@ export class AbstractController {
47
47
  mysteryWinSymbol = null;
48
48
  componentRegistry;
49
49
  skipFreeSpinSummary = false;
50
+ scatterCountForFx = 2;
50
51
  _lastWinAmount = 0;
51
52
  constructor(buttonsEventManager, mainContainer, connection) {
52
53
  this.buttonsEventManager = buttonsEventManager;
@@ -137,10 +138,16 @@ export class AbstractController {
137
138
  ];
138
139
  buttonsToChange.forEach((button) => button.flipDisabledFlag(shouldBeDisabled));
139
140
  }
141
+ disableFreeSpinButton(shouldBeDisabled) {
142
+ const freeSpinButton = this.mainContainer.getFreeSpinButton();
143
+ if (freeSpinButton)
144
+ freeSpinButton?.setInteractivity(!shouldBeDisabled);
145
+ }
140
146
  handleDisablingButtons(disabled) {
141
147
  this.disableBetButtons(disabled ?? this.shouldBetButtonsBeDisabled);
142
148
  this.disableSpeedButton(disabled ?? this.shouldButtonsBeDisabled);
143
149
  this.disableInformationButtons(disabled ?? this.shouldButtonsBeDisabled);
150
+ this.disableFreeSpinButton(disabled ?? this.shouldButtonsBeDisabled);
144
151
  }
145
152
  initButtons() {
146
153
  this.buttonsEventManager.initVolumeButtonEvent();
@@ -284,15 +291,23 @@ export class AbstractController {
284
291
  const streak = [];
285
292
  let howManyScattersAppeared = 0;
286
293
  for (let columnIndex = 0; columnIndex < RainMan.config.numberOfColumns; columnIndex++) {
287
- if (howManyScattersAppeared >= 2) {
294
+ if (howManyScattersAppeared >= this.scatterCountForFx) {
288
295
  reelsIndexesWithScatter.push(columnIndex);
289
296
  }
290
297
  for (let rowIndex = 0; rowIndex < RainMan.config.numberOfRows; rowIndex++) {
291
298
  const symbol = onStopBlindsConfiguration[columnIndex][rowIndex];
292
- if (symbol === this.mysteryFxSymbol) {
293
- howManyScattersAppeared++;
294
- streak.push(rowIndex);
295
- break;
299
+ if (Array.isArray(this.mysteryFxSymbol)) {
300
+ if (this.mysteryFxSymbol.includes(symbol)) {
301
+ howManyScattersAppeared++;
302
+ streak.push(rowIndex);
303
+ }
304
+ }
305
+ else {
306
+ if (symbol === this.mysteryFxSymbol) {
307
+ howManyScattersAppeared++;
308
+ streak.push(rowIndex);
309
+ break;
310
+ }
296
311
  }
297
312
  }
298
313
  }
@@ -422,7 +437,7 @@ export class AbstractController {
422
437
  autoplaySpin() {
423
438
  if (!RainMan.settingsStore.howManyAutoSpinsLeft)
424
439
  return;
425
- if (!RainMan.settingsStore.infinitySpinsEnabled) {
440
+ if (!RainMan.settingsStore.infinitySpinsEnabled || RainMan.settingsStore.howManyAutoSpinsLeft > 0) {
426
441
  RainMan.settingsStore.decreaseAutoSpinsCount();
427
442
  }
428
443
  if (RainMan.settingsStore.howManyAutoSpinsLeft === 0) {
@@ -577,7 +592,7 @@ export class AbstractController {
577
592
  this.unlockLogicAfterSpinning();
578
593
  if (RainMan.settingsStore.isFreeSpinsPlayEnabled) {
579
594
  this.uiController.increaseTotalFreeSpinWinAmount(spinLogic.winTotalAmountWithoutMystery);
580
- this.uiController.limitWinAmount(this.uiController.totalFreeSpinWinAmount);
595
+ this.uiController.limitWinAmount(spinLogic.winTotalAmount);
581
596
  }
582
597
  else {
583
598
  this.uiController.limitWinAmount(spinLogic.winTotalAmount);
@@ -687,6 +702,9 @@ export class AbstractController {
687
702
  this.columnsContainer.stopPlayingSymbolsInColumns();
688
703
  this.frame.stopPlayingWinLineAnimations();
689
704
  }
705
+ stopPlayingSymbolsTransformation() {
706
+ this.columnsContainer.stopPlayingSymbolsTransformationInColumns();
707
+ }
690
708
  async countBigWin(customAmount) {
691
709
  if (RainMan.config.disableBigWinOnBonus)
692
710
  return;
@@ -695,10 +713,12 @@ export class AbstractController {
695
713
  return;
696
714
  const amount = customAmount || this.uiController.recentWin;
697
715
  this.mainContainer.invokeBigWin();
716
+ this.changeGamePhase(gamePhases.BIG_WIN_INVOKED);
698
717
  const animationTimeDivider = RainMan.settingsStore.currentSpeed === SPEED_LEVELS.fast ? 3 : 1;
699
718
  await RainMan.componentRegistry
700
719
  .get(COMPONENTS.bigWin)
701
720
  .update(amount, RainMan.config.durationOfActions.updatableTextCountingDuration / animationTimeDivider);
721
+ this.changeGamePhase(gamePhases.STOPPING_HANDLING_ACTIONS);
702
722
  await new Promise((resolve) => {
703
723
  this.resolveBigWin = resolve;
704
724
  setTimeout(() => {
@@ -707,6 +727,7 @@ export class AbstractController {
707
727
  this.mainContainer.destroyBigWin();
708
728
  this.resolveBigWin();
709
729
  this.resolveBigWin = undefined;
730
+ this.changeGamePhase(gamePhases.IDLE);
710
731
  }, RainMan.config.durationOfActions.bigWinPhaseTimeout / animationTimeDivider);
711
732
  });
712
733
  }
@@ -715,11 +736,12 @@ export class AbstractController {
715
736
  return;
716
737
  const amount = winAmount || this.uiController.recentWin;
717
738
  this.mainContainer.invokeBonusWin();
718
- this.changeGamePhase(gamePhases.BIG_WIN_INVOKED);
739
+ this.changeGamePhase(gamePhases.BONUS_WIN_INVOKED);
719
740
  const animationTimeDivider = RainMan.settingsStore.currentSpeed === SPEED_LEVELS.fast ? 3 : 1;
720
741
  await RainMan.componentRegistry
721
742
  .get(COMPONENTS.bonusWin)
722
743
  .update(amount, RainMan.config.durationOfActions.updatableTextCountingDuration / animationTimeDivider);
744
+ this.changeGamePhase(gamePhases.STOPPING_HANDLING_ACTIONS);
723
745
  await new Promise((resolve) => {
724
746
  this.resolveBonusWin = resolve;
725
747
  setTimeout(() => {
@@ -728,6 +750,7 @@ export class AbstractController {
728
750
  this.mainContainer.destroyBonusWin();
729
751
  this.resolveBonusWin();
730
752
  this.resolveBonusWin = undefined;
753
+ this.changeGamePhase(gamePhases.IDLE);
731
754
  }, RainMan.config.durationOfActions.bonusWinPhaseTimeout / animationTimeDivider);
732
755
  });
733
756
  }
@@ -736,10 +759,12 @@ export class AbstractController {
736
759
  return;
737
760
  const amount = winAmount || RainMan.settingsStore.cumulativeWinAmount;
738
761
  this.mainContainer.invokeSuperBonusWin();
762
+ this.changeGamePhase(gamePhases.SUPER_BONUS_WIN_INVOKED);
739
763
  const animationTimeDivider = RainMan.settingsStore.currentSpeed === SPEED_LEVELS.fast ? 3 : 1;
740
764
  await RainMan.componentRegistry
741
765
  .get(COMPONENTS.superBonusWin)
742
766
  .update(amount, RainMan.config.durationOfActions.updatableTextCountingDuration / animationTimeDivider);
767
+ this.changeGamePhase(gamePhases.STOPPING_HANDLING_ACTIONS);
743
768
  await new Promise((resolve) => {
744
769
  this.resolveSuperBonusWin = resolve;
745
770
  setTimeout(() => {
@@ -748,6 +773,7 @@ export class AbstractController {
748
773
  this.mainContainer.destroySuperBonusWin();
749
774
  this.resolveSuperBonusWin();
750
775
  this.resolveSuperBonusWin = undefined;
776
+ this.changeGamePhase(gamePhases.IDLE);
751
777
  }, RainMan.config.durationOfActions.superBonusWinPhaseTimeout / animationTimeDivider);
752
778
  });
753
779
  }
@@ -6,15 +6,13 @@ import { RainMan } from "../../Rainman";
6
6
  * If the sounds was disabled than this will be not triggered
7
7
  */
8
8
  const resumeAudioContext = () => {
9
- if (RainMan.settingsStore.isSoundEnabled()) {
10
- if (document.hidden) {
11
- SoundManager.pauseAll();
12
- return;
13
- }
14
- else {
15
- SoundManager.resumeAll();
16
- return;
17
- }
9
+ if (RainMan.settingsStore.isSoundEnabled() && document.hidden) {
10
+ SoundManager.pauseAll();
11
+ return;
12
+ }
13
+ if (!document.hidden && RainMan.settingsStore.isSoundEnabled()) {
14
+ SoundManager.resumeAll();
15
+ return;
18
16
  }
19
17
  };
20
18
  /**
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pixi-rainman-game-engine",
3
- "version": "0.1.46",
3
+ "version": "0.1.48",
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",