pixi-rainman-game-engine 0.1.10 → 0.1.11

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 (36) hide show
  1. package/dist/ComponentRegistry/types.d.ts +2 -2
  2. package/dist/ComponentRegistry/types.js +1 -1
  3. package/dist/Rainman/Rainman.d.ts +2 -3
  4. package/dist/Rainman/Rainman.js +1 -3
  5. package/dist/Rainman/appConfig.js +2 -1
  6. package/dist/Rainman/types.d.ts +1 -1
  7. package/dist/application/ApiConfig/ApiConfig.js +3 -3
  8. package/dist/application/ButtonsEventManager/ButtonsEventManager.js +2 -2
  9. package/dist/application/SpinLogic/types.d.ts +1 -4
  10. package/dist/components/AbstractMainContainer/AbstractMainContainer.d.ts +4 -3
  11. package/dist/components/AbstractMainContainer/AbstractMainContainer.js +10 -3
  12. package/dist/components/common/AnimatedNumber.d.ts +12 -0
  13. package/dist/components/common/AnimatedNumber.js +44 -0
  14. package/dist/components/common/{FreeSpinBox.d.ts → FreeSpinButton.d.ts} +4 -4
  15. package/dist/components/common/{FreeSpinBox.js → FreeSpinButton.js} +5 -5
  16. package/dist/components/common/index.d.ts +2 -1
  17. package/dist/components/common/index.js +2 -1
  18. package/dist/components/dictionary/locales/en.json +3 -0
  19. package/dist/components/frame/AbstractColumnsContainer.js +4 -6
  20. package/dist/components/symbols/AbstractSymbolsColumn.d.ts +1 -1
  21. package/dist/components/symbols/AbstractSymbolsColumn.js +1 -0
  22. package/dist/connectivity/ConnectionWrapper.d.ts +2 -2
  23. package/dist/connectivity/ConnectionWrapper.js +2 -1
  24. package/dist/connectivity/LocalConnectionWrapper.d.ts +1 -1
  25. package/dist/connectivity/apiQueue.d.ts +1 -1
  26. package/dist/connectivity/serverConnection.d.ts +4 -1
  27. package/dist/connectivity/serverData.d.ts +6 -4
  28. package/dist/controllers/AbstractController.d.ts +11 -8
  29. package/dist/controllers/AbstractController.js +31 -15
  30. package/dist/controllers/UiController.js +1 -1
  31. package/dist/loading/AbstractLoadingContainer.d.ts +1 -1
  32. package/dist/loading/AbstractLoadingContainer.js +2 -1
  33. package/dist/stores/SettingsStore.d.ts +3 -1
  34. package/dist/stores/SettingsStore.js +18 -4
  35. package/dist/utils/assets/assetGetter.js +2 -7
  36. package/package.json +1 -1
@@ -1,4 +1,4 @@
1
- import { AbstractColumnsContainer, AbstractFrame, AbstractInnerFrame, AutoplayButton, Background, BaseButton, BUTTONS, COMPONENTS, FreeSpinBox, InfoButton, MessageBox, MoreButton, NegButton, PlusButton, RefreshButton, SpeedControlButton, StylefulUpdatableText, UpdatableTextComponent, UpdatableValueComponent, VolumeButton } from "../components";
1
+ import { AbstractColumnsContainer, AbstractFrame, AbstractInnerFrame, AutoplayButton, Background, BaseButton, BUTTONS, COMPONENTS, FreeSpinButton, InfoButton, MessageBox, MoreButton, NegButton, PlusButton, RefreshButton, SpeedControlButton, StylefulUpdatableText, UpdatableTextComponent, UpdatableValueComponent, VolumeButton } from "../components";
2
2
  export interface RegistryMap {
3
3
  [MessageBox.autoSpinTextRegistryName]: UpdatableValueComponent;
4
4
  [MessageBox.currentWinTextRegistryName]: UpdatableTextComponent;
@@ -8,7 +8,7 @@ export interface RegistryMap {
8
8
  [Background.registryName]: Background;
9
9
  [VolumeButton.registryName]: VolumeButton;
10
10
  [AutoplayButton.registryName]: AutoplayButton;
11
- [FreeSpinBox.registryName]?: FreeSpinBox;
11
+ [FreeSpinButton.registryName]?: FreeSpinButton;
12
12
  [AbstractFrame.registryName]: AbstractFrame;
13
13
  [AbstractInnerFrame.registryName]: AbstractInnerFrame;
14
14
  [AbstractColumnsContainer.registryName]: AbstractColumnsContainer;
@@ -1 +1 @@
1
- import { AbstractColumnsContainer, AbstractFrame, AbstractInnerFrame, AutoplayButton, Background, BUTTONS, COMPONENTS, FreeSpinBox, InfoButton, MessageBox, MoreButton, NegButton, PlusButton, RefreshButton, SpeedControlButton, VolumeButton, } from "../components";
1
+ import { AbstractColumnsContainer, AbstractFrame, AbstractInnerFrame, AutoplayButton, Background, BUTTONS, COMPONENTS, FreeSpinButton, InfoButton, MessageBox, MoreButton, NegButton, PlusButton, RefreshButton, SpeedControlButton, VolumeButton, } from "../components";
@@ -1,6 +1,6 @@
1
1
  import "../components/dictionary/i18n";
2
2
  import { Application } from "pixi.js";
3
- import { SpinLogicFactory, SymbolIds, WinTypeIds } from "../application";
3
+ import { SymbolIds, WinTypeIds } from "../application";
4
4
  import { ComponentRegistry } from "../ComponentRegistry";
5
5
  import { SettingsStore } from "../stores";
6
6
  import { AppConfig, Globals, OptionalAppConfig, RequiredAppConfig } from "./types";
@@ -11,7 +11,6 @@ export declare class RainMan {
11
11
  static winTypeIds: WinTypeIds;
12
12
  static settingsStore: SettingsStore;
13
13
  static componentRegistry: ComponentRegistry;
14
- static spinLogicFactory: SpinLogicFactory;
15
14
  static globals: Globals;
16
- constructor(app: Application, config: AppConfig, symbolIds: SymbolIds, winTypeIds: WinTypeIds, spinLogicFactory: SpinLogicFactory);
15
+ constructor(app: Application, config: AppConfig, symbolIds: SymbolIds, winTypeIds: WinTypeIds);
17
16
  }
@@ -11,16 +11,14 @@ export class RainMan {
11
11
  static winTypeIds;
12
12
  static settingsStore;
13
13
  static componentRegistry;
14
- static spinLogicFactory;
15
14
  static globals;
16
- constructor(app, config, symbolIds, winTypeIds, spinLogicFactory) {
15
+ constructor(app, config, symbolIds, winTypeIds) {
17
16
  RainMan.app = app;
18
17
  RainMan.config = merge(defaultAppConfig, config);
19
18
  RainMan.symbolIds = symbolIds;
20
19
  RainMan.winTypeIds = winTypeIds;
21
20
  RainMan.settingsStore = settingStore;
22
21
  RainMan.componentRegistry = new ComponentRegistry(app);
23
- RainMan.spinLogicFactory = spinLogicFactory;
24
22
  RainMan.globals = {
25
23
  currency: Currencies["USD"],
26
24
  };
@@ -13,6 +13,7 @@ export const defaultAppConfig = {
13
13
  mobileFontSize: 45,
14
14
  updatableSpineContainerFontSize: 50,
15
15
  updatableTextValueColor: new Color(0xffffff).toHex(),
16
+ autoplayGlowColor: new Color(0xff3000).toNumber(),
16
17
  fontFace: ["HelveticaNeueLTStd"],
17
18
  winLineIndicatorSymbolScale: 0.75,
18
19
  durationOfActions: {
@@ -21,7 +22,7 @@ export const defaultAppConfig = {
21
22
  timeToSpeedUp: 200,
22
23
  timeToSlowDown: 10,
23
24
  symbolDropDownTime: 500,
24
- wiggleTweenDuration: 400,
25
+ wiggleTweenDuration: 1000,
25
26
  balloonFlyUpTime: 500,
26
27
  timeToSetResults: 10,
27
28
  bigWinDuration: 2000,
@@ -8,7 +8,6 @@ export type RequiredAppConfig = {
8
8
  numberOfColumns: number;
9
9
  numberOfRows: number;
10
10
  fontColor: string;
11
- autoplayGlowColor: number;
12
11
  };
13
12
  export type OptionalAppConfig = {
14
13
  gameWidth: number;
@@ -21,6 +20,7 @@ export type OptionalAppConfig = {
21
20
  updatableTextValueColor: string;
22
21
  fontFace: string[];
23
22
  mobileFontFace?: string[];
23
+ autoplayGlowColor: number;
24
24
  winLineIndicatorSymbolScale: number;
25
25
  durationOfActions: {
26
26
  eachColumnsSpinDelay: number[];
@@ -47,11 +47,11 @@ export class ApiConfig {
47
47
  return {
48
48
  action: spinLogicResponse.action,
49
49
  balance: spinLogicResponse.balance,
50
- availableFreeSpins: spinLogicResponse.available_free_spins,
50
+ availableFreeSpins: spinLogicResponse.available_free_spins || 0,
51
51
  symbolsOnReels: blindsConfiguredByResponse,
52
52
  finalBlindsConfiguration: finalSpinPositionOnReels,
53
53
  winAmountTotal: spinLogicResponse.win_amount_total,
54
- extraData: spinLogicResponse.extra_data,
54
+ extraData: spinLogicResponse.extra_data || {},
55
55
  };
56
56
  }
57
57
  /**
@@ -62,7 +62,7 @@ export class ApiConfig {
62
62
  * @param defaultNumber
63
63
  * @returns {string | number}
64
64
  */
65
- getWinLineNumber(winLineAnimationName, defaultNumber = "unknown") {
65
+ getWinLineNumber(winLineAnimationName, defaultNumber = "") {
66
66
  return this.streakMap.get(winLineAnimationName) ?? defaultNumber;
67
67
  }
68
68
  /**
@@ -1,6 +1,6 @@
1
1
  import i18n from "i18next";
2
2
  import { sample } from "lodash";
3
- import { AutoplayButton, Background, BUTTONS, COMPONENTS, FreeSpinBox, MessageBox, SpeedControlButton, VolumeButton, } from "../../components";
3
+ import { AutoplayButton, Background, BUTTONS, COMPONENTS, FreeSpinButton, MessageBox, SpeedControlButton, VolumeButton, } from "../../components";
4
4
  import { RainMan } from "../../Rainman";
5
5
  import { allUiItems, hideLayerIfPresent, UI_ITEMS } from "../../utils";
6
6
  import { SPEED_LEVELS } from "../SpeedState";
@@ -141,7 +141,7 @@ export class ButtonsEventManager {
141
141
  */
142
142
  initFreeSpinModal() {
143
143
  RainMan.componentRegistry
144
- .getIfExists(FreeSpinBox.registryName)
144
+ .getIfExists(FreeSpinButton.registryName)
145
145
  ?.setOnClick(() => this.handleModalInvocation(UI_ITEMS.freeSpin));
146
146
  }
147
147
  /**
@@ -1,6 +1,4 @@
1
- import { ExtraData, SpinDataInterface, SymbolReels, TransformationDetails, WinWithTransformation } from "../../connectivity";
2
- import { ApiConfig } from "../ApiConfig";
3
- import { SpinLogic } from "./SpinLogic";
1
+ import { ExtraData, SymbolReels, TransformationDetails, WinWithTransformation } from "../../connectivity";
4
2
  export type AdaptedSpinResponse = {
5
3
  action: string;
6
4
  balance: number;
@@ -12,4 +10,3 @@ export type AdaptedSpinResponse = {
12
10
  standaloneTransformations: TransformationDetails[][];
13
11
  extraData: ExtraData;
14
12
  };
15
- export type SpinLogicFactory = (spinLogicResponse: SpinDataInterface, gameConfig: ApiConfig) => SpinLogic;
@@ -1,11 +1,12 @@
1
1
  import { Container, Graphics, Sprite } from "pixi.js";
2
2
  import { ButtonsEventManager } from "../../application";
3
3
  import { AbstractController } from "../../controllers";
4
+ import { RainMan } from "../../Rainman";
4
5
  import { Nullable } from "../../utils";
5
6
  import { UpdatableSpineContainer } from "../../winComponents";
6
7
  import { AbstractFreeSpinContainer } from "../AbstractFreeSpinContainer";
7
8
  import { LeftButtons, LeftButtonsLandscape, LeftButtonsMobile, MiddleButtons, RightButtons, RightButtonsLandscape, RightButtonsMobile } from "../buttons";
8
- import { FreeSpinBox, Logo, ResponsiveContainer } from "../common";
9
+ import { FreeSpinButton, Logo, ResponsiveContainer } from "../common";
9
10
  import { IResizableContainer } from "../common/IResizableContainer";
10
11
  import { AbstractFrame } from "../frame";
11
12
  import { MessageBox } from "../messageBox";
@@ -19,7 +20,7 @@ export declare abstract class AbstractMainContainer extends Container {
19
20
  protected logo: Nullable<Logo>;
20
21
  protected abstract background: IResizableContainer;
21
22
  protected animatedFront: Nullable<IResizableContainer>;
22
- protected freeSpinBox: Nullable<FreeSpinBox>;
23
+ protected freeSpinButton: Nullable<FreeSpinButton>;
23
24
  protected underFramePanel: Nullable<Graphics | Sprite>;
24
25
  protected portraitPlayButtons: Nullable<MiddleButtons>;
25
26
  protected landscapePlayButtons: Nullable<MiddleButtons>;
@@ -35,7 +36,7 @@ export declare abstract class AbstractMainContainer extends Container {
35
36
  protected abstract _frame: AbstractFrame;
36
37
  protected _controller: Nullable<AbstractController<unknown>>;
37
38
  protected constructor();
38
- protected abstract triggerStoreInitState(): void;
39
+ protected triggerStoreInitState(...containersWithSpines: typeof RainMan.settingsStore.containersWithSpines): void;
39
40
  get freeSpinPlate(): Nullable<AbstractFreeSpinContainer>;
40
41
  get freeSpinSummary(): Nullable<AbstractFreeSpinContainer>;
41
42
  protected createFreeSpinPlate(_resolver: () => void, _freeSpinCount: number, _isAdditionalFreeSpin: boolean): AbstractFreeSpinContainer | null;
@@ -16,7 +16,7 @@ export class AbstractMainContainer extends Container {
16
16
  buttonsEventManager;
17
17
  logo = null;
18
18
  animatedFront = null;
19
- freeSpinBox = null;
19
+ freeSpinButton = null;
20
20
  underFramePanel = null;
21
21
  portraitPlayButtons = null;
22
22
  landscapePlayButtons = null;
@@ -37,6 +37,13 @@ export class AbstractMainContainer extends Container {
37
37
  SoundManager.play(SoundTracks.music);
38
38
  RainMan.app.renderer.on("resize", () => this.resize());
39
39
  }
40
+ triggerStoreInitState(...containersWithSpines) {
41
+ RainMan.settingsStore.containersWithSpines.push(...containersWithSpines);
42
+ RainMan.settingsStore.containersWithSpines.forEach((spine) => {
43
+ spine.setAnimationTimescale(RainMan.settingsStore.batterySaverFlag);
44
+ });
45
+ RainMan.settingsStore.refreshBatterySaver();
46
+ }
40
47
  get freeSpinPlate() {
41
48
  return this._freeSpinPlate;
42
49
  }
@@ -230,8 +237,8 @@ export class AbstractMainContainer extends Container {
230
237
  }
231
238
  this.background.resize();
232
239
  this.animatedFront?.resize();
233
- if (this.freeSpinBox)
234
- this.freeSpinBox.resize();
240
+ if (this.freeSpinButton)
241
+ this.freeSpinButton.resize();
235
242
  this.addChild(this._frame);
236
243
  this._frame.resize();
237
244
  this.initComponentsForScreenRatio();
@@ -0,0 +1,12 @@
1
+ import { Container } from "pixi.js";
2
+ export declare class AnimatedNumber extends Container {
3
+ private spineName;
4
+ private number;
5
+ private digits;
6
+ constructor(spineName: string, number: number);
7
+ private setDigits;
8
+ private removeDigits;
9
+ get value(): number;
10
+ set value(value: number);
11
+ play(): void;
12
+ }
@@ -0,0 +1,44 @@
1
+ import { Container } from "pixi.js";
2
+ import { Spine } from "pixi-spine";
3
+ import { getSpineData } from "../../utils";
4
+ export class AnimatedNumber extends Container {
5
+ spineName;
6
+ number;
7
+ digits = [];
8
+ constructor(spineName, number) {
9
+ super();
10
+ this.spineName = spineName;
11
+ this.number = number;
12
+ this.setDigits();
13
+ }
14
+ setDigits() {
15
+ this.removeDigits();
16
+ Array.from(this.number.toString()).forEach((digit) => {
17
+ const digitSpine = new Spine(getSpineData(this.spineName));
18
+ digitSpine.state.setAnimation(0, digit, true);
19
+ digitSpine.x = (this.digits.at(-1)?.x || 0) + digitSpine.width;
20
+ if (digit === "." || digit === ",") {
21
+ digitSpine.x += 10;
22
+ digitSpine.y += 20;
23
+ }
24
+ digitSpine.state.timeScale = 0;
25
+ this.addChild(digitSpine);
26
+ this.digits.push(digitSpine);
27
+ });
28
+ this.pivot.set(this.width / 2, this.height / 2);
29
+ }
30
+ removeDigits() {
31
+ this.digits.forEach((digit) => this.removeChild(digit));
32
+ this.digits = [];
33
+ }
34
+ get value() {
35
+ return this.number;
36
+ }
37
+ set value(value) {
38
+ this.number = value;
39
+ this.setDigits();
40
+ }
41
+ play() {
42
+ this.digits.forEach((digit) => (digit.state.timeScale = 1));
43
+ }
44
+ }
@@ -3,15 +3,15 @@ import { ResumableContainer } from "./ResumableContainers";
3
3
  * This class represents button/box with ability to buy free games
4
4
  *
5
5
  * @export
6
- * @class FreeSpinBox
7
- * @typedef {FreeSpinBox}
6
+ * @class FreeSpinButton
7
+ * @typedef {FreeSpinButton}
8
8
  * @extends {ResumableContainer}
9
9
  */
10
- export declare class FreeSpinBox extends ResumableContainer {
10
+ export declare class FreeSpinButton extends ResumableContainer {
11
11
  private spineName;
12
12
  private animationName;
13
13
  private mobileSpineName;
14
- static readonly registryName = "freeSpinBox";
14
+ static readonly registryName = "freeSpinButton";
15
15
  private spine;
16
16
  private lastDeviceOrientation?;
17
17
  constructor(spineName: string, animationName: string, mobileSpineName?: string);
@@ -9,15 +9,15 @@ import { ResumableContainer } from "./ResumableContainers";
9
9
  * This class represents button/box with ability to buy free games
10
10
  *
11
11
  * @export
12
- * @class FreeSpinBox
13
- * @typedef {FreeSpinBox}
12
+ * @class FreeSpinButton
13
+ * @typedef {FreeSpinButton}
14
14
  * @extends {ResumableContainer}
15
15
  */
16
- export class FreeSpinBox extends ResumableContainer {
16
+ export class FreeSpinButton extends ResumableContainer {
17
17
  spineName;
18
18
  animationName;
19
19
  mobileSpineName;
20
- static registryName = "freeSpinBox";
20
+ static registryName = "freeSpinButton";
21
21
  spine;
22
22
  lastDeviceOrientation;
23
23
  constructor(spineName, animationName, mobileSpineName = spineName) {
@@ -25,7 +25,7 @@ export class FreeSpinBox extends ResumableContainer {
25
25
  this.spineName = spineName;
26
26
  this.animationName = animationName;
27
27
  this.mobileSpineName = mobileSpineName;
28
- this.name = FreeSpinBox.registryName;
28
+ this.name = FreeSpinButton.registryName;
29
29
  this.allSpines.push(this.spine);
30
30
  this.visible = true;
31
31
  this.parentLayer = UXLayer;
@@ -1,6 +1,7 @@
1
1
  export * from "./AnimatedBackground";
2
+ export * from "./AnimatedNumber";
2
3
  export * from "./Background";
3
- export * from "./FreeSpinBox";
4
+ export * from "./FreeSpinButton";
4
5
  export * from "./Logo";
5
6
  export * from "./LogoStatic";
6
7
  export * from "./ResponsiveComponent";
@@ -1,6 +1,7 @@
1
1
  export * from "./AnimatedBackground";
2
+ export * from "./AnimatedNumber";
2
3
  export * from "./Background";
3
- export * from "./FreeSpinBox";
4
+ export * from "./FreeSpinButton";
4
5
  export * from "./Logo";
5
6
  export * from "./LogoStatic";
6
7
  export * from "./ResponsiveComponent";
@@ -24,6 +24,9 @@
24
24
  "numberWithCurrency": "{{amount}}{{currency}}",
25
25
  "currentWin": "WIN: ",
26
26
  "currentFreeSpins": "FREE SPINS: ",
27
+ "linePays": "LINE {{winLineNumber}} PAYS {{amount}}",
28
+ "gamePays": "GAME PAYS {{amount}}",
29
+ "scatterPays": "SCATTER PAYS {{amount}}",
27
30
  "idleMessages": {
28
31
  "slow": ["HOLD SPACE FOR TURBO SPIN", "PLACE YOUR BETS!", "PRESS SPIN TO WIN", "TRY FAST MODE"],
29
32
  "normal": ["HOLD SPACE FOR TURBO SPIN", "PLACE YOUR BETS!", "PRESS SPIN TO WIN", "TRY TURBO FAST MODE"],
@@ -31,13 +31,13 @@ export class AbstractColumnsContainer extends Container {
31
31
  this.y = 0;
32
32
  this.name = AbstractColumnsContainer.registryName;
33
33
  this.parentLayer = FrameLayer;
34
- Ticker.shared.add((delta) => this.animate(delta));
35
34
  }
36
35
  init() {
37
36
  this.allColumns.forEach((column) => {
38
37
  RainMan.componentRegistry.add(column);
39
38
  this.addChild(column);
40
39
  });
40
+ Ticker.shared.add((delta) => this.animate(delta));
41
41
  this.doResize();
42
42
  }
43
43
  adaptToSpeed(speedLevel) {
@@ -198,11 +198,9 @@ export class AbstractColumnsContainer extends Container {
198
198
  createMask() {
199
199
  const mask = new Graphics();
200
200
  mask.beginFill(0xaaaaaa, 1);
201
- mask.lineTo(this.maskCoordinates.left, this.maskCoordinates.top);
202
- mask.lineTo(this.maskCoordinates.right, this.maskCoordinates.top);
203
- mask.lineTo(this.maskCoordinates.right, this.maskCoordinates.bottom);
204
- mask.lineTo(this.maskCoordinates.left, this.maskCoordinates.bottom);
205
- mask.lineTo(this.maskCoordinates.left, this.maskCoordinates.top);
201
+ const width = this.maskCoordinates.right - this.maskCoordinates.left;
202
+ const height = this.maskCoordinates.bottom - this.maskCoordinates.top;
203
+ mask.drawRect(this.maskCoordinates.left, this.maskCoordinates.top, width, height);
206
204
  mask.endFill();
207
205
  return mask;
208
206
  }
@@ -40,7 +40,7 @@ export declare abstract class AbstractSymbolsColumn extends Container implements
40
40
  private triggerNextColumnPromise;
41
41
  private spinEndPromise;
42
42
  skipHidingMysteryFx: boolean;
43
- protected abstract bonusSymbol: SymbolId;
43
+ protected bonusSymbol: SymbolId | null;
44
44
  private readonly _id;
45
45
  protected constructor(id: string, columnsContainer: AbstractColumnsContainer, allSymbols: SymbolId[]);
46
46
  protected abstract get tileHeight(): number;
@@ -43,6 +43,7 @@ export class AbstractSymbolsColumn extends Container {
43
43
  triggerNextColumnPromise = null;
44
44
  spinEndPromise = null;
45
45
  skipHidingMysteryFx = false;
46
+ bonusSymbol = null;
46
47
  _id;
47
48
  constructor(id, columnsContainer, allSymbols) {
48
49
  super();
@@ -1,4 +1,4 @@
1
- import { GambleCardColor, GambleDataInterface, InitDataInterface, ServerMessage, SpinData, SubscribableConnectionWrapper } from "../connectivity";
1
+ import { ExtraDataRequest, GambleCardColor, GambleDataInterface, InitDataInterface, ServerMessage, SpinData, SubscribableConnectionWrapper } from "../connectivity";
2
2
  export declare class ConnectionWrapper implements SubscribableConnectionWrapper {
3
3
  private webSocketConnection;
4
4
  private config;
@@ -17,7 +17,7 @@ export declare class ConnectionWrapper implements SubscribableConnectionWrapper
17
17
  initData(): InitDataInterface;
18
18
  fetchFreeSpinData(roundNumber: number, quantity: number, price: number): Promise<SpinData>;
19
19
  private transformFreeSpinData;
20
- fetchSpinData(roundNumber: number, bet: number): Promise<SpinData>;
20
+ fetchSpinData(roundNumber: number, bet: number, extra_data?: ExtraDataRequest): Promise<SpinData>;
21
21
  fetchGambleData(roundNumber: number, bet: number, choice: GambleCardColor): Promise<GambleDataInterface>;
22
22
  private getFreshWsInstance;
23
23
  private fetch;
@@ -93,7 +93,7 @@ export class ConnectionWrapper {
93
93
  transformFreeSpinData(data) {
94
94
  return { ...data, previous_gambles: [], results: [], win_amount_total: 0, wins: {}, extra_data: {} };
95
95
  }
96
- async fetchSpinData(roundNumber, bet) {
96
+ async fetchSpinData(roundNumber, bet, extra_data = {}) {
97
97
  if (typeof this.config?.token === "undefined") {
98
98
  throw new Error("Token was not defined");
99
99
  }
@@ -103,6 +103,7 @@ export class ConnectionWrapper {
103
103
  round_number: roundNumber,
104
104
  token: this.config.token,
105
105
  bet,
106
+ extra_data,
106
107
  });
107
108
  this._messageHistory.push(data);
108
109
  return new SpinData(this.config, data);
@@ -7,7 +7,7 @@ export declare class LocalConnectionWrapper implements SubscribableConnectionWra
7
7
  private config;
8
8
  private subscribers;
9
9
  private _initData;
10
- constructor(initResponse: InitDataInterface, localResponse: ServerMessage);
10
+ constructor(initResponse: InitDataInterface, localResponse: ServerMessage | ServerMessage[]);
11
11
  initCommunication(): Promise<void>;
12
12
  fetchInitData(): Promise<void>;
13
13
  initData(): InitDataInterface;
@@ -1,2 +1,2 @@
1
1
  import { ServerMessage } from "./serverConnection";
2
- export declare const apiQueue: (localResponse: ServerMessage) => any;
2
+ export declare const apiQueue: (localResponse: ServerMessage | ServerMessage[]) => any;
@@ -4,7 +4,7 @@ export interface ServerCommunication {
4
4
  initCommunication(): Promise<void>;
5
5
  fetchInitData(): Promise<void>;
6
6
  initData(): InitDataInterface;
7
- fetchSpinData(roundNumber: number, bet: number): Promise<SpinData>;
7
+ fetchSpinData(roundNumber: number, bet: number, extraData?: ExtraDataRequest): Promise<SpinData>;
8
8
  fetchFreeSpinData(roundNumber: number, amount: number, price: number): Promise<SpinData>;
9
9
  getMessageHistory(): ServerMessage[];
10
10
  }
@@ -30,6 +30,7 @@ export type InitServerRequest = BaseServerRequest & {
30
30
  export type SpinServerRequest = BaseServerRequest & {
31
31
  action: "spin";
32
32
  bet: number;
33
+ extra_data: ExtraDataRequest;
33
34
  };
34
35
  export type BuyFreeSpinServerRequest = BaseServerRequest & {
35
36
  action: "buy-free-spins";
@@ -41,6 +42,8 @@ export type GambleServerRequest = BaseServerRequest & {
41
42
  bet: number;
42
43
  choice: GambleCardColor;
43
44
  };
45
+ export interface ExtraDataRequest {
46
+ }
44
47
  export type ServerRequest = InitServerRequest | SpinServerRequest | BuyFreeSpinServerRequest | GambleServerRequest;
45
48
  export type ServerMessage = InitDataInterface | SpinDataInterface | GambleDataInterface;
46
49
  export {};
@@ -22,10 +22,12 @@ export interface FixedSymbolMultiplierTable {
22
22
  type: typeof PaytableTypes.fixed;
23
23
  quantity: FixedQuantityValue;
24
24
  }
25
- type RangeQuantityValue = Record<string, {
25
+ export type RangeQuantityValue = Record<string, {
26
26
  min: number;
27
27
  max: number;
28
- } | number[]>;
28
+ } | number[] | {
29
+ value: null;
30
+ }>;
29
31
  export interface RangeSymbolMultiplierTable {
30
32
  type: typeof PaytableTypes.range;
31
33
  quantity: RangeQuantityValue;
@@ -37,13 +39,13 @@ export interface SpinDataInterface {
37
39
  balance: number;
38
40
  round_number: number;
39
41
  status: Status;
40
- available_free_spins: number;
42
+ available_free_spins: number | null;
41
43
  previous_gambles: unknown[];
42
44
  results: Result[];
43
45
  symbols_on_reels: number[][];
44
46
  win_amount_total: number;
45
47
  wins: Record<string, RawWin>;
46
- extra_data: ExtraData;
48
+ extra_data: ExtraData | null;
47
49
  }
48
50
  export interface ExtraData {
49
51
  }
@@ -1,11 +1,12 @@
1
1
  /// <reference types="node" />
2
2
  import { ApiConfig, ButtonsEventManager, SpinLogic, SymbolId } from "../application";
3
3
  import { AbstractColumnsContainer, AbstractFrame, AbstractMainContainer } from "../components";
4
- import { FreeSpinWin, SpinData, StreakReelsIndexes, SubscribableConnectionWrapper } from "../connectivity";
4
+ import { ExtraDataRequest, FreeSpinWin, SpinData, SpinDataInterface, StreakReelsIndexes, SubscribableConnectionWrapper } from "../connectivity";
5
5
  import { GamePhase } from "../constants";
6
6
  import { DescribedPlayableAction } from "../DescribedPlayableAction";
7
7
  import { Nullable } from "../utils";
8
8
  import { QuickStopController } from "./QuickStopController";
9
+ import { SpinDataWithLogic } from "./types";
9
10
  import { UiController } from "./UiController";
10
11
  export declare abstract class AbstractController<T> {
11
12
  protected buttonsEventManager: ButtonsEventManager;
@@ -20,7 +21,7 @@ export declare abstract class AbstractController<T> {
20
21
  protected isSpinThrottled: boolean;
21
22
  protected currentlyPlayedAction: (Promise<void> | Promise<void[]>) | undefined;
22
23
  protected currentlyPlayedActionType: T | undefined;
23
- protected isPlayingFirstAction: boolean;
24
+ protected isPlayingUnskippableStreak: boolean;
24
25
  protected isLoopingWinActionsQueue: boolean;
25
26
  protected gamePhase: GamePhase;
26
27
  protected autoSpinBalance: number;
@@ -31,7 +32,7 @@ export declare abstract class AbstractController<T> {
31
32
  protected uiController: UiController;
32
33
  protected resolveBigWin?: () => void;
33
34
  protected resolveMysteryWin?: () => void;
34
- protected abstract mysteryFxSymbol: SymbolId;
35
+ protected mysteryFxSymbol: SymbolId | null;
35
36
  protected mysteryWinSymbol: Nullable<SymbolId>;
36
37
  private componentRegistry;
37
38
  protected _lastWinAmount: number;
@@ -62,12 +63,14 @@ export declare abstract class AbstractController<T> {
62
63
  private getBoughtNumberOfFreeSpins;
63
64
  private autoplaySpin;
64
65
  protected clearAndDestroyActions(): void;
65
- private disableAutoplay;
66
+ protected disableAutoplay(): void;
67
+ protected abstract spinLogicFactory(spinLogicResponse: SpinDataInterface, gameConfig: ApiConfig): SpinLogic;
66
68
  protected onDataFetch(_spinData: SpinData, _spinLogic: SpinLogic): void;
67
- private fetchAndPrepareSpinData;
68
- protected abstract canSkipActions(): boolean;
69
+ protected getExtraData(): ExtraDataRequest;
70
+ protected fetchAndPrepareSpinData(): Promise<SpinDataWithLogic>;
71
+ protected canSkipActions(): boolean;
69
72
  protected onSpinningStart(): Promise<void>;
70
- protected onSpinningEnd(_data: SpinData): Promise<void>;
73
+ protected onSpinningEnd(_spinLogic: SpinLogic): Promise<void>;
71
74
  private spin;
72
75
  private handleFreeSpinPlateInvocation;
73
76
  protected handleFreeSpinSummaryPlateInvocation(): Promise<void>;
@@ -75,7 +78,7 @@ export declare abstract class AbstractController<T> {
75
78
  private lockLogicWhileSpinning;
76
79
  private unlockLogicAfterSpinning;
77
80
  protected stopPlayingSymbolsStreak(): void;
78
- private countWinBigWin;
81
+ protected countWinBigWin(customAmount?: number): Promise<void>;
79
82
  protected countMysteryWin(winAmount?: number, quantity?: number): Promise<void>;
80
83
  protected parseStreak(streak: StreakReelsIndexes, reelsQuantity: number): StreakReelsIndexes;
81
84
  protected getSymbolIdsForStreak(streak: StreakReelsIndexes): SymbolId[];
@@ -1,7 +1,7 @@
1
1
  import i18next from "i18next";
2
2
  import { sample } from "lodash";
3
3
  import { ApiConfig, SoundManager, SoundTracks, SPEED_LEVELS, } from "../application";
4
- import { AbstractFrame, AutoplayButton, BUTTONS, COMPONENTS, FreeSpinBox, MessageBox, SpeedControlButton, } from "../components";
4
+ import { AbstractFrame, AutoplayButton, BUTTONS, COMPONENTS, FreeSpinButton, MessageBox, SpeedControlButton, } from "../components";
5
5
  import { gamePhases, PossibleWins } from "../constants";
6
6
  import { RainMan } from "../Rainman";
7
7
  import { ceilToDecimal, debugSpinData, debugSymbolsMatchConfiguration, getDeviceOrientation, hideLayerIfPresent, hidePaytable, logGroup, logGroupEnd, logInfo, logWarn, UI_ITEMS, } from "../utils";
@@ -20,7 +20,7 @@ export class AbstractController {
20
20
  isSpinThrottled = false;
21
21
  currentlyPlayedAction;
22
22
  currentlyPlayedActionType;
23
- isPlayingFirstAction = false;
23
+ isPlayingUnskippableStreak = false;
24
24
  isLoopingWinActionsQueue = false;
25
25
  gamePhase = gamePhases.IDLE;
26
26
  autoSpinBalance = 0;
@@ -31,6 +31,7 @@ export class AbstractController {
31
31
  uiController;
32
32
  resolveBigWin;
33
33
  resolveMysteryWin;
34
+ mysteryFxSymbol = null;
34
35
  mysteryWinSymbol = null;
35
36
  componentRegistry;
36
37
  _lastWinAmount = 0;
@@ -145,7 +146,7 @@ export class AbstractController {
145
146
  });
146
147
  }
147
148
  initSpinButton() {
148
- RainMan.componentRegistry.get(BUTTONS.refresh).setOnClick(() => this.throttledSpin());
149
+ RainMan.componentRegistry.get(BUTTONS.refresh).setOnClick(() => this.handleSpinLogic());
149
150
  RainMan.componentRegistry.get(BUTTONS.refresh).on("touchstart", () => {
150
151
  if (this.mobileSpinTimeout !== null)
151
152
  return;
@@ -304,7 +305,7 @@ export class AbstractController {
304
305
  }
305
306
  if (RainMan.settingsStore.isFreeSpinsBought) {
306
307
  this.uiController.totalNumberOfFreeSpins += this.getBoughtNumberOfFreeSpins();
307
- RainMan.componentRegistry.getIfExists(FreeSpinBox.registryName)?.setInteractivity(false);
308
+ RainMan.componentRegistry.getIfExists(FreeSpinButton.registryName)?.setInteractivity(false);
308
309
  this.changeGamePhase(gamePhases.IDLE);
309
310
  this.spin().then(() => RainMan.settingsStore.setIsFreeSpinBought(false));
310
311
  return;
@@ -357,12 +358,15 @@ export class AbstractController {
357
358
  RainMan.componentRegistry.get(BUTTONS.neg).flipDisabledFlag(false);
358
359
  }
359
360
  onDataFetch(_spinData, _spinLogic) { }
361
+ getExtraData() {
362
+ return {};
363
+ }
360
364
  async fetchAndPrepareSpinData() {
361
365
  this.changeGamePhase(gamePhases.DATA_FETCH);
362
366
  const data = RainMan.settingsStore.buyFreeSpinsData ||
363
- (await this.connection.fetchSpinData(this.roundNumber, RainMan.settingsStore.bet));
367
+ (await this.connection.fetchSpinData(this.roundNumber, RainMan.settingsStore.bet, this.getExtraData()));
364
368
  debugSpinData(data);
365
- const spinLogic = RainMan.spinLogicFactory(data.getRawData(), this.config);
369
+ const spinLogic = this.spinLogicFactory(data.getRawData(), this.config);
366
370
  this._lastWinAmount = this.mysteryWinSymbol ? spinLogic.winTotalAmountWithoutMystery : spinLogic.winTotalAmount;
367
371
  this.onDataFetch(data, spinLogic);
368
372
  if (spinLogic.winTotalAmount !== 0) {
@@ -379,8 +383,11 @@ export class AbstractController {
379
383
  spinLogic,
380
384
  };
381
385
  }
386
+ canSkipActions() {
387
+ return !this.isPlayingUnskippableStreak;
388
+ }
382
389
  async onSpinningStart() { }
383
- async onSpinningEnd(_data) { }
390
+ async onSpinningEnd(_spinLogic) { }
384
391
  async spin() {
385
392
  if (this.uiController.currentBalance < RainMan.settingsStore.bet) {
386
393
  try {
@@ -424,8 +431,8 @@ export class AbstractController {
424
431
  await this.onSpinningStart();
425
432
  this.columnsContainer.blindSpin();
426
433
  this.clearAndDestroyActions();
427
- const { data, spinLogic } = await this.fetchAndPrepareSpinData();
428
- const availableFreeSpins = data.getRawData().available_free_spins;
434
+ const { spinLogic } = await this.fetchAndPrepareSpinData();
435
+ const availableFreeSpins = spinLogic.availableFreeSpins;
429
436
  const availableFreeSpinsIncreased = availableFreeSpins > RainMan.settingsStore.howManyFreeSpinsLeft;
430
437
  this.hideFreeSpinsMessageBox = availableFreeSpinsIncreased;
431
438
  if (!availableFreeSpinsIncreased)
@@ -437,7 +444,7 @@ export class AbstractController {
437
444
  }
438
445
  if (availableFreeSpins === 0 && RainMan.settingsStore.isFreeSpinsPlayEnabled) {
439
446
  this.invokeFreeSpinSummaryPlateAfterWin = true;
440
- RainMan.componentRegistry.getIfExists(FreeSpinBox.registryName)?.setInteractivity(true);
447
+ RainMan.componentRegistry.getIfExists(FreeSpinButton.registryName)?.setInteractivity(true);
441
448
  }
442
449
  if (RainMan.componentRegistry.has(BUTTONS.gamble))
443
450
  RainMan.componentRegistry
@@ -452,6 +459,13 @@ export class AbstractController {
452
459
  this.changeGamePhase(gamePhases.HANDLING_ACTIONS);
453
460
  this.handleDisablingButtons(true);
454
461
  this.unlockLogicAfterSpinning();
462
+ if (RainMan.settingsStore.isFreeSpinsPlayEnabled) {
463
+ this.uiController.increaseTotalFreeSpinWinAmount(spinLogic.winTotalAmountWithoutMystery);
464
+ this.uiController.limitWinAmount(this.uiController.totalFreeSpinWinAmount);
465
+ }
466
+ else {
467
+ this.uiController.limitWinAmount(spinLogic.winTotalAmount);
468
+ }
455
469
  this.composeWinActionsQueue(spinLogic);
456
470
  await this.playWinActionQueue();
457
471
  await this.countWinBigWin();
@@ -464,12 +478,12 @@ export class AbstractController {
464
478
  this.quickStopController.handleResetCounter(!this.isSpinThrottled);
465
479
  logInfo("🔴 spinning end");
466
480
  logGroupEnd();
467
- await this.onSpinningEnd(data);
481
+ await this.onSpinningEnd(spinLogic);
468
482
  this.winActionsQueue = [];
469
483
  this.spinning = false;
470
484
  this.isSpinThrottled = false;
471
485
  this.hideFreeSpinsMessageBox = false;
472
- this.isPlayingFirstAction = false;
486
+ this.isPlayingUnskippableStreak = false;
473
487
  this.uiController.disableLimitWinAmount();
474
488
  RainMan.settingsStore.resetIsLastFreeSpin();
475
489
  this.mainContainer.destroyBigWin();
@@ -556,14 +570,16 @@ export class AbstractController {
556
570
  this.columnsContainer.stopPlayingSymbolsInColumns();
557
571
  this.frame.stopPlayingWinLineAnimations();
558
572
  }
559
- async countWinBigWin() {
560
- if (this.uiController.recentWin <= RainMan.config.constants.bigWinMultiplier * RainMan.settingsStore.bet)
573
+ async countWinBigWin(customAmount) {
574
+ if (this.uiController.recentWin <= RainMan.config.constants.bigWinMultiplier * RainMan.settingsStore.bet &&
575
+ !customAmount)
561
576
  return;
577
+ const amount = customAmount || this.uiController.recentWin;
562
578
  this.mainContainer.invokeBigWin();
563
579
  const animationTimeDivider = RainMan.settingsStore.currentSpeed === SPEED_LEVELS.fast ? 3 : 1;
564
580
  await RainMan.componentRegistry
565
581
  .get(COMPONENTS.bigWin)
566
- .update(this.uiController.recentWin, RainMan.config.durationOfActions.updatableTextCountingDuration / animationTimeDivider);
582
+ .update(amount, RainMan.config.durationOfActions.updatableTextCountingDuration / animationTimeDivider);
567
583
  await new Promise((resolve) => {
568
584
  this.resolveBigWin = resolve;
569
585
  setTimeout(() => {
@@ -78,9 +78,9 @@ export class UiController {
78
78
  this.lastBet = bet;
79
79
  const betButton = RainMan.componentRegistry.get(COMPONENTS.betText);
80
80
  betButton.set(bet);
81
- this.afterUpdateBet(bet);
82
81
  RainMan.settingsStore.updateBet(bet);
83
82
  RainMan.settingsStore.updateBetText(betButton.value);
83
+ this.afterUpdateBet(bet);
84
84
  }
85
85
  updateShownSpeedLevel(currentSpeed) {
86
86
  RainMan.componentRegistry.get(SpeedControlButton.registryName).adaptToSpeed(currentSpeed);
@@ -12,7 +12,7 @@ import { SpriteLoadingContainer } from "./SpriteLoadingContainer";
12
12
  */
13
13
  export declare abstract class AbstractLoadingContainer extends Container {
14
14
  protected abstract background: Background;
15
- protected abstract logo: Logo | LogoStatic;
15
+ protected logo: Logo | LogoStatic | null;
16
16
  loaderContainer: SpriteLoadingContainer;
17
17
  protected constructor();
18
18
  resize(): void;
@@ -11,6 +11,7 @@ import { SpriteLoadingContainer } from "./SpriteLoadingContainer";
11
11
  * @extends {Container}
12
12
  */
13
13
  export class AbstractLoadingContainer extends Container {
14
+ logo = null;
14
15
  loaderContainer;
15
16
  constructor() {
16
17
  super();
@@ -26,7 +27,7 @@ export class AbstractLoadingContainer extends Container {
26
27
  }
27
28
  resize() {
28
29
  this.background.resize();
29
- this.logo.resize();
30
+ this.logo?.resize();
30
31
  this.loaderContainer.resize();
31
32
  }
32
33
  remove() {
@@ -1,6 +1,6 @@
1
1
  import { SpeedLevel, SymbolId } from "../application";
2
2
  import { ResumableContainer, SpineWithResumableContainer, SpriteWithResumableContainer } from "../components";
3
- import { InitDataInterface, Point, SpinData } from "../connectivity";
3
+ import { InitDataInterface, Point, RangeQuantityValue, SpinData } from "../connectivity";
4
4
  import { PaytableData } from "./types";
5
5
  /**
6
6
  * This class represents global state of pixi and react application
@@ -62,6 +62,8 @@ export declare class SettingsStore {
62
62
  setSingleWinExceeds(newValue: number): void;
63
63
  setBalancedIncreased(newValue: number): void;
64
64
  setBalancedDecreased(newValue: number): void;
65
+ getMinimumPaytableValue(multipliers: RangeQuantityValue[keyof RangeQuantityValue]): number;
66
+ getMaximumPaytableValue(multipliers: RangeQuantityValue[keyof RangeQuantityValue]): number;
65
67
  setDataForSymbolPaytablePopup(symbolId: SymbolId, repositionCallback: () => void): void;
66
68
  resetDataForSymbolPaytablePopup(): void;
67
69
  setUpdateVolumeButtonTexture(fn: () => void): void;
@@ -190,6 +190,20 @@ export class SettingsStore {
190
190
  setBalancedDecreased(newValue) {
191
191
  this.balancedDecreased = newValue;
192
192
  }
193
+ getMinimumPaytableValue(multipliers) {
194
+ if ("min" in multipliers)
195
+ return multipliers.min;
196
+ if (Array.isArray(multipliers))
197
+ return first(multipliers) || 0;
198
+ return 0;
199
+ }
200
+ getMaximumPaytableValue(multipliers) {
201
+ if ("max" in multipliers)
202
+ return multipliers.max;
203
+ if (Array.isArray(multipliers))
204
+ return last(multipliers) || 0;
205
+ return 0;
206
+ }
193
207
  setDataForSymbolPaytablePopup(symbolId, repositionCallback) {
194
208
  this.popupPaytableRepositionCallback = repositionCallback;
195
209
  this.popupPaytableRepositionCallback();
@@ -203,13 +217,11 @@ export class SettingsStore {
203
217
  return { prefix: keyOfSymbol, value: symbolPaytable.quantity[keyOfSymbol].value };
204
218
  }
205
219
  const multipliers = symbolPaytable.quantity[keyOfSymbol];
206
- const min = "min" in multipliers ? multipliers.min : first(multipliers) || 0;
207
- const max = "max" in multipliers ? multipliers.max : last(multipliers) || 0;
208
220
  return {
209
221
  prefix: keyOfSymbol,
210
222
  value: {
211
- min,
212
- max,
223
+ min: this.getMinimumPaytableValue(multipliers),
224
+ max: this.getMaximumPaytableValue(multipliers),
213
225
  },
214
226
  };
215
227
  });
@@ -300,6 +312,8 @@ export class SettingsStore {
300
312
  let multipliers = multipliersForQuantity.value;
301
313
  if (typeof multipliers === "number")
302
314
  multipliers = [multipliers];
315
+ if (multipliers === null)
316
+ return [0];
303
317
  return multipliers.map((multiplier) => multiplier * this.bet);
304
318
  }
305
319
  const multipliers = "min" in multipliersForQuantity
@@ -51,13 +51,8 @@ export const getTextureSafely = (name) => {
51
51
  * @returns {Promise<void>}
52
52
  */
53
53
  export const loadAssets = async (assets, updateProgress) => {
54
- try {
55
- const aliases = Object.keys(assets).map((key) => ({ alias: key, src: assets[key] }));
56
- await Assets.load(aliases, (progress) => updateProgress?.(progress));
57
- }
58
- catch (err) {
59
- throw Error();
60
- }
54
+ const aliases = Object.keys(assets).map((key) => ({ alias: key, src: assets[key] }));
55
+ await Assets.load(aliases, (progress) => updateProgress?.(progress));
61
56
  };
62
57
  /**
63
58
  * Helper function for checking if given resources are present in pixi assets cache
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pixi-rainman-game-engine",
3
- "version": "0.1.10",
3
+ "version": "0.1.11",
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",