@energy8platform/game-engine 0.33.7 → 0.33.9

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 CHANGED
@@ -2455,10 +2455,11 @@ async function createSlotGame(opts) {
2455
2455
  // (hidden over the intro / non-slot scenes). Applies in BOTH base and replay modes.
2456
2456
  shell.setVisible(!!gameScene());
2457
2457
  game.scenes.on('change', () => shell.setVisible(!!gameScene()));
2458
- // The gate tracks the live wallet (for the affordability guard) but only PAINTS the balance per
2459
- // the HUD-timing rule: the debit is buffered during play→present and shown at afterPresent; the
2460
- // async win credit (/wallet/end-round, after the final ack) paints when it lands. `balanceGate`
2461
- // is the single source for both the displayed balance and `ensureAffordable`.
2458
+ // The gate tracks the live wallet (for the affordability guard) and PAINTS the balance per the
2459
+ // HUD-timing rule: the debit paints immediately (the stake leaves the balance on spin); a win
2460
+ // credit landing during play→present is held to afterPresent so it doesn't post before the
2461
+ // animation; the async credit (/wallet/end-round, after the final ack) paints when it lands.
2462
+ // `balanceGate` is the single source for both the displayed balance and `ensureAffordable`.
2462
2463
  const balanceGate = createBalanceGate((b) => shell.setBalance(b), balance);
2463
2464
  ps?.on('balanceUpdate', (d) => {
2464
2465
  balanceGate.onBalance(d.balance);
@@ -2501,6 +2502,11 @@ async function createSlotGame(opts) {
2501
2502
  size: () => ({ width: game.app.screen.width, height: game.app.screen.height }),
2502
2503
  });
2503
2504
  game.on('resize', ({ width, height }) => overlayCtl.resize(width, height));
2505
+ // Progressive WIN readout for cascade/tumble scenes. The window is open from the moment a
2506
+ // segment starts (WIN cleared to 0) until the host has painted that segment's final win; outside
2507
+ // it the host owns the readout alone. See winReporter.ts.
2508
+ const { createWinReporter } = await Promise.resolve().then(function () { return winReporter; });
2509
+ const winReporter$1 = createWinReporter((amount, opts) => shell.setWin(amount, opts));
2504
2510
  // Capabilities injected once per controller scene via onCreate (see `gameScene`/`ensureCreated`).
2505
2511
  sceneApi = {
2506
2512
  audio: createSceneAudio(game.audio),
@@ -2509,6 +2515,7 @@ async function createSlotGame(opts) {
2509
2515
  get safeArea() {
2510
2516
  return shell.safeArea;
2511
2517
  },
2518
+ reportWin: winReporter$1.report,
2512
2519
  },
2513
2520
  formatAmount: (v) => shell.formatWin(v),
2514
2521
  get bet() {
@@ -2729,7 +2736,8 @@ async function createSlotGame(opts) {
2729
2736
  // RETURN the promise: the replay modal awaits onReplay() and only reopens once the round's
2730
2737
  // animation has finished — returning void would reopen it instantly, over a running animation.
2731
2738
  return runRound({
2732
- // Suppress the debit paint from play() until this segment's afterPresent (HUD timing).
2739
+ // Open the play→present window: the debit still paints immediately, but a win credit that
2740
+ // lands mid-animation is held until afterPresent (HUD timing).
2733
2741
  play: (a, b, rid) => {
2734
2742
  balanceGate.beginPlay();
2735
2743
  return slotPlay$1.play(a, b, rid);
@@ -2747,12 +2755,15 @@ async function createSlotGame(opts) {
2747
2755
  // then counts UP to this segment's delta. prevWin (the cumulative-delta tracker) is
2748
2756
  // untouched; this only resets the DISPLAY.
2749
2757
  shell.setWin(0, { animate: false });
2758
+ winReporter$1.open(); // the scene may now grow WIN per cascade step
2750
2759
  },
2751
2760
  onSpinStart: () => scene.onSpinStart?.(),
2752
2761
  onSpinEnd: (last, ctx) => scene.onSpinEnd?.(last, ctx),
2753
2762
  afterPresent: (r) => {
2754
2763
  // WIN readout = THIS spin's win (cumulative delta); the cumulative total goes to the
2755
- // bonus counter (totalWin) via settle(), not the WIN readout.
2764
+ // bonus counter (totalWin) via settle(), not the WIN readout. A scene that reported
2765
+ // per-step wins already landed on this number, so the count-up is a no-op there.
2766
+ winReporter$1.close();
2756
2767
  shell.setWin(r.totalWin - prevWin);
2757
2768
  prevWin = r.totalWin;
2758
2769
  balanceGate.afterPresent();
@@ -2764,6 +2775,7 @@ async function createSlotGame(opts) {
2764
2775
  .catch(showPlayError)
2765
2776
  .finally(() => {
2766
2777
  presenting = false;
2778
+ winReporter$1.close(); // also closes the window when a segment threw mid-flight
2767
2779
  shell.setBusy(false);
2768
2780
  });
2769
2781
  };
@@ -2808,6 +2820,8 @@ async function createSlotGame(opts) {
2808
2820
  shell.setMode(bonusShellMode);
2809
2821
  }
2810
2822
  shell.setWin(0, { animate: false }); // clear WIN before this segment animates (see playRound)
2823
+ if (animate)
2824
+ winReporter$1.open(); // a fast-forward drain doesn't present → no reports expected
2811
2825
  if (animate)
2812
2826
  await scene.onSpin(r, ctx);
2813
2827
  if (inBonus) {
@@ -2815,6 +2829,7 @@ async function createSlotGame(opts) {
2815
2829
  if (v)
2816
2830
  applyBonusReadout(r, v, ctx.mode);
2817
2831
  }
2832
+ winReporter$1.close();
2818
2833
  shell.setWin(r.totalWin - prevWin); // THIS spin's win, not the cumulative bonus total
2819
2834
  prevWin = r.totalWin;
2820
2835
  ps.playAck(raw); // settles via /wallet/end-round on the FINAL segment
@@ -2839,6 +2854,7 @@ async function createSlotGame(opts) {
2839
2854
  }
2840
2855
  }
2841
2856
  finally {
2857
+ winReporter$1.close(); // also closes the window when a drained segment threw
2842
2858
  shell.setBusy(false);
2843
2859
  }
2844
2860
  };
@@ -3368,6 +3384,31 @@ var sceneStart = /*#__PURE__*/Object.freeze({
3368
3384
  resolveStartScene: resolveStartScene
3369
3385
  });
3370
3386
 
3387
+ function createWinReporter(paint) {
3388
+ let accepting = false;
3389
+ return {
3390
+ report(amountSoFar, opts) {
3391
+ if (!accepting || !Number.isFinite(amountSoFar))
3392
+ return;
3393
+ paint(Math.max(0, amountSoFar), opts);
3394
+ },
3395
+ open() {
3396
+ accepting = true;
3397
+ },
3398
+ close() {
3399
+ accepting = false;
3400
+ },
3401
+ get accepting() {
3402
+ return accepting;
3403
+ },
3404
+ };
3405
+ }
3406
+
3407
+ var winReporter = /*#__PURE__*/Object.freeze({
3408
+ __proto__: null,
3409
+ createWinReporter: createWinReporter
3410
+ });
3411
+
3371
3412
  // packages/game-engine/src/host/index.ts
3372
3413
 
3373
3414
  /**
@@ -3485,18 +3526,27 @@ var runRound$1 = /*#__PURE__*/Object.freeze({
3485
3526
 
3486
3527
  function createBalanceGate(paint, initial = 0) {
3487
3528
  let latest = initial;
3529
+ let painted = initial;
3488
3530
  let suppressed = false;
3531
+ const show = (amount) => {
3532
+ painted = amount;
3533
+ paint(amount);
3534
+ };
3489
3535
  return {
3490
3536
  onBalance(amount) {
3491
3537
  latest = amount;
3492
- if (!suppressed)
3493
- paint(amount);
3538
+ // During play→present, hold a CREDIT (balance rising) back so the win doesn't post before the
3539
+ // animation. A DEBIT (balance falling — the stake) always paints now: spin deducts instantly.
3540
+ if (suppressed && amount > painted)
3541
+ return;
3542
+ show(amount);
3494
3543
  },
3495
3544
  beginPlay() {
3496
3545
  suppressed = true;
3497
3546
  },
3498
3547
  afterPresent() {
3499
- paint(latest);
3548
+ if (latest !== painted)
3549
+ show(latest); // flush a credit that landed mid-present
3500
3550
  suppressed = false;
3501
3551
  },
3502
3552
  get balance() {
@@ -3784,6 +3834,7 @@ Object.defineProperty(exports, "socialize", {
3784
3834
  });
3785
3835
  exports.buildShellConfig = buildShellConfig;
3786
3836
  exports.createSlotGame = createSlotGame;
3837
+ exports.createWinReporter = createWinReporter;
3787
3838
  exports.resolveReplayBonusId = resolveReplayBonusId;
3788
3839
  exports.resolveStartScene = resolveStartScene;
3789
3840
  exports.stakeForAction = stakeForAction;