@energy8platform/game-engine 0.40.0 → 0.41.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/dist/slot.d.ts CHANGED
@@ -494,6 +494,8 @@ interface AnticipationOverride {
494
494
  slowdown?: PerReel<number>;
495
495
  /** Extra hold before landing. Scalar, or per-reel indexed by reel index. */
496
496
  holdMs?: PerReel<number>;
497
+ /** Growth of the gap between successive cells within a reel. Scalar, or per-reel. */
498
+ cellStaggerRamp?: PerReel<number>;
497
499
  }
498
500
  interface AnticipationConfig {
499
501
  enabled: boolean;
@@ -522,6 +524,16 @@ interface AnticipationConfig {
522
524
  progressiveSlowdown: number;
523
525
  /** Extra hold (ms) added per successive anticipated reel: reel #i gets `holdMs + i * this`. */
524
526
  progressiveHoldMs: number;
527
+ /**
528
+ * `cascade-drop`: how the gap between SUCCESSIVE CELLS grows inside an anticipated reel. The
529
+ * gap before cell #i is `cellStagger * <reel slowdown> * progressiveCellStagger ** i`, so 1
530
+ * (the default) keeps the reel's cells evenly spaced and > 1 makes each symbol land later than
531
+ * the last — the drip that turns a reel into a countdown.
532
+ *
533
+ * It compounds with `progressiveSlowdown`: that widens the base gap reel by reel, this widens
534
+ * it cell by cell, so a later reel drips both slower AND with a steeper ramp.
535
+ */
536
+ progressiveCellStagger: number;
525
537
  /** Optional grid zoom while anticipating (magnum-opus uses 1.3×). */
526
538
  zoom: {
527
539
  enabled: boolean;
@@ -766,6 +778,12 @@ interface SpinRunOpts {
766
778
  anticipateSlowdown?: PerReel<number>;
767
779
  /** Extra hold (ms) for anticipated reels. Scalar, or per-reel indexed by reel. */
768
780
  anticipateHoldMs?: PerReel<number>;
781
+ /**
782
+ * `cascade-drop`: growth of the gap between successive cells inside an anticipated reel — the
783
+ * gap before cell #i is `cellStagger * <reel slowdown> * ramp ** i`. 1 keeps the reel evenly
784
+ * spaced. Scalar, or per-reel indexed by reel.
785
+ */
786
+ anticipateCellStaggerRamp?: PerReel<number>;
769
787
  /**
770
788
  * Reels whose tape runs normally but whose landing is NOT handed back. The engine stops and
771
789
  * disposes of the tape as usual and leaves the real cells hidden and unseated; the caller owns
@@ -854,6 +872,8 @@ interface AnticipationDecision {
854
872
  slowdown: PerReel<number>;
855
873
  /** Extra hold before landing. Scalar or per-reel array, same as `slowdown`. */
856
874
  holdMs: PerReel<number>;
875
+ /** `cascade-drop`: growth of the gap between successive cells inside the reel (1 = even). */
876
+ cellStaggerRamp: PerReel<number>;
857
877
  }
858
878
  declare class AnticipationController {
859
879
  private _cfg;
package/dist/slot.esm.js CHANGED
@@ -1142,6 +1142,7 @@ const DEFAULT_REEL_CONFIG = {
1142
1142
  decide: null,
1143
1143
  progressiveSlowdown: 1,
1144
1144
  progressiveHoldMs: 0,
1145
+ progressiveCellStagger: 1,
1145
1146
  zoom: { enabled: false, scale: 1.15, ms: 600 },
1146
1147
  },
1147
1148
  cascade: {
@@ -1572,6 +1573,7 @@ class SpinEngine {
1572
1573
  const anticipate = new Set(opts?.anticipateReels ?? []);
1573
1574
  const defer = new Set(opts?.deferReveal ?? []);
1574
1575
  const holds = [];
1576
+ const ramps = [];
1575
1577
  const out = [];
1576
1578
  for (let reel = 0; reel < cols; reel++) {
1577
1579
  const idx = order(reel);
@@ -1590,6 +1592,7 @@ class SpinEngine {
1590
1592
  const hold = isAnticipated ? perReelValue(opts?.anticipateHoldMs, reel, 0) * f : 0;
1591
1593
  stopTime += hold;
1592
1594
  holds[reel] = hold;
1595
+ ramps[reel] = isAnticipated ? perReelValue(opts?.anticipateCellStaggerRamp, reel, 1) : 1;
1593
1596
  const speed = isAnticipated ? perReelValue(opts?.anticipateSlowdown, reel, 1) : 1;
1594
1597
  out.push({
1595
1598
  reel,
@@ -1602,7 +1605,7 @@ class SpinEngine {
1602
1605
  });
1603
1606
  }
1604
1607
  if (this._cfg.style === 'cascade-drop')
1605
- this._planDrop(out, holds, f, order);
1608
+ this._planDrop(out, holds, ramps, f, order);
1606
1609
  return out;
1607
1610
  }
1608
1611
  /**
@@ -1611,7 +1614,7 @@ class SpinEngine {
1611
1614
  * reel has fully landed and fill in `cellStopTimes`, so `plan()` stays the single source of truth
1612
1615
  * for WHEN anything happens — `_runDrop` below only executes these numbers.
1613
1616
  */
1614
- _planDrop(out, holds, f, order) {
1617
+ _planDrop(out, holds, ramps, f, order) {
1615
1618
  const bottomUp = this._cfg.dropOrder === 'bottom-up';
1616
1619
  // walk the reels in STOP order, so `stopOrder: 'rtl'` reverses the drop as well
1617
1620
  const byPosition = [];
@@ -1633,10 +1636,15 @@ class SpinEngine {
1633
1636
  // by formula while reels may overlap; queued behind the previous reel once the chain starts
1634
1637
  const chained = chainFrom >= 0 && position > 0 && position >= chainFrom;
1635
1638
  const start = (chained ? previousEnd + gap : position * gap) + (holds[p.reel] ?? 0);
1639
+ // The gap before cell #i is `cellStagger * scale * ramp**i` — accumulate rather than
1640
+ // multiply, so a ramp of 1 stays exactly the old evenly-spaced schedule.
1641
+ const ramp = ramps[p.reel] ?? 1;
1636
1642
  const cells = [];
1643
+ let offset = 0;
1637
1644
  for (let i = 0; i < rows; i++) {
1638
1645
  const row = bottomUp ? rows - 1 - i : i;
1639
- cells[row] = start + i * this._cfg.cellStagger * scale + fall;
1646
+ cells[row] = start + offset + fall;
1647
+ offset += this._cfg.cellStagger * scale * Math.pow(ramp, i);
1640
1648
  }
1641
1649
  p.cellStopTimes = cells;
1642
1650
  p.stopTime = cells.length ? Math.max(...cells) : start;
@@ -1892,7 +1900,13 @@ class SpinEngine {
1892
1900
  // Decides which trailing reels get the "anticipation" slow-down treatment, based purely on
1893
1901
  // the already-resolved landing grid (presentation only — never a secondary outcome decision).
1894
1902
  /** Fresh "nothing to anticipate" decision (fresh, so callers may mutate `reels` freely). */
1895
- const none = () => ({ active: false, reels: [], slowdown: 1, holdMs: 0 });
1903
+ const none = () => ({
1904
+ active: false,
1905
+ reels: [],
1906
+ slowdown: 1,
1907
+ holdMs: 0,
1908
+ cellStaggerRamp: 1,
1909
+ });
1896
1910
  class AnticipationController {
1897
1911
  _cfg;
1898
1912
  constructor(cfg) {
@@ -1925,7 +1939,7 @@ class AnticipationController {
1925
1939
  const o = Array.isArray(custom) ? { reels: custom } : custom;
1926
1940
  if (!o.reels?.length)
1927
1941
  return none();
1928
- return this.build(o.reels.slice(), o.slowdown, o.holdMs);
1942
+ return this.build(o.reels.slice(), o.slowdown, o.holdMs, o.cellStaggerRamp);
1929
1943
  }
1930
1944
  if (Array.isArray(this._cfg.reels)) {
1931
1945
  // explicit reel list — arm only if the threshold is met somewhere on the board
@@ -1954,10 +1968,11 @@ class AnticipationController {
1954
1968
  return this.build(reels);
1955
1969
  }
1956
1970
  /** Assemble a decision, applying the configured progression unless the caller pinned values. */
1957
- build(reels, slowdown, holdMs) {
1971
+ build(reels, slowdown, holdMs, cellStaggerRamp) {
1958
1972
  return {
1959
1973
  active: true,
1960
1974
  reels,
1975
+ cellStaggerRamp: cellStaggerRamp ?? this._cfg.progressiveCellStagger,
1961
1976
  slowdown: slowdown ??
1962
1977
  this.ramp(reels, this._cfg.slowdownFactor, (base, i) => i === 0 ? base : base * Math.pow(this._cfg.progressiveSlowdown, i)),
1963
1978
  holdMs: holdMs ??
@@ -3037,12 +3052,13 @@ function createReelSystem(opts) {
3037
3052
  if (!explicit)
3038
3053
  return anticipation.decide(target);
3039
3054
  if (!explicit.length)
3040
- return { active: false, reels: [], slowdown: 1, holdMs: 0 };
3055
+ return { active: false, reels: [], slowdown: 1, holdMs: 0, cellStaggerRamp: 1 };
3041
3056
  return {
3042
3057
  active: true,
3043
3058
  reels: explicit.slice(),
3044
3059
  slowdown: runOpts?.anticipateSlowdown ?? config.anticipation.slowdownFactor,
3045
3060
  holdMs: runOpts?.anticipateHoldMs ?? config.anticipation.holdMs,
3061
+ cellStaggerRamp: runOpts?.anticipateCellStaggerRamp ?? config.anticipation.progressiveCellStagger,
3046
3062
  };
3047
3063
  }
3048
3064
  /** Fold a resolved decision back onto the caller's run options (everything else passes through). */
@@ -3052,6 +3068,7 @@ function createReelSystem(opts) {
3052
3068
  anticipateReels: decision.active ? decision.reels : undefined,
3053
3069
  anticipateSlowdown: decision.slowdown,
3054
3070
  anticipateHoldMs: decision.holdMs,
3071
+ anticipateCellStaggerRamp: decision.cellStaggerRamp,
3055
3072
  };
3056
3073
  }
3057
3074
  buildGrid();