@energy8platform/shell 0.5.0 → 0.6.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/html.cjs.js +47 -6
- package/dist/html.cjs.js.map +1 -1
- package/dist/html.d.ts +34 -5
- package/dist/html.esm.js +47 -6
- package/dist/html.esm.js.map +1 -1
- package/dist/index.cjs.js +37 -2
- package/dist/index.cjs.js.map +1 -1
- package/dist/index.d.ts +34 -5
- package/dist/index.esm.js +37 -2
- package/dist/index.esm.js.map +1 -1
- package/dist/pixi.cjs.js +69 -10
- package/dist/pixi.cjs.js.map +1 -1
- package/dist/pixi.d.ts +34 -5
- package/dist/pixi.esm.js +69 -10
- package/dist/pixi.esm.js.map +1 -1
- package/package.json +1 -1
- package/src/core/ShellController.ts +29 -1
- package/src/core/renderer.ts +8 -1
- package/src/core/state.ts +10 -0
- package/src/core/types.ts +12 -1
- package/src/core/version.ts +1 -1
- package/src/ui/html/components/Settings.ts +14 -5
- package/src/ui/pixi/PixiRenderer.ts +9 -0
- package/src/ui/pixi/components/Settings.ts +23 -8
- package/src/ui/pixi/primitives/controls.ts +7 -0
package/dist/pixi.d.ts
CHANGED
|
@@ -23,6 +23,9 @@ declare class EventEmitter<TEvents extends {}> {
|
|
|
23
23
|
* `bonus` pairs with `setBonus()` to show a game-supplied label + value (adventure, hold-and-spin,
|
|
24
24
|
* respins — anything that isn't a plain free-spins counter). */
|
|
25
25
|
type ShellMode = 'base' | 'bonus' | 'freeSpins' | 'replay';
|
|
26
|
+
/** The three independent volume sliders shown in the Settings overlay. */
|
|
27
|
+
type VolumeKey = 'master' | 'music' | 'sfx';
|
|
28
|
+
type VolumeLevels = Record<VolumeKey, number>;
|
|
26
29
|
interface CurrencyConfig {
|
|
27
30
|
symbol: string;
|
|
28
31
|
position: 'left' | 'right';
|
|
@@ -284,9 +287,13 @@ interface ShellConfig {
|
|
|
284
287
|
* opening the built-in buy-bonus overlay (e.g. the game shows its own bonus UI). The button
|
|
285
288
|
* is shown whenever this OR `features.buyBonus` is set. */
|
|
286
289
|
onBonusBuy?: () => void;
|
|
290
|
+
/** Initial Settings-overlay volume slider positions (each 0..1, defaults to 1 = 100%). The shell
|
|
291
|
+
* keeps them stateful across opens; read/update at runtime via `shell.getVolume()` /
|
|
292
|
+
* `shell.setVolume()`, and listen to `settingChange` ({ key: 'master'|'music'|'sfx' }) to apply. */
|
|
293
|
+
volumes?: Partial<VolumeLevels>;
|
|
287
294
|
}
|
|
288
295
|
/** ShellConfig after the controller applies defaults (version, isSocial, replay, theme). No mount. */
|
|
289
|
-
type ResolvedShellConfig = Required<Pick<ShellConfig, 'language' | 'currency' | 'availableBets' | 'defaultBet' | 'balance' | 'win' | 'mode' | 'features' | 'gameInfo' | 'version' | 'isSocial' | 'replay'>> & Pick<ShellConfig, 'currentBet' | 'theme' | 'onBonusBuy'>;
|
|
296
|
+
type ResolvedShellConfig = Required<Pick<ShellConfig, 'language' | 'currency' | 'availableBets' | 'defaultBet' | 'balance' | 'win' | 'mode' | 'features' | 'gameInfo' | 'version' | 'isSocial' | 'replay'>> & Pick<ShellConfig, 'currentBet' | 'theme' | 'onBonusBuy' | 'volumes'>;
|
|
290
297
|
interface ShellState {
|
|
291
298
|
mode: ShellMode;
|
|
292
299
|
/** Sticky replay marker — true for a historical-round replay, regardless of the current
|
|
@@ -311,6 +318,9 @@ interface ShellState {
|
|
|
311
318
|
/** The currently activated `feature` option (e.g. Ante), or null. Drives the
|
|
312
319
|
* effective-bet readout tint and the BUY BONUS → DISABLE toggle on the bar. */
|
|
313
320
|
activeFeature: BonusOption$1 | null;
|
|
321
|
+
/** Volume slider positions (0..1) surfaced in the Settings overlay. Stateful across opens so a
|
|
322
|
+
* reopened overlay reflects the last-set positions instead of resetting to 100%. */
|
|
323
|
+
volumes: VolumeLevels;
|
|
314
324
|
}
|
|
315
325
|
interface ShellEvents {
|
|
316
326
|
spin: void;
|
|
@@ -433,6 +443,13 @@ interface ShellHost {
|
|
|
433
443
|
setSound(on: boolean): void;
|
|
434
444
|
/** An open Settings overlay registers an icon updater here (null clears it on close). */
|
|
435
445
|
setSoundRefresh(fn: ((on: boolean) => void) | null): void;
|
|
446
|
+
/** Current volume slider position (0..1) for master/music/sfx. */
|
|
447
|
+
getVolume(key: VolumeKey): number;
|
|
448
|
+
/** Set a volume slider (0..1): clamps, stores, emits `settingChange`, and live-updates an open
|
|
449
|
+
* Settings overlay. Called by the slider control on drag AND by game code as the public API. */
|
|
450
|
+
setVolume(key: VolumeKey, value: number): void;
|
|
451
|
+
/** An open Settings overlay registers a slider updater here (null clears it on close). */
|
|
452
|
+
setVolumeRefresh(fn: ((key: VolumeKey, value: number) => void) | null): void;
|
|
436
453
|
/** Logic-bearing actions invoked by renderer controls. */
|
|
437
454
|
readonly actions: ShellActions;
|
|
438
455
|
}
|
|
@@ -496,13 +513,14 @@ declare class ShellController extends EventEmitter<ShellEvents> implements Shell
|
|
|
496
513
|
tokens: ShellTokens;
|
|
497
514
|
layout: ShellLayoutMode;
|
|
498
515
|
soundOn: boolean;
|
|
499
|
-
readonly engineVersion = "0.
|
|
516
|
+
readonly engineVersion = "0.6.1";
|
|
500
517
|
readonly actions: ShellActions;
|
|
501
518
|
private renderer;
|
|
502
519
|
private i18n;
|
|
503
520
|
private kbd?;
|
|
504
521
|
private overlay;
|
|
505
522
|
private soundRefresh;
|
|
523
|
+
private volumeRefresh;
|
|
506
524
|
private prevBalance;
|
|
507
525
|
private prevWin;
|
|
508
526
|
private destroyed;
|
|
@@ -527,11 +545,22 @@ declare class ShellController extends EventEmitter<ShellEvents> implements Shell
|
|
|
527
545
|
closeModal(): void;
|
|
528
546
|
setSound(on: boolean): void;
|
|
529
547
|
setSoundRefresh(fn: ((on: boolean) => void) | null): void;
|
|
548
|
+
getVolume(key: VolumeKey): number;
|
|
549
|
+
/** Set a volume slider (0..1). Shared by the slider control (drag) and game code (public API):
|
|
550
|
+
* clamps, stores so a reopened Settings overlay reflects it, emits `settingChange`, and
|
|
551
|
+
* live-updates the slider if the overlay is currently open. */
|
|
552
|
+
setVolume(key: VolumeKey, value: number): void;
|
|
553
|
+
setVolumeRefresh(fn: ((key: VolumeKey, value: number) => void) | null): void;
|
|
530
554
|
activateFeature(bonus: BonusOption$1): void;
|
|
531
555
|
deactivateFeature(): void;
|
|
532
556
|
private money;
|
|
533
557
|
setBalance(n: number): void;
|
|
534
|
-
|
|
558
|
+
/** Set the WIN readout. Counts up/down from the previous value by default. Pass
|
|
559
|
+
* `{ animate: false }` to SNAP instantly (renderBar cancels any in-flight count-up) — used by the
|
|
560
|
+
* host to clear WIN to 0 at spin start, where an animated count-DOWN would look wrong. */
|
|
561
|
+
setWin(n: number, opts?: {
|
|
562
|
+
animate?: boolean;
|
|
563
|
+
}): void;
|
|
535
564
|
setBet(n: number): void;
|
|
536
565
|
setMode(mode: ShellMode): void;
|
|
537
566
|
setBusy(busy: boolean): void;
|
|
@@ -566,7 +595,7 @@ interface I18n {
|
|
|
566
595
|
declare function createI18n(opts: I18nOptions): I18n;
|
|
567
596
|
|
|
568
597
|
/** The @energy8platform/shell package version, stamped into the game-info footer. */
|
|
569
|
-
declare const PACKAGE_VERSION = "0.
|
|
598
|
+
declare const PACKAGE_VERSION = "0.6.1";
|
|
570
599
|
|
|
571
600
|
/** A shell: the renderer-agnostic controller plus the surface facade (safeArea/barHeight/setVisible)
|
|
572
601
|
* an embedding host reads. `createShell`, `createGameShell` and `createPixiShell` all return this. */
|
|
@@ -706,4 +735,4 @@ declare function createPixiShell(config: PixiShellConfig): PixiGameShell;
|
|
|
706
735
|
declare function removePixiShell(): Promise<void>;
|
|
707
736
|
|
|
708
737
|
export { DEFAULT_ACCENT, PACKAGE_VERSION, PixiRenderer, SCHEMES, ShellController, createI18n, createPixiShell, createShell, normalizeLang, removePixiShell, resolveConfig, resolveTheme, socialize };
|
|
709
|
-
export type { AutoplayConfig, AutoplayOptions, BonusCardContext, BonusOption, BonusReadout, CellRef, CreateShellOptions, CurrencyConfig, FreeSpinsState, GameInfoContent, GameInfoSection, GameMode, I18n, I18nOptions, Lang, ModalAction, ModalOptions, OverlayHandle, OverlayRequest, PaylineDef, PaytableRow, PixiGameShell, PixiShellConfig, PixiShellSurface, ReplayModalOptions, ResolvedShellConfig, SafeArea, ShapeDef, Shell, ShellActions, ShellConfig, ShellEvents, ShellFeatures, ShellHost, ShellLayoutMode, ShellMode, ShellRenderer, ShellState, ShellSurface, ShellTokens, ThemeConfig, WinSection };
|
|
738
|
+
export type { AutoplayConfig, AutoplayOptions, BonusCardContext, BonusOption, BonusReadout, CellRef, CreateShellOptions, CurrencyConfig, FreeSpinsState, GameInfoContent, GameInfoSection, GameMode, I18n, I18nOptions, Lang, ModalAction, ModalOptions, OverlayHandle, OverlayRequest, PaylineDef, PaytableRow, PixiGameShell, PixiShellConfig, PixiShellSurface, ReplayModalOptions, ResolvedShellConfig, SafeArea, ShapeDef, Shell, ShellActions, ShellConfig, ShellEvents, ShellFeatures, ShellHost, ShellLayoutMode, ShellMode, ShellRenderer, ShellState, ShellSurface, ShellTokens, ThemeConfig, VolumeKey, VolumeLevels, WinSection };
|
package/dist/pixi.esm.js
CHANGED
|
@@ -66,8 +66,17 @@ function createInitialState(config) {
|
|
|
66
66
|
freeSpins: { current: 0, total: 0, totalWin: 0 },
|
|
67
67
|
bonus: null,
|
|
68
68
|
activeFeature: null,
|
|
69
|
+
volumes: {
|
|
70
|
+
master: clampVolume(config.volumes?.master),
|
|
71
|
+
music: clampVolume(config.volumes?.music),
|
|
72
|
+
sfx: clampVolume(config.volumes?.sfx),
|
|
73
|
+
},
|
|
69
74
|
};
|
|
70
75
|
}
|
|
76
|
+
/** Clamp a configured volume to 0..1, defaulting to full (1) when unset/invalid. */
|
|
77
|
+
function clampVolume(v) {
|
|
78
|
+
return typeof v === 'number' && Number.isFinite(v) ? Math.max(0, Math.min(1, v)) : 1;
|
|
79
|
+
}
|
|
71
80
|
/** Step bet up/down within availableBets, clamped at the ends. */
|
|
72
81
|
function stepBet(state, direction) {
|
|
73
82
|
const idx = state.availableBets.indexOf(state.bet);
|
|
@@ -1438,7 +1447,7 @@ class KeyboardController {
|
|
|
1438
1447
|
|
|
1439
1448
|
// AUTO-GENERATED by scripts/gen-version.mjs — do not edit. Mirrors package.json "version".
|
|
1440
1449
|
/** The @energy8platform/shell package version, stamped into the game-info footer. */
|
|
1441
|
-
const PACKAGE_VERSION = '0.
|
|
1450
|
+
const PACKAGE_VERSION = '0.6.1';
|
|
1442
1451
|
|
|
1443
1452
|
/** Apply defaults to the raw config (the mount target lives on the renderer, not here). */
|
|
1444
1453
|
function resolveConfig(config) {
|
|
@@ -1455,6 +1464,7 @@ function resolveConfig(config) {
|
|
|
1455
1464
|
features: config.features,
|
|
1456
1465
|
theme: config.theme,
|
|
1457
1466
|
onBonusBuy: config.onBonusBuy,
|
|
1467
|
+
volumes: config.volumes,
|
|
1458
1468
|
version: config.version ?? '1.0.0',
|
|
1459
1469
|
isSocial: config.isSocial ?? false,
|
|
1460
1470
|
replay: config.replay ?? config.mode === 'replay',
|
|
@@ -1476,6 +1486,7 @@ class ShellController extends EventEmitter {
|
|
|
1476
1486
|
kbd;
|
|
1477
1487
|
overlay = null;
|
|
1478
1488
|
soundRefresh = null;
|
|
1489
|
+
volumeRefresh = null;
|
|
1479
1490
|
prevBalance;
|
|
1480
1491
|
prevWin;
|
|
1481
1492
|
destroyed = false;
|
|
@@ -1659,6 +1670,7 @@ class ShellController extends EventEmitter {
|
|
|
1659
1670
|
return;
|
|
1660
1671
|
this.overlay = null;
|
|
1661
1672
|
this.soundRefresh = null;
|
|
1673
|
+
this.volumeRefresh = null;
|
|
1662
1674
|
this.renderer.closeOverlay();
|
|
1663
1675
|
}
|
|
1664
1676
|
// ── sound ──────────────────────────────────────────────────────────────────
|
|
@@ -1671,6 +1683,22 @@ class ShellController extends EventEmitter {
|
|
|
1671
1683
|
setSoundRefresh(fn) {
|
|
1672
1684
|
this.soundRefresh = fn;
|
|
1673
1685
|
}
|
|
1686
|
+
// ── volume ─────────────────────────────────────────────────────────────────
|
|
1687
|
+
getVolume(key) {
|
|
1688
|
+
return this.state.volumes[key];
|
|
1689
|
+
}
|
|
1690
|
+
/** Set a volume slider (0..1). Shared by the slider control (drag) and game code (public API):
|
|
1691
|
+
* clamps, stores so a reopened Settings overlay reflects it, emits `settingChange`, and
|
|
1692
|
+
* live-updates the slider if the overlay is currently open. */
|
|
1693
|
+
setVolume(key, value) {
|
|
1694
|
+
const v = Math.max(0, Math.min(1, value));
|
|
1695
|
+
this.state.volumes[key] = v;
|
|
1696
|
+
this.emit('settingChange', { key, value: v });
|
|
1697
|
+
this.volumeRefresh?.(key, v);
|
|
1698
|
+
}
|
|
1699
|
+
setVolumeRefresh(fn) {
|
|
1700
|
+
this.volumeRefresh = fn;
|
|
1701
|
+
}
|
|
1674
1702
|
// ── features ─────────────────────────────────────────────────────────────────
|
|
1675
1703
|
activateFeature(bonus) {
|
|
1676
1704
|
this.state.activeFeature = bonus;
|
|
@@ -1697,10 +1725,17 @@ class ShellController extends EventEmitter {
|
|
|
1697
1725
|
this.prevBalance = n;
|
|
1698
1726
|
this.money('balance', from, n);
|
|
1699
1727
|
}
|
|
1700
|
-
|
|
1728
|
+
/** Set the WIN readout. Counts up/down from the previous value by default. Pass
|
|
1729
|
+
* `{ animate: false }` to SNAP instantly (renderBar cancels any in-flight count-up) — used by the
|
|
1730
|
+
* host to clear WIN to 0 at spin start, where an animated count-DOWN would look wrong. */
|
|
1731
|
+
setWin(n, opts) {
|
|
1701
1732
|
const from = this.prevWin;
|
|
1702
1733
|
this.state.win = n;
|
|
1703
1734
|
this.prevWin = n;
|
|
1735
|
+
if (opts?.animate === false) {
|
|
1736
|
+
this.renderer.renderBar(); // instant repaint from state; cancels running money anims
|
|
1737
|
+
return;
|
|
1738
|
+
}
|
|
1704
1739
|
this.money('win', from, n);
|
|
1705
1740
|
}
|
|
1706
1741
|
setBet(n) {
|
|
@@ -3947,6 +3982,12 @@ class Slider extends Container {
|
|
|
3947
3982
|
this.thumb.circle(cx, cy, r).fill('#ffffff');
|
|
3948
3983
|
this.hitArea = new Rectangle(0, 0, this.w, h);
|
|
3949
3984
|
}
|
|
3985
|
+
/** Programmatically move the thumb (0..1) without firing onInput — for live updates driven by
|
|
3986
|
+
* `host.setVolume()` while the overlay is open. */
|
|
3987
|
+
setValue(v) {
|
|
3988
|
+
this._value = Math.max(0, Math.min(1, v));
|
|
3989
|
+
this.draw();
|
|
3990
|
+
}
|
|
3950
3991
|
setLayoutSize(w) {
|
|
3951
3992
|
if (w != null)
|
|
3952
3993
|
this.w = w;
|
|
@@ -4035,10 +4076,13 @@ function buildBody$1(host, width) {
|
|
|
4035
4076
|
speaker.active = on;
|
|
4036
4077
|
});
|
|
4037
4078
|
col.add(glassRow(host, [textNode(host, host.t('Sound')), new Spacer(), speaker]));
|
|
4038
|
-
// Volume sliders
|
|
4039
|
-
|
|
4040
|
-
|
|
4041
|
-
col.add(sliderRow(host, width, '
|
|
4079
|
+
// Volume sliders — positions read from the shell's stored volumes (stateful across opens); the
|
|
4080
|
+
// registered refreshers let `host.setVolume()` from game code move the thumbs live.
|
|
4081
|
+
const updaters = {};
|
|
4082
|
+
col.add(sliderRow(host, width, 'master', host.t('Master volume'), updaters));
|
|
4083
|
+
col.add(sliderRow(host, width, 'music', host.t('Music'), updaters));
|
|
4084
|
+
col.add(sliderRow(host, width, 'sfx', host.t('SFX'), updaters));
|
|
4085
|
+
host.setVolumeRefresh?.((key, v) => updaters[key]?.(v));
|
|
4042
4086
|
// Game info link
|
|
4043
4087
|
const infoIcon = makeIcon('info', 22, '#ffffff');
|
|
4044
4088
|
const infoLabel = textNode(host, host.t('Game info'));
|
|
@@ -4082,7 +4126,7 @@ function glassRow(host, children, opts = {}) {
|
|
|
4082
4126
|
return row;
|
|
4083
4127
|
}
|
|
4084
4128
|
/** A column row: head (label + live % value) over a draggable slider. */
|
|
4085
|
-
function sliderRow(host, bodyWidth, key, label) {
|
|
4129
|
+
function sliderRow(host, bodyWidth, key, label, updaters) {
|
|
4086
4130
|
const row = new FlexBox({
|
|
4087
4131
|
direction: 'column',
|
|
4088
4132
|
align: 'stretch',
|
|
@@ -4091,14 +4135,19 @@ function sliderRow(host, bodyWidth, key, label) {
|
|
|
4091
4135
|
background: { fill: host.tokens.plaqueGlass, radius: 16 },
|
|
4092
4136
|
});
|
|
4093
4137
|
const head = new FlexBox({ direction: 'row', align: 'center', justify: 'space-between' });
|
|
4094
|
-
const
|
|
4138
|
+
const initial = host.getVolume(key);
|
|
4139
|
+
const valueText = makeText(`${Math.round(initial * 100)}%`, { size: 13, weight: '700', color: host.tokens.plaqueLabel });
|
|
4095
4140
|
head.add(makeText(label, { size: 14, weight: '600', color: '#ffffff' }));
|
|
4096
4141
|
head.add(new Spacer(), { grow: 1 });
|
|
4097
4142
|
head.add(valueText);
|
|
4098
|
-
const slider = new Slider(host,
|
|
4143
|
+
const slider = new Slider(host, initial, (v) => {
|
|
4099
4144
|
valueText.text = `${Math.round(v * 100)}%`;
|
|
4100
|
-
host.
|
|
4145
|
+
host.setVolume(key, v);
|
|
4101
4146
|
});
|
|
4147
|
+
updaters[key] = (v) => {
|
|
4148
|
+
valueText.text = `${Math.round(v * 100)}%`;
|
|
4149
|
+
slider.setValue(v);
|
|
4150
|
+
};
|
|
4102
4151
|
row.add(head);
|
|
4103
4152
|
row.add(slider);
|
|
4104
4153
|
return row;
|
|
@@ -5882,6 +5931,13 @@ class PixiRenderer {
|
|
|
5882
5931
|
// make sure the stage delivers global pointer moves (drag, sliders)
|
|
5883
5932
|
this.app.stage.eventMode = 'static';
|
|
5884
5933
|
this.app.renderer.on('resize', this.onResize);
|
|
5934
|
+
// Seed the layout from the CURRENT screen size. The renderer was resized to the container during
|
|
5935
|
+
// boot (ViewportManager.refresh) BEFORE this shell mounted and subscribed above, so that initial
|
|
5936
|
+
// 'resize' event is already gone and won't fire again on a stationary device. Without this seed
|
|
5937
|
+
// the controller's layout stays at its 'wide' DEFAULT — a portrait mobile would show the DESKTOP
|
|
5938
|
+
// bar. (The HTML renderer gets this for free: its ResizeObserver fires immediately on observe.)
|
|
5939
|
+
if (this.screenW > 0)
|
|
5940
|
+
this.host.notifyResize(this.screenW, this.screenH);
|
|
5885
5941
|
whenFontReady(() => {
|
|
5886
5942
|
if (!this.destroyed)
|
|
5887
5943
|
this.renderBar();
|
|
@@ -6128,6 +6184,9 @@ class PixiRenderer {
|
|
|
6128
6184
|
notifyResize: (w, h) => host.notifyResize(w, h),
|
|
6129
6185
|
setSound: (on) => host.setSound(on),
|
|
6130
6186
|
setSoundRefresh: (fn) => host.setSoundRefresh(fn),
|
|
6187
|
+
getVolume: (key) => host.getVolume(key),
|
|
6188
|
+
setVolume: (key, v) => host.setVolume(key, v),
|
|
6189
|
+
setVolumeRefresh: (fn) => host.setVolumeRefresh(fn),
|
|
6131
6190
|
// — Pixi-specific surface —
|
|
6132
6191
|
get ticker() { return self.app.ticker; },
|
|
6133
6192
|
get canvas() { return self.app.canvas; },
|