@energy8platform/platform-core 0.23.3 → 0.24.1

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.
@@ -2,23 +2,26 @@ import type { CurrencyConfig } from './types';
2
2
 
3
3
  /** The shared money formatter for every shell readout (balance, win, total win, bet, prices).
4
4
  *
5
- * `decimals` is the MAXIMUM number of fraction digits; `minDecimals` (defaults to `decimals`)
6
- * is the MINIMUM. The value is rounded to `decimals`, then trailing zeros are trimmed down to
7
- * — but never past `minDecimals`. With `minDecimals` unset both bounds are equal, so the
8
- * output is always exactly `decimals` places (the classic behaviour).
5
+ * `maxDecimals` is the MAXIMUM fraction digits (default 2); `minDecimals` (defaults to
6
+ * `maxDecimals`) is the MINIMUM. By default the value is shown at exactly `minDecimals` places.
7
+ * With `variableDecimals` used only for win / total-win it is rounded to `maxDecimals`, then
8
+ * trailing zeros are trimmed down to (but never past) `minDecimals`, so small wins keep their
9
+ * significant digits. Balance / bet / prices stay fixed at `minDecimals`.
9
10
  *
10
- * Example with `decimals: 4, minDecimals: 2`:
11
- * 0.0673 → 0,0673 0.0670 → 0,067 0.0600 → 0,06
12
- * 0.0004 → 0,0004 0.0040 → 0,004 0.3000 → 0,30 0.0000 → 0,00
11
+ * Example with `maxDecimals: 4, minDecimals: 2`:
12
+ * fixed → 0.0673 → 0,07 0.3 → 0,30
13
+ * variable → 0.0673 → 0,0673 0.067 → 0,067 0.3 → 0,30 0 → 0,00
13
14
  */
14
- export function formatCurrency(value: number, currency: CurrencyConfig): string {
15
- const decimals = currency.decimals ?? 2;
16
- const minDecimals = Math.max(0, Math.min(decimals, currency.minDecimals ?? decimals));
15
+ export function formatCurrency(value: number, currency: CurrencyConfig, variableDecimals = false): string {
16
+ const maxDecimals = currency.maxDecimals ?? 2;
17
+ const minDecimals = Math.max(0, Math.min(maxDecimals, currency.minDecimals ?? maxDecimals));
17
18
  const thousands = currency.separator?.thousands ?? '.';
18
19
  const decimal = currency.separator?.decimal ?? ',';
19
20
  const safe = Number.isFinite(value) ? value : 0;
20
21
 
21
- const fixed = safe.toFixed(decimals); // round at the max precision, e.g. "0.0670"
22
+ // fixed callers round at minDecimals; variable callers round at maxDecimals then trim back down.
23
+ const places = variableDecimals ? maxDecimals : minDecimals;
24
+ const fixed = safe.toFixed(places);
22
25
  const [intPart, rawFrac = ''] = fixed.split('.');
23
26
  // trim trailing zeros, but keep at least `minDecimals` fraction digits
24
27
  let fracPart = rawFrac;
@@ -209,6 +209,12 @@ export const SHELL_CSS = SHELL_FONT_CSS + `
209
209
  #${SHELL_ROOT_ID} .ge-gi-mode-st span { color:var(--shell-plaque-label); font-size:10px; letter-spacing:.1em; text-transform:uppercase; }
210
210
  #${SHELL_ROOT_ID} .ge-gi-mode-st b { color:#fff; font-size:14px; font-weight:800; font-variant-numeric:tabular-nums; }
211
211
  #${SHELL_ROOT_ID} .ge-gi-mode-desc { color:rgba(255,255,255,.78); font-size:14px; line-height:1.5; margin:0; }
212
+ /* shapes — row list (grid illustration left, name + description right), modes-style text */
213
+ #${SHELL_ROOT_ID} .ge-gi-shapes { display:flex; flex-direction:column; }
214
+ #${SHELL_ROOT_ID} .ge-gi-shape { display:flex; align-items:center; gap:16px; padding:12px 0; }
215
+ #${SHELL_ROOT_ID} .ge-gi-shape + .ge-gi-shape { border-top:1px solid var(--shell-plaque-line); }
216
+ #${SHELL_ROOT_ID} .ge-gi-shape .ge-gi-pl-svg { flex:0 0 auto; width:96px; }
217
+ #${SHELL_ROOT_ID} .ge-gi-shape-tx { flex:1; min-width:0; display:flex; flex-direction:column; gap:4px; }
212
218
  #${SHELL_ROOT_ID} .ge-gi-custom { color:rgba(255,255,255,.88); font-size:15px; line-height:1.6; }
213
219
 
214
220
  /* buy bonus cards — art-forward, centred, flat (no gradients); --card-acc/--card-ink per card.
@@ -3,11 +3,12 @@ export type ShellMode = 'base' | 'freeSpins' | 'replay';
3
3
  export interface CurrencyConfig {
4
4
  symbol: string;
5
5
  position: 'left' | 'right';
6
- /** Maximum fraction digits (default 2). The value is rounded to this precision. */
7
- decimals?: number;
8
- /** Minimum fraction digits (defaults to `decimals`). Trailing zeros are trimmed down to
9
- * this many places, so small wins keep their significant digits (e.g. 0.0673) while round
10
- * amounts stay compact (e.g. 0.30). */
6
+ /** Maximum fraction digits (default 2). Win / total-win readouts are rounded to this precision;
7
+ * balance / bet / prices stay fixed at `minDecimals`. */
8
+ maxDecimals?: number;
9
+ /** Minimum fraction digits (defaults to `maxDecimals`). For win / total-win, trailing zeros are
10
+ * trimmed down to this many places so small wins keep their significant digits (e.g. 0.0673)
11
+ * while round amounts stay compact (e.g. 0.30). Everything else is shown at exactly this many. */
11
12
  minDecimals?: number;
12
13
  separator?: { thousands?: string; decimal?: string };
13
14
  }
@@ -75,6 +76,15 @@ export interface PaylineDef {
75
76
  /** A single grid cell, 0-based, row 0 = top. */
76
77
  export type CellRef = [col: number, row: number];
77
78
 
79
+ /** A named winning shape: an arbitrary set of grid cells (not one-per-column like a payline),
80
+ * shown as a grid illustration with its name and optional description. */
81
+ export interface ShapeDef {
82
+ /** The lit cells, in any pattern. */
83
+ cells: CellRef[];
84
+ name: string;
85
+ description?: string;
86
+ }
87
+
78
88
  /** How a game pays — drives the GameInfo win-section illustration. One section = one kind.
79
89
  * `example`/`winExample`/`loseExample` are optional; omit them for an auto-drawn illustration
80
90
  * sized to `grid`. */
@@ -90,6 +100,7 @@ export type WinSection = {
90
100
  | { kind: 'cluster'; minCount: number; example?: CellRef[] }
91
101
  | { kind: 'anywhere'; minCount: number; example?: CellRef[] }
92
102
  | { kind: 'ways'; winExample?: CellRef[]; loseExample?: CellRef[] }
103
+ | { kind: 'shapes'; shapes: ShapeDef[] }
93
104
  );
94
105
 
95
106
  /** A playable mode / bonus-buy option, shown for comparison (informational only). */
@@ -138,7 +149,9 @@ export interface AutoplayOptions {
138
149
  }
139
150
 
140
151
  export interface FreeSpinsState {
141
- current: number;
152
+ /** Spin index for the `current / total` counter. Set to `null` (or omit) to instead show just
153
+ * `total` as a single number — drive a countdown by decrementing `total` each spin. */
154
+ current?: number | null;
142
155
  total: number;
143
156
  totalWin: number;
144
157
  }