@energy8platform/platform-core 0.19.0 → 0.21.0
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.
- package/README.md +238 -1
- package/dist/dev-bridge.cjs.js +104 -0
- package/dist/dev-bridge.cjs.js.map +1 -1
- package/dist/dev-bridge.d.ts +64 -1
- package/dist/dev-bridge.esm.js +104 -0
- package/dist/dev-bridge.esm.js.map +1 -1
- package/dist/index.cjs.js +2053 -0
- package/dist/index.cjs.js.map +1 -1
- package/dist/index.d.ts +372 -2
- package/dist/index.esm.js +2051 -1
- package/dist/index.esm.js.map +1 -1
- package/dist/shell.cjs.js +1993 -0
- package/dist/shell.cjs.js.map +1 -0
- package/dist/shell.d.ts +320 -0
- package/dist/shell.esm.js +1989 -0
- package/dist/shell.esm.js.map +1 -0
- package/package.json +7 -2
- package/scripts/build-shell-font.mjs +64 -0
- package/src/PlatformSession.ts +10 -0
- package/src/dev-bridge/DevBridge.ts +160 -2
- package/src/dev-bridge/index.ts +1 -1
- package/src/index.ts +17 -1
- package/src/shell/GameShell.ts +294 -0
- package/src/shell/INTER-LICENSE.txt +93 -0
- package/src/shell/colors.ts +32 -0
- package/src/shell/components/BottomBar.ts +217 -0
- package/src/shell/components/BuyBonus.ts +163 -0
- package/src/shell/components/GameInfo.ts +253 -0
- package/src/shell/components/Modal.ts +36 -0
- package/src/shell/components/ReplayModal.ts +56 -0
- package/src/shell/components/Settings.ts +60 -0
- package/src/shell/components/icons.ts +40 -0
- package/src/shell/components/pickers.ts +76 -0
- package/src/shell/components/primitives.ts +84 -0
- package/src/shell/fonts.ts +13 -0
- package/src/shell/format.ts +36 -0
- package/src/shell/i18n.ts +67 -0
- package/src/shell/index.ts +20 -0
- package/src/shell/motion.ts +43 -0
- package/src/shell/shell.css.ts +371 -0
- package/src/shell/state.ts +30 -0
- package/src/shell/theme.ts +56 -0
- package/src/shell/types.ts +191 -0
|
@@ -0,0 +1,1993 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Minimal typed event emitter — internal utility for platform-core.
|
|
5
|
+
*
|
|
6
|
+
* Supports `void` event types — events that carry no data can be emitted
|
|
7
|
+
* without arguments: `emitter.emit('eventName')`.
|
|
8
|
+
*
|
|
9
|
+
* Mirrors the EventEmitter shipped from game-engine's core, copied here
|
|
10
|
+
* so platform-core has no upward dependency on game-engine.
|
|
11
|
+
*/
|
|
12
|
+
// eslint-disable-next-line @typescript-eslint/no-empty-object-type
|
|
13
|
+
class EventEmitter {
|
|
14
|
+
listeners = new Map();
|
|
15
|
+
on(event, handler) {
|
|
16
|
+
if (!this.listeners.has(event)) {
|
|
17
|
+
this.listeners.set(event, new Set());
|
|
18
|
+
}
|
|
19
|
+
this.listeners.get(event).add(handler);
|
|
20
|
+
return this;
|
|
21
|
+
}
|
|
22
|
+
once(event, handler) {
|
|
23
|
+
const wrapper = (data) => {
|
|
24
|
+
this.off(event, wrapper);
|
|
25
|
+
handler(data);
|
|
26
|
+
};
|
|
27
|
+
return this.on(event, wrapper);
|
|
28
|
+
}
|
|
29
|
+
off(event, handler) {
|
|
30
|
+
this.listeners.get(event)?.delete(handler);
|
|
31
|
+
return this;
|
|
32
|
+
}
|
|
33
|
+
emit(...args) {
|
|
34
|
+
const [event, data] = args;
|
|
35
|
+
const handlers = this.listeners.get(event);
|
|
36
|
+
if (handlers) {
|
|
37
|
+
for (const handler of handlers) {
|
|
38
|
+
handler(data);
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
removeAllListeners(event) {
|
|
43
|
+
if (event) {
|
|
44
|
+
this.listeners.delete(event);
|
|
45
|
+
}
|
|
46
|
+
else {
|
|
47
|
+
this.listeners.clear();
|
|
48
|
+
}
|
|
49
|
+
return this;
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
function createInitialState(config) {
|
|
54
|
+
return {
|
|
55
|
+
mode: config.mode,
|
|
56
|
+
balance: config.balance,
|
|
57
|
+
win: config.win,
|
|
58
|
+
bet: config.currentBet ?? config.defaultBet,
|
|
59
|
+
availableBets: [...config.availableBets],
|
|
60
|
+
busy: false,
|
|
61
|
+
autoplay: { active: false, remaining: 0 },
|
|
62
|
+
turbo: 0,
|
|
63
|
+
buyBonusEnabled: true,
|
|
64
|
+
freeSpins: { current: 0, total: 0, totalWin: 0, lastWin: 0 },
|
|
65
|
+
activeFeature: null,
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
/** Step bet up/down within availableBets, clamped at the ends. */
|
|
69
|
+
function stepBet(state, direction) {
|
|
70
|
+
const idx = state.availableBets.indexOf(state.bet);
|
|
71
|
+
const next = Math.max(0, Math.min(state.availableBets.length - 1, idx + direction));
|
|
72
|
+
return state.availableBets[next];
|
|
73
|
+
}
|
|
74
|
+
/** Cycle turbo level 0..maxLevels (wraps back to 0). */
|
|
75
|
+
function nextTurbo(current, maxLevels) {
|
|
76
|
+
if (maxLevels <= 0)
|
|
77
|
+
return 0;
|
|
78
|
+
return current >= maxLevels ? 0 : current + 1;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/** The single brand accent (purple). The bar accent default (theme.ts) and the buy-a-bonus
|
|
82
|
+
* card default both derive from this, so a rebrand only touches one constant. */
|
|
83
|
+
const BRAND_ACCENT = '#8b5cf6';
|
|
84
|
+
/** Type-default accents for bonus options. A per-option `accentColor` overrides these. */
|
|
85
|
+
const ACCENT_BONUS = BRAND_ACCENT; // brand purple — buy a bonus round
|
|
86
|
+
const ACCENT_FEATURE = '#f0b429'; // gold — activate a base-game feature (e.g. Ante)
|
|
87
|
+
/** The accent a card/button/bet-tint uses: explicit override, else the type default. */
|
|
88
|
+
function effectiveAccent(b) {
|
|
89
|
+
return b.accentColor ?? (b.type === 'feature' ? ACCENT_FEATURE : ACCENT_BONUS);
|
|
90
|
+
}
|
|
91
|
+
/** Readable text colour for a solid accent button: dark on light accents, white on dark.
|
|
92
|
+
* Only #rgb / #rrggbb are measured; anything else (named, var(), etc.) → white. */
|
|
93
|
+
function contrastText(accent) {
|
|
94
|
+
const rgb = parseHex(accent);
|
|
95
|
+
if (!rgb)
|
|
96
|
+
return '#ffffff';
|
|
97
|
+
// Relative luminance (sRGB, perceptual-ish). >0.6 → use dark ink.
|
|
98
|
+
const lum = (0.299 * rgb[0] + 0.587 * rgb[1] + 0.114 * rgb[2]) / 255;
|
|
99
|
+
return lum > 0.6 ? '#1a1205' : '#ffffff';
|
|
100
|
+
}
|
|
101
|
+
function parseHex(hex) {
|
|
102
|
+
const m = /^#?([0-9a-f]{3}|[0-9a-f]{6})$/i.exec(hex.trim());
|
|
103
|
+
if (!m)
|
|
104
|
+
return null;
|
|
105
|
+
let h = m[1];
|
|
106
|
+
if (h.length === 3)
|
|
107
|
+
h = h[0] + h[0] + h[1] + h[1] + h[2] + h[2];
|
|
108
|
+
return [parseInt(h.slice(0, 2), 16), parseInt(h.slice(2, 4), 16), parseInt(h.slice(4, 6), 16)];
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
const DEFAULT_ACCENT = BRAND_ACCENT; // brand purple — BUY BONUS + active states (single source: colors.ts)
|
|
112
|
+
// Neutral palettes for each scheme. Everything the shell paints reads from these
|
|
113
|
+
// tokens, so a single `scheme` flip recolours bar, icons, overlays and toggles.
|
|
114
|
+
const PALETTE = {
|
|
115
|
+
dark: {
|
|
116
|
+
fg: '#f3f5fa', muted: '#9aa3b6', icon: '#c7cedb', iconActive: '#ffffff',
|
|
117
|
+
surface: '#0c111c', hairline: 'rgba(255,255,255,.07)',
|
|
118
|
+
veil: 'rgba(255,255,255,.05)', veilStrong: 'rgba(255,255,255,.1)', track: 'rgba(255,255,255,.16)',
|
|
119
|
+
soft: '#dfe4ee', spin: '#f4f6fb', spinFg: '#141a28',
|
|
120
|
+
},
|
|
121
|
+
light: {
|
|
122
|
+
fg: '#15202e', muted: '#5a6678', icon: '#3c4658', iconActive: '#0b1220',
|
|
123
|
+
surface: '#eef1f7', hairline: 'rgba(15,23,42,.12)',
|
|
124
|
+
veil: 'rgba(15,23,42,.05)', veilStrong: 'rgba(15,23,42,.09)', track: 'rgba(15,23,42,.22)',
|
|
125
|
+
soft: '#3a4453', spin: '#1c2434', spinFg: '#f3f6fb',
|
|
126
|
+
},
|
|
127
|
+
};
|
|
128
|
+
/** CSS custom-property block for the shell root. `scheme` picks the palette;
|
|
129
|
+
* only accent and buyBonus are additionally game-overridable. */
|
|
130
|
+
function buildThemeVars(theme = {}) {
|
|
131
|
+
const p = PALETTE[theme.scheme === 'light' ? 'light' : 'dark'];
|
|
132
|
+
return [
|
|
133
|
+
`--shell-fg: ${p.fg}`,
|
|
134
|
+
`--shell-muted: ${p.muted}`,
|
|
135
|
+
`--shell-icon: ${p.icon}`,
|
|
136
|
+
`--shell-icon-active: ${p.iconActive}`,
|
|
137
|
+
`--shell-surface: ${p.surface}`,
|
|
138
|
+
`--shell-hairline: ${p.hairline}`,
|
|
139
|
+
`--shell-veil: ${p.veil}`,
|
|
140
|
+
`--shell-veil-strong: ${p.veilStrong}`,
|
|
141
|
+
`--shell-track: ${p.track}`,
|
|
142
|
+
`--shell-soft: ${p.soft}`,
|
|
143
|
+
`--shell-spin: ${p.spin}`,
|
|
144
|
+
`--shell-spin-fg: ${p.spinFg}`,
|
|
145
|
+
`--shell-radius: 12px`,
|
|
146
|
+
// Plaque tokens — the grouped dark/glass panel language shared by the control bar
|
|
147
|
+
// AND the overlays. Scheme-independent (always dark, white-on-dark) so bar + overlays
|
|
148
|
+
// stay visually identical regardless of the dark/light `scheme`.
|
|
149
|
+
`--shell-plaque-dark: rgba(6,9,15,.76)`,
|
|
150
|
+
`--shell-plaque-glass: rgba(30,36,48,.5)`,
|
|
151
|
+
`--shell-plaque-glass-hover: rgba(40,48,64,.62)`,
|
|
152
|
+
// Opaque surface for centred modals (confirm, bet/autoplay pickers) so they read solid,
|
|
153
|
+
// not see-through, over the frosted backdrop.
|
|
154
|
+
`--shell-plaque-solid: #1a2030`,
|
|
155
|
+
`--shell-plaque-line: rgba(255,255,255,.22)`,
|
|
156
|
+
`--shell-plaque-label: rgba(255,255,255,.6)`,
|
|
157
|
+
`--shell-accent: ${theme.accent ?? DEFAULT_ACCENT}`,
|
|
158
|
+
// buy-bonus tint follows the accent by default; only overridden when set explicitly
|
|
159
|
+
`--shell-buybonus: ${theme.buyBonusColor ?? theme.accent ?? DEFAULT_ACCENT}`,
|
|
160
|
+
].join('; ') + ';';
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
/* eslint-disable */
|
|
164
|
+
// Inter (variable, weight 100–900) embedded as base64 woff2 — self-contained so the
|
|
165
|
+
// shell renders identically on every platform with no external asset or network fetch.
|
|
166
|
+
// Subsets: latin (covers EN/ES/FR incl. all accents + œ/Œ) + cyrillic (covers RU/UK).
|
|
167
|
+
// Inter is licensed under the SIL Open Font License 1.1 — see ./INTER-LICENSE.txt
|
|
168
|
+
// (https://github.com/rsms/inter).
|
|
169
|
+
// GENERATED by scripts/build-shell-font.mjs — regenerate, don't hand-edit.
|
|
170
|
+
const SHELL_FONT_CSS = `@font-face{font-family:'Inter';font-style:normal;font-display:swap;font-weight:100 900;src:url(data:font/woff2;base64,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) format('woff2');unicode-range:U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,U+0304,U+0308,U+0329,U+2000-206F,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD;}` +
|
|
171
|
+
`@font-face{font-family:'Inter';font-style:normal;font-display:swap;font-weight:100 900;src:url(data:font/woff2;base64,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) format('woff2');unicode-range:U+0301,U+0400-045F,U+0490-0491,U+04B0-04B1,U+2116;}`;
|
|
172
|
+
|
|
173
|
+
const SHELL_ROOT_ID = '__ge-game-shell__';
|
|
174
|
+
// Inter (bundled, base64) leads the stack so the shell renders identically on every
|
|
175
|
+
// platform; the system fonts stay as graceful fallback if the webfont ever fails to load.
|
|
176
|
+
const SHELL_CSS = SHELL_FONT_CSS + `
|
|
177
|
+
#${SHELL_ROOT_ID} {
|
|
178
|
+
position: absolute; inset: 0;
|
|
179
|
+
pointer-events: none; z-index: 9000;
|
|
180
|
+
font-family: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
|
|
181
|
+
color: var(--shell-fg);
|
|
182
|
+
}
|
|
183
|
+
#${SHELL_ROOT_ID} svg { display:block; }
|
|
184
|
+
|
|
185
|
+
/* floating readouts (no panel) */
|
|
186
|
+
#${SHELL_ROOT_ID} .ge-rd { font-weight:700; font-size:13px; line-height:1; white-space:nowrap;
|
|
187
|
+
text-shadow:0 1px 3px rgba(0,0,0,.65); font-variant-numeric:tabular-nums; }
|
|
188
|
+
#${SHELL_ROOT_ID} .ge-rd .ge-lbl { display:block; color:var(--shell-muted); font-weight:600;
|
|
189
|
+
font-size:9px; letter-spacing:.1em; text-transform:uppercase; margin-bottom:4px; text-shadow:none; }
|
|
190
|
+
|
|
191
|
+
/* icon buttons (borderless) */
|
|
192
|
+
#${SHELL_ROOT_ID} .ge-iconbtn { pointer-events:auto; cursor:pointer; border:none; background:none;
|
|
193
|
+
padding:0; color:var(--shell-icon); width:40px; height:40px; display:flex; align-items:center;
|
|
194
|
+
justify-content:center; font-size:24px; position:relative; transition:transform .08s ease; }
|
|
195
|
+
#${SHELL_ROOT_ID} .ge-iconbtn:active { transform:scale(.92); }
|
|
196
|
+
#${SHELL_ROOT_ID} .ge-iconbtn[disabled] { opacity:.35; cursor:default; }
|
|
197
|
+
#${SHELL_ROOT_ID} .ge-iconbtn.ge-active { color:var(--shell-icon-active); }
|
|
198
|
+
|
|
199
|
+
/* SPIN — white disc by default, big glyph reaching for the rim; lights up accent on hover */
|
|
200
|
+
#${SHELL_ROOT_ID} .ge-shell-spin { pointer-events:auto; cursor:pointer; border:3px solid #000; border-radius:50%;
|
|
201
|
+
width:86px; height:86px; background:var(--shell-spin); color:var(--shell-spin-fg); box-sizing:border-box; font-size:68px;
|
|
202
|
+
display:flex; align-items:center; justify-content:center;
|
|
203
|
+
transition:transform .08s ease, box-shadow .12s ease, background .12s ease, color .12s ease; }
|
|
204
|
+
#${SHELL_ROOT_ID} .ge-shell-spin:hover { background:var(--shell-accent); color:#fff;
|
|
205
|
+
box-shadow:0 0 0 3px var(--shell-accent), 0 0 18px 2px var(--shell-accent); }
|
|
206
|
+
#${SHELL_ROOT_ID} .ge-shell-spin:active { transform:scale(.94); }
|
|
207
|
+
/* disabled: dim via filter (stays opaque) so the plaque seam doesn't show through the disc */
|
|
208
|
+
#${SHELL_ROOT_ID} .ge-shell-spin[disabled] { filter:grayscale(.4) brightness(.62); box-shadow:none; cursor:default; }
|
|
209
|
+
/* spinning while a spin is in progress */
|
|
210
|
+
#${SHELL_ROOT_ID} .ge-shell-spin.ge-spinning svg { transform-origin:50% 50%; animation:ge-spin-rot .8s linear infinite; }
|
|
211
|
+
@keyframes ge-spin-rot { to { transform:rotate(360deg); } }
|
|
212
|
+
/* autoplay running: STOP glyph + countdown stacked in the disc */
|
|
213
|
+
#${SHELL_ROOT_ID} .ge-shell-spin.ge-stop { position:relative; }
|
|
214
|
+
#${SHELL_ROOT_ID} .ge-spin-stop { display:flex; } /* STOP glyph at the disc's full size, like SPIN */
|
|
215
|
+
/* no entrance animation: the bar re-renders many times per spin, so any animation here
|
|
216
|
+
would replay each time and make the counter visibly jump. tabular-nums keeps it steady. */
|
|
217
|
+
#${SHELL_ROOT_ID} .ge-spin-count { position:absolute; inset:0; display:flex; align-items:center; justify-content:center;
|
|
218
|
+
font-size:22px; font-weight:800; line-height:1; font-variant-numeric:tabular-nums; }
|
|
219
|
+
|
|
220
|
+
/* BUY BONUS — round accent badge, 2-line label, text pulses + accent glow on hover */
|
|
221
|
+
#${SHELL_ROOT_ID} .ge-shell-buybonus { pointer-events:auto; cursor:pointer; box-sizing:border-box;
|
|
222
|
+
width:80px; height:80px; border-radius:50%; border:3px solid #000; background:var(--shell-buybonus);
|
|
223
|
+
color:#fff; font-weight:800; letter-spacing:.02em; font-size:13px; line-height:1.08; text-align:center;
|
|
224
|
+
display:flex; align-items:center; justify-content:center; transition:transform .08s ease, box-shadow .12s ease; }
|
|
225
|
+
#${SHELL_ROOT_ID} .ge-shell-buybonus span { display:inline-block; will-change:transform; }
|
|
226
|
+
#${SHELL_ROOT_ID} .ge-shell-buybonus:hover { box-shadow:0 0 0 3px var(--shell-accent), 0 0 16px 1px var(--shell-accent); }
|
|
227
|
+
#${SHELL_ROOT_ID} .ge-shell-buybonus:hover span { animation:ge-bb-pulse .7s ease-in-out infinite; }
|
|
228
|
+
#${SHELL_ROOT_ID} .ge-shell-buybonus:active { transform:scale(.96); }
|
|
229
|
+
#${SHELL_ROOT_ID} .ge-shell-buybonus[disabled] { filter:grayscale(.5) brightness(.72); box-shadow:none; cursor:default; }
|
|
230
|
+
#${SHELL_ROOT_ID} .ge-shell-buybonus[disabled] span { animation:none; }
|
|
231
|
+
@keyframes ge-bb-pulse { 0%,100%{transform:scale(1)} 50%{transform:scale(1.16)} }
|
|
232
|
+
|
|
233
|
+
/* host = bottom-anchored flex column: [win pill (on overflow)] above [the bar] */
|
|
234
|
+
#${SHELL_ROOT_ID} .ge-shell-barhost { position:absolute; left:0; right:0; bottom:0; pointer-events:none;
|
|
235
|
+
display:flex; flex-direction:column; align-items:center; justify-content:flex-end; gap:8px;
|
|
236
|
+
transform-origin:bottom center; }
|
|
237
|
+
/* bottom bar: transparent, two zones (wide default) */
|
|
238
|
+
#${SHELL_ROOT_ID} .ge-shell-bottom { width:100%; box-sizing:border-box; pointer-events:none;
|
|
239
|
+
display:flex; align-items:center; justify-content:space-between; padding:0 18px 14px; gap:14px; }
|
|
240
|
+
#${SHELL_ROOT_ID} .ge-zone { display:flex; align-items:center; gap:14px; pointer-events:none; }
|
|
241
|
+
#${SHELL_ROOT_ID} .ge-zone > * { pointer-events:auto; }
|
|
242
|
+
#${SHELL_ROOT_ID} .ge-betstep { display:flex; flex-direction:column; gap:2px; }
|
|
243
|
+
/* auto stacked over turbo (far-right column, base/desktop) */
|
|
244
|
+
#${SHELL_ROOT_ID} .ge-autoturbo { display:flex; flex-direction:column; gap:2px; }
|
|
245
|
+
#${SHELL_ROOT_ID} .ge-autoturbo .ge-iconbtn { width:40px; height:30px; }
|
|
246
|
+
|
|
247
|
+
/* mobile (portrait) — full-width stacked plaques: [balance · win] · [controls] · [− bet +] */
|
|
248
|
+
#${SHELL_ROOT_ID}.ge-mobile .ge-shell-bottom { flex-direction:column; align-items:center; gap:14px; padding:8px 12px 8px; }
|
|
249
|
+
#${SHELL_ROOT_ID}.ge-mobile .ge-m-top { width:100%; height:46px; justify-content:space-between; }
|
|
250
|
+
#${SHELL_ROOT_ID}.ge-mobile .ge-m-controls { display:flex; align-items:center; justify-content:space-between;
|
|
251
|
+
width:100%; box-sizing:border-box; height:62px; border-radius:18px; padding:0 18px; }
|
|
252
|
+
#${SHELL_ROOT_ID}.ge-mobile .ge-m-controls .ge-iconbtn,
|
|
253
|
+
#${SHELL_ROOT_ID}.ge-mobile .ge-m-controls .ge-rd,
|
|
254
|
+
#${SHELL_ROOT_ID}.ge-mobile .ge-m-controls .ge-rd .ge-lbl { color:#fff; }
|
|
255
|
+
#${SHELL_ROOT_ID}.ge-mobile .ge-m-controls .ge-rd { text-shadow:none; text-align:center; }
|
|
256
|
+
/* mobile: restore accent hover + autoplay glow (the white rule above out-specifies the base ones) */
|
|
257
|
+
#${SHELL_ROOT_ID}.ge-mobile .ge-m-controls .ge-iconbtn:hover,
|
|
258
|
+
#${SHELL_ROOT_ID}.ge-mobile .ge-m-controls .ge-iconbtn.ge-glow { color:var(--shell-accent); }
|
|
259
|
+
#${SHELL_ROOT_ID}.ge-mobile .ge-m-bet { width:100%; height:46px; padding:0 18px; gap:8px; justify-content:space-between; }
|
|
260
|
+
#${SHELL_ROOT_ID}.ge-mobile .ge-shell-spin { width:84px; height:84px; font-size:66px; }
|
|
261
|
+
#${SHELL_ROOT_ID}.ge-mobile .ge-shell-buybonus { width:50px; height:50px; font-size:9px; border-width:2px; }
|
|
262
|
+
/* landscape overflow — size the column to content, centre it; JS scales the whole stack to fit */
|
|
263
|
+
#${SHELL_ROOT_ID} .ge-shell-barhost.ge-fit { left:50%; right:auto; width:max-content; max-width:none; }
|
|
264
|
+
#${SHELL_ROOT_ID} .ge-shell-barhost.ge-fit .ge-shell-bottom { width:max-content; }
|
|
265
|
+
|
|
266
|
+
/* full-screen overlays — header (title left, prominent X/back), scrolling body, vh-clamped for popout/mobile.
|
|
267
|
+
Frosted backdrop: the game shows through, blurred, behind a light dark tint (white-on-blur).
|
|
268
|
+
Same glass "plaque" language as the control bar; scheme-independent. */
|
|
269
|
+
#${SHELL_ROOT_ID} .ge-shell-overlay { position:absolute; inset:0; z-index:50; pointer-events:auto;
|
|
270
|
+
background:rgba(12,17,28,.5); backdrop-filter:blur(20px) saturate(120%);
|
|
271
|
+
-webkit-backdrop-filter:blur(20px) saturate(120%);
|
|
272
|
+
display:flex; flex-direction:column; animation:ge-ov-in .16s ease-out; }
|
|
273
|
+
/* SPIN/BUY BONUS use z-index:3 to overlap their plaques; lift the overlay clear above them */
|
|
274
|
+
@keyframes ge-ov-in { from { opacity:0; } to { opacity:1; } }
|
|
275
|
+
#${SHELL_ROOT_ID} .ge-ov-head { flex:0 0 auto; display:flex; align-items:center; gap:8px; padding:6px 10px; }
|
|
276
|
+
#${SHELL_ROOT_ID} .ge-ov-title { flex:1; margin:0; text-align:center; color:#fff; font-weight:800; letter-spacing:.04em;
|
|
277
|
+
text-transform:uppercase; font-size:clamp(13px,2.6vh,16px); }
|
|
278
|
+
#${SHELL_ROOT_ID} .ge-ov-spacer { flex:0 0 auto; width:32px; }
|
|
279
|
+
#${SHELL_ROOT_ID} .ge-ov-nav { flex:0 0 auto; display:flex; align-items:center; justify-content:center;
|
|
280
|
+
width:32px; height:32px; border-radius:9px; cursor:pointer; color:#fff;
|
|
281
|
+
background:var(--shell-plaque-dark); border:none; font-size:18px;
|
|
282
|
+
transition:background .12s ease, color .12s ease; }
|
|
283
|
+
#${SHELL_ROOT_ID} .ge-ov-nav:hover { background:var(--shell-plaque-glass); color:var(--shell-accent); }
|
|
284
|
+
#${SHELL_ROOT_ID} .ge-ov-scroll { flex:1 1 auto; min-height:0; overflow-y:auto; overflow-x:hidden; }
|
|
285
|
+
#${SHELL_ROOT_ID} .ge-ov-body { max-width:800px; margin:0 auto; box-sizing:border-box;
|
|
286
|
+
padding:clamp(6px,2vh,16px) clamp(16px,4vw,24px) clamp(16px,4vh,28px); }
|
|
287
|
+
|
|
288
|
+
/* full-width overlay rows — glass plaque, white-on-dark */
|
|
289
|
+
#${SHELL_ROOT_ID} .ge-ov-row { display:flex; align-items:center; gap:12px; width:100%; box-sizing:border-box;
|
|
290
|
+
padding:clamp(11px,2.2vh,15px) 16px; margin-bottom:10px; border:none;
|
|
291
|
+
border-radius:16px; background:var(--shell-plaque-glass); color:#fff; font-size:14px; font-weight:600; }
|
|
292
|
+
#${SHELL_ROOT_ID} .ge-ov-row .ge-grow { flex:1; text-align:left; }
|
|
293
|
+
#${SHELL_ROOT_ID} button.ge-ov-row { cursor:pointer; font-family:inherit; transition:background .12s ease, color .12s ease; }
|
|
294
|
+
#${SHELL_ROOT_ID} button.ge-ov-row:hover { background:var(--shell-plaque-glass-hover); color:var(--shell-accent); }
|
|
295
|
+
#${SHELL_ROOT_ID} .ge-ov-row.ge-col { flex-direction:column; align-items:stretch; gap:10px; }
|
|
296
|
+
#${SHELL_ROOT_ID} .ge-ov-row .ge-row-head { display:flex; justify-content:space-between; align-items:center; }
|
|
297
|
+
#${SHELL_ROOT_ID} .ge-ov-row .ge-row-head .ge-val { color:var(--shell-plaque-label); font-variant-numeric:tabular-nums; font-weight:700; }
|
|
298
|
+
#${SHELL_ROOT_ID} .ge-slider { width:100%; accent-color:var(--shell-accent); }
|
|
299
|
+
#${SHELL_ROOT_ID} .ge-toggle { flex:0 0 auto; width:42px; height:24px; border-radius:999px; background:var(--shell-plaque-line);
|
|
300
|
+
border:none; cursor:pointer; position:relative; }
|
|
301
|
+
#${SHELL_ROOT_ID} .ge-toggle.ge-on { background:var(--shell-accent); }
|
|
302
|
+
#${SHELL_ROOT_ID} .ge-toggle i { position:absolute; top:2px; left:2px; width:20px; height:20px; border-radius:50%;
|
|
303
|
+
background:#fff; transition:left .12s ease; }
|
|
304
|
+
#${SHELL_ROOT_ID} .ge-toggle.ge-on i { left:20px; }
|
|
305
|
+
/* sound on/off — speaker icon button (replaces the toggle) */
|
|
306
|
+
#${SHELL_ROOT_ID} .ge-snd { pointer-events:auto; cursor:pointer; border:none; background:none; padding:0;
|
|
307
|
+
width:36px; height:36px; display:flex; align-items:center; justify-content:center; font-size:24px;
|
|
308
|
+
color:#fff; transition:color .12s ease, transform .08s ease; }
|
|
309
|
+
#${SHELL_ROOT_ID} .ge-snd:not(.ge-active) { color:var(--shell-plaque-label); }
|
|
310
|
+
#${SHELL_ROOT_ID} .ge-snd:hover { color:var(--shell-accent); }
|
|
311
|
+
#${SHELL_ROOT_ID} .ge-snd:active { transform:scale(.92); }
|
|
312
|
+
|
|
313
|
+
/* game info — each section is its own glass plaque; body text sized for comfortable reading */
|
|
314
|
+
#${SHELL_ROOT_ID} .ge-gi-sec { margin-bottom:12px; background:var(--shell-plaque-glass);
|
|
315
|
+
border-radius:16px; padding:16px 18px; }
|
|
316
|
+
#${SHELL_ROOT_ID} .ge-gi-sec h3 { color:var(--shell-plaque-label); font-size:11px; letter-spacing:.14em;
|
|
317
|
+
text-transform:uppercase; margin:0 0 12px; }
|
|
318
|
+
#${SHELL_ROOT_ID} .ge-gi-sec p { color:rgba(255,255,255,.88); font-size:15px; line-height:1.6; margin:0; }
|
|
319
|
+
|
|
320
|
+
/* controls — two blocks (gameplay / menu & info), icon/name/description per control */
|
|
321
|
+
#${SHELL_ROOT_ID} .ge-gi-ctl-block + .ge-gi-ctl-block { margin-top:16px; padding-top:4px; border-top:1px solid var(--shell-plaque-line); }
|
|
322
|
+
#${SHELL_ROOT_ID} .ge-gi-ctl-block-h { color:var(--shell-plaque-label); font-size:11px; letter-spacing:.12em;
|
|
323
|
+
text-transform:uppercase; margin:8px 0 2px; font-weight:700; }
|
|
324
|
+
#${SHELL_ROOT_ID} .ge-gi-ctl { display:flex; align-items:center; gap:14px; padding:9px 0; }
|
|
325
|
+
#${SHELL_ROOT_ID} .ge-gi-ctl + .ge-gi-ctl { border-top:1px solid var(--shell-plaque-line); }
|
|
326
|
+
#${SHELL_ROOT_ID} .ge-gi-ctl-ic { flex:0 0 auto; width:48px; height:48px; display:flex; align-items:center;
|
|
327
|
+
justify-content:center; font-size:26px; color:#fff; }
|
|
328
|
+
/* bet shows both chevrons, stacked & smaller */
|
|
329
|
+
#${SHELL_ROOT_ID} .ge-gi-ctl-ic--bet { flex-direction:column; font-size:18px; gap:0; }
|
|
330
|
+
/* buy bonus draws the real control-bar badge, scaled down & non-interactive */
|
|
331
|
+
#${SHELL_ROOT_ID} .ge-gi-ctl-ic .ge-shell-buybonus { width:46px; height:46px; font-size:8px; border-width:2px; pointer-events:none; }
|
|
332
|
+
#${SHELL_ROOT_ID} .ge-gi-ctl-tx { display:flex; flex-direction:column; gap:2px; }
|
|
333
|
+
#${SHELL_ROOT_ID} .ge-gi-ctl-tx b { color:#fff; font-size:15px; font-weight:700; }
|
|
334
|
+
#${SHELL_ROOT_ID} .ge-gi-ctl-tx span { color:rgba(255,255,255,.7); font-size:13px; line-height:1.4; }
|
|
335
|
+
|
|
336
|
+
/* paytable — cards: symbol image on top, name, then win tiers "<count> x<mult>" */
|
|
337
|
+
#${SHELL_ROOT_ID} .ge-gi-pt-grid { display:grid; grid-template-columns:repeat(auto-fill, minmax(120px,1fr)); gap:10px; }
|
|
338
|
+
#${SHELL_ROOT_ID} .ge-gi-pt-card { display:flex; flex-direction:column; align-items:center; gap:8px;
|
|
339
|
+
background:var(--shell-plaque-dark); border-radius:14px; padding:14px 12px; }
|
|
340
|
+
#${SHELL_ROOT_ID} .ge-gi-pt-sym { display:flex; flex-direction:column; align-items:center; gap:5px; min-width:0; }
|
|
341
|
+
#${SHELL_ROOT_ID} .ge-gi-pt-sym img { width:56px; height:56px; object-fit:contain; }
|
|
342
|
+
#${SHELL_ROOT_ID} .ge-gi-pt-sym span { color:#fff; font-size:13px; font-weight:700; text-transform:uppercase; letter-spacing:.05em; }
|
|
343
|
+
#${SHELL_ROOT_ID} .ge-gi-pt-wins { display:flex; flex-direction:column; align-items:stretch; gap:2px; width:100%; }
|
|
344
|
+
#${SHELL_ROOT_ID} .ge-gi-pt-win { display:flex; justify-content:space-between; align-items:baseline; gap:14px;
|
|
345
|
+
font-size:13.5px; font-variant-numeric:tabular-nums; white-space:nowrap; }
|
|
346
|
+
#${SHELL_ROOT_ID} .ge-gi-pt-win i { color:var(--shell-plaque-label); font-style:normal; }
|
|
347
|
+
#${SHELL_ROOT_ID} .ge-gi-pt-win b { color:var(--shell-accent); font-weight:700; }
|
|
348
|
+
|
|
349
|
+
/* wins — accent-filled mini-grid(s); classic = one per line, cluster/anywhere = one, ways = two */
|
|
350
|
+
#${SHELL_ROOT_ID} .ge-gi-pl-grid { display:grid; grid-template-columns:repeat(auto-fill, minmax(72px,1fr)); gap:12px; }
|
|
351
|
+
#${SHELL_ROOT_ID} .ge-gi-pl-item { display:flex; flex-direction:column; align-items:center; gap:6px; }
|
|
352
|
+
#${SHELL_ROOT_ID} .ge-gi-pl-svg { width:100%; height:auto; max-width:140px; }
|
|
353
|
+
#${SHELL_ROOT_ID} .ge-gi-pl-cell { fill:var(--shell-plaque-line); }
|
|
354
|
+
#${SHELL_ROOT_ID} .ge-gi-pl-on { fill:var(--shell-accent); }
|
|
355
|
+
#${SHELL_ROOT_ID} .ge-gi-pl-cap { color:#fff; font-size:13px; font-weight:700; }
|
|
356
|
+
#${SHELL_ROOT_ID} .ge-gi-win-row { display:flex; align-items:flex-start; gap:16px; flex-wrap:wrap; }
|
|
357
|
+
#${SHELL_ROOT_ID} .ge-gi-win-row .ge-gi-pl-svg { flex:0 0 auto; width:140px; }
|
|
358
|
+
#${SHELL_ROOT_ID} .ge-gi-win-desc { flex:1; min-width:160px; color:rgba(255,255,255,.78); font-size:14px; line-height:1.5; margin:0; }
|
|
359
|
+
#${SHELL_ROOT_ID} .ge-gi-win-two { display:flex; gap:22px; flex-wrap:wrap; }
|
|
360
|
+
#${SHELL_ROOT_ID} .ge-gi-win-col { display:flex; flex-direction:column; align-items:center; gap:6px; }
|
|
361
|
+
#${SHELL_ROOT_ID} .ge-gi-win-col .ge-gi-pl-svg { width:140px; }
|
|
362
|
+
#${SHELL_ROOT_ID} .ge-gi-win-tag { font-size:12px; font-weight:700; }
|
|
363
|
+
#${SHELL_ROOT_ID} .ge-gi-win-ok { color:#4ade80; }
|
|
364
|
+
#${SHELL_ROOT_ID} .ge-gi-win-no { color:#f87171; }
|
|
365
|
+
#${SHELL_ROOT_ID} .ge-gi-win-badge { display:inline-block; margin-left:10px; padding:2px 8px; border-radius:999px;
|
|
366
|
+
font-size:11px; font-weight:700; letter-spacing:.02em; color:var(--shell-accent);
|
|
367
|
+
background:var(--shell-plaque-dark); vertical-align:middle; }
|
|
368
|
+
|
|
369
|
+
/* modes — comparison cards */
|
|
370
|
+
#${SHELL_ROOT_ID} .ge-gi-modes { display:flex; flex-direction:column; }
|
|
371
|
+
#${SHELL_ROOT_ID} .ge-gi-mode { padding:12px 0; }
|
|
372
|
+
#${SHELL_ROOT_ID} .ge-gi-mode + .ge-gi-mode { border-top:1px solid var(--shell-plaque-line); }
|
|
373
|
+
#${SHELL_ROOT_ID} .ge-gi-mode-top { display:flex; flex-wrap:wrap; align-items:baseline; justify-content:space-between; gap:6px 16px; margin-bottom:6px; }
|
|
374
|
+
#${SHELL_ROOT_ID} .ge-gi-mode-h { color:#fff; font-size:16px; font-weight:800; }
|
|
375
|
+
#${SHELL_ROOT_ID} .ge-gi-mode-stats { display:flex; flex-wrap:wrap; gap:14px; }
|
|
376
|
+
#${SHELL_ROOT_ID} .ge-gi-mode-st { display:flex; align-items:baseline; gap:5px; }
|
|
377
|
+
#${SHELL_ROOT_ID} .ge-gi-mode-st span { color:var(--shell-plaque-label); font-size:10px; letter-spacing:.1em; text-transform:uppercase; }
|
|
378
|
+
#${SHELL_ROOT_ID} .ge-gi-mode-st b { color:#fff; font-size:14px; font-weight:800; font-variant-numeric:tabular-nums; }
|
|
379
|
+
#${SHELL_ROOT_ID} .ge-gi-mode-desc { color:rgba(255,255,255,.78); font-size:14px; line-height:1.5; margin:0; }
|
|
380
|
+
#${SHELL_ROOT_ID} .ge-gi-custom { color:rgba(255,255,255,.88); font-size:15px; line-height:1.6; }
|
|
381
|
+
|
|
382
|
+
/* buy bonus cards — art-forward, centred, flat (no gradients); --card-acc/--card-ink per card.
|
|
383
|
+
order: title → thumb → description → (spacer) → volatility → price → full-bleed button. */
|
|
384
|
+
/* wide: horizontal scrolling strip. Cards scale PROPORTIONALLY with the viewport — every
|
|
385
|
+
dimension is in em-units, so the single font-size knob shrinks the whole card uniformly
|
|
386
|
+
(like the control bar's fit-scale), keeping small popouts readable. mobile: vertical stack. */
|
|
387
|
+
/* the buy-bonus scroll area is a SIZE CONTAINER, so the cards' cqh units measure the overlay
|
|
388
|
+
(the popout frame) and not the browser window — cards fit without any vertical scroll. */
|
|
389
|
+
#${SHELL_ROOT_ID} [data-ge="buybonus-overlay"] .ge-ov-scroll { container-type:size; }
|
|
390
|
+
#${SHELL_ROOT_ID} [data-ge="buybonus-overlay"] .ge-ov-body { padding:clamp(8px,3cqh,16px); }
|
|
391
|
+
#${SHELL_ROOT_ID} .ge-bb-grid { display:flex; gap:14px; overflow-x:auto; overflow-y:hidden; padding-bottom:6px;
|
|
392
|
+
scroll-snap-type:x proximity; -webkit-overflow-scrolling:touch; }
|
|
393
|
+
/* the one knob that scales the whole card — cqh measures the overlay (popout frame), not the
|
|
394
|
+
browser window, so cards shrink to fit the real container height. */
|
|
395
|
+
#${SHELL_ROOT_ID} .ge-bb-grid .ge-bonus-card { flex:0 0 18.5em; scroll-snap-align:start;
|
|
396
|
+
font-size:clamp(7px, 4cqh, 13px); }
|
|
397
|
+
/* mobile: vertical stack at a fixed, readable size — scroll the list, don't shrink the cards */
|
|
398
|
+
#${SHELL_ROOT_ID}.ge-mobile .ge-bb-grid { display:flex; flex-direction:column; gap:14px; overflow:visible; }
|
|
399
|
+
#${SHELL_ROOT_ID}.ge-mobile .ge-bb-grid .ge-bonus-card { flex:0 0 auto; font-size:13px; }
|
|
400
|
+
#${SHELL_ROOT_ID} .ge-bonus-card { display:flex; flex-direction:column; border-radius:1.4em; overflow:hidden;
|
|
401
|
+
background:var(--shell-plaque-glass); border:1px solid var(--shell-plaque-line); color:#fff; text-align:center;
|
|
402
|
+
pointer-events:auto; cursor:pointer; transition:box-shadow .12s ease, background .12s ease; }
|
|
403
|
+
#${SHELL_ROOT_ID} .ge-bonus-card:hover:not(.ge-bonus-off) {
|
|
404
|
+
box-shadow:0 0 0 1px var(--card-acc), 0 12px 34px -12px var(--card-acc); }
|
|
405
|
+
#${SHELL_ROOT_ID} .ge-bonus-body { display:flex; flex-direction:column; align-items:center; flex:1; padding:1.25em 1.1em .9em; }
|
|
406
|
+
#${SHELL_ROOT_ID} .ge-bonus-title { font-size:1.3em; font-weight:800; letter-spacing:.04em; text-transform:uppercase;
|
|
407
|
+
color:var(--card-acc); margin-bottom:.75em; }
|
|
408
|
+
#${SHELL_ROOT_ID} .ge-bonus-thumb { height:6.2em; display:flex; align-items:center; justify-content:center; margin-bottom:.7em; }
|
|
409
|
+
#${SHELL_ROOT_ID} .ge-bonus-thumb img { max-height:100%; max-width:100%; object-fit:contain; filter:drop-shadow(0 6px 14px rgba(0,0,0,.45)); }
|
|
410
|
+
#${SHELL_ROOT_ID} .ge-bonus-thumb-ph { font-size:3.5em; color:var(--card-acc); opacity:.85; display:flex; }
|
|
411
|
+
#${SHELL_ROOT_ID} .ge-bonus-desc { font-size:.96em; line-height:1.45; color:rgba(255,255,255,.82); }
|
|
412
|
+
#${SHELL_ROOT_ID} .ge-bonus-spacer { flex:1; min-height:.7em; }
|
|
413
|
+
#${SHELL_ROOT_ID} .ge-bonus-vol { display:flex; justify-content:center; align-items:center; gap:0;
|
|
414
|
+
font-size:2.1em; line-height:1; margin-bottom:.55em; }
|
|
415
|
+
#${SHELL_ROOT_ID} .ge-bonus-vol-on, #${SHELL_ROOT_ID} .ge-bonus-vol-off { display:inline-flex; gap:0; }
|
|
416
|
+
#${SHELL_ROOT_ID} .ge-bonus-vol-on { color:var(--card-acc); }
|
|
417
|
+
#${SHELL_ROOT_ID} .ge-bonus-vol-off { color:rgba(255,255,255,.18); }
|
|
418
|
+
#${SHELL_ROOT_ID} .ge-bonus-price { font-size:1.6em; font-weight:800; color:#fff; font-variant-numeric:tabular-nums; white-space:nowrap; }
|
|
419
|
+
#${SHELL_ROOT_ID} .ge-bonus-cta { width:100%; border:none; padding:1.15em; font-weight:800; font-size:1.05em;
|
|
420
|
+
letter-spacing:.05em; text-transform:uppercase; cursor:pointer; background:var(--card-acc); color:var(--card-ink);
|
|
421
|
+
transition:filter .12s ease; }
|
|
422
|
+
#${SHELL_ROOT_ID} .ge-bonus-cta:hover:not([disabled]) { filter:brightness(1.06); }
|
|
423
|
+
/* unaffordable / disabled */
|
|
424
|
+
#${SHELL_ROOT_ID} .ge-bonus-card.ge-bonus-off { opacity:.62; cursor:default; }
|
|
425
|
+
#${SHELL_ROOT_ID} .ge-bonus-off .ge-bonus-title { color:rgba(255,255,255,.6); }
|
|
426
|
+
#${SHELL_ROOT_ID} .ge-bonus-off .ge-bonus-vol-on { color:rgba(255,255,255,.4); }
|
|
427
|
+
#${SHELL_ROOT_ID} .ge-bonus-off .ge-bonus-cta { background:#8d939e; color:#3a3f47; cursor:default; }
|
|
428
|
+
/* buy-bonus confirm — bonus preview inside the shared sheet card (see .ge-sheet-actions below) */
|
|
429
|
+
#${SHELL_ROOT_ID} .ge-confirm-preview { display:flex; flex-direction:column; align-items:center; gap:10px; text-align:center; }
|
|
430
|
+
#${SHELL_ROOT_ID} .ge-confirm-preview .ge-bonus-thumb,
|
|
431
|
+
#${SHELL_ROOT_ID} .ge-confirm-preview .ge-bonus-vol { margin:0; }
|
|
432
|
+
/* effective-bet readout while a feature is active (value colour set inline to the feature accent) */
|
|
433
|
+
#${SHELL_ROOT_ID} .ge-rd.ge-bet-feature { font-weight:800; }
|
|
434
|
+
/* bet control — compact pill in a thin footer at the screen bottom (footer only as tall as the pill) */
|
|
435
|
+
#${SHELL_ROOT_ID} .ge-bb-betbar { flex:0 0 auto; display:flex; justify-content:center; padding:4px; }
|
|
436
|
+
#${SHELL_ROOT_ID} .ge-bb-betpill { display:inline-flex; align-items:center; gap:4px;
|
|
437
|
+
background:var(--shell-plaque-dark); border-radius:999px; padding:3px 5px; }
|
|
438
|
+
#${SHELL_ROOT_ID} .ge-bb-betstep { pointer-events:auto; cursor:pointer; width:32px; height:32px; border:none; border-radius:50%;
|
|
439
|
+
background:none; color:#fff; font-size:20px; display:flex; align-items:center; justify-content:center;
|
|
440
|
+
transition:color .12s ease, transform .08s ease; }
|
|
441
|
+
#${SHELL_ROOT_ID} .ge-bb-betstep:hover { color:var(--shell-accent); }
|
|
442
|
+
#${SHELL_ROOT_ID} .ge-bb-betstep:active { transform:scale(.9); }
|
|
443
|
+
#${SHELL_ROOT_ID} .ge-bb-betval { min-width:80px; text-align:center; padding:0 2px; }
|
|
444
|
+
#${SHELL_ROOT_ID} .ge-bb-betval span { display:block; font-size:7px; font-weight:600; letter-spacing:.14em; text-transform:uppercase;
|
|
445
|
+
color:var(--shell-plaque-label); }
|
|
446
|
+
#${SHELL_ROOT_ID} .ge-bb-betval b { font-size:14px; font-weight:800; font-variant-numeric:tabular-nums; color:#fff; }
|
|
447
|
+
|
|
448
|
+
/* ═══ base/wide plaque bar — grouped dark + glass panels (reference-style) ═══ */
|
|
449
|
+
#${SHELL_ROOT_ID} .ge-zone-plaques { gap:0; } /* panels connect; buttons overlap */
|
|
450
|
+
#${SHELL_ROOT_ID} .ge-pl { display:flex; align-items:center; height:56px; box-sizing:border-box;
|
|
451
|
+
border-radius:16px; padding:0 20px; gap:18px; }
|
|
452
|
+
#${SHELL_ROOT_ID} .ge-pl-dark { background:var(--shell-plaque-dark); }
|
|
453
|
+
#${SHELL_ROOT_ID} .ge-pl-glass { background:var(--shell-plaque-glass); }
|
|
454
|
+
/* FS spins-counter plaque (wide) — sits between balance and bet, glass like balance */
|
|
455
|
+
#${SHELL_ROOT_ID} .ge-fscount { justify-content:center; min-width:150px; }
|
|
456
|
+
#${SHELL_ROOT_ID} .ge-pl .ge-rd { color:#fff; text-shadow:none; }
|
|
457
|
+
#${SHELL_ROOT_ID} .ge-pl .ge-rd .ge-lbl { color:var(--shell-plaque-label); }
|
|
458
|
+
#${SHELL_ROOT_ID} .ge-pl .ge-iconbtn { color:#fff; }
|
|
459
|
+
/* LEFT: [menu] ⊐ coin ⊏ [balance] — coin overlaps both; balance fixed-wide so it doesn't jiggle */
|
|
460
|
+
#${SHELL_ROOT_ID} .ge-pl-menu { border-radius:16px 0 0 16px; padding-right:20px; }
|
|
461
|
+
#${SHELL_ROOT_ID} .ge-pl-bal { border-radius:0 16px 16px 0; padding-left:24px; min-width:240px; }
|
|
462
|
+
#${SHELL_ROOT_ID} .ge-zone-plaques .ge-shell-buybonus { margin:0 -16px; position:relative; z-index:3; }
|
|
463
|
+
/* RIGHT: [bet] · |divider| · [auto · SPIN · turbo] */
|
|
464
|
+
#${SHELL_ROOT_ID} .ge-pl-bet { border-radius:16px 0 0 16px; justify-content:space-between;
|
|
465
|
+
width:210px; padding-right:8px; } /* fixed: bet value never reflows the panel */
|
|
466
|
+
#${SHELL_ROOT_ID} .ge-pl-divider { align-self:center; flex:0 0 auto; width:1px; height:30px;
|
|
467
|
+
background:var(--shell-plaque-line); }
|
|
468
|
+
#${SHELL_ROOT_ID} .ge-spinwrap { display:flex; align-items:center; gap:10px; height:56px;
|
|
469
|
+
border-radius:0 16px 16px 0; padding:0 8px 0 14px; }
|
|
470
|
+
#${SHELL_ROOT_ID} .ge-spinwrap .ge-iconbtn { color:#fff; }
|
|
471
|
+
#${SHELL_ROOT_ID} .ge-spinwrap .ge-shell-spin { margin:0 -2px; position:relative; z-index:3; }
|
|
472
|
+
/* tighter bet chevrons, parked next to autoplay */
|
|
473
|
+
#${SHELL_ROOT_ID} .ge-pl-bet .ge-betstep { gap:0; }
|
|
474
|
+
#${SHELL_ROOT_ID} .ge-pl-bet .ge-betstep .ge-iconbtn { height:24px; }
|
|
475
|
+
/* accent hover highlight on every control */
|
|
476
|
+
#${SHELL_ROOT_ID} .ge-pl .ge-iconbtn:hover, #${SHELL_ROOT_ID} .ge-spinwrap .ge-iconbtn:hover,
|
|
477
|
+
#${SHELL_ROOT_ID} .ge-iconbtn:hover { color:var(--shell-accent); }
|
|
478
|
+
/* turbo at rest (level 0) reads as dimmed — clearly off, but lighter than a true [disabled] control */
|
|
479
|
+
#${SHELL_ROOT_ID} [data-ge="turbo"]:not(.ge-active) { opacity:.5; }
|
|
480
|
+
#${SHELL_ROOT_ID} [data-ge="turbo"]:not(.ge-active):hover { opacity:1; }
|
|
481
|
+
/* autoplay glow while running (beats the white .ge-spinwrap/.ge-pl colour rules) */
|
|
482
|
+
#${SHELL_ROOT_ID} .ge-iconbtn.ge-glow { color:var(--shell-accent); filter:drop-shadow(0 0 6px var(--shell-accent)); }
|
|
483
|
+
/* tappable stake readout opens the bet picker */
|
|
484
|
+
#${SHELL_ROOT_ID} .ge-betbtn { pointer-events:auto; cursor:pointer; border-radius:8px; transition:color .12s ease; }
|
|
485
|
+
#${SHELL_ROOT_ID} .ge-betbtn:hover { color:var(--shell-accent); }
|
|
486
|
+
#${SHELL_ROOT_ID} .ge-betbtn.ge-disabled { pointer-events:none; }
|
|
487
|
+
/* WIN — inline between the groups, same block height/shape as the other plaques (glass tone) */
|
|
488
|
+
#${SHELL_ROOT_ID} .ge-winpill { flex:0 0 auto; align-self:center; display:flex; align-items:center;
|
|
489
|
+
justify-content:center; gap:8px; height:56px; box-sizing:border-box; padding:0 24px; border-radius:16px;
|
|
490
|
+
white-space:nowrap; pointer-events:none; background:var(--shell-plaque-glass); color:#fff;
|
|
491
|
+
font-size:16px; font-weight:800; font-variant-numeric:tabular-nums; text-shadow:none; }
|
|
492
|
+
#${SHELL_ROOT_ID} .ge-winpill .ge-lbl { display:inline; margin:0; color:rgba(255,255,255,.7);
|
|
493
|
+
font-size:10px; letter-spacing:.12em; }
|
|
494
|
+
/* lifted above the bar on overflow — compact pill (flex child of the host, scaled with it) */
|
|
495
|
+
#${SHELL_ROOT_ID} .ge-winpill.ge-up { height:auto; padding:6px 16px; border-radius:999px; }
|
|
496
|
+
|
|
497
|
+
/* centred CARD modal — opaque card on a frosted backdrop, accent title heading at the top
|
|
498
|
+
(no ✕), content, full-bleed footer button(s). Shared by the buy-bonus confirm AND the
|
|
499
|
+
bet/autoplay pickers so every centred modal is the same type. Close via backdrop click. */
|
|
500
|
+
#${SHELL_ROOT_ID} .ge-sheet { position:absolute; inset:0; z-index:60; pointer-events:auto; display:flex;
|
|
501
|
+
align-items:center; justify-content:center; padding:clamp(10px,4vh,24px); box-sizing:border-box;
|
|
502
|
+
background:rgba(12,17,28,.5); backdrop-filter:blur(var(--ge-sheet-blur,20px)) saturate(120%);
|
|
503
|
+
-webkit-backdrop-filter:blur(var(--ge-sheet-blur,20px)) saturate(120%); animation:ge-ov-in .16s ease-out; }
|
|
504
|
+
/* GameShell.fitModal() scales the whole card down (transform) so it fits short popouts uniformly */
|
|
505
|
+
#${SHELL_ROOT_ID} .ge-modal-card { width:100%; max-width:420px; box-sizing:border-box; overflow:hidden;
|
|
506
|
+
transform-origin:center center; background:var(--shell-plaque-solid); border-radius:20px; display:flex; flex-direction:column; }
|
|
507
|
+
/* ✕ pinned to the overlay corner (the screen), not the card */
|
|
508
|
+
#${SHELL_ROOT_ID} .ge-modal-close { position:absolute; top:12px; right:12px; z-index:2; width:36px; height:36px;
|
|
509
|
+
border:none; border-radius:50%; cursor:pointer; pointer-events:auto; background:var(--shell-plaque-dark); color:#fff;
|
|
510
|
+
display:flex; align-items:center; justify-content:center; font-size:20px; transition:background .12s ease, color .12s ease; }
|
|
511
|
+
#${SHELL_ROOT_ID} .ge-modal-close:hover { background:var(--shell-plaque-glass); color:var(--shell-accent); }
|
|
512
|
+
#${SHELL_ROOT_ID} .ge-modal-body { padding:18px; display:flex; flex-direction:column; gap:16px; }
|
|
513
|
+
#${SHELL_ROOT_ID} .ge-modal-title { margin:0; text-align:center; color:var(--card-acc, var(--shell-accent));
|
|
514
|
+
font-weight:800; letter-spacing:.04em; text-transform:uppercase; font-size:18px; }
|
|
515
|
+
#${SHELL_ROOT_ID} .ge-modal-text { margin:0; text-align:center; color:rgba(255,255,255,.85); font-size:14px; line-height:1.5; }
|
|
516
|
+
#${SHELL_ROOT_ID} .ge-sheet-grid { display:grid; gap:10px; }
|
|
517
|
+
#${SHELL_ROOT_ID} .ge-chip { pointer-events:auto; cursor:pointer; border:1px solid var(--shell-plaque-line);
|
|
518
|
+
border-radius:12px; background:rgba(255,255,255,.04); color:#fff; font-size:15px; font-weight:700;
|
|
519
|
+
font-variant-numeric:tabular-nums; padding:12px 8px; transition:background .12s ease, border-color .12s ease; }
|
|
520
|
+
#${SHELL_ROOT_ID} .ge-chip:hover { background:var(--shell-plaque-glass-hover); }
|
|
521
|
+
#${SHELL_ROOT_ID} .ge-chip.ge-on { border-color:var(--shell-accent); background:var(--shell-accent); color:#fff; }
|
|
522
|
+
/* full-bleed footer button(s), flush to the card's bottom edge (card clips the corners) */
|
|
523
|
+
#${SHELL_ROOT_ID} .ge-modal-actions { display:flex; }
|
|
524
|
+
#${SHELL_ROOT_ID} .ge-modal-actions > * { flex:1; }
|
|
525
|
+
#${SHELL_ROOT_ID} .ge-modal-btn { width:100%; border:none; padding:16px; font-size:15px; font-weight:800;
|
|
526
|
+
letter-spacing:.04em; text-transform:uppercase; cursor:pointer; pointer-events:auto; transition:filter .12s ease; }
|
|
527
|
+
#${SHELL_ROOT_ID} .ge-modal-btn:hover:not([disabled]) { filter:brightness(1.08); }
|
|
528
|
+
#${SHELL_ROOT_ID} .ge-modal-btn--accent { background:var(--card-acc, var(--shell-accent)); color:#fff; }
|
|
529
|
+
#${SHELL_ROOT_ID} .ge-modal-btn--ghost { background:var(--shell-plaque-glass-hover); color:#fff; }
|
|
530
|
+
/* replay summary — label/value rows, accented total-win row */
|
|
531
|
+
#${SHELL_ROOT_ID} .ge-replay-rows { display:flex; flex-direction:column; }
|
|
532
|
+
#${SHELL_ROOT_ID} .ge-replay-row { display:flex; justify-content:space-between; align-items:baseline; gap:16px; padding:11px 2px; }
|
|
533
|
+
#${SHELL_ROOT_ID} .ge-replay-row + .ge-replay-row { border-top:1px solid var(--shell-plaque-line); }
|
|
534
|
+
#${SHELL_ROOT_ID} .ge-replay-row span { color:var(--shell-plaque-label); text-transform:uppercase; letter-spacing:.07em; font-size:11px; font-weight:700; }
|
|
535
|
+
#${SHELL_ROOT_ID} .ge-replay-row b { color:#fff; font-weight:800; font-size:15px; font-variant-numeric:tabular-nums; }
|
|
536
|
+
#${SHELL_ROOT_ID} .ge-replay-total span { color:#fff; font-size:12px; }
|
|
537
|
+
#${SHELL_ROOT_ID} .ge-replay-total b { color:var(--shell-accent); font-size:19px; }
|
|
538
|
+
|
|
539
|
+
#${SHELL_ROOT_ID}.ge-shell-hidden { opacity:0; pointer-events:none; transition:opacity .25s ease; }
|
|
540
|
+
`;
|
|
541
|
+
|
|
542
|
+
/** The shared money formatter for every shell readout (balance, win, total win, bet, prices).
|
|
543
|
+
*
|
|
544
|
+
* `decimals` is the MAXIMUM number of fraction digits; `minDecimals` (defaults to `decimals`)
|
|
545
|
+
* is the MINIMUM. The value is rounded to `decimals`, then trailing zeros are trimmed down to
|
|
546
|
+
* — but never past — `minDecimals`. With `minDecimals` unset both bounds are equal, so the
|
|
547
|
+
* output is always exactly `decimals` places (the classic behaviour).
|
|
548
|
+
*
|
|
549
|
+
* Example with `decimals: 4, minDecimals: 2`:
|
|
550
|
+
* 0.0673 → 0,0673 0.0670 → 0,067 0.0600 → 0,06
|
|
551
|
+
* 0.0004 → 0,0004 0.0040 → 0,004 0.3000 → 0,30 0.0000 → 0,00
|
|
552
|
+
*/
|
|
553
|
+
function formatCurrency(value, currency) {
|
|
554
|
+
const decimals = currency.decimals ?? 2;
|
|
555
|
+
const minDecimals = Math.max(0, Math.min(decimals, currency.minDecimals ?? decimals));
|
|
556
|
+
const thousands = currency.separator?.thousands ?? '.';
|
|
557
|
+
const decimal = currency.separator?.decimal ?? ',';
|
|
558
|
+
const safe = Number.isFinite(value) ? value : 0;
|
|
559
|
+
const fixed = safe.toFixed(decimals); // round at the max precision, e.g. "0.0670"
|
|
560
|
+
const [intPart, rawFrac = ''] = fixed.split('.');
|
|
561
|
+
// trim trailing zeros, but keep at least `minDecimals` fraction digits
|
|
562
|
+
let fracPart = rawFrac;
|
|
563
|
+
if (fracPart.length > minDecimals) {
|
|
564
|
+
fracPart = fracPart.replace(/0+$/, '');
|
|
565
|
+
if (fracPart.length < minDecimals)
|
|
566
|
+
fracPart = fracPart.padEnd(minDecimals, '0');
|
|
567
|
+
}
|
|
568
|
+
const grouped = intPart.replace(/\B(?=(\d{3})+(?!\d))/g, thousands);
|
|
569
|
+
const number = fracPart.length ? `${grouped}${decimal}${fracPart}` : grouped;
|
|
570
|
+
return currency.position === 'left'
|
|
571
|
+
? `${currency.symbol}${number}`
|
|
572
|
+
: `${number} ${currency.symbol}`;
|
|
573
|
+
}
|
|
574
|
+
|
|
575
|
+
// Sharp monochrome icon set, traced from the brand sheet. Every glyph uses
|
|
576
|
+
// currentColor so the caller's color cascades (active states recolour via CSS);
|
|
577
|
+
// hollow shapes set fill-rule="evenodd". All return an inline <svg> sized 1em.
|
|
578
|
+
const SVGS = {
|
|
579
|
+
// sharp angular set, traced from the brand sheet — monochrome currentColor
|
|
580
|
+
// (glow via CSS drop-shadow). Hollow shapes rely on fill-rule="evenodd".
|
|
581
|
+
// hollow loop ring + two arrowheads (renders dark-on-bright inside SPIN)
|
|
582
|
+
spin: `<g fill="currentColor" fill-rule="evenodd"><path d="M17.92 4.68 L13.91 2.81 L10.55 1.6 L12.05 3.47 L11.95 3.56 L10.93 3.65 L9.25 4.12 L7.94 4.77 L7.01 5.42 L5.61 6.82 L4.58 8.41 L4.03 9.53 L3.28 11.67 L2.91 13.91 L4.49 16.52 L4.68 16.62 L6.54 14.94 L6.17 14.01 L6.08 13.17 L5.98 13.07 L5.98 11.49 L6.08 10.93 L6.45 9.71 L6.82 8.97 L7.48 8.04 L8.22 7.29 L9.25 6.54 L9.99 6.17 L11.21 5.8 L11.77 5.7 L13.73 5.7 L13.82 5.8 L14.57 5.89 L15.87 6.45 L16.06 6.45 L17.92 4.86Z"/><path d="M19.6 7.66 L19.42 7.57 L17.83 9.16 L18.11 9.9 L18.2 10.74 L18.3 10.83 L18.3 12.7 L18.2 12.79 L18.11 13.54 L17.46 15.03 L16.9 15.87 L15.87 16.9 L14.94 17.55 L14.1 17.92 L13.17 18.2 L12.05 18.39 L11.02 18.39 L10.93 18.3 L9.9 18.2 L9.06 17.92 L8.41 17.55 L8.22 17.55 L6.73 18.76 L6.26 19.32 L11.21 21.56 L12.7 22.03 L13.45 22.4 L13.73 22.4 L12.23 20.53 L12.33 20.44 L13.54 20.35 L15.31 19.79 L16.8 18.95 L17.64 18.3 L18.76 17.08 L19.51 15.96 L20.16 14.66 L20.25 14.19 L20.53 13.63 L20.72 12.98 L20.72 12.61 L20.91 12.14 L21.09 10.37 L19.97 8.5Z"/></g>`,
|
|
583
|
+
turbo: `<g fill="currentColor" fill-rule="evenodd"><path d="M19.16 1.71 L16.47 1.71 L16.36 1.6 L13.23 1.6 L12.78 1.71 L11.78 3.39 L10.55 5.85 L10.32 6.07 L8.31 9.99 L8.09 10.21 L7.19 12.11 L12 12.34 L14.46 9.88 L14.24 9.65 L12.89 9.54 L12.89 9.2 L17.03 4.4Z"/><path d="M19.83 6.97 L18.93 7.64 L16.36 9.99 L9.65 16.47 L4.17 21.62 L4.17 22.4 L5.4 21.39 L13.34 13.9 L13.45 14.12 L10.43 20.16 L10.21 20.83 L9.65 21.95 L9.76 21.95 L19.27 10.32 L19.27 10.21 L17.59 10.1 L17.48 9.88 L18.71 8.53Z"/></g>`,
|
|
584
|
+
// bolt with 1/2/3 speed lines — escalating turbo level
|
|
585
|
+
turbo1: `<g fill="currentColor" fill-rule="evenodd"><path d="M21.82 1.6 L21.59 1.72 L21.01 1.72 L20.44 1.95 L18.7 2.29 L18.12 2.29 L18.01 2.41 L16.39 2.64 L14.77 3.1 L13.73 5.41 L13.73 5.64 L13.27 6.45 L13.27 6.68 L12.81 7.49 L12.81 7.72 L12.35 8.53 L12.35 8.76 L11.77 9.8 L11.42 10.84 L10.15 13.5 L10.15 13.73 L14.54 13.73 L14.66 13.85 L13.39 16.16 L12.81 16.97 L11.54 19.4 L11.31 19.63 L11.08 20.2 L10.04 21.82 L9.8 22.4 L9.92 22.4 L20.9 11.19 L22.05 10.15 L22.05 10.04 L16.62 10.15 L16.51 10.27 L15.7 10.27 L15.58 10.15 L15.81 9.69 L16.28 9.23 L21.48 2.29Z"/><path d="M8.53 12.46 L7.72 12.58 L7.26 12.81 L6.92 12.81 L6.45 13.04 L6.11 13.04 L5.64 13.27 L3.68 13.73 L1.95 14.31 L1.95 14.43 L7.61 14.43Z"/></g>`,
|
|
586
|
+
turbo2: `<g fill="currentColor" fill-rule="evenodd"><path d="M22.4 2.68 L18.38 3.61 L15.4 4.43 L14.68 5.98 L13.96 7.93 L13.65 8.45 L13.65 8.65 L13.34 9.17 L13.34 9.37 L13.03 9.89 L13.03 10.1 L12.72 10.61 L12.72 10.82 L12.41 11.33 L12.41 11.54 L12.1 12.05 L12.1 12.26 L11.79 12.77 L11.49 13.7 L15.3 13.7 L15.5 13.9 L14.47 15.55 L14.16 16.27 L13.96 16.48 L11.28 21.32 L11.9 20.91 L20.65 11.95 L22.09 10.61 L22.09 10.51 L20.55 10.51 L20.44 10.61 L16.32 10.71 L16.22 10.51 L19.83 6.08Z"/><path d="M10.04 15.04 L9.43 15.14 L8.81 14.93 L7.98 14.93 L7.88 15.04 L6.34 15.14 L6.23 15.24 L5.62 15.24 L4.28 15.55 L2.84 15.76 L1.6 16.17 L10.04 16.17Z"/><path d="M10.56 9.99 L9.43 10.1 L9.32 10.2 L7.37 10.51 L6.85 10.71 L6.44 10.71 L5.51 11.02 L3.04 11.54 L2.94 11.74 L5.1 11.85 L5.2 11.95 L6.65 11.95 L6.75 12.05 L9.01 12.05 L9.12 12.15 L9.73 12.15 L10.56 10.3Z"/></g>`,
|
|
587
|
+
turbo3: `<g fill="currentColor" fill-rule="evenodd"><path d="M22.4 3 L22 3 L20.3 3.5 L19.9 3.5 L19.5 3.7 L19.1 3.7 L15.9 4.6 L13.9 9.1 L13.9 9.3 L13.6 10 L13.2 10.7 L13.2 10.9 L12.6 12.1 L12.3 13 L12 13.6 L15.8 13.6 L15.9 13.8 L14.4 16.3 L13.7 17.7 L13.2 18.4 L12.8 19.1 L12.5 19.8 L12 20.5 L11.9 21 L22.2 10.6 L22.2 10.5 L16.8 10.6 L16.7 10.4 L22.3 3.3Z"/><path d="M11 11.3 L9.9 11.3 L9.8 11.4 L7.8 11.6 L6 11.9 L5.4 12.1 L3.9 12.3 L3.1 12.5 L3.1 12.6 L5.6 12.7 L5.7 12.8 L7.9 12.8 L8 12.9 L10.4 12.9 L11 11.6Z"/><path d="M12.7 7.3 L12 7.3 L11.9 7.4 L10.7 7.5 L10.1 7.7 L9.6 7.7 L7.2 8.2 L6.7 8.4 L5.5 8.6 L5.6 8.8 L7 8.8 L7.1 8.9 L8.5 8.9 L8.6 9 L12 9.1Z"/><path d="M10.9 15.4 L10.3 15.7 L10 15.7 L9.1 15.4 L7.8 15.5 L7.7 15.6 L6.9 15.6 L6.8 15.7 L6 15.7 L5.9 15.8 L4.1 16 L3.5 16.2 L2.5 16.3 L1.6 16.6 L2.6 16.6 L2.7 16.7 L10.9 16.7Z"/></g>`,
|
|
588
|
+
autoplay: `<g fill="currentColor" fill-rule="evenodd"><path d="M19.48 5.36 L15.02 1.6 L14.82 1.6 L15.62 3.28 L15.52 3.78 L14.82 3.78 L14.72 3.88 L12.64 3.88 L12.54 3.78 L5.71 3.78 L3.23 6.35 L3.23 16.26 L3.33 16.26 L4.82 14.48 L4.82 12.5 L4.72 12.4 L4.72 7.25 L4.82 7.05 L6.4 5.56 L8.29 5.56 L8.38 5.46 L19.48 5.46Z"/><path d="M9.87 8.83 L9.87 15.27 L9.97 15.27 L11.26 14.38 L15.12 12.1 L15.12 12 L11.16 9.62 L10.66 9.23Z"/><path d="M20.67 7.94 L20.17 8.34 L19.18 9.52 L19.18 16.85 L17.5 18.44 L17.1 18.54 L4.42 18.54 L4.42 18.64 L7.3 21.21 L8.78 22.4 L8.78 22 L8.09 20.42 L8.19 20.22 L18.29 20.22 L20.77 17.65 L20.67 17.35Z"/></g>`,
|
|
589
|
+
// hollow square — stop autoplay / halt
|
|
590
|
+
stop: `<g fill="currentColor" fill-rule="evenodd"><path d="M22.4 3.04 L21.1 4.82 L21.1 9.47 L21.03 9.54 L21.03 12.41 L21.1 12.96 L21.03 18.84 L21.1 19.18 L20.35 20.01 L19.39 20.89 L17.27 20.89 L16.72 20.96 L4.75 20.96 L4.68 20.89 L3.04 22.33 L4.13 22.33 L4.2 22.4 L6.66 22.4 L6.73 22.33 L9.13 22.33 L9.19 22.4 L20.48 22.4 L22.4 20.48Z"/><path d="M20.96 1.6 L3.38 1.6 L1.6 3.45 L1.6 20.96 L2.9 19.12 L2.9 4.75 L4.54 3.11 L19.12 3.11 L19.46 2.97Z"/></g>`,
|
|
591
|
+
menu: `<g fill="currentColor" fill-rule="evenodd"><path d="M1.6 13.52 L19.61 13.52 L22.27 10.73 L22.4 10.35 L4.39 10.35 L2.36 12.51Z"/><path d="M1.6 6.04 L19.61 6.04 L22.15 3.38 L22.4 2.87 L4.39 2.87 L2.36 5.02Z"/><path d="M1.6 21 L3.25 21 L3.38 21.13 L4.9 21.13 L5.02 21 L19.61 21 L22.4 17.96 L4.52 17.83 L4.26 17.96 L2.36 19.99Z"/></g>`,
|
|
592
|
+
betUp: `<g fill="currentColor" fill-rule="evenodd"><path d="M21.01 15.43 L19.18 13.07 L14.68 6.75 L12.32 3.64 L12 3.42 L8.57 7.93 L6.42 11.04 L2.89 15.75 L2.78 16.07 L12 8.14 L12.21 8.14 L20.68 15.43Z"/><path d="M22.4 20.26 L20.04 17.79 L12.21 10.28 L12 10.28 L6.53 15.43 L6.42 15.65 L1.81 20.15 L1.6 20.58 L12 14.04 L12.21 14.04 L12.75 14.47 L18.11 17.68 L18.65 18.11 L19.51 18.54 L22.29 20.36Z"/></g>`,
|
|
593
|
+
betDown: `<g fill="currentColor" fill-rule="evenodd"><path d="M1.6 2.74 L1.84 3.22 L5.57 7.43 L11.7 14.04 L12.06 14.28 L12.42 14.04 L18.79 7.19 L22.4 2.98 L22.28 2.86 L18.55 5.51 L12.06 10.44 L5.81 5.63Z"/><path d="M20.84 9.35 L18.07 11.52 L12.18 16.57 L11.94 16.57 L4.49 10.2 L3.52 9.48 L3.4 9.6 L8.57 16.57 L8.81 17.05 L10.26 18.85 L10.5 19.33 L11.34 20.3 L11.58 20.78 L12.18 21.26 L18.43 12.84Z"/></g>`,
|
|
594
|
+
minus: `<g fill="currentColor" fill-rule="evenodd"><path d="M1.6 13.26 L12.42 13.26 L12.5 13.18 L19.81 13.26 L22.4 10.82 L4.27 10.74Z"/></g>`,
|
|
595
|
+
plus: `<g fill="currentColor" fill-rule="evenodd"><path d="M13.16 1.6 L12.26 3.17 L10.77 5.42 L10.77 10.73 L10.69 10.8 L4.33 10.8 L1.79 13.2 L10.69 13.2 L10.77 13.27 L10.77 22.4 L12.41 19.71 L13.16 18.66 L13.16 13.27 L13.23 13.2 L19.59 13.2 L22.21 10.8 L13.23 10.8 L13.16 10.73Z"/></g>`,
|
|
596
|
+
gift: `<rect x="4" y="9" width="16" height="11" rx="2" fill="currentColor"/><path d="M9 9a2.5 2.5 0 1 1 3-3 2.5 2.5 0 1 1 3 3z" fill="currentColor"/><rect x="11" y="9" width="2" height="11" fill="rgba(0,0,0,.35)"/>`,
|
|
597
|
+
info: `<g fill="currentColor" fill-rule="evenodd"><path d="M14.22 7.78 L11.13 7.86 L9.4 9.21 L9.17 9.29 L10.15 9.29 L10.23 9.59 L9.32 13.73 L8.87 17.12 L8.57 18.78 L8.5 19.69 L8.27 20.74 L8.12 22.32 L8.04 22.4 L13.62 17.95 L11.96 17.95 L11.89 17.58 L12.41 15.69 L12.49 15.01 L12.87 13.73 L12.87 13.43 L13.24 12.15 L13.77 9.97 L14 8.68 L14.22 8.08Z"/><path d="M15.96 1.6 L12.94 1.6 L11.74 3.18 L11.51 3.63 L10.08 5.67 L12.79 5.59 L14.37 3.71Z"/></g>`,
|
|
598
|
+
soundOn: `<g fill="currentColor" fill-rule="evenodd"><path d="M16.61 6.68 L17.16 8.4 L17.4 9.97 L17.4 13.88 L17.32 13.95 L17.16 15.44 L16.61 17.16 L18.88 13.72 L18.88 12.86 L18.96 12.78 L18.88 10.12Z"/><path d="M19.27 4.34 L19.58 4.88 L19.66 5.28 L20.05 6.14 L20.6 7.93 L20.91 9.5 L20.91 10.44 L20.99 10.51 L20.99 13.25 L20.91 13.33 L20.91 14.19 L20.76 15.13 L20.05 17.63 L19.27 19.43 L20.29 18.02 L21.15 16.61 L22.4 14.19 L22.4 9.58 L21.31 7.39 L20.29 5.74Z"/><path d="M13.17 1.76 L10.28 4.34 L6.29 8.17 L1.6 8.25 L1.6 15.6 L2.15 15.75 L6.29 16.3 L9.97 19.58 L13.17 22.24Z"/><path d="M11.69 5.59 L11.92 5.67 L11.92 19.04 L11.69 19.12 L7.07 14.82 L4.57 14.5 L4.42 14.35 L4.42 10.51 L4.57 10.36 L7.07 10.28Z"/></g>`,
|
|
599
|
+
soundOff: `<g fill="currentColor" fill-rule="evenodd"><path d="M13.68 13.18 L12.53 14.4 L12.53 18.44 L12.3 18.51 L10.4 16.76 L9.33 17.83 L12.84 20.88 L13.75 21.56 L13.75 13.18Z"/><path d="M17.71 8.69 L17.49 8.91 L17.56 9.9 L17.64 9.98 L17.64 13.1 L17.33 14.7 L17.03 15.39 L18.78 12.88 L18.78 11.66 L18.86 11.58 L18.78 10.29Z"/><path d="M19.47 6.63 L19.85 7.47 L20.46 9.6 L20.53 10.13 L20.53 13.03 L20.08 15.01 L19.31 16.91 L20.61 14.93 L21.3 13.49 L21.52 13.18 L21.52 9.9 L20.76 8.53Z"/><path d="M20.91 2.21 L16.5 6.63 L13.98 9.3 L13.75 9.22 L13.75 1.6 L10.86 4.19 L7.05 7.85 L2.4 7.92 L2.48 8 L2.48 15.16 L6.51 15.7 L7.73 14.32 L5.3 14.02 L5.22 13.94 L5.22 10.13 L5.75 9.98 L7.81 9.9 L12.3 5.33 L12.53 5.41 L12.53 10.9 L10.32 13.26 L6.9 17.14 L2.55 22.4 L6.9 18.36 L9.79 15.47 L13.52 11.5 L18.25 5.71Z"/></g>`,
|
|
600
|
+
close: `<g fill="currentColor" fill-rule="evenodd"><path d="M22.16 1.6 L21.3 2.09 L20.44 2.82 L16.89 5.27 L12.12 10.29 L10.53 8.82 L7.35 5.39 L2.21 2.09 L5.88 7.23 L8.21 9.55 L10.41 12 L5.76 16.89 L2.33 21.54 L1.84 22.4 L2.58 22.03 L2.95 21.67 L4.17 20.93 L5.88 19.59 L7.23 18.73 L12 13.71 L12.12 13.71 L17.02 18.73 L22.16 22.16 L22.16 21.91 L20.2 19.46 L18.48 17.02 L13.71 12.12 L13.71 11.88 L18.48 6.86 L20.32 4.17 L21.67 2.46Z"/></g>`,
|
|
601
|
+
back: `<path d="M15 6l-6 6 6 6" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>`,
|
|
602
|
+
chevronRight: `<path d="M9 6l6 6-6 6" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>`,
|
|
603
|
+
star: `<path d="M12 3l2.6 5.6 6.1.7-4.5 4.2 1.2 6L12 16.9 6.6 19.5l1.2-6L3.3 9.3l6.1-.7z" fill="currentColor"/>`,
|
|
604
|
+
// volatility bolt (buy-bonus cards) — supplied art, scaled from its 1254 viewBox into 24×24
|
|
605
|
+
lightning: `<path transform="scale(0.019139)" d="M747,205L433,629L622,633L497,986L801,550L614,547Z" fill="currentColor"/>`,
|
|
606
|
+
};
|
|
607
|
+
/** Inline SVG string for an icon, sized to 1em (scale via font-size/width). */
|
|
608
|
+
function icon(name) {
|
|
609
|
+
return `<svg viewBox="0 0 24 24" width="1em" height="1em" aria-hidden="true">${SVGS[name]}</svg>`;
|
|
610
|
+
}
|
|
611
|
+
|
|
612
|
+
/** Render a (possibly socialised) two-word label across two lines — the BUY BONUS badge.
|
|
613
|
+
* Shared so the bottom-bar button and the Game-info control legend break identically. */
|
|
614
|
+
function twoLine(label) {
|
|
615
|
+
return label.split(/\s+/).join('<br>');
|
|
616
|
+
}
|
|
617
|
+
/** A centred CARD modal — frosted backdrop + opaque card with an accent title heading and an
|
|
618
|
+
* overlay ✕ in the top-right. The shared chrome for every centred modal (buy-bonus confirm,
|
|
619
|
+
* bet, autoplay, generic openModal). Append content to `body`; append full-bleed footer
|
|
620
|
+
* button(s) directly to `card`. Closes only via the ✕ / footer buttons — the backdrop does NOT. */
|
|
621
|
+
function createCardModal(opts) {
|
|
622
|
+
const root = document.createElement('div');
|
|
623
|
+
root.className = 'ge-sheet';
|
|
624
|
+
root.dataset.ge = opts.ge;
|
|
625
|
+
if (opts.blur != null)
|
|
626
|
+
root.style.setProperty('--ge-sheet-blur', `${opts.blur}px`);
|
|
627
|
+
const card = document.createElement('div');
|
|
628
|
+
card.className = 'ge-modal-card';
|
|
629
|
+
if (opts.accent)
|
|
630
|
+
card.style.setProperty('--card-acc', opts.accent);
|
|
631
|
+
const body = document.createElement('div');
|
|
632
|
+
body.className = 'ge-modal-body';
|
|
633
|
+
const h = document.createElement('h4');
|
|
634
|
+
h.className = 'ge-modal-title';
|
|
635
|
+
h.textContent = opts.title;
|
|
636
|
+
body.appendChild(h);
|
|
637
|
+
card.appendChild(body);
|
|
638
|
+
root.appendChild(card);
|
|
639
|
+
// ✕ lives on the overlay itself (top-right of the screen), not on the card
|
|
640
|
+
if (opts.closable !== false) {
|
|
641
|
+
const close = document.createElement('button');
|
|
642
|
+
close.className = 'ge-modal-close';
|
|
643
|
+
close.dataset.ge = 'modal-close';
|
|
644
|
+
close.setAttribute('aria-label', 'Close');
|
|
645
|
+
close.innerHTML = icon('close');
|
|
646
|
+
close.addEventListener('click', opts.onClose);
|
|
647
|
+
root.appendChild(close);
|
|
648
|
+
}
|
|
649
|
+
return { root, card, body };
|
|
650
|
+
}
|
|
651
|
+
/** Full-screen overlay. Returns { root, body }; append content to body. */
|
|
652
|
+
function createOverlay(opts) {
|
|
653
|
+
const root = document.createElement('div');
|
|
654
|
+
root.className = 'ge-shell-overlay';
|
|
655
|
+
const head = document.createElement('div');
|
|
656
|
+
head.className = 'ge-ov-head';
|
|
657
|
+
if (opts.onBack) {
|
|
658
|
+
const back = document.createElement('button');
|
|
659
|
+
back.className = 'ge-ov-nav';
|
|
660
|
+
back.dataset.ge = 'info-back';
|
|
661
|
+
back.innerHTML = icon('back');
|
|
662
|
+
back.addEventListener('click', opts.onBack);
|
|
663
|
+
head.appendChild(back);
|
|
664
|
+
}
|
|
665
|
+
else {
|
|
666
|
+
// reserve a slot equal to the close button so the title stays centred
|
|
667
|
+
const spacer = document.createElement('div');
|
|
668
|
+
spacer.className = 'ge-ov-spacer';
|
|
669
|
+
head.appendChild(spacer);
|
|
670
|
+
}
|
|
671
|
+
const h = document.createElement('h4');
|
|
672
|
+
h.className = 'ge-ov-title';
|
|
673
|
+
h.textContent = opts.title;
|
|
674
|
+
head.appendChild(h);
|
|
675
|
+
const close = document.createElement('button');
|
|
676
|
+
close.className = 'ge-ov-nav';
|
|
677
|
+
close.setAttribute('aria-label', 'Close');
|
|
678
|
+
close.innerHTML = icon('close');
|
|
679
|
+
close.addEventListener('click', opts.onClose);
|
|
680
|
+
head.appendChild(close);
|
|
681
|
+
// Header stays fixed; only this wrapper scrolls — the X never scrolls away,
|
|
682
|
+
// and vh-clamped padding keeps it usable on small popouts (e.g. 400×225).
|
|
683
|
+
const scroll = document.createElement('div');
|
|
684
|
+
scroll.className = 'ge-ov-scroll';
|
|
685
|
+
const body = document.createElement('div');
|
|
686
|
+
body.className = 'ge-ov-body';
|
|
687
|
+
scroll.appendChild(body);
|
|
688
|
+
root.append(head, scroll);
|
|
689
|
+
return { root, body };
|
|
690
|
+
}
|
|
691
|
+
|
|
692
|
+
/** A floating labelled money readout (balance/win/bet). */
|
|
693
|
+
function readout(ge, label, value) {
|
|
694
|
+
const el = document.createElement('div');
|
|
695
|
+
el.dataset.ge = ge;
|
|
696
|
+
el.className = `ge-rd ge-${ge}`;
|
|
697
|
+
el.innerHTML = `<span class="ge-lbl">${label}</span>`;
|
|
698
|
+
el.append(document.createTextNode(value));
|
|
699
|
+
return el;
|
|
700
|
+
}
|
|
701
|
+
// Resting icon is turbo1 (1 line, grey via .ge-iconbtn); engaging turbo adds the
|
|
702
|
+
// .ge-active class which paints it white. Higher levels add more speed lines.
|
|
703
|
+
// level: 0 → turbo1 (grey), 1 → turbo1 (white), 2 → turbo2, 3 → turbo3.
|
|
704
|
+
function turboIcon(level) {
|
|
705
|
+
return ['turbo1', 'turbo1', 'turbo2', 'turbo3'][Math.max(0, Math.min(3, level))];
|
|
706
|
+
}
|
|
707
|
+
/** A borderless icon button. */
|
|
708
|
+
function iconBtn(ge, name, onClick, active = false) {
|
|
709
|
+
const b = document.createElement('button');
|
|
710
|
+
b.className = `ge-iconbtn${active ? ' ge-active' : ''}`;
|
|
711
|
+
b.dataset.ge = ge;
|
|
712
|
+
b.innerHTML = icon(name);
|
|
713
|
+
b.addEventListener('click', () => { if (!b.disabled)
|
|
714
|
+
onClick(); });
|
|
715
|
+
return b;
|
|
716
|
+
}
|
|
717
|
+
function renderBottomBar(shell) {
|
|
718
|
+
const { state, config } = shell;
|
|
719
|
+
const fmt = (n) => formatCurrency(n, config.currency);
|
|
720
|
+
const mobile = shell.layout === 'mobile';
|
|
721
|
+
const bar = document.createElement('div');
|
|
722
|
+
bar.className = 'ge-shell-bottom';
|
|
723
|
+
bar.dataset.geMode = state.mode;
|
|
724
|
+
// menu icon button (always)
|
|
725
|
+
const menu = iconBtn('menu', 'menu', () => shell.openMenu());
|
|
726
|
+
// All three modes share the base plaque layout. FS/replay hide the controls that
|
|
727
|
+
// don't apply; FS puts the spins counter in the centre pill (where WIN normally is).
|
|
728
|
+
const isBase = state.mode === 'base';
|
|
729
|
+
const isFS = state.mode === 'freeSpins';
|
|
730
|
+
const balance = readout('balance', shell.t('Balance'), fmt(state.balance));
|
|
731
|
+
// With a feature active (e.g. Ante) the BET readout shows the effective stake, tinted with
|
|
732
|
+
// the feature accent; the base state.bet is unchanged and returns once the feature is off.
|
|
733
|
+
const feature = state.activeFeature;
|
|
734
|
+
const betShown = feature ? state.bet * feature.priceMultiplier : state.bet;
|
|
735
|
+
const betValue = readout('bet-value', shell.t('Bet'), fmt(betShown));
|
|
736
|
+
if (feature) {
|
|
737
|
+
betValue.classList.add('ge-bet-feature');
|
|
738
|
+
betValue.style.color = effectiveAccent(feature);
|
|
739
|
+
}
|
|
740
|
+
const turbo = config.features.turbo > 0
|
|
741
|
+
? iconBtn('turbo', turboIcon(state.turbo), () => onTurbo(shell), state.turbo > 0) : null;
|
|
742
|
+
// interactive controls — base mode only
|
|
743
|
+
let betDown = null, betUp = null;
|
|
744
|
+
let spin = null, auto = null, buy = null;
|
|
745
|
+
if (isBase) {
|
|
746
|
+
betDown = iconBtn('bet-down', 'minus', () => onBet(shell, -1));
|
|
747
|
+
betUp = iconBtn('bet-up', 'plus', () => onBet(shell, 1));
|
|
748
|
+
betValue.classList.add('ge-betbtn'); // tap the stake → bet picker
|
|
749
|
+
betValue.addEventListener('click', () => { if (!betLocked(shell))
|
|
750
|
+
shell.openBetPicker(); });
|
|
751
|
+
spin = spinButton(shell);
|
|
752
|
+
auto = config.features.autoplay ? autoButton(shell) : null;
|
|
753
|
+
buy = config.features.buyBonus !== false ? buyBtn(shell) : null;
|
|
754
|
+
}
|
|
755
|
+
const winEl = state.win > 0 ? readout('win', shell.t('Win'), fmt(state.win)) : null;
|
|
756
|
+
// FS readouts — the spins counter plus the accumulated/last win for the round.
|
|
757
|
+
const fsCounter = isFS ? readout('fs-counter', shell.t('Free spins'), `${state.freeSpins.current} / ${state.freeSpins.total}`) : null;
|
|
758
|
+
const fsTotalWin = isFS ? readout('fs-totalwin', shell.t('Total win'), fmt(state.freeSpins.totalWin)) : null;
|
|
759
|
+
const fsLastWin = isFS ? readout('fs-lastwin', shell.t('Last win'), fmt(state.freeSpins.lastWin)) : null;
|
|
760
|
+
if (mobile) {
|
|
761
|
+
// rows: [balance · win/(FS last+total)] · [menu · auto · (spin | FS counter) · turbo · buy] · [− bet +]
|
|
762
|
+
bar.appendChild(plaque('ge-m-top ge-pl ge-pl-glass', compact([balance, winEl, fsLastWin, fsTotalWin])));
|
|
763
|
+
const center = isBase ? spin : fsCounter;
|
|
764
|
+
bar.appendChild(plaque('ge-m-controls ge-pl-dark', compact([menu, auto, center, turbo, buy])));
|
|
765
|
+
bar.appendChild(plaque('ge-m-bet ge-pl ge-pl-dark', compact([betDown, betValue, betUp])));
|
|
766
|
+
}
|
|
767
|
+
else {
|
|
768
|
+
// LEFT: [menu] ⊐ BUY BONUS coin ⊏ [balance]
|
|
769
|
+
const menuPlaque = plaque('ge-pl ge-pl-dark ge-pl-menu', [menu]);
|
|
770
|
+
const balPlaque = plaque('ge-pl ge-pl-glass ge-pl-bal', [balance]);
|
|
771
|
+
const left = zone('ge-zone-left ge-zone-plaques', ...compact([menuPlaque, buy, balPlaque]));
|
|
772
|
+
// RIGHT: [bet (+ step)] · |divider| · [auto · SPIN · turbo]
|
|
773
|
+
const betKids = [betValue];
|
|
774
|
+
if (betUp && betDown) {
|
|
775
|
+
const step = document.createElement('div');
|
|
776
|
+
step.className = 'ge-betstep';
|
|
777
|
+
step.append(betUp, betDown);
|
|
778
|
+
betKids.push(step);
|
|
779
|
+
}
|
|
780
|
+
const betPlaque = plaque('ge-pl ge-pl-dark ge-pl-bet', betKids);
|
|
781
|
+
const divider = document.createElement('div');
|
|
782
|
+
divider.className = 'ge-pl-divider';
|
|
783
|
+
const spinWrap = document.createElement('div');
|
|
784
|
+
spinWrap.className = 'ge-spinwrap ge-pl-dark';
|
|
785
|
+
spinWrap.append(...compact([auto, spin, turbo]));
|
|
786
|
+
const right = zone('ge-zone-right ge-zone-plaques', betPlaque, divider, spinWrap);
|
|
787
|
+
// MIDDLE: FS → last win · counter · total win plaque; base/replay → WIN pill (lifts on overflow)
|
|
788
|
+
let middle = null;
|
|
789
|
+
if (isFS)
|
|
790
|
+
middle = plaque('ge-pl ge-pl-glass ge-fscount', compact([fsLastWin, fsCounter, fsTotalWin]));
|
|
791
|
+
else if (winEl) {
|
|
792
|
+
winEl.classList.add('ge-winpill');
|
|
793
|
+
middle = winEl;
|
|
794
|
+
}
|
|
795
|
+
bar.append(...compact([left, middle, right]));
|
|
796
|
+
}
|
|
797
|
+
applyBusy(shell, bar);
|
|
798
|
+
return bar;
|
|
799
|
+
}
|
|
800
|
+
function zone(cls, ...children) {
|
|
801
|
+
const z = document.createElement('div');
|
|
802
|
+
z.className = `ge-zone ${cls}`;
|
|
803
|
+
z.append(...children);
|
|
804
|
+
return z;
|
|
805
|
+
}
|
|
806
|
+
/** A rounded background panel ("plaque") grouping a set of controls. */
|
|
807
|
+
function plaque(cls, children) {
|
|
808
|
+
const d = document.createElement('div');
|
|
809
|
+
d.className = cls;
|
|
810
|
+
d.append(...children);
|
|
811
|
+
return d;
|
|
812
|
+
}
|
|
813
|
+
function compact(items) { return items.filter((x) => x !== null); }
|
|
814
|
+
function buyBtn(shell) {
|
|
815
|
+
const buy = document.createElement('button');
|
|
816
|
+
buy.className = 'ge-shell-buybonus';
|
|
817
|
+
buy.dataset.ge = 'buybonus';
|
|
818
|
+
const feature = shell.state.activeFeature;
|
|
819
|
+
if (feature) {
|
|
820
|
+
// A feature is active → this button turns into DISABLE (tinted with the feature accent).
|
|
821
|
+
const accent = effectiveAccent(feature);
|
|
822
|
+
buy.classList.add('ge-disable');
|
|
823
|
+
buy.innerHTML = `<span>${shell.t('DISABLE')}</span>`;
|
|
824
|
+
buy.style.background = accent;
|
|
825
|
+
buy.style.color = contrastText(accent);
|
|
826
|
+
buy.addEventListener('click', () => { if (!buy.disabled)
|
|
827
|
+
shell.deactivateFeature(); });
|
|
828
|
+
}
|
|
829
|
+
else {
|
|
830
|
+
buy.innerHTML = `<span>${twoLine(shell.t('BUY BONUS'))}</span>`;
|
|
831
|
+
buy.addEventListener('click', () => { if (!buy.disabled)
|
|
832
|
+
shell.openBuyBonus(); });
|
|
833
|
+
}
|
|
834
|
+
return buy;
|
|
835
|
+
}
|
|
836
|
+
function onBet(shell, dir) {
|
|
837
|
+
if (shell.state.busy)
|
|
838
|
+
return;
|
|
839
|
+
const next = stepBet(shell.state, dir);
|
|
840
|
+
if (next !== shell.state.bet) {
|
|
841
|
+
shell.state.bet = next;
|
|
842
|
+
shell.emit('betChange', next);
|
|
843
|
+
shell.render();
|
|
844
|
+
}
|
|
845
|
+
}
|
|
846
|
+
function onTurbo(shell) {
|
|
847
|
+
const next = nextTurbo(shell.state.turbo, shell.config.features.turbo);
|
|
848
|
+
shell.state.turbo = next;
|
|
849
|
+
shell.emit('turboChange', next);
|
|
850
|
+
shell.render();
|
|
851
|
+
}
|
|
852
|
+
function betLocked(shell) {
|
|
853
|
+
return shell.state.busy || shell.state.autoplay.active;
|
|
854
|
+
}
|
|
855
|
+
/** SPIN disc — rotates while busy; becomes a STOP + countdown while autoplay runs. */
|
|
856
|
+
function spinButton(shell) {
|
|
857
|
+
const { state } = shell;
|
|
858
|
+
const sp = document.createElement('button');
|
|
859
|
+
sp.className = 'ge-shell-spin';
|
|
860
|
+
sp.dataset.ge = 'spin';
|
|
861
|
+
if (state.autoplay.active) {
|
|
862
|
+
sp.classList.add('ge-stop');
|
|
863
|
+
const rem = state.autoplay.remaining;
|
|
864
|
+
const label = Number.isFinite(rem) ? String(rem) : '∞';
|
|
865
|
+
sp.innerHTML = `<span class="ge-spin-stop">${icon('stop')}</span><span class="ge-spin-count">${label}</span>`;
|
|
866
|
+
sp.addEventListener('click', () => { if (!sp.disabled)
|
|
867
|
+
stopAutoplay(shell); });
|
|
868
|
+
}
|
|
869
|
+
else {
|
|
870
|
+
sp.innerHTML = icon('spin');
|
|
871
|
+
if (state.busy)
|
|
872
|
+
sp.classList.add('ge-spinning');
|
|
873
|
+
sp.addEventListener('click', () => { if (!sp.disabled)
|
|
874
|
+
shell.emit('spin'); });
|
|
875
|
+
}
|
|
876
|
+
return sp;
|
|
877
|
+
}
|
|
878
|
+
/** Autoplay icon button — opens the count picker; glows accent while running. */
|
|
879
|
+
function autoButton(shell) {
|
|
880
|
+
const active = shell.state.autoplay.active;
|
|
881
|
+
const b = iconBtn('autoplay', 'autoplay', () => onAutoplay(shell), active);
|
|
882
|
+
if (active)
|
|
883
|
+
b.classList.add('ge-glow');
|
|
884
|
+
return b;
|
|
885
|
+
}
|
|
886
|
+
function onAutoplay(shell) {
|
|
887
|
+
if (shell.state.autoplay.active)
|
|
888
|
+
stopAutoplay(shell);
|
|
889
|
+
else
|
|
890
|
+
shell.openAutoplayPicker();
|
|
891
|
+
}
|
|
892
|
+
function stopAutoplay(shell) {
|
|
893
|
+
shell.state.autoplay = { active: false, remaining: 0 };
|
|
894
|
+
shell.emit('autoplayStop');
|
|
895
|
+
shell.render();
|
|
896
|
+
}
|
|
897
|
+
function applyBusy(shell, bar) {
|
|
898
|
+
const { busy } = shell.state;
|
|
899
|
+
const auto = shell.state.autoplay.active;
|
|
900
|
+
const lockBet = busy || auto;
|
|
901
|
+
const disable = (ge, off) => {
|
|
902
|
+
const el = bar.querySelector(`[data-ge="${ge}"]`);
|
|
903
|
+
if (el)
|
|
904
|
+
el.disabled = off;
|
|
905
|
+
};
|
|
906
|
+
// also disable the stepper that's already at the end of the bet range
|
|
907
|
+
const i = shell.state.availableBets.indexOf(shell.state.bet);
|
|
908
|
+
disable('bet-up', lockBet || i >= shell.state.availableBets.length - 1);
|
|
909
|
+
disable('bet-down', lockBet || i <= 0);
|
|
910
|
+
disable('spin', busy && !auto); // keep the STOP disc clickable through autoplay
|
|
911
|
+
disable('autoplay', busy && !auto); // keep autoplay (stop) clickable through autoplay
|
|
912
|
+
const betVal = bar.querySelector('[data-ge="bet-value"]');
|
|
913
|
+
if (betVal)
|
|
914
|
+
betVal.classList.toggle('ge-disabled', lockBet);
|
|
915
|
+
const buy = bar.querySelector('[data-ge="buybonus"]');
|
|
916
|
+
// disabled for the whole autoplay run (not just per-spin busy) so it doesn't flicker/pulse
|
|
917
|
+
if (buy)
|
|
918
|
+
buy.disabled = busy || auto || !shell.state.buyBonusEnabled;
|
|
919
|
+
}
|
|
920
|
+
|
|
921
|
+
function openSettingsModal(shell) {
|
|
922
|
+
const { root, body } = createOverlay({ title: shell.t('Settings'), onClose: () => root.remove() });
|
|
923
|
+
root.dataset.ge = 'settings-modal';
|
|
924
|
+
// Sound on/off (starts on) — full-width row with a speaker icon button
|
|
925
|
+
const sound = (() => {
|
|
926
|
+
let on = true;
|
|
927
|
+
const btn = document.createElement('button');
|
|
928
|
+
btn.className = 'ge-snd ge-active';
|
|
929
|
+
btn.dataset.ge = 'setting-sound';
|
|
930
|
+
btn.setAttribute('aria-label', 'Sound');
|
|
931
|
+
const paint = () => {
|
|
932
|
+
btn.innerHTML = icon(on ? 'soundOn' : 'soundOff');
|
|
933
|
+
btn.classList.toggle('ge-active', on);
|
|
934
|
+
btn.setAttribute('aria-pressed', String(on));
|
|
935
|
+
};
|
|
936
|
+
paint();
|
|
937
|
+
btn.addEventListener('click', () => {
|
|
938
|
+
on = !on;
|
|
939
|
+
paint();
|
|
940
|
+
shell.emit('settingChange', { key: 'sound', value: on });
|
|
941
|
+
});
|
|
942
|
+
const row = document.createElement('div');
|
|
943
|
+
row.className = 'ge-ov-row';
|
|
944
|
+
row.innerHTML = `<span class="ge-grow">${shell.t('Sound')}</span>`;
|
|
945
|
+
row.appendChild(btn);
|
|
946
|
+
return row;
|
|
947
|
+
})();
|
|
948
|
+
body.appendChild(sound);
|
|
949
|
+
// Volume sliders — full-width column rows with a live value readout
|
|
950
|
+
const slider = (key, label) => {
|
|
951
|
+
const row = document.createElement('div');
|
|
952
|
+
row.className = 'ge-ov-row ge-col';
|
|
953
|
+
const head = document.createElement('div');
|
|
954
|
+
head.className = 'ge-row-head';
|
|
955
|
+
const val = document.createElement('span');
|
|
956
|
+
val.className = 'ge-val';
|
|
957
|
+
val.textContent = '100%';
|
|
958
|
+
head.innerHTML = `<span>${label}</span>`;
|
|
959
|
+
head.appendChild(val);
|
|
960
|
+
const input = document.createElement('input');
|
|
961
|
+
input.type = 'range';
|
|
962
|
+
input.min = '0';
|
|
963
|
+
input.max = '1';
|
|
964
|
+
input.step = '0.05';
|
|
965
|
+
input.value = '1';
|
|
966
|
+
input.className = 'ge-slider';
|
|
967
|
+
input.dataset.ge = `setting-${key}`;
|
|
968
|
+
input.addEventListener('input', () => {
|
|
969
|
+
val.textContent = `${Math.round(Number(input.value) * 100)}%`;
|
|
970
|
+
shell.emit('settingChange', { key, value: Number(input.value) });
|
|
971
|
+
});
|
|
972
|
+
row.append(head, input);
|
|
973
|
+
return row;
|
|
974
|
+
};
|
|
975
|
+
body.appendChild(slider('master', shell.t('Master volume')));
|
|
976
|
+
body.appendChild(slider('music', shell.t('Music')));
|
|
977
|
+
body.appendChild(slider('sfx', shell.t('SFX')));
|
|
978
|
+
// Game info — full-width row button that opens its own overlay
|
|
979
|
+
const gameInfo = document.createElement('button');
|
|
980
|
+
gameInfo.className = 'ge-ov-row';
|
|
981
|
+
gameInfo.dataset.ge = 'game-info-btn';
|
|
982
|
+
gameInfo.style.marginTop = '6px';
|
|
983
|
+
gameInfo.innerHTML = `<span style="width:22px;font-size:22px">${icon('info')}</span><span class="ge-grow">${shell.t('Game info')}</span><span style="width:20px;font-size:20px;color:var(--shell-muted)">${icon('chevronRight')}</span>`;
|
|
984
|
+
gameInfo.addEventListener('click', () => { root.remove(); shell.openInfo(); });
|
|
985
|
+
body.appendChild(gameInfo);
|
|
986
|
+
return root;
|
|
987
|
+
}
|
|
988
|
+
|
|
989
|
+
const SVG_NS = 'http://www.w3.org/2000/svg';
|
|
990
|
+
function openGameInfoModal(shell) {
|
|
991
|
+
const { root, body } = createOverlay({
|
|
992
|
+
title: shell.t('Game info'),
|
|
993
|
+
onClose: () => root.remove(),
|
|
994
|
+
onBack: () => { root.remove(); shell.openSettings(); },
|
|
995
|
+
});
|
|
996
|
+
root.dataset.ge = 'info-modal';
|
|
997
|
+
const sections = shell.config.gameInfo.sections ?? [];
|
|
998
|
+
// Default placement: modes first, controls second, the rest in declaration order.
|
|
999
|
+
// An explicit `order` overrides; ties keep declaration order (stable).
|
|
1000
|
+
const base = (s, i) => s.order ?? (s.type === 'modes' ? -2 : s.type === 'controls' ? -1 : i);
|
|
1001
|
+
sections
|
|
1002
|
+
.map((s, i) => ({ s, i, k: base(s, i) }))
|
|
1003
|
+
.sort((a, b) => a.k - b.k || a.i - b.i)
|
|
1004
|
+
.forEach(({ s }) => body.appendChild(renderSection(shell, s)));
|
|
1005
|
+
return root;
|
|
1006
|
+
}
|
|
1007
|
+
function renderSection(shell, s) {
|
|
1008
|
+
switch (s.type) {
|
|
1009
|
+
case 'modes': return sectionModes(s.modes, sec('info-modes', s.title, shell.t('Modes')));
|
|
1010
|
+
case 'controls': return sectionControls(shell, sec('info-controls', s.title, shell.t('Controls')));
|
|
1011
|
+
case 'paytable': return sectionPaytable(s.rows, sec('info-paytable', s.title, shell.t('Paytable')));
|
|
1012
|
+
case 'wins': return sectionWins(s, sec('info-wins', s.title, shell.t(winFallbackTitle(s.kind))));
|
|
1013
|
+
case 'custom': return sectionCustom(s, sec('info-custom', s.title, ''));
|
|
1014
|
+
}
|
|
1015
|
+
}
|
|
1016
|
+
/** A titled glass-plaque section shell. */
|
|
1017
|
+
function sec(ge, title, fallback) {
|
|
1018
|
+
const el = document.createElement('section');
|
|
1019
|
+
el.dataset.ge = ge;
|
|
1020
|
+
el.className = 'ge-gi-sec';
|
|
1021
|
+
const t = title ?? fallback;
|
|
1022
|
+
if (t) {
|
|
1023
|
+
const h = document.createElement('h3');
|
|
1024
|
+
h.textContent = t;
|
|
1025
|
+
el.appendChild(h);
|
|
1026
|
+
}
|
|
1027
|
+
return el;
|
|
1028
|
+
}
|
|
1029
|
+
// ── modes (rows — varying description lengths read better than fixed cards) ────
|
|
1030
|
+
function sectionModes(modes, el) {
|
|
1031
|
+
const list = document.createElement('div');
|
|
1032
|
+
list.className = 'ge-gi-modes';
|
|
1033
|
+
for (const m of modes)
|
|
1034
|
+
list.appendChild(modeRow(m));
|
|
1035
|
+
el.appendChild(list);
|
|
1036
|
+
return el;
|
|
1037
|
+
}
|
|
1038
|
+
function modeRow(m) {
|
|
1039
|
+
const row = document.createElement('div');
|
|
1040
|
+
row.className = 'ge-gi-mode';
|
|
1041
|
+
const stat = (label, val) => `<span class="ge-gi-mode-st"><span>${label}</span><b>${val}</b></span>`;
|
|
1042
|
+
let stats = '';
|
|
1043
|
+
if (m.price != null)
|
|
1044
|
+
stats += stat('Price', m.price);
|
|
1045
|
+
if (typeof m.rtp === 'number')
|
|
1046
|
+
stats += stat('RTP', `${m.rtp}%`);
|
|
1047
|
+
if (m.maxWin != null)
|
|
1048
|
+
stats += stat('Max win', m.maxWin);
|
|
1049
|
+
row.innerHTML =
|
|
1050
|
+
`<div class="ge-gi-mode-top"><span class="ge-gi-mode-h">${m.title}</span>` +
|
|
1051
|
+
(stats ? `<span class="ge-gi-mode-stats">${stats}</span>` : '') + '</div>' +
|
|
1052
|
+
(m.description ? `<p class="ge-gi-mode-desc">${m.description}</p>` : '');
|
|
1053
|
+
return row;
|
|
1054
|
+
}
|
|
1055
|
+
function sectionControls(shell, el) {
|
|
1056
|
+
const { features } = shell.config;
|
|
1057
|
+
const slot = (inner, cls = '') => `<span class="ge-gi-ctl-ic ${cls}">${inner}</span>`;
|
|
1058
|
+
const buyLabel = twoLine(shell.t('BUY BONUS'));
|
|
1059
|
+
const buyBadge = slot(`<span class="ge-shell-buybonus"><span>${buyLabel}</span></span>`);
|
|
1060
|
+
// Block 1 — gameplay. Bet is split into two rows: one to raise, one to lower.
|
|
1061
|
+
const game = [
|
|
1062
|
+
{ vis: slot(icon('spin')), name: 'Spin', desc: 'Start a spin at the current bet.', on: true },
|
|
1063
|
+
{ vis: slot(icon('plus')), name: 'Raise bet', desc: 'Increase your stake.', on: true },
|
|
1064
|
+
{ vis: slot(icon('minus')), name: 'Lower bet', desc: 'Decrease your stake.', on: true },
|
|
1065
|
+
{ vis: slot(icon('autoplay')), name: 'Autoplay', desc: 'Spin automatically a set number of times.', on: features.autoplay },
|
|
1066
|
+
{ vis: slot(icon('turbo1')), name: 'Turbo', desc: 'Speed up spin animations.', on: features.turbo > 0 },
|
|
1067
|
+
{ vis: buyBadge, name: 'Buy bonus', desc: 'Pay a fixed cost to enter a bonus feature.', on: features.buyBonus !== false },
|
|
1068
|
+
];
|
|
1069
|
+
// Block 2 — menu & overlay chrome (always available).
|
|
1070
|
+
const menu = [
|
|
1071
|
+
{ vis: slot(icon('menu')), name: 'Menu', desc: 'Open settings and game info.', on: true },
|
|
1072
|
+
{ vis: slot(icon('soundOn')), name: 'Sound', desc: 'Mute or unmute the game.', on: true },
|
|
1073
|
+
{ vis: slot(icon('info')), name: 'Game info', desc: 'Open the paytable and rules.', on: true },
|
|
1074
|
+
{ vis: slot(icon('close')), name: 'Close', desc: 'Dismiss the current overlay.', on: true },
|
|
1075
|
+
];
|
|
1076
|
+
el.appendChild(ctlBlock(shell, 'Game', game));
|
|
1077
|
+
el.appendChild(ctlBlock(shell, 'Menu & info', menu));
|
|
1078
|
+
return el;
|
|
1079
|
+
}
|
|
1080
|
+
function ctlBlock(shell, label, rows) {
|
|
1081
|
+
const block = document.createElement('div');
|
|
1082
|
+
block.className = 'ge-gi-ctl-block';
|
|
1083
|
+
const h = document.createElement('h4');
|
|
1084
|
+
h.className = 'ge-gi-ctl-block-h';
|
|
1085
|
+
h.textContent = shell.t(label);
|
|
1086
|
+
block.appendChild(h);
|
|
1087
|
+
for (const r of rows.filter((x) => x.on)) {
|
|
1088
|
+
const row = document.createElement('div');
|
|
1089
|
+
row.className = 'ge-gi-ctl';
|
|
1090
|
+
row.innerHTML = `${r.vis}<div class="ge-gi-ctl-tx"><b>${shell.t(r.name)}</b><span>${shell.t(r.desc)}</span></div>`;
|
|
1091
|
+
block.appendChild(row);
|
|
1092
|
+
}
|
|
1093
|
+
return block;
|
|
1094
|
+
}
|
|
1095
|
+
// ── paytable (cards — image on top, name, then win tiers "<count> x<mult>") ────
|
|
1096
|
+
function sectionPaytable(rows, el) {
|
|
1097
|
+
const grid = document.createElement('div');
|
|
1098
|
+
grid.className = 'ge-gi-pt-grid';
|
|
1099
|
+
for (const r of rows)
|
|
1100
|
+
grid.appendChild(paytableCard(r));
|
|
1101
|
+
el.appendChild(grid);
|
|
1102
|
+
return el;
|
|
1103
|
+
}
|
|
1104
|
+
function paytableCard(r) {
|
|
1105
|
+
const card = document.createElement('div');
|
|
1106
|
+
card.className = 'ge-gi-pt-card';
|
|
1107
|
+
const sym = document.createElement('div');
|
|
1108
|
+
sym.className = 'ge-gi-pt-sym';
|
|
1109
|
+
if (r.symbol.image) {
|
|
1110
|
+
const img = document.createElement('img');
|
|
1111
|
+
img.src = r.symbol.image;
|
|
1112
|
+
img.alt = r.symbol.text ?? '';
|
|
1113
|
+
sym.appendChild(img);
|
|
1114
|
+
}
|
|
1115
|
+
if (r.symbol.text) {
|
|
1116
|
+
const t = document.createElement('span');
|
|
1117
|
+
t.textContent = r.symbol.text;
|
|
1118
|
+
sym.appendChild(t);
|
|
1119
|
+
}
|
|
1120
|
+
const wins = document.createElement('div');
|
|
1121
|
+
wins.className = 'ge-gi-pt-wins';
|
|
1122
|
+
for (const w of r.wins) {
|
|
1123
|
+
const wi = document.createElement('span');
|
|
1124
|
+
wi.className = 'ge-gi-pt-win';
|
|
1125
|
+
wi.innerHTML = (w.count ? `<i>${w.count}</i> ` : '') + `<b>x${w.multiplier}</b>`;
|
|
1126
|
+
wins.appendChild(wi);
|
|
1127
|
+
}
|
|
1128
|
+
card.append(sym, wins);
|
|
1129
|
+
return card;
|
|
1130
|
+
}
|
|
1131
|
+
// ── wins (one section = one pay type; cells filled in the accent colour, no line) ──
|
|
1132
|
+
function winFallbackTitle(kind) {
|
|
1133
|
+
return { classic: 'Paylines', cluster: 'Cluster pays', anywhere: 'Pays anywhere', ways: 'Ways to win' }[kind];
|
|
1134
|
+
}
|
|
1135
|
+
function sectionWins(s, el) {
|
|
1136
|
+
if (s.kind === 'classic') {
|
|
1137
|
+
if (s.description)
|
|
1138
|
+
el.appendChild(winDesc(s.description));
|
|
1139
|
+
const wrap = document.createElement('div');
|
|
1140
|
+
wrap.className = 'ge-gi-pl-grid';
|
|
1141
|
+
s.lines.forEach((line, i) => {
|
|
1142
|
+
const def = Array.isArray(line) ? { pattern: line } : line;
|
|
1143
|
+
wrap.appendChild(lineItem(s.grid, def, i + 1));
|
|
1144
|
+
});
|
|
1145
|
+
el.appendChild(wrap);
|
|
1146
|
+
}
|
|
1147
|
+
else if (s.kind === 'cluster' || s.kind === 'anywhere') {
|
|
1148
|
+
badge(el, `min ${s.minCount}`);
|
|
1149
|
+
const row = document.createElement('div');
|
|
1150
|
+
row.className = 'ge-gi-win-row';
|
|
1151
|
+
const example = s.example ?? (s.kind === 'cluster' ? clusterExample(s.grid, s.minCount) : anywhereExample(s.grid, s.minCount));
|
|
1152
|
+
row.appendChild(gridSvg(s.grid, example));
|
|
1153
|
+
if (s.description)
|
|
1154
|
+
row.appendChild(winDesc(s.description));
|
|
1155
|
+
el.appendChild(row);
|
|
1156
|
+
}
|
|
1157
|
+
else {
|
|
1158
|
+
if (s.description)
|
|
1159
|
+
el.appendChild(winDesc(s.description));
|
|
1160
|
+
const two = document.createElement('div');
|
|
1161
|
+
two.className = 'ge-gi-win-two';
|
|
1162
|
+
two.append(waysCol('✓ wins', 'ge-gi-win-ok', s.grid, s.winExample ?? waysWin(s.grid)), waysCol('✗ no win', 'ge-gi-win-no', s.grid, s.loseExample ?? waysLose(s.grid)));
|
|
1163
|
+
el.appendChild(two);
|
|
1164
|
+
}
|
|
1165
|
+
return el;
|
|
1166
|
+
}
|
|
1167
|
+
function winDesc(text) {
|
|
1168
|
+
const p = document.createElement('p');
|
|
1169
|
+
p.className = 'ge-gi-win-desc';
|
|
1170
|
+
p.textContent = text;
|
|
1171
|
+
return p;
|
|
1172
|
+
}
|
|
1173
|
+
/** Append a "min N" pill to the section header. */
|
|
1174
|
+
function badge(el, text) {
|
|
1175
|
+
const h = el.querySelector('h3');
|
|
1176
|
+
if (!h)
|
|
1177
|
+
return;
|
|
1178
|
+
const b = document.createElement('span');
|
|
1179
|
+
b.className = 'ge-gi-win-badge';
|
|
1180
|
+
b.textContent = text;
|
|
1181
|
+
h.appendChild(b);
|
|
1182
|
+
}
|
|
1183
|
+
/** A cols×rows grid SVG; `on` cells are filled in the accent colour, the rest are faint. */
|
|
1184
|
+
function gridSvg(grid, on) {
|
|
1185
|
+
const { cols, rows } = grid;
|
|
1186
|
+
const W = 100, H = Math.round((rows / cols) * 100);
|
|
1187
|
+
const cw = W / cols, ch = H / rows;
|
|
1188
|
+
const svg = document.createElementNS(SVG_NS, 'svg');
|
|
1189
|
+
svg.setAttribute('viewBox', `0 0 ${W} ${H}`);
|
|
1190
|
+
svg.setAttribute('class', 'ge-gi-pl-svg');
|
|
1191
|
+
const onSet = new Set(on.map(([c, r]) => `${c},${r}`));
|
|
1192
|
+
for (let y = 0; y < rows; y++)
|
|
1193
|
+
for (let x = 0; x < cols; x++) {
|
|
1194
|
+
const r = document.createElementNS(SVG_NS, 'rect');
|
|
1195
|
+
r.setAttribute('x', String(x * cw + 1));
|
|
1196
|
+
r.setAttribute('y', String(y * ch + 1));
|
|
1197
|
+
r.setAttribute('width', String(cw - 2));
|
|
1198
|
+
r.setAttribute('height', String(ch - 2));
|
|
1199
|
+
r.setAttribute('rx', '2');
|
|
1200
|
+
r.setAttribute('class', onSet.has(`${x},${y}`) ? 'ge-gi-pl-on' : 'ge-gi-pl-cell');
|
|
1201
|
+
svg.appendChild(r);
|
|
1202
|
+
}
|
|
1203
|
+
return svg;
|
|
1204
|
+
}
|
|
1205
|
+
/** A classic payline: number caption on top, filled cells (no connecting line). */
|
|
1206
|
+
function lineItem(grid, def, n) {
|
|
1207
|
+
const item = document.createElement('div');
|
|
1208
|
+
item.className = 'ge-gi-pl-item';
|
|
1209
|
+
const cap = document.createElement('span');
|
|
1210
|
+
cap.className = 'ge-gi-pl-cap';
|
|
1211
|
+
cap.textContent = String(n);
|
|
1212
|
+
const on = def.pattern.map((rowIdx, col) => [col, rowIdx]);
|
|
1213
|
+
item.append(cap, gridSvg(grid, on)); // caption first → renders above the grid
|
|
1214
|
+
return item;
|
|
1215
|
+
}
|
|
1216
|
+
function waysCol(tag, tagCls, grid, cells) {
|
|
1217
|
+
const col = document.createElement('div');
|
|
1218
|
+
col.className = 'ge-gi-win-col';
|
|
1219
|
+
const t = document.createElement('span');
|
|
1220
|
+
t.className = `ge-gi-win-tag ${tagCls}`;
|
|
1221
|
+
t.textContent = tag;
|
|
1222
|
+
col.append(t, gridSvg(grid, cells));
|
|
1223
|
+
return col;
|
|
1224
|
+
}
|
|
1225
|
+
// Default illustrations (used when the section omits an explicit example).
|
|
1226
|
+
function clusterExample(grid, n) {
|
|
1227
|
+
const w = Math.min(grid.cols, Math.max(1, Math.ceil(Math.sqrt(n))));
|
|
1228
|
+
const cells = [];
|
|
1229
|
+
for (let y = 0; y < grid.rows && cells.length < n; y++)
|
|
1230
|
+
for (let x = 0; x < w && cells.length < n; x++)
|
|
1231
|
+
cells.push([x, y]);
|
|
1232
|
+
return cells;
|
|
1233
|
+
}
|
|
1234
|
+
function anywhereExample(grid, n) {
|
|
1235
|
+
const count = Math.min(n, grid.cols * grid.rows);
|
|
1236
|
+
const cells = [];
|
|
1237
|
+
for (let i = 0; i < count; i++)
|
|
1238
|
+
cells.push([Math.floor((i * grid.cols) / count), (i * 2 + 1) % grid.rows]);
|
|
1239
|
+
return cells;
|
|
1240
|
+
}
|
|
1241
|
+
function waysWin(grid) {
|
|
1242
|
+
const cells = [];
|
|
1243
|
+
for (let c = 0; c < grid.cols; c++)
|
|
1244
|
+
cells.push([c, c % grid.rows]); // one symbol on every reel
|
|
1245
|
+
return cells;
|
|
1246
|
+
}
|
|
1247
|
+
function waysLose(grid) {
|
|
1248
|
+
const gap = Math.floor(grid.cols / 2);
|
|
1249
|
+
return waysWin(grid).filter(([c]) => c !== gap); // a broken chain (reel `gap` empty)
|
|
1250
|
+
}
|
|
1251
|
+
// ── custom ───────────────────────────────────────────────────────────────────
|
|
1252
|
+
function sectionCustom(s, el) {
|
|
1253
|
+
if (s.node) {
|
|
1254
|
+
el.appendChild(s.node);
|
|
1255
|
+
}
|
|
1256
|
+
else if (s.html) {
|
|
1257
|
+
const d = document.createElement('div');
|
|
1258
|
+
d.className = 'ge-gi-custom';
|
|
1259
|
+
d.innerHTML = s.html;
|
|
1260
|
+
el.appendChild(d);
|
|
1261
|
+
}
|
|
1262
|
+
return el;
|
|
1263
|
+
}
|
|
1264
|
+
|
|
1265
|
+
/** Buy-bonus overlay — a grid of art-forward cards, one per option. */
|
|
1266
|
+
function openBuyBonusOverlay(shell) {
|
|
1267
|
+
const bonuses = shell.config.features.buyBonus;
|
|
1268
|
+
if (bonuses === false || bonuses.length === 0)
|
|
1269
|
+
return null;
|
|
1270
|
+
const { root, body } = createOverlay({ title: shell.t('Buy bonus'), onClose: () => root.remove() });
|
|
1271
|
+
root.dataset.ge = 'buybonus-overlay';
|
|
1272
|
+
// Re-render the grid whenever the bet changes so every card's price stays live.
|
|
1273
|
+
const renderGrid = () => {
|
|
1274
|
+
body.innerHTML = '';
|
|
1275
|
+
const grid = document.createElement('div');
|
|
1276
|
+
grid.className = 'ge-bb-grid';
|
|
1277
|
+
for (const bonus of bonuses)
|
|
1278
|
+
grid.appendChild(buildCard(shell, bonus, root));
|
|
1279
|
+
body.appendChild(grid);
|
|
1280
|
+
};
|
|
1281
|
+
renderGrid();
|
|
1282
|
+
root.appendChild(buildBetBar(shell, renderGrid)); // thin bottom footer, only as tall as the pill
|
|
1283
|
+
return root;
|
|
1284
|
+
}
|
|
1285
|
+
/** Bet control — a compact −/+ pill around the live stake, in a thin footer at the screen bottom.
|
|
1286
|
+
* Stepping repaints the value, re-prices the cards, and updates the control bar. */
|
|
1287
|
+
function buildBetBar(shell, onChange) {
|
|
1288
|
+
const bar = document.createElement('div');
|
|
1289
|
+
bar.className = 'ge-bb-betbar';
|
|
1290
|
+
const pill = document.createElement('div');
|
|
1291
|
+
pill.className = 'ge-bb-betpill';
|
|
1292
|
+
const val = document.createElement('div');
|
|
1293
|
+
val.className = 'ge-bb-betval';
|
|
1294
|
+
const down = stepButton('bb-bet-down', 'minus');
|
|
1295
|
+
const up = stepButton('bb-bet-up', 'plus');
|
|
1296
|
+
// Mirror the control bar: disable a stepper at the end of the bet range, and lock both
|
|
1297
|
+
// while busy — so changing the stake behaves identically here and on the bottom bar.
|
|
1298
|
+
const paint = () => {
|
|
1299
|
+
val.innerHTML = `<span>${shell.t('Bet')}</span><b>${formatCurrency(shell.state.bet, shell.config.currency)}</b>`;
|
|
1300
|
+
const i = shell.state.availableBets.indexOf(shell.state.bet);
|
|
1301
|
+
down.disabled = shell.state.busy || i <= 0;
|
|
1302
|
+
up.disabled = shell.state.busy || i >= shell.state.availableBets.length - 1;
|
|
1303
|
+
};
|
|
1304
|
+
const step = (dir) => () => {
|
|
1305
|
+
const next = stepBet(shell.state, dir);
|
|
1306
|
+
if (next === shell.state.bet)
|
|
1307
|
+
return;
|
|
1308
|
+
shell.state.bet = next;
|
|
1309
|
+
shell.emit('betChange', next);
|
|
1310
|
+
shell.render();
|
|
1311
|
+
paint();
|
|
1312
|
+
onChange();
|
|
1313
|
+
};
|
|
1314
|
+
down.addEventListener('click', step(-1));
|
|
1315
|
+
up.addEventListener('click', step(1));
|
|
1316
|
+
paint();
|
|
1317
|
+
pill.append(down, val, up);
|
|
1318
|
+
bar.appendChild(pill);
|
|
1319
|
+
return bar;
|
|
1320
|
+
}
|
|
1321
|
+
function stepButton(ge, name) {
|
|
1322
|
+
const b = document.createElement('button');
|
|
1323
|
+
b.className = 'ge-bb-betstep';
|
|
1324
|
+
b.dataset.ge = ge;
|
|
1325
|
+
b.innerHTML = icon(name);
|
|
1326
|
+
return b;
|
|
1327
|
+
}
|
|
1328
|
+
/** A grid card: title → thumbnail → description → volatility → price → full-bleed CTA.
|
|
1329
|
+
* Clicking (when affordable) opens the confirmation modal. */
|
|
1330
|
+
function buildCard(shell, bonus, overlay) {
|
|
1331
|
+
const accent = effectiveAccent(bonus);
|
|
1332
|
+
const card = document.createElement('div');
|
|
1333
|
+
card.className = 'ge-bonus-card';
|
|
1334
|
+
card.dataset.ge = `bonus-card-${bonus.id}`;
|
|
1335
|
+
card.style.setProperty('--card-acc', accent);
|
|
1336
|
+
card.style.setProperty('--card-ink', contrastText(accent));
|
|
1337
|
+
card.appendChild(cardBody(shell, bonus));
|
|
1338
|
+
const cta = document.createElement('button');
|
|
1339
|
+
cta.className = 'ge-bonus-cta';
|
|
1340
|
+
cta.dataset.ge = `bonus-cta-${bonus.id}`;
|
|
1341
|
+
cta.textContent = shell.t(actionLabel(bonus));
|
|
1342
|
+
card.appendChild(cta);
|
|
1343
|
+
const enabled = isAffordable(shell, bonus);
|
|
1344
|
+
if (!enabled) {
|
|
1345
|
+
card.classList.add('ge-bonus-off');
|
|
1346
|
+
cta.disabled = true;
|
|
1347
|
+
}
|
|
1348
|
+
else {
|
|
1349
|
+
// Stack the confirm on top of the overlay grid (cancel returns to the grid).
|
|
1350
|
+
card.addEventListener('click', () => { overlay.appendChild(buildConfirm(shell, bonus, overlay)); shell.fitModals(); });
|
|
1351
|
+
}
|
|
1352
|
+
return card;
|
|
1353
|
+
}
|
|
1354
|
+
/** The shared card interior (everything above the action area), reused by the confirm modal. */
|
|
1355
|
+
function cardBody(shell, bonus) {
|
|
1356
|
+
const price = bonus.priceMultiplier * shell.state.bet;
|
|
1357
|
+
const wrap = document.createElement('div');
|
|
1358
|
+
wrap.className = 'ge-bonus-body';
|
|
1359
|
+
wrap.innerHTML =
|
|
1360
|
+
`<div class="ge-bonus-title">${bonus.title}</div>` +
|
|
1361
|
+
`<div class="ge-bonus-thumb">${thumb(bonus)}</div>` +
|
|
1362
|
+
`<div class="ge-bonus-desc">${bonus.description}</div>` +
|
|
1363
|
+
`<div class="ge-bonus-spacer"></div>` +
|
|
1364
|
+
(bonus.volatility ? `<div class="ge-bonus-vol">${volatility(bonus.volatility)}</div>` : '') +
|
|
1365
|
+
`<div class="ge-bonus-price">${formatCurrency(price, shell.config.currency)}</div>`;
|
|
1366
|
+
return wrap;
|
|
1367
|
+
}
|
|
1368
|
+
/** Confirmation modal — the shared card chrome (accent title heading, no ✕) with a bonus
|
|
1369
|
+
* preview body and a full-bleed Cancel + action footer. */
|
|
1370
|
+
function buildConfirm(shell, bonus, overlay) {
|
|
1371
|
+
const accent = effectiveAccent(bonus);
|
|
1372
|
+
const ui = createCardModal({ ge: 'bonus-confirm', title: bonus.title, accent, onClose: () => ui.root.remove() });
|
|
1373
|
+
const price = bonus.priceMultiplier * shell.state.bet;
|
|
1374
|
+
const preview = document.createElement('div');
|
|
1375
|
+
preview.className = 'ge-confirm-preview';
|
|
1376
|
+
preview.innerHTML =
|
|
1377
|
+
`<div class="ge-bonus-thumb">${thumb(bonus)}</div>` +
|
|
1378
|
+
`<div class="ge-bonus-desc">${bonus.description}</div>` +
|
|
1379
|
+
(bonus.volatility ? `<div class="ge-bonus-vol">${volatility(bonus.volatility)}</div>` : '') +
|
|
1380
|
+
`<div class="ge-bonus-price">${formatCurrency(price, shell.config.currency)}</div>`;
|
|
1381
|
+
ui.body.appendChild(preview);
|
|
1382
|
+
const actions = document.createElement('div');
|
|
1383
|
+
actions.className = 'ge-modal-actions';
|
|
1384
|
+
const cancel = document.createElement('button');
|
|
1385
|
+
cancel.className = 'ge-modal-btn ge-modal-btn--ghost';
|
|
1386
|
+
cancel.dataset.ge = 'bonus-confirm-cancel';
|
|
1387
|
+
cancel.textContent = shell.t('Cancel');
|
|
1388
|
+
cancel.addEventListener('click', () => ui.root.remove());
|
|
1389
|
+
const buy = document.createElement('button');
|
|
1390
|
+
buy.className = 'ge-modal-btn ge-modal-btn--accent';
|
|
1391
|
+
buy.dataset.ge = 'bonus-confirm-buy';
|
|
1392
|
+
buy.textContent = shell.t(actionLabel(bonus));
|
|
1393
|
+
buy.style.color = contrastText(accent); // bg comes from --card-acc on the card
|
|
1394
|
+
buy.addEventListener('click', () => {
|
|
1395
|
+
// Re-check at click time: the confirm modal stays open across state changes, so a spin
|
|
1396
|
+
// starting (busy), buy-bonus being disabled, or the balance dropping must block the purchase.
|
|
1397
|
+
if (!isAffordable(shell, bonus))
|
|
1398
|
+
return;
|
|
1399
|
+
if (bonus.type === 'feature')
|
|
1400
|
+
shell.activateFeature(bonus);
|
|
1401
|
+
else
|
|
1402
|
+
shell.emit('buyBonusSelect', { id: bonus.id });
|
|
1403
|
+
ui.root.remove();
|
|
1404
|
+
overlay.remove();
|
|
1405
|
+
});
|
|
1406
|
+
actions.append(cancel, buy);
|
|
1407
|
+
ui.card.appendChild(actions);
|
|
1408
|
+
return ui.root;
|
|
1409
|
+
}
|
|
1410
|
+
function thumb(bonus) {
|
|
1411
|
+
if (bonus.thumbnail)
|
|
1412
|
+
return `<img src="${bonus.thumbnail}" alt="${bonus.title}">`;
|
|
1413
|
+
return `<span class="ge-bonus-thumb-ph">${icon('gift')}</span>`;
|
|
1414
|
+
}
|
|
1415
|
+
/** Volatility as five lightning bolts (the supplied SVG); `level` lit in the accent, rest dimmed. */
|
|
1416
|
+
function volatility(level) {
|
|
1417
|
+
const n = Math.max(0, Math.min(5, level));
|
|
1418
|
+
const bolt = icon('lightning');
|
|
1419
|
+
return `<span class="ge-bonus-vol-on">${bolt.repeat(n)}</span>` +
|
|
1420
|
+
`<span class="ge-bonus-vol-off">${bolt.repeat(5 - n)}</span>`;
|
|
1421
|
+
}
|
|
1422
|
+
function actionLabel(bonus) {
|
|
1423
|
+
return bonus.type === 'feature' ? 'Activate' : 'Buy';
|
|
1424
|
+
}
|
|
1425
|
+
function isAffordable(shell, bonus) {
|
|
1426
|
+
if (shell.state.busy || !shell.state.buyBonusEnabled)
|
|
1427
|
+
return false;
|
|
1428
|
+
return bonus.priceMultiplier * shell.state.bet <= shell.state.balance;
|
|
1429
|
+
}
|
|
1430
|
+
|
|
1431
|
+
/** A centred picker (chips grid + accent Confirm) on the shared card modal. */
|
|
1432
|
+
function buildSheet(opts) {
|
|
1433
|
+
const ui = createCardModal({ ge: opts.ge, title: opts.title, onClose: () => ui.root.remove() });
|
|
1434
|
+
const grid = document.createElement('div');
|
|
1435
|
+
grid.className = 'ge-sheet-grid';
|
|
1436
|
+
grid.style.gridTemplateColumns = `repeat(${opts.columns}, 1fr)`;
|
|
1437
|
+
let selected = opts.selected;
|
|
1438
|
+
const chips = [];
|
|
1439
|
+
for (const c of opts.choices) {
|
|
1440
|
+
const chip = document.createElement('button');
|
|
1441
|
+
chip.className = 'ge-chip' + (c.id === selected ? ' ge-on' : '');
|
|
1442
|
+
chip.dataset.id = c.id;
|
|
1443
|
+
chip.textContent = c.label;
|
|
1444
|
+
chip.addEventListener('click', () => {
|
|
1445
|
+
selected = c.id;
|
|
1446
|
+
for (const x of chips)
|
|
1447
|
+
x.classList.toggle('ge-on', x.dataset.id === selected);
|
|
1448
|
+
});
|
|
1449
|
+
chips.push(chip);
|
|
1450
|
+
grid.appendChild(chip);
|
|
1451
|
+
}
|
|
1452
|
+
ui.body.appendChild(grid);
|
|
1453
|
+
// Single full-bleed Confirm; dismissal is the ✕ (top-right). No Cancel button.
|
|
1454
|
+
const confirm = document.createElement('button');
|
|
1455
|
+
confirm.className = 'ge-modal-btn ge-modal-btn--accent';
|
|
1456
|
+
confirm.dataset.ge = 'sheet-confirm';
|
|
1457
|
+
confirm.textContent = opts.confirmLabel;
|
|
1458
|
+
confirm.addEventListener('click', () => { opts.onConfirm(selected); ui.root.remove(); });
|
|
1459
|
+
ui.card.appendChild(confirm);
|
|
1460
|
+
return ui.root;
|
|
1461
|
+
}
|
|
1462
|
+
/** Bet picker — all available bets as chips (3 per row), accent Confirm applies it. */
|
|
1463
|
+
function openBetModal(shell) {
|
|
1464
|
+
return buildSheet({
|
|
1465
|
+
ge: 'bet-modal', title: shell.t('Bet'), columns: 3, confirmLabel: shell.t('Confirm'),
|
|
1466
|
+
choices: shell.state.availableBets.map((b) => ({ id: String(b), label: formatCurrency(b, shell.config.currency) })),
|
|
1467
|
+
selected: String(shell.state.bet),
|
|
1468
|
+
onConfirm: (id) => {
|
|
1469
|
+
const v = Number(id);
|
|
1470
|
+
if (v !== shell.state.bet) {
|
|
1471
|
+
shell.state.bet = v;
|
|
1472
|
+
shell.emit('betChange', v);
|
|
1473
|
+
}
|
|
1474
|
+
shell.render();
|
|
1475
|
+
},
|
|
1476
|
+
});
|
|
1477
|
+
}
|
|
1478
|
+
const AUTOPLAY_COUNTS = [10, 25, 50, 100, 250, 500, 1000, 2000, Infinity];
|
|
1479
|
+
/** Autoplay picker — spin counts incl. ∞; Confirm starts autoplay. */
|
|
1480
|
+
function openAutoplayModal(shell) {
|
|
1481
|
+
return buildSheet({
|
|
1482
|
+
ge: 'autoplay-modal', title: shell.t('Autoplay'), columns: 3, confirmLabel: shell.t('Start'),
|
|
1483
|
+
choices: AUTOPLAY_COUNTS.map((n) => ({ id: String(n), label: Number.isFinite(n) ? String(n) : '∞' })),
|
|
1484
|
+
selected: String(shell.state.autoplay.remaining || 10),
|
|
1485
|
+
onConfirm: (id) => {
|
|
1486
|
+
const remaining = Number(id); // "Infinity" → Infinity
|
|
1487
|
+
shell.state.autoplay = { active: true, remaining };
|
|
1488
|
+
shell.emit('autoplayStart', { active: true, remaining });
|
|
1489
|
+
shell.render();
|
|
1490
|
+
},
|
|
1491
|
+
});
|
|
1492
|
+
}
|
|
1493
|
+
|
|
1494
|
+
/** Build a generic, externally-triggered modal (title + body text + optional action buttons),
|
|
1495
|
+
* on the shared card-modal chrome. Each action runs its `on` then closes; the ✕ (if
|
|
1496
|
+
* `availableClose`) and the actions are the only ways to dismiss. See GameShell.openModal. */
|
|
1497
|
+
function buildModal(opts) {
|
|
1498
|
+
const ui = createCardModal({
|
|
1499
|
+
ge: 'modal',
|
|
1500
|
+
title: opts.title,
|
|
1501
|
+
closable: opts.availableClose,
|
|
1502
|
+
blur: opts.blurLevel,
|
|
1503
|
+
onClose: () => ui.root.remove(),
|
|
1504
|
+
});
|
|
1505
|
+
const text = document.createElement('p');
|
|
1506
|
+
text.className = 'ge-modal-text';
|
|
1507
|
+
text.dataset.ge = 'modal-body';
|
|
1508
|
+
text.textContent = opts.body;
|
|
1509
|
+
ui.body.appendChild(text);
|
|
1510
|
+
if (opts.actions?.length) {
|
|
1511
|
+
const actions = document.createElement('div');
|
|
1512
|
+
actions.className = 'ge-modal-actions';
|
|
1513
|
+
for (const a of opts.actions) {
|
|
1514
|
+
const btn = document.createElement('button');
|
|
1515
|
+
btn.className = 'ge-modal-btn';
|
|
1516
|
+
btn.dataset.ge = 'modal-action';
|
|
1517
|
+
btn.textContent = a.title;
|
|
1518
|
+
if (a.color) {
|
|
1519
|
+
btn.style.background = a.color;
|
|
1520
|
+
btn.style.color = contrastText(a.color);
|
|
1521
|
+
}
|
|
1522
|
+
else
|
|
1523
|
+
btn.classList.add('ge-modal-btn--ghost');
|
|
1524
|
+
btn.addEventListener('click', () => { a.on?.(); ui.root.remove(); });
|
|
1525
|
+
actions.appendChild(btn);
|
|
1526
|
+
}
|
|
1527
|
+
ui.card.appendChild(actions);
|
|
1528
|
+
}
|
|
1529
|
+
return ui.root;
|
|
1530
|
+
}
|
|
1531
|
+
|
|
1532
|
+
/** The replay summary modal — built on the shared card chrome, but NOT dismissable: no ✕,
|
|
1533
|
+
* and the backdrop never closes it. The only way out is START REPLAY, which closes the
|
|
1534
|
+
* modal, runs `onReplay`, then reopens it (whether the handler resolves OR rejects, so a
|
|
1535
|
+
* failed replay can't strand the user). */
|
|
1536
|
+
function buildReplayModal(shell, opts) {
|
|
1537
|
+
const { bonusId, bet, payoutMultiplier } = opts;
|
|
1538
|
+
const fmt = (n) => formatCurrency(n, shell.config.currency);
|
|
1539
|
+
const bonus = Array.isArray(shell.config.features.buyBonus)
|
|
1540
|
+
? shell.config.features.buyBonus.find((b) => b.id === bonusId)
|
|
1541
|
+
: undefined;
|
|
1542
|
+
const mode = bonus?.title ?? bonusId;
|
|
1543
|
+
const costMultiplier = bonus?.priceMultiplier ?? 1;
|
|
1544
|
+
const ui = createCardModal({
|
|
1545
|
+
ge: 'replay-modal',
|
|
1546
|
+
title: shell.t('Replay'),
|
|
1547
|
+
closable: false, // no ✕; the backdrop never dismisses it either
|
|
1548
|
+
onClose: () => { }, // unused — there is no close affordance
|
|
1549
|
+
});
|
|
1550
|
+
const rows = document.createElement('div');
|
|
1551
|
+
rows.className = 'ge-replay-rows';
|
|
1552
|
+
const row = (label, value, total = false) => {
|
|
1553
|
+
const r = document.createElement('div');
|
|
1554
|
+
r.className = `ge-replay-row${total ? ' ge-replay-total' : ''}`;
|
|
1555
|
+
const l = document.createElement('span');
|
|
1556
|
+
l.textContent = shell.t(label);
|
|
1557
|
+
const v = document.createElement('b');
|
|
1558
|
+
v.textContent = value;
|
|
1559
|
+
r.append(l, v);
|
|
1560
|
+
rows.appendChild(r);
|
|
1561
|
+
};
|
|
1562
|
+
row('Mode', mode);
|
|
1563
|
+
row('Base bet', fmt(bet));
|
|
1564
|
+
row('Cost multiplier', `${costMultiplier}×`);
|
|
1565
|
+
row('Total cost bet', fmt(bet * costMultiplier));
|
|
1566
|
+
row('Payout multiplier', `${payoutMultiplier}×`);
|
|
1567
|
+
row('Total win', fmt(payoutMultiplier * bet), true);
|
|
1568
|
+
ui.body.appendChild(rows);
|
|
1569
|
+
const actions = document.createElement('div');
|
|
1570
|
+
actions.className = 'ge-modal-actions';
|
|
1571
|
+
const btn = document.createElement('button');
|
|
1572
|
+
btn.className = 'ge-modal-btn ge-modal-btn--accent';
|
|
1573
|
+
btn.dataset.ge = 'replay-start';
|
|
1574
|
+
btn.textContent = shell.t('Start replay');
|
|
1575
|
+
btn.addEventListener('click', () => {
|
|
1576
|
+
ui.root.remove(); // close immediately
|
|
1577
|
+
// Reopen after the handler settles. On rejection we still reopen — this modal is the only
|
|
1578
|
+
// way out of replay mode, so a failed play must not strand the user on an empty screen.
|
|
1579
|
+
const reopen = () => { shell.openReplay(opts); };
|
|
1580
|
+
Promise.resolve(opts.onReplay()).then(reopen, reopen);
|
|
1581
|
+
});
|
|
1582
|
+
actions.appendChild(btn);
|
|
1583
|
+
ui.card.appendChild(actions);
|
|
1584
|
+
return ui.root;
|
|
1585
|
+
}
|
|
1586
|
+
|
|
1587
|
+
/** True when the user (or environment) prefers no motion. Missing matchMedia
|
|
1588
|
+
* — e.g. jsdom / SSR — is treated as reduced so animations never block. */
|
|
1589
|
+
function prefersReducedMotion() {
|
|
1590
|
+
const mm = globalThis.matchMedia;
|
|
1591
|
+
if (typeof mm !== 'function')
|
|
1592
|
+
return true;
|
|
1593
|
+
return mm('(prefers-reduced-motion: reduce)').matches;
|
|
1594
|
+
}
|
|
1595
|
+
/** Animate el's text from `from` to `to` via `fmt`. Skips to final value when
|
|
1596
|
+
* motion is reduced or rAF is unavailable. Returns a canceler that stops the loop —
|
|
1597
|
+
* call it before the target node is detached so the rAF chain can't keep writing to
|
|
1598
|
+
* (and pinning) a dead node, or stack up overlapping loops on rapid updates. */
|
|
1599
|
+
function countUp(el, from, to, fmt, durationMs = 450) {
|
|
1600
|
+
const raf = globalThis.requestAnimationFrame;
|
|
1601
|
+
const caf = globalThis.cancelAnimationFrame;
|
|
1602
|
+
if (prefersReducedMotion() || typeof raf !== 'function') {
|
|
1603
|
+
el.textContent = fmt(to);
|
|
1604
|
+
return () => { };
|
|
1605
|
+
}
|
|
1606
|
+
let handle = 0;
|
|
1607
|
+
let cancelled = false;
|
|
1608
|
+
const origin = globalThis.performance?.now() ?? 0;
|
|
1609
|
+
const tick = (t) => {
|
|
1610
|
+
if (cancelled)
|
|
1611
|
+
return;
|
|
1612
|
+
const start = origin;
|
|
1613
|
+
const p = Math.min(1, (t - start) / durationMs);
|
|
1614
|
+
const eased = 1 - Math.pow(1 - p, 3); // easeOutCubic
|
|
1615
|
+
el.textContent = fmt(from + (to - from) * eased);
|
|
1616
|
+
if (p < 1)
|
|
1617
|
+
handle = raf(tick);
|
|
1618
|
+
else
|
|
1619
|
+
el.textContent = fmt(to);
|
|
1620
|
+
};
|
|
1621
|
+
handle = raf(tick);
|
|
1622
|
+
return () => {
|
|
1623
|
+
cancelled = true;
|
|
1624
|
+
if (typeof caf === 'function')
|
|
1625
|
+
caf(handle);
|
|
1626
|
+
};
|
|
1627
|
+
}
|
|
1628
|
+
|
|
1629
|
+
// Social-casino language. English is the source (and, for now, the only) language; `socialize`
|
|
1630
|
+
// rewrites the restricted gambling vocabulary into social-safe phrasing while preserving case.
|
|
1631
|
+
//
|
|
1632
|
+
// Ordering matters: the longest / most specific phrases are listed first so they win over their
|
|
1633
|
+
// constituent words (e.g. "buy bonus" before "buy", "pay out" before "pay"). The JS alternation
|
|
1634
|
+
// tries entries left-to-right at each position, so a phrase earlier in this list takes priority.
|
|
1635
|
+
//
|
|
1636
|
+
// Conflicting duplicates in the source table are resolved to a single replacement here
|
|
1637
|
+
// (betting→playing, total bet→total play, paid out→won, pays out→win).
|
|
1638
|
+
const RULES = [
|
|
1639
|
+
['be awarded to player’s accounts', 'appear in player’s accounts'],
|
|
1640
|
+
["be awarded to player's accounts", "appear in player's accounts"],
|
|
1641
|
+
['place your bets', 'come and play / join in the game'],
|
|
1642
|
+
['at the cost of', 'for'],
|
|
1643
|
+
['cost of', 'can be played for'],
|
|
1644
|
+
['win feature', 'play feature'],
|
|
1645
|
+
['total bet', 'total play'],
|
|
1646
|
+
['buy bonus', 'get bonus'],
|
|
1647
|
+
['bonus buy', 'bonus / feature'],
|
|
1648
|
+
['pay out', 'win / won'],
|
|
1649
|
+
['paid out', 'won'],
|
|
1650
|
+
['pays out', 'win'],
|
|
1651
|
+
['bet/s', 'play/s'],
|
|
1652
|
+
['betting', 'playing'],
|
|
1653
|
+
['rebet', 'respin'],
|
|
1654
|
+
['stake', 'play amount'],
|
|
1655
|
+
['payer', 'winner'],
|
|
1656
|
+
['bets', 'plays'],
|
|
1657
|
+
['pays', 'wins'],
|
|
1658
|
+
['paid', 'won'],
|
|
1659
|
+
['bought', 'instantly triggered'],
|
|
1660
|
+
['purchase', 'play'],
|
|
1661
|
+
['deposit', 'get coins'],
|
|
1662
|
+
['withdraw', 'redeem'],
|
|
1663
|
+
['currency', 'token'],
|
|
1664
|
+
['gamble', 'play'],
|
|
1665
|
+
['wager', 'play'],
|
|
1666
|
+
['credit', 'balance'],
|
|
1667
|
+
['money', 'coins'],
|
|
1668
|
+
['cash', 'coins'],
|
|
1669
|
+
['fund', 'balance'],
|
|
1670
|
+
['bet', 'play'],
|
|
1671
|
+
['pay', 'win'],
|
|
1672
|
+
['buy', 'play'],
|
|
1673
|
+
];
|
|
1674
|
+
const MAP = new Map(RULES.map(([k, v]) => [k.toLowerCase(), v]));
|
|
1675
|
+
const escapeRe = (s) => s.replace(/[.*+?^${}()|[\]\\/]/g, '\\$&');
|
|
1676
|
+
// Letter-bounded so we only swap whole words/phrases (e.g. "pay" inside "Autoplay" is left alone).
|
|
1677
|
+
const PATTERN = new RegExp(`(?<![A-Za-z])(?:${RULES.map(([k]) => escapeRe(k)).join('|')})(?![A-Za-z])`, 'gi');
|
|
1678
|
+
/** Carry the matched text's capitalisation onto the replacement: ALL CAPS → upper, Capitalised
|
|
1679
|
+
* (first letter) → capitalise the replacement's first letter, otherwise lower-case as written. */
|
|
1680
|
+
function applyCase(match, repl) {
|
|
1681
|
+
const letters = match.replace(/[^A-Za-z]/g, '');
|
|
1682
|
+
if (letters && letters === letters.toUpperCase())
|
|
1683
|
+
return repl.toUpperCase();
|
|
1684
|
+
if (/^[^A-Za-z]*[A-Z]/.test(match))
|
|
1685
|
+
return repl.charAt(0).toUpperCase() + repl.slice(1);
|
|
1686
|
+
return repl;
|
|
1687
|
+
}
|
|
1688
|
+
/** Rewrite restricted gambling terms in `text` to social-safe phrasing, preserving case. */
|
|
1689
|
+
function socialize(text) {
|
|
1690
|
+
return text.replace(PATTERN, (m) => {
|
|
1691
|
+
const repl = MAP.get(m.toLowerCase());
|
|
1692
|
+
return repl == null ? m : applyCase(m, repl);
|
|
1693
|
+
});
|
|
1694
|
+
}
|
|
1695
|
+
|
|
1696
|
+
const REMOVE_FADE_MS = 300;
|
|
1697
|
+
class GameShell extends EventEmitter {
|
|
1698
|
+
config;
|
|
1699
|
+
state;
|
|
1700
|
+
root;
|
|
1701
|
+
styleEl;
|
|
1702
|
+
barHost = document.createElement('div');
|
|
1703
|
+
modalHost = document.createElement('div');
|
|
1704
|
+
destroyed = false;
|
|
1705
|
+
layout = 'wide';
|
|
1706
|
+
ro = null;
|
|
1707
|
+
prevBalance = 0;
|
|
1708
|
+
prevWin = 0;
|
|
1709
|
+
moneyAnims = [];
|
|
1710
|
+
keysBound = false;
|
|
1711
|
+
constructor(config) {
|
|
1712
|
+
super();
|
|
1713
|
+
this.config = config;
|
|
1714
|
+
this.state = createInitialState(config);
|
|
1715
|
+
this.styleEl = document.createElement('style');
|
|
1716
|
+
this.styleEl.textContent = SHELL_CSS;
|
|
1717
|
+
this.root = document.createElement('div');
|
|
1718
|
+
this.root.id = SHELL_ROOT_ID;
|
|
1719
|
+
this.root.setAttribute('style', buildThemeVars(config.theme));
|
|
1720
|
+
config.mount.append(this.styleEl, this.root);
|
|
1721
|
+
this.barHost.className = 'ge-shell-barhost';
|
|
1722
|
+
this.root.appendChild(this.barHost);
|
|
1723
|
+
this.modalHost.className = 'ge-shell-modalhost';
|
|
1724
|
+
this.root.appendChild(this.modalHost);
|
|
1725
|
+
this.prevBalance = this.state.balance;
|
|
1726
|
+
this.prevWin = this.state.win;
|
|
1727
|
+
this.observeLayout();
|
|
1728
|
+
if (typeof document !== 'undefined') {
|
|
1729
|
+
document.addEventListener('keydown', this.handleKeyDown);
|
|
1730
|
+
this.keysBound = true;
|
|
1731
|
+
}
|
|
1732
|
+
this.render();
|
|
1733
|
+
// re-fit once the bundled webfont swaps in (text metrics change → row width changes)
|
|
1734
|
+
if (typeof document !== 'undefined' && document.fonts) {
|
|
1735
|
+
document.fonts.ready.then(() => { if (!this.destroyed)
|
|
1736
|
+
this.applyFitScale(); });
|
|
1737
|
+
}
|
|
1738
|
+
}
|
|
1739
|
+
render() {
|
|
1740
|
+
if (this.destroyed)
|
|
1741
|
+
return;
|
|
1742
|
+
this.cancelMoneyAnims(); // stop in-flight count-ups before their nodes are torn down below
|
|
1743
|
+
this.root.classList.toggle('ge-mobile', this.layout === 'mobile');
|
|
1744
|
+
this.barHost.innerHTML = '';
|
|
1745
|
+
this.barHost.appendChild(renderBottomBar(this));
|
|
1746
|
+
this.animateMoney();
|
|
1747
|
+
this.applyFitScale();
|
|
1748
|
+
}
|
|
1749
|
+
cancelMoneyAnims() {
|
|
1750
|
+
for (const cancel of this.moneyAnims)
|
|
1751
|
+
cancel();
|
|
1752
|
+
this.moneyAnims = [];
|
|
1753
|
+
}
|
|
1754
|
+
/** Keep the WIN pill inline between the groups; float it above when it won't fit. */
|
|
1755
|
+
/**
|
|
1756
|
+
* Landscape bar fills the width when it fits. When it overflows, the WIN pill is
|
|
1757
|
+
* lifted above the bar (unscaled, so it stays readable) and the remaining row is
|
|
1758
|
+
* centred and scaled down to fit — keeping the controls as large as possible.
|
|
1759
|
+
*/
|
|
1760
|
+
applyFitScale() {
|
|
1761
|
+
if (this.destroyed)
|
|
1762
|
+
return;
|
|
1763
|
+
const host = this.barHost;
|
|
1764
|
+
const bar = host.querySelector('.ge-shell-bottom');
|
|
1765
|
+
if (!bar)
|
|
1766
|
+
return;
|
|
1767
|
+
// reset to baseline (idempotent — the pill may have been lifted on a prior pass)
|
|
1768
|
+
const pill = host.querySelector('.ge-winpill');
|
|
1769
|
+
if (pill && pill.parentElement === host) { // put a lifted pill back inline
|
|
1770
|
+
const right = bar.querySelector('.ge-zone-right');
|
|
1771
|
+
if (right)
|
|
1772
|
+
bar.insertBefore(pill, right);
|
|
1773
|
+
else
|
|
1774
|
+
bar.appendChild(pill);
|
|
1775
|
+
pill.classList.remove('ge-up');
|
|
1776
|
+
}
|
|
1777
|
+
host.classList.remove('ge-fit');
|
|
1778
|
+
host.style.transform = '';
|
|
1779
|
+
if (this.layout === 'mobile') {
|
|
1780
|
+
// shrink the whole stack to fit narrow phones (mobile-s, or large numbers in a row)
|
|
1781
|
+
let need = 0;
|
|
1782
|
+
for (const row of Array.from(bar.children))
|
|
1783
|
+
need = Math.max(need, row.scrollWidth);
|
|
1784
|
+
const avail = bar.clientWidth;
|
|
1785
|
+
if (need > avail + 1 && avail > 0)
|
|
1786
|
+
host.style.transform = `scale(${Math.max(0.7, avail / need).toFixed(4)})`;
|
|
1787
|
+
return;
|
|
1788
|
+
}
|
|
1789
|
+
if (bar.scrollWidth <= bar.clientWidth + 1)
|
|
1790
|
+
return; // bar fits inline → leave it
|
|
1791
|
+
// overflow: lift the pill onto its own row above the bar (flex column → real 8px gap)
|
|
1792
|
+
if (pill) {
|
|
1793
|
+
host.insertBefore(pill, bar);
|
|
1794
|
+
pill.classList.add('ge-up');
|
|
1795
|
+
}
|
|
1796
|
+
if (bar.scrollWidth <= bar.clientWidth + 1)
|
|
1797
|
+
return; // bar now fits full-width, pill above
|
|
1798
|
+
// still too wide → shrink the whole stack (pill + bar) to fit, anchored bottom-centre
|
|
1799
|
+
host.classList.add('ge-fit');
|
|
1800
|
+
const natural = bar.offsetWidth, avail = this.root.clientWidth - 12;
|
|
1801
|
+
const s = natural > 0 && avail > 0 ? Math.min(1, avail / natural) : 1;
|
|
1802
|
+
host.style.transform = `translateX(-50%) scale(${s.toFixed(4)})`;
|
|
1803
|
+
}
|
|
1804
|
+
/** Spacebar starts a spin — same path as the spin disc. Ignored while a spin is running,
|
|
1805
|
+
* while autoplay is active, outside base mode, when an overlay/modal is open, or when an
|
|
1806
|
+
* editable element is focused. `repeat` (held key) is ignored so it can't spam. */
|
|
1807
|
+
handleKeyDown = (e) => {
|
|
1808
|
+
if (this.destroyed || e.code !== 'Space' || e.repeat)
|
|
1809
|
+
return;
|
|
1810
|
+
const t = e.target;
|
|
1811
|
+
if (t && (t.isContentEditable || /^(INPUT|TEXTAREA|SELECT)$/.test(t.tagName)))
|
|
1812
|
+
return;
|
|
1813
|
+
if (this.modalHost.childElementCount > 0)
|
|
1814
|
+
return; // an overlay/modal is open
|
|
1815
|
+
if (this.state.mode !== 'base' || this.state.busy || this.state.autoplay.active)
|
|
1816
|
+
return;
|
|
1817
|
+
e.preventDefault();
|
|
1818
|
+
this.emit('spin');
|
|
1819
|
+
};
|
|
1820
|
+
setLayout(layout) {
|
|
1821
|
+
if (layout === this.layout)
|
|
1822
|
+
return;
|
|
1823
|
+
this.layout = layout;
|
|
1824
|
+
this.render();
|
|
1825
|
+
}
|
|
1826
|
+
/** Resolve a built-in shell string. English is the source; with `isSocial` it is run through
|
|
1827
|
+
* the social-casino word-swap. Game-supplied strings should NOT be passed through this. */
|
|
1828
|
+
t(text) { return this.config.isSocial ? socialize(text) : text; }
|
|
1829
|
+
/** Toggle the social vocabulary at runtime (re-renders the bar; reopen overlays to refresh them). */
|
|
1830
|
+
setSocial(isSocial) { this.config.isSocial = isSocial; this.render(); }
|
|
1831
|
+
/** Recolour the shell at runtime (e.g. switch dark/light scheme). */
|
|
1832
|
+
setTheme(theme) {
|
|
1833
|
+
this.config.theme = theme;
|
|
1834
|
+
this.root.setAttribute('style', buildThemeVars(theme));
|
|
1835
|
+
}
|
|
1836
|
+
observeLayout() {
|
|
1837
|
+
const RO = globalThis.ResizeObserver;
|
|
1838
|
+
if (typeof RO !== 'function')
|
|
1839
|
+
return; // jsdom: stays 'wide'
|
|
1840
|
+
this.ro = new RO((entries) => {
|
|
1841
|
+
const rect = entries[0]?.contentRect;
|
|
1842
|
+
const w = rect?.width ?? 0, h = rect?.height ?? 0;
|
|
1843
|
+
// portrait → stacked mobile; landscape (incl. popouts) → one row, scaled to fit if it overflows
|
|
1844
|
+
this.setLayout(w !== 0 && h > w ? 'mobile' : 'wide');
|
|
1845
|
+
this.applyFitScale();
|
|
1846
|
+
this.fitModals(); // re-scale open card modals when the popout resizes
|
|
1847
|
+
});
|
|
1848
|
+
this.ro.observe(this.root);
|
|
1849
|
+
}
|
|
1850
|
+
animateMoney() {
|
|
1851
|
+
const fmt = (n) => formatCurrency(n, this.config.currency);
|
|
1852
|
+
const bal = this.barHost.querySelector('[data-ge="balance"]');
|
|
1853
|
+
const win = this.barHost.querySelector('[data-ge="win"]');
|
|
1854
|
+
if (bal && this.state.balance !== this.prevBalance)
|
|
1855
|
+
this.moneyAnims.push(animateReadout(bal, this.prevBalance, this.state.balance, fmt));
|
|
1856
|
+
if (win && this.state.win !== this.prevWin)
|
|
1857
|
+
this.moneyAnims.push(animateReadout(win, this.prevWin, this.state.win, fmt));
|
|
1858
|
+
this.prevBalance = this.state.balance;
|
|
1859
|
+
this.prevWin = this.state.win;
|
|
1860
|
+
}
|
|
1861
|
+
setBalance(n) { this.state.balance = n; this.render(); }
|
|
1862
|
+
setWin(n) { this.state.win = n; this.render(); }
|
|
1863
|
+
setBet(n) { this.state.bet = n; this.render(); }
|
|
1864
|
+
setMode(mode) { this.state.mode = mode; this.render(); }
|
|
1865
|
+
setBusy(busy) { this.state.busy = busy; this.render(); }
|
|
1866
|
+
setAutoplay(a) { this.state.autoplay = a; this.render(); }
|
|
1867
|
+
setTurbo(level) { this.state.turbo = level; this.render(); }
|
|
1868
|
+
setBuyBonusEnabled(enabled) { this.state.buyBonusEnabled = enabled; this.render(); }
|
|
1869
|
+
setFreeSpins(fs) { this.state.freeSpins = fs; this.render(); }
|
|
1870
|
+
showModal(el) {
|
|
1871
|
+
this.modalHost.innerHTML = '';
|
|
1872
|
+
this.modalHost.appendChild(el);
|
|
1873
|
+
this.fitModals();
|
|
1874
|
+
}
|
|
1875
|
+
/** Uniformly scale every open centred card modal (`.ge-sheet`) down so it fits a short/narrow
|
|
1876
|
+
* popout — the same idea as the bar's fit-scale. Covers the pickers, generic + replay modals,
|
|
1877
|
+
* AND the buy-bonus confirm (which is hosted inside the overlay, not directly in modalHost).
|
|
1878
|
+
* Full-screen overlays handle their own responsiveness (scroll + vh-clamp). */
|
|
1879
|
+
fitModals() {
|
|
1880
|
+
if (this.destroyed)
|
|
1881
|
+
return;
|
|
1882
|
+
this.modalHost.querySelectorAll('.ge-sheet').forEach((el) => this.fitSheet(el));
|
|
1883
|
+
}
|
|
1884
|
+
/** Fraction of the frame a card modal may occupy; the rest is breathing-room margin. Keeps
|
|
1885
|
+
* modals from filling a small popout edge-to-edge (so even short pickers scale down there). */
|
|
1886
|
+
static MODAL_FIT = 0.86;
|
|
1887
|
+
fitSheet(root) {
|
|
1888
|
+
const card = root.querySelector('.ge-modal-card');
|
|
1889
|
+
if (!card)
|
|
1890
|
+
return;
|
|
1891
|
+
card.style.transform = ''; // reset before measuring the natural size
|
|
1892
|
+
const availW = root.clientWidth, availH = root.clientHeight;
|
|
1893
|
+
const w = card.offsetWidth, h = card.offsetHeight;
|
|
1894
|
+
if (w <= 0 || h <= 0 || availW <= 0 || availH <= 0)
|
|
1895
|
+
return;
|
|
1896
|
+
const fit = GameShell.MODAL_FIT;
|
|
1897
|
+
const s = Math.min(1, (availW * fit) / w, (availH * fit) / h);
|
|
1898
|
+
if (s < 0.999)
|
|
1899
|
+
card.style.transform = `scale(${s.toFixed(4)})`;
|
|
1900
|
+
}
|
|
1901
|
+
/** Activate a `feature` option (e.g. Ante): the bar shows the effective bet, tinted with
|
|
1902
|
+
* the feature accent, and BUY BONUS becomes DISABLE. */
|
|
1903
|
+
activateFeature(bonus) {
|
|
1904
|
+
this.state.activeFeature = bonus;
|
|
1905
|
+
this.emit('featureActivate', { id: bonus.id });
|
|
1906
|
+
this.render();
|
|
1907
|
+
}
|
|
1908
|
+
/** Clear the active feature — reverts the bet readout and the BUY BONUS button. */
|
|
1909
|
+
deactivateFeature() {
|
|
1910
|
+
const prev = this.state.activeFeature;
|
|
1911
|
+
if (!prev)
|
|
1912
|
+
return;
|
|
1913
|
+
this.state.activeFeature = null;
|
|
1914
|
+
this.emit('featureDeactivate', { id: prev.id });
|
|
1915
|
+
this.render();
|
|
1916
|
+
}
|
|
1917
|
+
openMenu() { this.emit('menuOpen'); this.openSettings(); }
|
|
1918
|
+
openSettings() { this.emit('settingsOpen'); this.showModal(openSettingsModal(this)); }
|
|
1919
|
+
openInfo() { this.emit('infoOpen'); this.showModal(openGameInfoModal(this)); }
|
|
1920
|
+
openBuyBonus() {
|
|
1921
|
+
const overlay = openBuyBonusOverlay(this);
|
|
1922
|
+
if (overlay)
|
|
1923
|
+
this.showModal(overlay);
|
|
1924
|
+
}
|
|
1925
|
+
/** Open a generic, externally-driven modal (title + body + optional action buttons).
|
|
1926
|
+
* Each action runs its `on` then closes; the ✕ shows when `availableClose` is true. */
|
|
1927
|
+
openModal(opts) { this.showModal(buildModal(opts)); }
|
|
1928
|
+
/** Open the non-dismissable replay summary modal (START REPLAY → onReplay → reopen). */
|
|
1929
|
+
openReplay(opts) {
|
|
1930
|
+
if (this.destroyed)
|
|
1931
|
+
return;
|
|
1932
|
+
this.showModal(buildReplayModal(this, opts));
|
|
1933
|
+
}
|
|
1934
|
+
/** Bet picker — list of available bets with an accent Confirm. */
|
|
1935
|
+
openBetPicker() { this.showModal(openBetModal(this)); }
|
|
1936
|
+
/** Autoplay picker — spin-count list; Confirm starts autoplay. */
|
|
1937
|
+
openAutoplayPicker() { this.showModal(openAutoplayModal(this)); }
|
|
1938
|
+
destroy() {
|
|
1939
|
+
if (this.destroyed)
|
|
1940
|
+
return Promise.resolve();
|
|
1941
|
+
this.destroyed = true;
|
|
1942
|
+
this.ro?.disconnect();
|
|
1943
|
+
this.ro = null;
|
|
1944
|
+
if (this.keysBound) {
|
|
1945
|
+
document.removeEventListener('keydown', this.handleKeyDown);
|
|
1946
|
+
this.keysBound = false;
|
|
1947
|
+
}
|
|
1948
|
+
this.cancelMoneyAnims();
|
|
1949
|
+
this.removeAllListeners();
|
|
1950
|
+
this.root.classList.add('ge-shell-hidden');
|
|
1951
|
+
return new Promise((resolve) => {
|
|
1952
|
+
setTimeout(() => {
|
|
1953
|
+
this.root.remove();
|
|
1954
|
+
this.styleEl.remove();
|
|
1955
|
+
resolve();
|
|
1956
|
+
}, REMOVE_FADE_MS);
|
|
1957
|
+
});
|
|
1958
|
+
}
|
|
1959
|
+
}
|
|
1960
|
+
/** Count-up the trailing text node of a .ge-rd readout (keeps its label span).
|
|
1961
|
+
* Returns the count-up canceler so the shell can stop it before the node is replaced. */
|
|
1962
|
+
function animateReadout(el, from, to, fmt) {
|
|
1963
|
+
const textNode = el.lastChild;
|
|
1964
|
+
if (!textNode || textNode.nodeType !== Node.TEXT_NODE) {
|
|
1965
|
+
el.textContent = fmt(to);
|
|
1966
|
+
return () => { };
|
|
1967
|
+
}
|
|
1968
|
+
const proxy = {
|
|
1969
|
+
set textContent(v) { textNode.data = v; },
|
|
1970
|
+
get textContent() { return textNode.data; },
|
|
1971
|
+
};
|
|
1972
|
+
return countUp(proxy, from, to, fmt);
|
|
1973
|
+
}
|
|
1974
|
+
|
|
1975
|
+
let active = null;
|
|
1976
|
+
function createGameShell(config) {
|
|
1977
|
+
if (active)
|
|
1978
|
+
return active;
|
|
1979
|
+
active = new GameShell(config);
|
|
1980
|
+
return active;
|
|
1981
|
+
}
|
|
1982
|
+
function removeGameShell() {
|
|
1983
|
+
if (!active)
|
|
1984
|
+
return Promise.resolve();
|
|
1985
|
+
const shell = active;
|
|
1986
|
+
active = null;
|
|
1987
|
+
return shell.destroy();
|
|
1988
|
+
}
|
|
1989
|
+
|
|
1990
|
+
exports.GameShell = GameShell;
|
|
1991
|
+
exports.createGameShell = createGameShell;
|
|
1992
|
+
exports.removeGameShell = removeGameShell;
|
|
1993
|
+
//# sourceMappingURL=shell.cjs.js.map
|