@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.esm.js CHANGED
@@ -2454,10 +2454,11 @@ async function createSlotGame(opts) {
2454
2454
  // (hidden over the intro / non-slot scenes). Applies in BOTH base and replay modes.
2455
2455
  shell.setVisible(!!gameScene());
2456
2456
  game.scenes.on('change', () => shell.setVisible(!!gameScene()));
2457
- // The gate tracks the live wallet (for the affordability guard) but only PAINTS the balance per
2458
- // the HUD-timing rule: the debit is buffered during play→present and shown at afterPresent; the
2459
- // async win credit (/wallet/end-round, after the final ack) paints when it lands. `balanceGate`
2460
- // is the single source for both the displayed balance and `ensureAffordable`.
2457
+ // The gate tracks the live wallet (for the affordability guard) and PAINTS the balance per the
2458
+ // HUD-timing rule: the debit paints immediately (the stake leaves the balance on spin); a win
2459
+ // credit landing during play→present is held to afterPresent so it doesn't post before the
2460
+ // animation; the async credit (/wallet/end-round, after the final ack) paints when it lands.
2461
+ // `balanceGate` is the single source for both the displayed balance and `ensureAffordable`.
2461
2462
  const balanceGate = createBalanceGate((b) => shell.setBalance(b), balance);
2462
2463
  ps?.on('balanceUpdate', (d) => {
2463
2464
  balanceGate.onBalance(d.balance);
@@ -2500,6 +2501,11 @@ async function createSlotGame(opts) {
2500
2501
  size: () => ({ width: game.app.screen.width, height: game.app.screen.height }),
2501
2502
  });
2502
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));
2503
2509
  // Capabilities injected once per controller scene via onCreate (see `gameScene`/`ensureCreated`).
2504
2510
  sceneApi = {
2505
2511
  audio: createSceneAudio(game.audio),
@@ -2508,6 +2514,7 @@ async function createSlotGame(opts) {
2508
2514
  get safeArea() {
2509
2515
  return shell.safeArea;
2510
2516
  },
2517
+ reportWin: winReporter$1.report,
2511
2518
  },
2512
2519
  formatAmount: (v) => shell.formatWin(v),
2513
2520
  get bet() {
@@ -2728,7 +2735,8 @@ async function createSlotGame(opts) {
2728
2735
  // RETURN the promise: the replay modal awaits onReplay() and only reopens once the round's
2729
2736
  // animation has finished — returning void would reopen it instantly, over a running animation.
2730
2737
  return runRound({
2731
- // Suppress the debit paint from play() until this segment's afterPresent (HUD timing).
2738
+ // Open the play→present window: the debit still paints immediately, but a win credit that
2739
+ // lands mid-animation is held until afterPresent (HUD timing).
2732
2740
  play: (a, b, rid) => {
2733
2741
  balanceGate.beginPlay();
2734
2742
  return slotPlay$1.play(a, b, rid);
@@ -2746,12 +2754,15 @@ async function createSlotGame(opts) {
2746
2754
  // then counts UP to this segment's delta. prevWin (the cumulative-delta tracker) is
2747
2755
  // untouched; this only resets the DISPLAY.
2748
2756
  shell.setWin(0, { animate: false });
2757
+ winReporter$1.open(); // the scene may now grow WIN per cascade step
2749
2758
  },
2750
2759
  onSpinStart: () => scene.onSpinStart?.(),
2751
2760
  onSpinEnd: (last, ctx) => scene.onSpinEnd?.(last, ctx),
2752
2761
  afterPresent: (r) => {
2753
2762
  // WIN readout = THIS spin's win (cumulative delta); the cumulative total goes to the
2754
- // 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();
2755
2766
  shell.setWin(r.totalWin - prevWin);
2756
2767
  prevWin = r.totalWin;
2757
2768
  balanceGate.afterPresent();
@@ -2763,6 +2774,7 @@ async function createSlotGame(opts) {
2763
2774
  .catch(showPlayError)
2764
2775
  .finally(() => {
2765
2776
  presenting = false;
2777
+ winReporter$1.close(); // also closes the window when a segment threw mid-flight
2766
2778
  shell.setBusy(false);
2767
2779
  });
2768
2780
  };
@@ -2807,6 +2819,8 @@ async function createSlotGame(opts) {
2807
2819
  shell.setMode(bonusShellMode);
2808
2820
  }
2809
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
2810
2824
  if (animate)
2811
2825
  await scene.onSpin(r, ctx);
2812
2826
  if (inBonus) {
@@ -2814,6 +2828,7 @@ async function createSlotGame(opts) {
2814
2828
  if (v)
2815
2829
  applyBonusReadout(r, v, ctx.mode);
2816
2830
  }
2831
+ winReporter$1.close();
2817
2832
  shell.setWin(r.totalWin - prevWin); // THIS spin's win, not the cumulative bonus total
2818
2833
  prevWin = r.totalWin;
2819
2834
  ps.playAck(raw); // settles via /wallet/end-round on the FINAL segment
@@ -2838,6 +2853,7 @@ async function createSlotGame(opts) {
2838
2853
  }
2839
2854
  }
2840
2855
  finally {
2856
+ winReporter$1.close(); // also closes the window when a drained segment threw
2841
2857
  shell.setBusy(false);
2842
2858
  }
2843
2859
  };
@@ -3367,6 +3383,31 @@ var sceneStart = /*#__PURE__*/Object.freeze({
3367
3383
  resolveStartScene: resolveStartScene
3368
3384
  });
3369
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
+
3370
3411
  // packages/game-engine/src/host/index.ts
3371
3412
 
3372
3413
  /**
@@ -3484,18 +3525,27 @@ var runRound$1 = /*#__PURE__*/Object.freeze({
3484
3525
 
3485
3526
  function createBalanceGate(paint, initial = 0) {
3486
3527
  let latest = initial;
3528
+ let painted = initial;
3487
3529
  let suppressed = false;
3530
+ const show = (amount) => {
3531
+ painted = amount;
3532
+ paint(amount);
3533
+ };
3488
3534
  return {
3489
3535
  onBalance(amount) {
3490
3536
  latest = amount;
3491
- if (!suppressed)
3492
- paint(amount);
3537
+ // During play→present, hold a CREDIT (balance rising) back so the win doesn't post before the
3538
+ // animation. A DEBIT (balance falling — the stake) always paints now: spin deducts instantly.
3539
+ if (suppressed && amount > painted)
3540
+ return;
3541
+ show(amount);
3493
3542
  },
3494
3543
  beginPlay() {
3495
3544
  suppressed = true;
3496
3545
  },
3497
3546
  afterPresent() {
3498
- paint(latest);
3547
+ if (latest !== painted)
3548
+ show(latest); // flush a credit that landed mid-present
3499
3549
  suppressed = false;
3500
3550
  },
3501
3551
  get balance() {
@@ -3765,5 +3815,5 @@ var autoplay = /*#__PURE__*/Object.freeze({
3765
3815
  createAutoplayLoop: createAutoplayLoop
3766
3816
  });
3767
3817
 
3768
- export { buildShellConfig, createSlotGame, resolveReplayBonusId, resolveStartScene, stakeForAction };
3818
+ export { buildShellConfig, createSlotGame, createWinReporter, resolveReplayBonusId, resolveStartScene, stakeForAction };
3769
3819
  //# sourceMappingURL=host.esm.js.map