@energy8platform/game-engine 0.33.8 → 0.33.10
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/host.cjs.js +41 -1
- package/dist/host.cjs.js.map +1 -1
- package/dist/host.d.ts +58 -2
- package/dist/host.esm.js +41 -2
- package/dist/host.esm.js.map +1 -1
- package/dist/slot.cjs.js +2 -0
- package/dist/slot.cjs.js.map +1 -1
- package/dist/slot.d.ts +18 -10
- package/dist/slot.esm.js +2 -0
- package/dist/slot.esm.js.map +1 -1
- package/dist/vite.cjs.js +1 -0
- package/dist/vite.cjs.js.map +1 -1
- package/dist/vite.d.ts +6 -0
- package/dist/vite.esm.js +1 -0
- package/dist/vite.esm.js.map +1 -1
- package/package.json +3 -3
- package/src/host/createSlotGame.ts +15 -1
- package/src/host/index.ts +3 -0
- package/src/host/sceneController.ts +20 -0
- package/src/host/winReporter.ts +57 -0
- package/src/slot/index.ts +1 -1
- package/src/slot/system/ReelSystem.ts +20 -3
- package/src/vite/index.ts +8 -0
package/dist/host.d.ts
CHANGED
|
@@ -956,6 +956,43 @@ declare function resolveReplayBonusId(model: GameModel, stakeMode: string): stri
|
|
|
956
956
|
*/
|
|
957
957
|
declare function resolveStartScene(scenes: SceneRegistration[], isReplay: boolean, explicitStart?: string): string;
|
|
958
958
|
|
|
959
|
+
/**
|
|
960
|
+
* Gate for the PROGRESSIVE (cascade / tumble) WIN readout.
|
|
961
|
+
*
|
|
962
|
+
* By default the WIN readout is entirely host-driven: cleared to 0 when a segment starts, set to
|
|
963
|
+
* that segment's win once `onSpin` resolves. A cascade game pays in steps, so its scene wants the
|
|
964
|
+
* number to climb WHILE the segment presents — `api.shell.reportWin(amountSoFar)`.
|
|
965
|
+
*
|
|
966
|
+
* The gate keeps the host in charge:
|
|
967
|
+
* - reports are honoured ONLY between `open()` (segment start, WIN already cleared) and `close()`
|
|
968
|
+
* (the host paints the segment's final value) — a scene still animating after an abort, or one
|
|
969
|
+
* reporting from a mode transition, can't overwrite the host's number,
|
|
970
|
+
* - `amountSoFar` is ABSOLUTE (the segment's win up to now, not the step delta), so a re-report or
|
|
971
|
+
* a collapse-to-final on skip is idempotent. Reports should be non-decreasing within a segment;
|
|
972
|
+
* a lower value counts the readout back DOWN,
|
|
973
|
+
* - garbage (NaN / Infinity) is dropped and negatives clamp to 0, so a math bug can't paint "NaN"
|
|
974
|
+
* into the bar.
|
|
975
|
+
*
|
|
976
|
+
* Pure + unit-testable: the shell paint is injected.
|
|
977
|
+
*/
|
|
978
|
+
interface WinReportOptions {
|
|
979
|
+
/** `false` snaps instead of counting up (e.g. collapsing to the final value on skip). */
|
|
980
|
+
animate?: boolean;
|
|
981
|
+
/** Count-up length in ms (shell default 450) — pass the cascade step's length. */
|
|
982
|
+
durationMs?: number;
|
|
983
|
+
}
|
|
984
|
+
interface WinReporter {
|
|
985
|
+
/** Scene-facing (`api.shell.reportWin`): the win accumulated by the presenting segment so far. */
|
|
986
|
+
report(amountSoFar: number, opts?: WinReportOptions): void;
|
|
987
|
+
/** A segment starts presenting — the scene may now grow WIN. */
|
|
988
|
+
open(): void;
|
|
989
|
+
/** The host takes the readout back (final value, round end, or an error). */
|
|
990
|
+
close(): void;
|
|
991
|
+
/** True while reports are honoured. */
|
|
992
|
+
readonly accepting: boolean;
|
|
993
|
+
}
|
|
994
|
+
declare function createWinReporter(paint: (amount: number, opts?: WinReportOptions) => void): WinReporter;
|
|
995
|
+
|
|
959
996
|
/** Everything a scene needs to render one segment. The host builds it per segment. */
|
|
960
997
|
interface RenderContext {
|
|
961
998
|
/** Bet for this round (major units). Stable for the whole round. */
|
|
@@ -1023,6 +1060,25 @@ interface SceneShell {
|
|
|
1023
1060
|
bottom: number;
|
|
1024
1061
|
left: number;
|
|
1025
1062
|
};
|
|
1063
|
+
/**
|
|
1064
|
+
* Grow the shell's WIN readout WHILE a segment presents — for cascade/tumble games that pay in
|
|
1065
|
+
* steps instead of one lump at the end.
|
|
1066
|
+
*
|
|
1067
|
+
* `amountSoFar` is ABSOLUTE: the win accumulated by this segment up to now (not the step's
|
|
1068
|
+
* delta), so a re-report, a skip, or an aborted step can just restate the truth. The host owns
|
|
1069
|
+
* the readout around it — it clears WIN to 0 when the segment starts and sets the segment's
|
|
1070
|
+
* final value once `onSpin` resolves (counting up from your last report, so matching numbers
|
|
1071
|
+
* produce no jump). In a bonus this moves WIN only; the Total Win accumulator still lands once
|
|
1072
|
+
* per segment.
|
|
1073
|
+
*
|
|
1074
|
+
* Only honoured while a segment is presenting (inside `onSpin`); calls from anywhere else are
|
|
1075
|
+
* ignored, so a scene still ticking after an abort can't overwrite the host's final number.
|
|
1076
|
+
*
|
|
1077
|
+
* `durationMs` sets this count-up's length (default 450ms) — pass your step length (or a shorter
|
|
1078
|
+
* one under turbo) so each count-up finishes before the next step lands. `{ animate: false }`
|
|
1079
|
+
* snaps, e.g. when collapsing to the final value on skip.
|
|
1080
|
+
*/
|
|
1081
|
+
reportWin(amountSoFar: number, opts?: WinReportOptions): void;
|
|
1026
1082
|
}
|
|
1027
1083
|
interface AutoplaySceneState {
|
|
1028
1084
|
running: boolean;
|
|
@@ -1070,5 +1126,5 @@ interface SlotSceneController<T extends SlotSpinResultBase = SlotSpinResultBase>
|
|
|
1070
1126
|
onResume?(): void;
|
|
1071
1127
|
}
|
|
1072
1128
|
|
|
1073
|
-
export { buildShellConfig, createSlotGame, resolveReplayBonusId, resolveStartScene, stakeForAction };
|
|
1074
|
-
export type { AutoplaySceneState, CreateSlotGameOptions, OverlayShowOptions, RenderContext, SceneApi, SceneAudio, SceneNavData, SceneOverlay, SceneRegistration, SceneShell, ShellFactory, SlotGameHandle, SlotSceneController, SlotShellOptions, StakeIntegration };
|
|
1129
|
+
export { buildShellConfig, createSlotGame, createWinReporter, resolveReplayBonusId, resolveStartScene, stakeForAction };
|
|
1130
|
+
export type { AutoplaySceneState, CreateSlotGameOptions, OverlayShowOptions, RenderContext, SceneApi, SceneAudio, SceneNavData, SceneOverlay, SceneRegistration, SceneShell, ShellFactory, SlotGameHandle, SlotSceneController, SlotShellOptions, StakeIntegration, WinReportOptions, WinReporter };
|
package/dist/host.esm.js
CHANGED
|
@@ -2501,6 +2501,11 @@ async function createSlotGame(opts) {
|
|
|
2501
2501
|
size: () => ({ width: game.app.screen.width, height: game.app.screen.height }),
|
|
2502
2502
|
});
|
|
2503
2503
|
game.on('resize', ({ width, height }) => overlayCtl.resize(width, height));
|
|
2504
|
+
// Progressive WIN readout for cascade/tumble scenes. The window is open from the moment a
|
|
2505
|
+
// segment starts (WIN cleared to 0) until the host has painted that segment's final win; outside
|
|
2506
|
+
// it the host owns the readout alone. See winReporter.ts.
|
|
2507
|
+
const { createWinReporter } = await Promise.resolve().then(function () { return winReporter; });
|
|
2508
|
+
const winReporter$1 = createWinReporter((amount, opts) => shell.setWin(amount, opts));
|
|
2504
2509
|
// Capabilities injected once per controller scene via onCreate (see `gameScene`/`ensureCreated`).
|
|
2505
2510
|
sceneApi = {
|
|
2506
2511
|
audio: createSceneAudio(game.audio),
|
|
@@ -2509,6 +2514,7 @@ async function createSlotGame(opts) {
|
|
|
2509
2514
|
get safeArea() {
|
|
2510
2515
|
return shell.safeArea;
|
|
2511
2516
|
},
|
|
2517
|
+
reportWin: winReporter$1.report,
|
|
2512
2518
|
},
|
|
2513
2519
|
formatAmount: (v) => shell.formatWin(v),
|
|
2514
2520
|
get bet() {
|
|
@@ -2748,12 +2754,15 @@ async function createSlotGame(opts) {
|
|
|
2748
2754
|
// then counts UP to this segment's delta. prevWin (the cumulative-delta tracker) is
|
|
2749
2755
|
// untouched; this only resets the DISPLAY.
|
|
2750
2756
|
shell.setWin(0, { animate: false });
|
|
2757
|
+
winReporter$1.open(); // the scene may now grow WIN per cascade step
|
|
2751
2758
|
},
|
|
2752
2759
|
onSpinStart: () => scene.onSpinStart?.(),
|
|
2753
2760
|
onSpinEnd: (last, ctx) => scene.onSpinEnd?.(last, ctx),
|
|
2754
2761
|
afterPresent: (r) => {
|
|
2755
2762
|
// WIN readout = THIS spin's win (cumulative delta); the cumulative total goes to the
|
|
2756
|
-
// bonus counter (totalWin) via settle(), not the WIN readout.
|
|
2763
|
+
// bonus counter (totalWin) via settle(), not the WIN readout. A scene that reported
|
|
2764
|
+
// per-step wins already landed on this number, so the count-up is a no-op there.
|
|
2765
|
+
winReporter$1.close();
|
|
2757
2766
|
shell.setWin(r.totalWin - prevWin);
|
|
2758
2767
|
prevWin = r.totalWin;
|
|
2759
2768
|
balanceGate.afterPresent();
|
|
@@ -2765,6 +2774,7 @@ async function createSlotGame(opts) {
|
|
|
2765
2774
|
.catch(showPlayError)
|
|
2766
2775
|
.finally(() => {
|
|
2767
2776
|
presenting = false;
|
|
2777
|
+
winReporter$1.close(); // also closes the window when a segment threw mid-flight
|
|
2768
2778
|
shell.setBusy(false);
|
|
2769
2779
|
});
|
|
2770
2780
|
};
|
|
@@ -2809,6 +2819,8 @@ async function createSlotGame(opts) {
|
|
|
2809
2819
|
shell.setMode(bonusShellMode);
|
|
2810
2820
|
}
|
|
2811
2821
|
shell.setWin(0, { animate: false }); // clear WIN before this segment animates (see playRound)
|
|
2822
|
+
if (animate)
|
|
2823
|
+
winReporter$1.open(); // a fast-forward drain doesn't present → no reports expected
|
|
2812
2824
|
if (animate)
|
|
2813
2825
|
await scene.onSpin(r, ctx);
|
|
2814
2826
|
if (inBonus) {
|
|
@@ -2816,6 +2828,7 @@ async function createSlotGame(opts) {
|
|
|
2816
2828
|
if (v)
|
|
2817
2829
|
applyBonusReadout(r, v, ctx.mode);
|
|
2818
2830
|
}
|
|
2831
|
+
winReporter$1.close();
|
|
2819
2832
|
shell.setWin(r.totalWin - prevWin); // THIS spin's win, not the cumulative bonus total
|
|
2820
2833
|
prevWin = r.totalWin;
|
|
2821
2834
|
ps.playAck(raw); // settles via /wallet/end-round on the FINAL segment
|
|
@@ -2840,6 +2853,7 @@ async function createSlotGame(opts) {
|
|
|
2840
2853
|
}
|
|
2841
2854
|
}
|
|
2842
2855
|
finally {
|
|
2856
|
+
winReporter$1.close(); // also closes the window when a drained segment threw
|
|
2843
2857
|
shell.setBusy(false);
|
|
2844
2858
|
}
|
|
2845
2859
|
};
|
|
@@ -3369,6 +3383,31 @@ var sceneStart = /*#__PURE__*/Object.freeze({
|
|
|
3369
3383
|
resolveStartScene: resolveStartScene
|
|
3370
3384
|
});
|
|
3371
3385
|
|
|
3386
|
+
function createWinReporter(paint) {
|
|
3387
|
+
let accepting = false;
|
|
3388
|
+
return {
|
|
3389
|
+
report(amountSoFar, opts) {
|
|
3390
|
+
if (!accepting || !Number.isFinite(amountSoFar))
|
|
3391
|
+
return;
|
|
3392
|
+
paint(Math.max(0, amountSoFar), opts);
|
|
3393
|
+
},
|
|
3394
|
+
open() {
|
|
3395
|
+
accepting = true;
|
|
3396
|
+
},
|
|
3397
|
+
close() {
|
|
3398
|
+
accepting = false;
|
|
3399
|
+
},
|
|
3400
|
+
get accepting() {
|
|
3401
|
+
return accepting;
|
|
3402
|
+
},
|
|
3403
|
+
};
|
|
3404
|
+
}
|
|
3405
|
+
|
|
3406
|
+
var winReporter = /*#__PURE__*/Object.freeze({
|
|
3407
|
+
__proto__: null,
|
|
3408
|
+
createWinReporter: createWinReporter
|
|
3409
|
+
});
|
|
3410
|
+
|
|
3372
3411
|
// packages/game-engine/src/host/index.ts
|
|
3373
3412
|
|
|
3374
3413
|
/**
|
|
@@ -3776,5 +3815,5 @@ var autoplay = /*#__PURE__*/Object.freeze({
|
|
|
3776
3815
|
createAutoplayLoop: createAutoplayLoop
|
|
3777
3816
|
});
|
|
3778
3817
|
|
|
3779
|
-
export { buildShellConfig, createSlotGame, resolveReplayBonusId, resolveStartScene, stakeForAction };
|
|
3818
|
+
export { buildShellConfig, createSlotGame, createWinReporter, resolveReplayBonusId, resolveStartScene, stakeForAction };
|
|
3780
3819
|
//# sourceMappingURL=host.esm.js.map
|