pixi-reels 0.3.2 → 0.4.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.
Files changed (44) hide show
  1. package/CHANGELOG.md +167 -0
  2. package/dist/{SpineSymbol-D2jhCzFW.js → SpineSymbol-CfVrc5Pk.js} +12 -6
  3. package/dist/{SpineSymbol-D2jhCzFW.js.map → SpineSymbol-CfVrc5Pk.js.map} +1 -1
  4. package/dist/SpineSymbol-JA5PdEbT.cjs +2 -0
  5. package/dist/{SpineSymbol-B40TevSr.cjs.map → SpineSymbol-JA5PdEbT.cjs.map} +1 -1
  6. package/dist/cascade/CascadeAnticipationPhase.d.ts.map +1 -1
  7. package/dist/config/types.d.ts +76 -1
  8. package/dist/config/types.d.ts.map +1 -1
  9. package/dist/core/Reel.d.ts +74 -2
  10. package/dist/core/Reel.d.ts.map +1 -1
  11. package/dist/core/ReelSet.d.ts +54 -3
  12. package/dist/core/ReelSet.d.ts.map +1 -1
  13. package/dist/core/ReelSetBuilder.d.ts +48 -1
  14. package/dist/core/ReelSetBuilder.d.ts.map +1 -1
  15. package/dist/debug/debug.d.ts +59 -0
  16. package/dist/debug/debug.d.ts.map +1 -1
  17. package/dist/index.cjs +4 -4
  18. package/dist/index.cjs.map +1 -1
  19. package/dist/index.d.ts +3 -3
  20. package/dist/index.d.ts.map +1 -1
  21. package/dist/index.js +318 -200
  22. package/dist/index.js.map +1 -1
  23. package/dist/spin/SpinController.d.ts +23 -3
  24. package/dist/spin/SpinController.d.ts.map +1 -1
  25. package/dist/spin/phases/AdjustPhase.d.ts.map +1 -1
  26. package/dist/spin/phases/AnticipationPhase.d.ts.map +1 -1
  27. package/dist/spin/phases/DropStopPhase.d.ts.map +1 -1
  28. package/dist/spin/phases/StartPhase.d.ts.map +1 -1
  29. package/dist/spin/phases/StopPhase.d.ts.map +1 -1
  30. package/dist/spine/SpineReelSymbol.d.ts +19 -0
  31. package/dist/spine/SpineReelSymbol.d.ts.map +1 -1
  32. package/dist/spine.cjs +1 -1
  33. package/dist/spine.cjs.map +1 -1
  34. package/dist/spine.js +14 -14
  35. package/dist/spine.js.map +1 -1
  36. package/dist/spotlight/SymbolSpotlight.d.ts.map +1 -1
  37. package/dist/symbols/ReelSymbol.d.ts +6 -4
  38. package/dist/symbols/ReelSymbol.d.ts.map +1 -1
  39. package/dist/symbols/SpineSymbol.d.ts.map +1 -1
  40. package/dist/symbols/SpriteSymbol.d.ts.map +1 -1
  41. package/dist/utils/gsapRef.d.ts +14 -0
  42. package/dist/utils/gsapRef.d.ts.map +1 -0
  43. package/package.json +1 -1
  44. package/dist/SpineSymbol-B40TevSr.cjs +0 -2
package/CHANGELOG.md CHANGED
@@ -1,5 +1,172 @@
1
1
  # pixi-reels
2
2
 
3
+ ## 0.4.0
4
+
5
+ ### Minor Changes
6
+
7
+ - [#98](https://github.com/schmooky/pixi-reels/pull/98) [`b4bacca`](https://github.com/schmooky/pixi-reels/commit/b4bacca9bac5aa6048ca9d5062de8ef1e04aeeea) Thanks [@igaming-bulochka](https://github.com/igaming-bulochka)! - Auto-pick `SharedRectMaskStrategy` when any registered symbol has `unmask: true` and `symbolGap.x > 0`.
8
+
9
+ The default `RectMaskStrategy` draws one mask rect per reel, with the gaps between reels NOT clipped — fine in the common case. But when an `unmask: true` symbol renders above the reel mask, neighboring (still-masked) symbols on adjacent reels visibly clip at the column gap, and players see a half-cropped neighbor next to the unmasked overlay.
10
+
11
+ The auto-pick now triggers in either case:
12
+
13
+ - **big symbols** registered (`SymbolData.size` with `w > 1` or `h > 1`), or
14
+ - **unmasked symbols** registered (`SymbolData.unmask: true`),
15
+
16
+ provided the layout has a horizontal gap (`symbolGap.x > 0`). Explicit `.maskStrategy(...)` calls always win.
17
+
18
+ Console emits a one-line `console.info` hint identifying which condition triggered the auto-pick. Pairs with the existing big-symbol auto-pick — the same mechanism, broader trigger set.
19
+
20
+ - [#91](https://github.com/schmooky/pixi-reels/pull/91) [`d211ca4`](https://github.com/schmooky/pixi-reels/commit/d211ca495e626c18b92187902a527aa182d0bbbb) Thanks [@igaming-bulochka](https://github.com/igaming-bulochka)! - Add `ReelSetBuilder.gsap(instance)` for explicit GSAP dependency injection.
21
+
22
+ The engine internally drives every tween, timeline, and `delayedCall` through a single bound `gsap` instance. By default that is the `gsap` resolved at the engine's own module path — fine for the common case where bundler `dedupe` collapses both the engine's and the consumer's `'gsap'` to one module instance.
23
+
24
+ In setups where two `gsap` instances exist at runtime (symlinked workspaces, npm-link, misconfigured `dedupe`), tweens started by the engine live on a different root timeline than the one the consumer drives — animations stall, double-fire, or freeze on hidden tabs. Calling `.gsap(myGsap)` in the builder rebinds the engine to the consumer's instance:
25
+
26
+ ```ts
27
+ import { gsap } from 'gsap';
28
+
29
+ const reelSet = new ReelSetBuilder()
30
+ .reels(5).visibleRows(3).symbolSize(200, 200)
31
+ .symbols(...)
32
+ .ticker(app.ticker)
33
+ .gsap(gsap) // ensure engine and app share one instance
34
+ .build();
35
+ ```
36
+
37
+ Internally this is implemented via a tiny `getGsap()`/`setGsap()` shim in `utils/gsapRef.ts`. Every internal animation site now reads through `getGsap()` instead of importing `'gsap'` directly. A regression-guard test asserts no runtime `gsap.timeline(`/`gsap.to(`/`gsap.delayedCall(` calls outside the shim itself.
38
+
39
+ No behavioural change for consumers who don't call `.gsap()`.
40
+
41
+ - [#99](https://github.com/schmooky/pixi-reels/pull/99) [`544607d`](https://github.com/schmooky/pixi-reels/commit/544607d8f413d9fa7dfcba65f3219819096a65f6) Thanks [@igaming-bulochka](https://github.com/igaming-bulochka)! - Add a frame-state recorder to the debug module: `startRecording(reelSet, tag)`, `stopRecording(reelSet)`, `getFrames(tag?)`, `clearFrames()`.
42
+
43
+ Each lifecycle event (`spin:start`, `spin:reelLanded`, `spin:allLanded`, `spin:complete`) captures one `DebugSnapshot` while a recording session is active. Frames are tagged with the string passed to `startRecording`, so multiple sessions can share one global log and be filtered out via `getFrames(tag)`. Per-process buffer is capped at 1000 frames by default (rolling window); override via `startRecording(reelSet, tag, { maxFrames })`. Recording auto-detaches when the reel set emits `'destroyed'`.
44
+
45
+ Designed for AI agents and debug harnesses that need a frame-by-frame trace of a spin sequence — particularly useful for diagnosing flicker, double-fires, or off-by-one frame issues that aren't visible from a single point-in-time `debugSnapshot`.
46
+
47
+ Also exposed on `__PIXI_REELS_DEBUG` after `enableDebug(reelSet)`:
48
+
49
+ ```js
50
+ __PIXI_REELS_DEBUG.startRecording("my-tag");
51
+ await reelSet.spin();
52
+ __PIXI_REELS_DEBUG.stopRecording();
53
+ __PIXI_REELS_DEBUG.getFrames("my-tag");
54
+ ```
55
+
56
+ `startRecording` is idempotent per reel set — calling it twice on the same set replaces the prior session.
57
+
58
+ - [#95](https://github.com/schmooky/pixi-reels/pull/95) [`1abfc45`](https://github.com/schmooky/pixi-reels/commit/1abfc45a445ec9491ddee69367f827333735acdf) Thanks [@igaming-bulochka](https://github.com/igaming-bulochka)! - Add `Reel.setSymbolAt(visibleRow, symbolId)` and `ReelSet.setSymbolAt(col, row, symbolId)` — public API for swapping a single visible cell's symbol identity in place at rest.
59
+
60
+ Useful for live presentation effects that don't fit the `setResult` / `placeSymbols` flow:
61
+
62
+ - converting a symbol to a wild after a cascade pop,
63
+ - swapping to a sticky variant after a win is paid out.
64
+
65
+ The method funnels into the same internal activate path as the rest of the engine, so the swapped-in symbol gets its proper parent (masked vs unmasked container), `zIndex`, and visual reset for free — no follow-up `refreshZIndex` required.
66
+
67
+ Validation (all guards fail loud):
68
+
69
+ - throws if the reel is in motion (`speed !== 0` or `isStopping`) — a mid-spin swap would be overwritten by the next wrap/stop frame anyway.
70
+ - throws if `visibleRow` is not an integer in `[0, visibleRows)`.
71
+ - throws if `symbolId` is not registered.
72
+ - throws if the target row is a non-anchor cell of a big-symbol block.
73
+ - throws if the target row currently holds the anchor of a big-symbol block — big blocks span multiple cells (and possibly reels) and require `placeSymbols` plus the cross-reel OCCUPIED coordinator.
74
+ - throws if `symbolId` itself is a big symbol — same reason.
75
+ - `ReelSet.setSymbolAt` additionally throws if the cell currently has an active pin; call `unpin(col, row)` first to overwrite.
76
+
77
+ Emits `symbol:created` on the per-reel event bus, matching motion-driven swaps.
78
+
79
+ - [#78](https://github.com/schmooky/pixi-reels/pull/78) [`9f6f0da`](https://github.com/schmooky/pixi-reels/commit/9f6f0dac52bcb01936422e719db020c2e6b76280) Thanks [@igaming-bulochka](https://github.com/igaming-bulochka)! - Add: `reelSet.spin({ holdReels: [...] })` for subset spinning.
80
+
81
+ Held reels skip START / SPIN / STOP entirely and stay on whatever symbols they're currently showing — no more "fragment the board into one ReelSet per column" workaround for Hold & Win, sticky / expanding wilds, or trigger-column bonus respins. Held reels count as already-landed for the `spin:allLanded` resolver, so only the non-held reels actually animate.
82
+
83
+ ```ts
84
+ // Hold reels 0 and 4; only reels 1, 2, 3 reroll.
85
+ const spin = reelSet.spin({ holdReels: [0, 4] });
86
+ reelSet.setResult(serverGrid); // entries at 0/4 are ignored
87
+ await spin;
88
+ ```
89
+
90
+ Behaviour:
91
+
92
+ - `setResult(grid)` still expects a full `reelCount`-length grid; held entries are ignored.
93
+ - `setAnticipation([...])` silently filters held indices.
94
+ - `setStopDelays([...])` entries at held indices are ignored.
95
+ - No `spin:reelLanded` / `spin:stopping` event fires for held reels; `spin:allLanded` fires once every non-held reel lands.
96
+ - Out-of-range / duplicate / non-integer entries in `holdReels` are silently filtered.
97
+ - Big-symbol blocks crossing the held / non-held boundary are not supported — author results so big symbols stay inside a contiguous run of non-held reels.
98
+
99
+ Exports `SpinOptions` from the package root.
100
+
101
+ - [#92](https://github.com/schmooky/pixi-reels/pull/92) [`aa8be14`](https://github.com/schmooky/pixi-reels/commit/aa8be149aa7c9f8ff4195b6850b767b8bf402bcc) Thanks [@igaming-bulochka](https://github.com/igaming-bulochka)! - Make `SymbolData.unmask: true` actually re-parent the symbol view to `viewport.unmaskedContainer`.
102
+
103
+ Until now the `unmask` flag on `SymbolData` was accepted by the builder but never read by the engine — symbols always landed inside the reel's masked container regardless of the flag. With this change, every code path that places a symbol into the reel — `_setupSymbolPositions`, `_replaceSymbol` (both stub-install and stub-replace branches and the regular swap), and `reshape` — consults `_symbolsData[id].unmask` and parents the view to `viewport.unmaskedContainer` when set.
104
+
105
+ When unmasked, the engine sets the view's X to `reel.container.x` and adds `reel.container.y` to the view's Y so the at-rest cell position aligns with the reel column (since `unmaskedContainer` sits at viewport-local 0,0).
106
+
107
+ Documented limitation in `SymbolData.unmask` JSDoc: `ReelMotion` writes `view.y` in reel-local coords every frame, so an unmasked symbol on the strip will appear shifted vertically by `reel.container.y` while the reel is spinning. Treat `unmask: true` as a _landed-state_ flag — it is correct at rest and during static frames, but not designed to stay visually accurate while the reel is spinning. For mid-spin "stays visible above mask" overlays, use a cell pin instead.
108
+
109
+ **Pyramid layouts:** registering any unmasked symbol on a slot where any reel has a non-zero `offsetY` (pyramid / trapezoid) now throws at `build()`. Reason: the same motion-layer issue persists at landing — `snapToGrid` writes reel-local Y, mispositioning the unmasked view by `reel.container.y` even at rest. Use cell pins for above-mask overlays on pyramid slots, or remove the per-reel offset.
110
+
111
+ - [#104](https://github.com/schmooky/pixi-reels/pull/104) [`1dc8d08`](https://github.com/schmooky/pixi-reels/commit/1dc8d084ad171b8347312991c98cfbfc07bed451) Thanks [@feddorovich](https://github.com/feddorovich)! - `reelSet.spin()` accepts an optional `{ mode: 'standard' | 'cascade' }` argument that picks the phase chain for a single spin. Tumble-cascade slots can now do classic strip-spin + bounce on the first round and drop-in tumble on subsequent waves.
112
+
113
+ `.cascade(...)` on the builder still wires the drop-in phases — but they are now registered under `dropStart` / `dropStop` keys instead of overwriting `start` / `stop`. The default mode flips to `'cascade'` when `.cascade(...)` was called, so existing callers that just call `spin()` without args see no change.
114
+
115
+ Calling `spin({ mode: 'cascade' })` on a builder that didn't configure `.cascade(...)` throws a clear error. The new `SpinOptions` type is exported from the package barrel.
116
+
117
+ - [#103](https://github.com/schmooky/pixi-reels/pull/103) [`18474ee`](https://github.com/schmooky/pixi-reels/commit/18474eebbc0ed16b63f2e6b9f8af1acb9c5ea2d2) Thanks [@feddorovich](https://github.com/feddorovich)! - Added `ReelSet.requestSkip()` (and `SpinController.requestSkip()`) — a slam-stop entry point that's safe to call before `setResult()` arrives. If the result is already pending, it behaves exactly like `skip()`. Otherwise the skip is queued and fires automatically as soon as `setResult()` lands.
118
+
119
+ Use this from UI handlers in server-driven slots: a player tapping the spin button to slam-stop before the WebSocket response reaches the client no longer snaps every reel onto whatever buffer state happened to be mid-scroll. Existing `skip()` is unchanged.
120
+
121
+ ### Patch Changes
122
+
123
+ - [#93](https://github.com/schmooky/pixi-reels/pull/93) [`f111da8`](https://github.com/schmooky/pixi-reels/commit/f111da858ec0ca11a72ac389538b29f43f8c4262) Thanks [@igaming-bulochka](https://github.com/igaming-bulochka)! - Fix: `Reel._replaceSymbol` now sets the canonical zIndex inline on every symbol activation.
124
+
125
+ Previously the activate path set `view.zIndex = 0` and relied on a follow-up `refreshZIndex()` call to apply the real formula `(symbolData.zIndex ?? 0) * 100 + arrayIndex`. All current callers happen to call `refreshZIndex` after, but the contract was fragile: any future caller that swapped a single symbol via the activate path would see the wrong layering until the next motion-wrap.
126
+
127
+ A new private helper `_computeSymbolZIndex(symbolId, index)` centralizes the formula and is used by both `refreshZIndex` (full rescan) and `_replaceSymbol` (single-symbol activate). OCCUPIED stubs receive `arrayIndex` directly, matching what `refreshZIndex` would assign.
128
+
129
+ No public API change. The fix unblocks future single-symbol swap APIs (e.g. a public `setSymbolAt`) without forcing every caller to remember to `refreshZIndex` afterwards.
130
+
131
+ - [#97](https://github.com/schmooky/pixi-reels/pull/97) [`db32899`](https://github.com/schmooky/pixi-reels/commit/db32899c832ce68e7ba1aaf797bedaf3a85d6fa3) Thanks [@igaming-bulochka](https://github.com/igaming-bulochka)! - Fix: `ReelSetBuilder.bufferSymbols(count)` now clamps `0`, negative numbers, `NaN`, and non-finite values to the minimum of 1, with a single console warning per process.
132
+
133
+ Buffer rows are off-screen cells the reel keeps around the visible window so symbols can fade/slide in cleanly. The motion layer's wrap detection assumes at least one buffer row above and one below — passing `0` would produce an inconsistent state that surfaced later as visible flicker on motion-wrap, not as a clear configuration error at build time.
134
+
135
+ The clamp is preferred over a thrown error so existing user code that accidentally passed `0` keeps running. The warning fires once per process (regardless of how many builders hit the bad value) so logs stay readable when a faulty default is wired into a loop.
136
+
137
+ - [#94](https://github.com/schmooky/pixi-reels/pull/94) [`6a5c8d1`](https://github.com/schmooky/pixi-reels/commit/6a5c8d192025c0746cab311491b2984173c15d30) Thanks [@igaming-bulochka](https://github.com/igaming-bulochka)! - Fix: `SpineReelSymbol` one-shot animation promises (`playWin` / `playLanding` / `playOut`) no longer dangle when the track is hijacked.
138
+
139
+ Three previously-leaking scenarios now settle the returned promise instead of hanging forever:
140
+
141
+ - **Concurrent one-shots** — calling `playOut()` while `playWin()` is in flight resolves the prior `playWin` promise (its track was overwritten) before starting the new one.
142
+ - **`playBlur` mid-animation** — entering a SPIN that triggers blur while a win is still animating settles the win promise.
143
+ - **Listener leak** — back-to-back one-shots no longer accumulate stale listeners on the Spine state. Each new one-shot detaches the prior listener.
144
+
145
+ Refactored to a single internal `_resolveOneShot()` helper called from `onActivate`, `onDeactivate`, `stopAnimation`, `playBlur`, and the start of every new `_playOneShot`. The track-entry guard (`done !== entry`) is preserved so unrelated entries firing complete on the same track are correctly ignored.
146
+
147
+ This unblocks reliable `await symbol.playWin()` patterns in win presenters and cascade orchestration.
148
+
149
+ - [#77](https://github.com/schmooky/pixi-reels/pull/77) [`265136a`](https://github.com/schmooky/pixi-reels/commit/265136a58cbcc4b289b6a070928345ca656c2cc1) Thanks [@igaming-bulochka](https://github.com/igaming-bulochka)! - Fix: stop reparenting recycled symbols on spotlight hide and always anchor `Reel._replaceSymbol` to its own container.
150
+
151
+ Two related bugs caused symbols to render in the wrong reel after rapid spin/skip cycles, particularly when the win spotlight runs alongside an expanding-wild mechanic that triggers many `placeSymbols` calls in quick succession:
152
+
153
+ - `SymbolSpotlight.hide()` reparented every symbol it had ever tracked back to its `originalParent`, even when `promoteAboveMask: false` (no reparenting on `show()`) or after the shared symbol pool had recycled the instance into a different reel. The recycled symbol got yanked from its new owner, leaving a hole there and a stranger in the original reel.
154
+ - `Reel._replaceSymbol` used the captured `oldSymbol.view.parent` as the destination for the replacement view. If the old symbol had been moved (by the spotlight or by pool recycling), the new symbol landed in a foreign container — symbols accumulated in the wrong reel across spins.
155
+
156
+ Both paths now anchor to the reel's own container; the spotlight only reparents symbols whose view is still in `spotlightContainer` (i.e., never recycled away).
157
+
158
+ - [#101](https://github.com/schmooky/pixi-reels/pull/101) [`7a7670c`](https://github.com/schmooky/pixi-reels/commit/7a7670cf1a98e2b2778069a728147452ece2dc66) Thanks [@feddorovich](https://github.com/feddorovich)! - `ReelSymbol.activate()` and `ReelSymbol.deactivate()` now both reset the container's `alpha`, `scale`, `rotation`, `filters`, and `zIndex`. Previously a subclass that decorated `view` from a spin-lifecycle hook (e.g. attaching a `BlurFilter` in `onReelSpinStart`) had to remember to undo every property on its own — and any path that skipped a hook (a buffer cell that exited spin without `onReelSpinEnd`, a slam-stop that bypassed the lifecycle) left a recycled symbol carrying stale state into its next life. The most visible symptom was a "blurred" cell appearing after a cascade refill once a symbol had been pooled mid-spin.
159
+
160
+ `ReelSymbol.destroy()` now inlines the lifecycle hooks (`stopAnimation`, `onDeactivate`) instead of going through `deactivate()`, so it doesn't try to reset transform / filter state on a view that was already torn down by a parent `container.destroy({ children: true })`.
161
+
162
+ The same-id early-return path inside `Reel._setSymbolAt` bypasses the deactivate/activate cycle, so the matching reset has been added there too.
163
+
164
+ No public API change. Subclasses that already cleared their own filter / transform state continue to work and just do a few redundant assignments.
165
+
166
+ - [#102](https://github.com/schmooky/pixi-reels/pull/102) [`a2be4b8`](https://github.com/schmooky/pixi-reels/commit/a2be4b83544b66bd3650f14de251dcf51424b552) Thanks [@feddorovich](https://github.com/feddorovich)! - `SpinController.skip()` now fires `onReelSpinEnd` and `onReelLanded` on every reel that hadn't already landed, regardless of which phase was active when the slam-stop arrived. Previously these symbol-level hooks fired only when the active phase happened to be `StopPhase` or `DropStopPhase` (their `onSkip()` called the notifications); a skip during `StartPhase` / `SpinPhase` / `AnticipationPhase` / `AdjustPhase` left visible symbols without an end-of-spin signal — most visibly, motion blur (or any other decoration attached in `onReelSpinStart`) stayed on the cell after the slam.
167
+
168
+ The notifications moved out of `StopPhase.onSkip` / `DropStopPhase.onSkip` into the controller so there's a single source of truth and no double-fire. Natural-stop flow is unchanged — those phases still fire the hooks themselves before the bounce.
169
+
3
170
  ## 0.3.2
4
171
 
5
172
  ### Patch Changes
@@ -14,16 +14,16 @@ var t = class {
14
14
  return this._isDestroyed;
15
15
  }
16
16
  activate(e) {
17
- this._symbolId = e, this.view.visible = !0, this.onActivate(e);
17
+ this._symbolId = e, this.view.visible = !0, this.view.alpha = 1, this.view.scale.set(1, 1), this.view.rotation = 0, this.view.filters = null, this.view.zIndex = 0, this.onActivate(e);
18
18
  }
19
19
  deactivate() {
20
- this.stopAnimation(), this.onDeactivate(), this._symbolId = "", this.view.visible = !1;
20
+ this.stopAnimation(), this.onDeactivate(), this._symbolId = "", this.view.visible = !1, this.view.alpha = 1, this.view.scale.set(1, 1), this.view.rotation = 0, this.view.filters = null, this.view.zIndex = 0;
21
21
  }
22
22
  reset() {
23
23
  this.deactivate();
24
24
  }
25
25
  destroy() {
26
- this._isDestroyed ||= (this.deactivate(), this.onDestroy(), this.view.destroy({ children: !0 }), !0);
26
+ this._isDestroyed ||= (this.stopAnimation(), this.onDeactivate(), this.onDestroy(), this.view.destroyed || this.view.destroy({ children: !0 }), !0);
27
27
  }
28
28
  onDestroy() {}
29
29
  onReelSpinStart() {}
@@ -53,7 +53,10 @@ var i = class extends t {
53
53
  t && (this._currentSkeletonKey !== e && (this._spine && (this.view.removeChild(this._spine), this._spine.destroy()), this._spine = new n({ skeletonData: t }), this.view.addChild(this._spine), this._currentSkeletonKey = e), this._spine.skeleton.data.findSkin(this._defaultSkin) && (this._spine.skeleton.setSkinByName(this._defaultSkin), this._spine.skeleton.setSlotsToSetupPose()), this._spine.skeleton.data.findAnimation(this._idleAnimation) && this._spine.state.setAnimation(0, this._idleAnimation, !0));
54
54
  }
55
55
  onDeactivate() {
56
- this._spine && (this._spine.state.clearListeners(), this._spine.state.clearTracks()), this._winResolve = null;
56
+ if (this._spine && (this._spine.state.clearListeners(), this._spine.state.clearTracks()), this._winResolve) {
57
+ let e = this._winResolve;
58
+ this._winResolve = null, e();
59
+ }
57
60
  }
58
61
  async playWin() {
59
62
  if (this._spine && this._spine.skeleton.data.findAnimation(this._winAnimation)) return new Promise((e) => {
@@ -73,10 +76,13 @@ var i = class extends t {
73
76
  n.width > 0 && n.height > 0 && this._spine.scale.set(e / n.width, t / n.height);
74
77
  }
75
78
  onDestroy() {
76
- this._spine &&= (this._spine.state.clearListeners(), this._spine.destroy(), null);
79
+ if (this._spine &&= (this._spine.state.clearListeners(), this._spine.destroy(), null), this._winResolve) {
80
+ let e = this._winResolve;
81
+ this._winResolve = null, e();
82
+ }
77
83
  }
78
84
  };
79
85
  //#endregion
80
86
  export { t as n, i as t };
81
87
 
82
- //# sourceMappingURL=SpineSymbol-D2jhCzFW.js.map
88
+ //# sourceMappingURL=SpineSymbol-CfVrc5Pk.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"SpineSymbol-D2jhCzFW.js","names":[],"sources":["../src/symbols/ReelSymbol.ts","../src/symbols/SpineSymbol.ts"],"sourcesContent":["import { Container } from 'pixi.js';\nimport type { Disposable } from '../utils/Disposable.js';\n\n/**\n * One visible cell on a reel — the thing that actually draws.\n *\n * `ReelSymbol` is the abstract base class. Subclass it to pick a rendering\n * technology (`SpriteSymbol`, `AnimatedSpriteSymbol`, `SpineSymbol`, or a\n * custom class of your own). The reel set pools instances aggressively:\n * one instance is reused many times as it scrolls off one identity and on\n * to another, so implementations must never assume \"I was just created\".\n *\n * Required lifecycle hooks:\n *\n * - `onActivate(symbolId)` — the pool just handed me a new identity. Swap\n * texture, restart animations, bring myself out of any \"ended\" pose.\n * - `onDeactivate()` — I am about to be pooled. Pause animations, clear\n * listeners, leave myself in a clean state for the next activation.\n * - `playWin()` — the spotlight is celebrating me. Return a promise that\n * resolves when the one-shot animation is done.\n * - `stopAnimation()` — spotlight is over, return to idle.\n * - `resize(w, h)` — the reel's cell size changed (on every symbol swap).\n * Store the dimensions and reposition internal children. Forgetting\n * this is the single most common \"why do my symbols scatter\" bug.\n *\n * ```\n * create → activate(symbolId) → [playWin / stopAnimation]\n * → deactivate\n * → activate(newId) → ...\n * ```\n *\n * There's no hidden GC. Hold resources? Override `onDestroy()`.\n */\nexport abstract class ReelSymbol implements Disposable {\n /** The PixiJS container that holds this symbol's visual. */\n public readonly view: Container;\n\n private _symbolId: string = '';\n private _isDestroyed = false;\n\n constructor() {\n this.view = new Container();\n }\n\n get symbolId(): string {\n return this._symbolId;\n }\n\n get isDestroyed(): boolean {\n return this._isDestroyed;\n }\n\n /**\n * Activate the symbol with a new identity.\n * Called when the symbol enters the visible reel or is recycled from the pool.\n */\n activate(symbolId: string): void {\n this._symbolId = symbolId;\n this.view.visible = true;\n this.onActivate(symbolId);\n }\n\n /**\n * Deactivate the symbol before returning it to the pool.\n * Stops any running animations and hides the view.\n */\n deactivate(): void {\n this.stopAnimation();\n this.onDeactivate();\n this._symbolId = '';\n this.view.visible = false;\n }\n\n /** Pool reset — aliases deactivate. */\n reset(): void {\n this.deactivate();\n }\n\n destroy(): void {\n if (this._isDestroyed) return;\n this.deactivate();\n this.onDestroy();\n this.view.destroy({ children: true });\n this._isDestroyed = true;\n }\n\n /** Subclass hook: set up visuals for the given symbolId. */\n protected abstract onActivate(symbolId: string): void;\n\n /** Subclass hook: clean up visuals. */\n protected abstract onDeactivate(): void;\n\n /** Subclass hook: additional cleanup on destroy. */\n protected onDestroy(): void {\n // Override if needed\n }\n\n /** Play the win/highlight animation for this symbol. Resolves when complete. */\n abstract playWin(): Promise<void>;\n\n /** Immediately stop any running animation and return to idle. */\n abstract stopAnimation(): void;\n\n /** Resize the symbol's visual to fit the given dimensions. */\n abstract resize(width: number, height: number): void;\n\n /**\n * Lifecycle hook: the owning reel has started spinning.\n * Default: no-op. Override (e.g. SpineReelSymbol.autoPlayBlur) to swap to\n * a blur animation automatically.\n */\n onReelSpinStart(): void {}\n\n /**\n * Lifecycle hook: the owning reel is about to stop (just before bounce).\n * Default: no-op.\n */\n onReelSpinEnd(): void {}\n\n /**\n * Lifecycle hook: the owning reel has landed on its final symbols.\n * Default: no-op. Override (e.g. SpineReelSymbol.autoPlayLanding) to fire\n * a landing animation concurrently with the bounce.\n */\n onReelLanded(): void {}\n}\n","import { ReelSymbol } from './ReelSymbol.js';\n\n// Spine types imported dynamically — this is an optional peer dependency\nlet SpineClass: any = null;\n\nasync function loadSpine(): Promise<void> {\n try {\n const spineModule = await import('@esotericsoftware/spine-pixi-v8');\n SpineClass = spineModule.Spine;\n } catch {\n // Spine not available — SpineSymbol will throw on construction\n }\n}\n\n// Attempt to load Spine on module init\nloadSpine();\n\nexport interface SpineSymbolOptions {\n /** Map of symbolId → SkeletonData. */\n skeletonDataMap: Record<string, any>;\n /** Default animation name to play in idle. Default: 'idle'. */\n idleAnimation?: string;\n /** Animation name to play on win. Default: 'win'. */\n winAnimation?: string;\n /** Default skin name. Default: 'default'. */\n defaultSkin?: string;\n}\n\n/**\n * Symbol implementation using Spine 2D skeletal animation.\n *\n * Requires `@esotericsoftware/spine-pixi-v8` as an optional peer dependency.\n * If Spine is not installed, constructing a SpineSymbol will throw.\n */\nexport class SpineSymbol extends ReelSymbol {\n private _spine: any = null;\n private _skeletonDataMap: Record<string, any>;\n private _idleAnimation: string;\n private _winAnimation: string;\n private _defaultSkin: string;\n private _winResolve: (() => void) | null = null;\n private _currentSkeletonKey: string = '';\n\n constructor(options: SpineSymbolOptions) {\n super();\n if (!SpineClass) {\n throw new Error(\n 'SpineSymbol requires @esotericsoftware/spine-pixi-v8 to be installed. ' +\n 'Install it with: npm install @esotericsoftware/spine-pixi-v8',\n );\n }\n this._skeletonDataMap = options.skeletonDataMap;\n this._idleAnimation = options.idleAnimation ?? 'idle';\n this._winAnimation = options.winAnimation ?? 'win';\n this._defaultSkin = options.defaultSkin ?? 'default';\n }\n\n protected onActivate(symbolId: string): void {\n const skeletonData = this._skeletonDataMap[symbolId];\n if (!skeletonData) return;\n\n // Reuse existing spine if same skeleton data\n if (this._currentSkeletonKey !== symbolId) {\n if (this._spine) {\n this.view.removeChild(this._spine);\n this._spine.destroy();\n }\n this._spine = new SpineClass({ skeletonData });\n this.view.addChild(this._spine);\n this._currentSkeletonKey = symbolId;\n }\n\n // Set to idle\n if (this._spine.skeleton.data.findSkin(this._defaultSkin)) {\n this._spine.skeleton.setSkinByName(this._defaultSkin);\n this._spine.skeleton.setSlotsToSetupPose();\n }\n if (this._spine.skeleton.data.findAnimation(this._idleAnimation)) {\n this._spine.state.setAnimation(0, this._idleAnimation, true);\n }\n }\n\n protected onDeactivate(): void {\n if (this._spine) {\n this._spine.state.clearListeners();\n this._spine.state.clearTracks();\n }\n this._winResolve = null;\n }\n\n async playWin(): Promise<void> {\n if (!this._spine) return;\n if (!this._spine.skeleton.data.findAnimation(this._winAnimation)) return;\n\n return new Promise<void>((resolve) => {\n this._winResolve = resolve;\n const entry = this._spine.state.setAnimation(0, this._winAnimation, false);\n this._spine.state.addListener({\n complete: (trackEntry: any) => {\n if (trackEntry === entry) {\n this._spine.state.clearListeners();\n // Return to idle\n if (this._spine.skeleton.data.findAnimation(this._idleAnimation)) {\n this._spine.state.setAnimation(0, this._idleAnimation, true);\n }\n this._winResolve = null;\n resolve();\n }\n },\n });\n });\n }\n\n stopAnimation(): void {\n if (!this._spine) return;\n this._spine.state.clearListeners();\n if (this._spine.skeleton.data.findAnimation(this._idleAnimation)) {\n this._spine.state.setAnimation(0, this._idleAnimation, true);\n }\n if (this._winResolve) {\n this._winResolve();\n this._winResolve = null;\n }\n }\n\n resize(width: number, height: number): void {\n if (!this._spine) return;\n const bounds = this._spine.getBounds();\n if (bounds.width > 0 && bounds.height > 0) {\n this._spine.scale.set(\n width / bounds.width,\n height / bounds.height,\n );\n }\n }\n\n protected override onDestroy(): void {\n if (this._spine) {\n this._spine.state.clearListeners();\n this._spine.destroy();\n this._spine = null;\n }\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"SpineSymbol-CfVrc5Pk.js","names":[],"sources":["../src/symbols/ReelSymbol.ts","../src/symbols/SpineSymbol.ts"],"sourcesContent":["import { Container } from 'pixi.js';\nimport type { Disposable } from '../utils/Disposable.js';\n\n/**\n * One visible cell on a reel — the thing that actually draws.\n *\n * `ReelSymbol` is the abstract base class. Subclass it to pick a rendering\n * technology (`SpriteSymbol`, `AnimatedSpriteSymbol`, `SpineSymbol`, or a\n * custom class of your own). The reel set pools instances aggressively:\n * one instance is reused many times as it scrolls off one identity and on\n * to another, so implementations must never assume \"I was just created\".\n *\n * Required lifecycle hooks:\n *\n * - `onActivate(symbolId)` — the pool just handed me a new identity. Swap\n * texture, restart animations, bring myself out of any \"ended\" pose.\n * - `onDeactivate()` — I am about to be pooled. Pause animations, clear\n * listeners, leave myself in a clean state for the next activation.\n * - `playWin()` — the spotlight is celebrating me. Return a promise that\n * resolves when the one-shot animation is done.\n * - `stopAnimation()` — spotlight is over, return to idle.\n * - `resize(w, h)` — the reel's cell size changed (on every symbol swap).\n * Store the dimensions and reposition internal children. Forgetting\n * this is the single most common \"why do my symbols scatter\" bug.\n *\n * ```\n * create → activate(symbolId) → [playWin / stopAnimation]\n * → deactivate\n * → activate(newId) → ...\n * ```\n *\n * There's no hidden GC. Hold resources? Override `onDestroy()`.\n */\nexport abstract class ReelSymbol implements Disposable {\n /** The PixiJS container that holds this symbol's visual. */\n public readonly view: Container;\n\n private _symbolId: string = '';\n private _isDestroyed = false;\n\n constructor() {\n this.view = new Container();\n }\n\n get symbolId(): string {\n return this._symbolId;\n }\n\n get isDestroyed(): boolean {\n return this._isDestroyed;\n }\n\n /**\n * Activate the symbol with a new identity. Called when the symbol enters\n * the visible reel or is recycled from the pool. Resets container\n * transform / filter state for parity with deactivate().\n */\n activate(symbolId: string): void {\n this._symbolId = symbolId;\n this.view.visible = true;\n this.view.alpha = 1;\n this.view.scale.set(1, 1);\n this.view.rotation = 0;\n this.view.filters = null;\n this.view.zIndex = 0;\n this.onActivate(symbolId);\n }\n\n /**\n * Deactivate the symbol before returning it to the pool. Stops\n * animations, hides the view, and resets container transform / filter\n * state so subclass decorations don't leak across recycles.\n */\n deactivate(): void {\n this.stopAnimation();\n this.onDeactivate();\n this._symbolId = '';\n this.view.visible = false;\n this.view.alpha = 1;\n this.view.scale.set(1, 1);\n this.view.rotation = 0;\n this.view.filters = null;\n this.view.zIndex = 0;\n }\n\n /** Pool reset — aliases deactivate. */\n reset(): void {\n this.deactivate();\n }\n\n destroy(): void {\n if (this._isDestroyed) return;\n this.stopAnimation();\n this.onDeactivate();\n this.onDestroy();\n if (!this.view.destroyed) this.view.destroy({ children: true });\n this._isDestroyed = true;\n }\n\n /** Subclass hook: set up visuals for the given symbolId. */\n protected abstract onActivate(symbolId: string): void;\n\n /** Subclass hook: clean up visuals. */\n protected abstract onDeactivate(): void;\n\n /** Subclass hook: additional cleanup on destroy. */\n protected onDestroy(): void {\n // Override if needed\n }\n\n /** Play the win/highlight animation for this symbol. Resolves when complete. */\n abstract playWin(): Promise<void>;\n\n /** Immediately stop any running animation and return to idle. */\n abstract stopAnimation(): void;\n\n /** Resize the symbol's visual to fit the given dimensions. */\n abstract resize(width: number, height: number): void;\n\n /**\n * Lifecycle hook: the owning reel has started spinning.\n * Default: no-op. Override (e.g. SpineReelSymbol.autoPlayBlur) to swap to\n * a blur animation automatically.\n */\n onReelSpinStart(): void {}\n\n /**\n * Lifecycle hook: the owning reel is about to stop (just before bounce).\n * Default: no-op.\n */\n onReelSpinEnd(): void {}\n\n /**\n * Lifecycle hook: the owning reel has landed on its final symbols.\n * Default: no-op. Override (e.g. SpineReelSymbol.autoPlayLanding) to fire\n * a landing animation concurrently with the bounce.\n */\n onReelLanded(): void {}\n}\n","import { ReelSymbol } from './ReelSymbol.js';\n\n// Spine types imported dynamically — this is an optional peer dependency\nlet SpineClass: any = null;\n\nasync function loadSpine(): Promise<void> {\n try {\n const spineModule = await import('@esotericsoftware/spine-pixi-v8');\n SpineClass = spineModule.Spine;\n } catch {\n // Spine not available — SpineSymbol will throw on construction\n }\n}\n\n// Attempt to load Spine on module init\nloadSpine();\n\nexport interface SpineSymbolOptions {\n /** Map of symbolId → SkeletonData. */\n skeletonDataMap: Record<string, any>;\n /** Default animation name to play in idle. Default: 'idle'. */\n idleAnimation?: string;\n /** Animation name to play on win. Default: 'win'. */\n winAnimation?: string;\n /** Default skin name. Default: 'default'. */\n defaultSkin?: string;\n}\n\n/**\n * Symbol implementation using Spine 2D skeletal animation.\n *\n * Requires `@esotericsoftware/spine-pixi-v8` as an optional peer dependency.\n * If Spine is not installed, constructing a SpineSymbol will throw.\n */\nexport class SpineSymbol extends ReelSymbol {\n private _spine: any = null;\n private _skeletonDataMap: Record<string, any>;\n private _idleAnimation: string;\n private _winAnimation: string;\n private _defaultSkin: string;\n private _winResolve: (() => void) | null = null;\n private _currentSkeletonKey: string = '';\n\n constructor(options: SpineSymbolOptions) {\n super();\n if (!SpineClass) {\n throw new Error(\n 'SpineSymbol requires @esotericsoftware/spine-pixi-v8 to be installed. ' +\n 'Install it with: npm install @esotericsoftware/spine-pixi-v8',\n );\n }\n this._skeletonDataMap = options.skeletonDataMap;\n this._idleAnimation = options.idleAnimation ?? 'idle';\n this._winAnimation = options.winAnimation ?? 'win';\n this._defaultSkin = options.defaultSkin ?? 'default';\n }\n\n protected onActivate(symbolId: string): void {\n const skeletonData = this._skeletonDataMap[symbolId];\n if (!skeletonData) return;\n\n // Reuse existing spine if same skeleton data\n if (this._currentSkeletonKey !== symbolId) {\n if (this._spine) {\n this.view.removeChild(this._spine);\n this._spine.destroy();\n }\n this._spine = new SpineClass({ skeletonData });\n this.view.addChild(this._spine);\n this._currentSkeletonKey = symbolId;\n }\n\n // Set to idle\n if (this._spine.skeleton.data.findSkin(this._defaultSkin)) {\n this._spine.skeleton.setSkinByName(this._defaultSkin);\n this._spine.skeleton.setSlotsToSetupPose();\n }\n if (this._spine.skeleton.data.findAnimation(this._idleAnimation)) {\n this._spine.state.setAnimation(0, this._idleAnimation, true);\n }\n }\n\n protected onDeactivate(): void {\n if (this._spine) {\n this._spine.state.clearListeners();\n this._spine.state.clearTracks();\n }\n // Settle any pending playWin so awaiters don't hang when the symbol is\n // recycled mid-animation. Mirrors the same fix in SpineReelSymbol.\n if (this._winResolve) {\n const r = this._winResolve;\n this._winResolve = null;\n r();\n }\n }\n\n async playWin(): Promise<void> {\n if (!this._spine) return;\n if (!this._spine.skeleton.data.findAnimation(this._winAnimation)) return;\n\n return new Promise<void>((resolve) => {\n this._winResolve = resolve;\n const entry = this._spine.state.setAnimation(0, this._winAnimation, false);\n this._spine.state.addListener({\n complete: (trackEntry: any) => {\n if (trackEntry === entry) {\n this._spine.state.clearListeners();\n // Return to idle\n if (this._spine.skeleton.data.findAnimation(this._idleAnimation)) {\n this._spine.state.setAnimation(0, this._idleAnimation, true);\n }\n this._winResolve = null;\n resolve();\n }\n },\n });\n });\n }\n\n stopAnimation(): void {\n if (!this._spine) return;\n this._spine.state.clearListeners();\n if (this._spine.skeleton.data.findAnimation(this._idleAnimation)) {\n this._spine.state.setAnimation(0, this._idleAnimation, true);\n }\n if (this._winResolve) {\n this._winResolve();\n this._winResolve = null;\n }\n }\n\n resize(width: number, height: number): void {\n if (!this._spine) return;\n const bounds = this._spine.getBounds();\n if (bounds.width > 0 && bounds.height > 0) {\n this._spine.scale.set(\n width / bounds.width,\n height / bounds.height,\n );\n }\n }\n\n protected override onDestroy(): void {\n if (this._spine) {\n this._spine.state.clearListeners();\n this._spine.destroy();\n this._spine = null;\n }\n if (this._winResolve) {\n const r = this._winResolve;\n this._winResolve = null;\n r();\n }\n }\n}\n"],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ let e=require(`pixi.js`);var t=class{view;_symbolId=``;_isDestroyed=!1;constructor(){this.view=new e.Container}get symbolId(){return this._symbolId}get isDestroyed(){return this._isDestroyed}activate(e){this._symbolId=e,this.view.visible=!0,this.view.alpha=1,this.view.scale.set(1,1),this.view.rotation=0,this.view.filters=null,this.view.zIndex=0,this.onActivate(e)}deactivate(){this.stopAnimation(),this.onDeactivate(),this._symbolId=``,this.view.visible=!1,this.view.alpha=1,this.view.scale.set(1,1),this.view.rotation=0,this.view.filters=null,this.view.zIndex=0}reset(){this.deactivate()}destroy(){this._isDestroyed||=(this.stopAnimation(),this.onDeactivate(),this.onDestroy(),this.view.destroyed||this.view.destroy({children:!0}),!0)}onDestroy(){}onReelSpinStart(){}onReelSpinEnd(){}onReelLanded(){}},n=null;async function r(){try{n=(await import(`@esotericsoftware/spine-pixi-v8`)).Spine}catch{}}r();var i=class extends t{_spine=null;_skeletonDataMap;_idleAnimation;_winAnimation;_defaultSkin;_winResolve=null;_currentSkeletonKey=``;constructor(e){if(super(),!n)throw Error(`SpineSymbol requires @esotericsoftware/spine-pixi-v8 to be installed. Install it with: npm install @esotericsoftware/spine-pixi-v8`);this._skeletonDataMap=e.skeletonDataMap,this._idleAnimation=e.idleAnimation??`idle`,this._winAnimation=e.winAnimation??`win`,this._defaultSkin=e.defaultSkin??`default`}onActivate(e){let t=this._skeletonDataMap[e];t&&(this._currentSkeletonKey!==e&&(this._spine&&(this.view.removeChild(this._spine),this._spine.destroy()),this._spine=new n({skeletonData:t}),this.view.addChild(this._spine),this._currentSkeletonKey=e),this._spine.skeleton.data.findSkin(this._defaultSkin)&&(this._spine.skeleton.setSkinByName(this._defaultSkin),this._spine.skeleton.setSlotsToSetupPose()),this._spine.skeleton.data.findAnimation(this._idleAnimation)&&this._spine.state.setAnimation(0,this._idleAnimation,!0))}onDeactivate(){if(this._spine&&(this._spine.state.clearListeners(),this._spine.state.clearTracks()),this._winResolve){let e=this._winResolve;this._winResolve=null,e()}}async playWin(){if(this._spine&&this._spine.skeleton.data.findAnimation(this._winAnimation))return new Promise(e=>{this._winResolve=e;let t=this._spine.state.setAnimation(0,this._winAnimation,!1);this._spine.state.addListener({complete:n=>{n===t&&(this._spine.state.clearListeners(),this._spine.skeleton.data.findAnimation(this._idleAnimation)&&this._spine.state.setAnimation(0,this._idleAnimation,!0),this._winResolve=null,e())}})})}stopAnimation(){this._spine&&(this._spine.state.clearListeners(),this._spine.skeleton.data.findAnimation(this._idleAnimation)&&this._spine.state.setAnimation(0,this._idleAnimation,!0),this._winResolve&&=(this._winResolve(),null))}resize(e,t){if(!this._spine)return;let n=this._spine.getBounds();n.width>0&&n.height>0&&this._spine.scale.set(e/n.width,t/n.height)}onDestroy(){if(this._spine&&=(this._spine.state.clearListeners(),this._spine.destroy(),null),this._winResolve){let e=this._winResolve;this._winResolve=null,e()}}};Object.defineProperty(exports,`n`,{enumerable:!0,get:function(){return t}}),Object.defineProperty(exports,`t`,{enumerable:!0,get:function(){return i}});
2
+ //# sourceMappingURL=SpineSymbol-JA5PdEbT.cjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"SpineSymbol-B40TevSr.cjs","names":[],"sources":["../src/symbols/ReelSymbol.ts","../src/symbols/SpineSymbol.ts"],"sourcesContent":["import { Container } from 'pixi.js';\nimport type { Disposable } from '../utils/Disposable.js';\n\n/**\n * One visible cell on a reel — the thing that actually draws.\n *\n * `ReelSymbol` is the abstract base class. Subclass it to pick a rendering\n * technology (`SpriteSymbol`, `AnimatedSpriteSymbol`, `SpineSymbol`, or a\n * custom class of your own). The reel set pools instances aggressively:\n * one instance is reused many times as it scrolls off one identity and on\n * to another, so implementations must never assume \"I was just created\".\n *\n * Required lifecycle hooks:\n *\n * - `onActivate(symbolId)` — the pool just handed me a new identity. Swap\n * texture, restart animations, bring myself out of any \"ended\" pose.\n * - `onDeactivate()` — I am about to be pooled. Pause animations, clear\n * listeners, leave myself in a clean state for the next activation.\n * - `playWin()` — the spotlight is celebrating me. Return a promise that\n * resolves when the one-shot animation is done.\n * - `stopAnimation()` — spotlight is over, return to idle.\n * - `resize(w, h)` — the reel's cell size changed (on every symbol swap).\n * Store the dimensions and reposition internal children. Forgetting\n * this is the single most common \"why do my symbols scatter\" bug.\n *\n * ```\n * create → activate(symbolId) → [playWin / stopAnimation]\n * → deactivate\n * → activate(newId) → ...\n * ```\n *\n * There's no hidden GC. Hold resources? Override `onDestroy()`.\n */\nexport abstract class ReelSymbol implements Disposable {\n /** The PixiJS container that holds this symbol's visual. */\n public readonly view: Container;\n\n private _symbolId: string = '';\n private _isDestroyed = false;\n\n constructor() {\n this.view = new Container();\n }\n\n get symbolId(): string {\n return this._symbolId;\n }\n\n get isDestroyed(): boolean {\n return this._isDestroyed;\n }\n\n /**\n * Activate the symbol with a new identity.\n * Called when the symbol enters the visible reel or is recycled from the pool.\n */\n activate(symbolId: string): void {\n this._symbolId = symbolId;\n this.view.visible = true;\n this.onActivate(symbolId);\n }\n\n /**\n * Deactivate the symbol before returning it to the pool.\n * Stops any running animations and hides the view.\n */\n deactivate(): void {\n this.stopAnimation();\n this.onDeactivate();\n this._symbolId = '';\n this.view.visible = false;\n }\n\n /** Pool reset — aliases deactivate. */\n reset(): void {\n this.deactivate();\n }\n\n destroy(): void {\n if (this._isDestroyed) return;\n this.deactivate();\n this.onDestroy();\n this.view.destroy({ children: true });\n this._isDestroyed = true;\n }\n\n /** Subclass hook: set up visuals for the given symbolId. */\n protected abstract onActivate(symbolId: string): void;\n\n /** Subclass hook: clean up visuals. */\n protected abstract onDeactivate(): void;\n\n /** Subclass hook: additional cleanup on destroy. */\n protected onDestroy(): void {\n // Override if needed\n }\n\n /** Play the win/highlight animation for this symbol. Resolves when complete. */\n abstract playWin(): Promise<void>;\n\n /** Immediately stop any running animation and return to idle. */\n abstract stopAnimation(): void;\n\n /** Resize the symbol's visual to fit the given dimensions. */\n abstract resize(width: number, height: number): void;\n\n /**\n * Lifecycle hook: the owning reel has started spinning.\n * Default: no-op. Override (e.g. SpineReelSymbol.autoPlayBlur) to swap to\n * a blur animation automatically.\n */\n onReelSpinStart(): void {}\n\n /**\n * Lifecycle hook: the owning reel is about to stop (just before bounce).\n * Default: no-op.\n */\n onReelSpinEnd(): void {}\n\n /**\n * Lifecycle hook: the owning reel has landed on its final symbols.\n * Default: no-op. Override (e.g. SpineReelSymbol.autoPlayLanding) to fire\n * a landing animation concurrently with the bounce.\n */\n onReelLanded(): void {}\n}\n","import { ReelSymbol } from './ReelSymbol.js';\n\n// Spine types imported dynamically — this is an optional peer dependency\nlet SpineClass: any = null;\n\nasync function loadSpine(): Promise<void> {\n try {\n const spineModule = await import('@esotericsoftware/spine-pixi-v8');\n SpineClass = spineModule.Spine;\n } catch {\n // Spine not available — SpineSymbol will throw on construction\n }\n}\n\n// Attempt to load Spine on module init\nloadSpine();\n\nexport interface SpineSymbolOptions {\n /** Map of symbolId → SkeletonData. */\n skeletonDataMap: Record<string, any>;\n /** Default animation name to play in idle. Default: 'idle'. */\n idleAnimation?: string;\n /** Animation name to play on win. Default: 'win'. */\n winAnimation?: string;\n /** Default skin name. Default: 'default'. */\n defaultSkin?: string;\n}\n\n/**\n * Symbol implementation using Spine 2D skeletal animation.\n *\n * Requires `@esotericsoftware/spine-pixi-v8` as an optional peer dependency.\n * If Spine is not installed, constructing a SpineSymbol will throw.\n */\nexport class SpineSymbol extends ReelSymbol {\n private _spine: any = null;\n private _skeletonDataMap: Record<string, any>;\n private _idleAnimation: string;\n private _winAnimation: string;\n private _defaultSkin: string;\n private _winResolve: (() => void) | null = null;\n private _currentSkeletonKey: string = '';\n\n constructor(options: SpineSymbolOptions) {\n super();\n if (!SpineClass) {\n throw new Error(\n 'SpineSymbol requires @esotericsoftware/spine-pixi-v8 to be installed. ' +\n 'Install it with: npm install @esotericsoftware/spine-pixi-v8',\n );\n }\n this._skeletonDataMap = options.skeletonDataMap;\n this._idleAnimation = options.idleAnimation ?? 'idle';\n this._winAnimation = options.winAnimation ?? 'win';\n this._defaultSkin = options.defaultSkin ?? 'default';\n }\n\n protected onActivate(symbolId: string): void {\n const skeletonData = this._skeletonDataMap[symbolId];\n if (!skeletonData) return;\n\n // Reuse existing spine if same skeleton data\n if (this._currentSkeletonKey !== symbolId) {\n if (this._spine) {\n this.view.removeChild(this._spine);\n this._spine.destroy();\n }\n this._spine = new SpineClass({ skeletonData });\n this.view.addChild(this._spine);\n this._currentSkeletonKey = symbolId;\n }\n\n // Set to idle\n if (this._spine.skeleton.data.findSkin(this._defaultSkin)) {\n this._spine.skeleton.setSkinByName(this._defaultSkin);\n this._spine.skeleton.setSlotsToSetupPose();\n }\n if (this._spine.skeleton.data.findAnimation(this._idleAnimation)) {\n this._spine.state.setAnimation(0, this._idleAnimation, true);\n }\n }\n\n protected onDeactivate(): void {\n if (this._spine) {\n this._spine.state.clearListeners();\n this._spine.state.clearTracks();\n }\n this._winResolve = null;\n }\n\n async playWin(): Promise<void> {\n if (!this._spine) return;\n if (!this._spine.skeleton.data.findAnimation(this._winAnimation)) return;\n\n return new Promise<void>((resolve) => {\n this._winResolve = resolve;\n const entry = this._spine.state.setAnimation(0, this._winAnimation, false);\n this._spine.state.addListener({\n complete: (trackEntry: any) => {\n if (trackEntry === entry) {\n this._spine.state.clearListeners();\n // Return to idle\n if (this._spine.skeleton.data.findAnimation(this._idleAnimation)) {\n this._spine.state.setAnimation(0, this._idleAnimation, true);\n }\n this._winResolve = null;\n resolve();\n }\n },\n });\n });\n }\n\n stopAnimation(): void {\n if (!this._spine) return;\n this._spine.state.clearListeners();\n if (this._spine.skeleton.data.findAnimation(this._idleAnimation)) {\n this._spine.state.setAnimation(0, this._idleAnimation, true);\n }\n if (this._winResolve) {\n this._winResolve();\n this._winResolve = null;\n }\n }\n\n resize(width: number, height: number): void {\n if (!this._spine) return;\n const bounds = this._spine.getBounds();\n if (bounds.width > 0 && bounds.height > 0) {\n this._spine.scale.set(\n width / bounds.width,\n height / bounds.height,\n );\n }\n }\n\n protected override onDestroy(): void {\n if (this._spine) {\n this._spine.state.clearListeners();\n this._spine.destroy();\n this._spine = null;\n }\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"SpineSymbol-JA5PdEbT.cjs","names":[],"sources":["../src/symbols/ReelSymbol.ts","../src/symbols/SpineSymbol.ts"],"sourcesContent":["import { Container } from 'pixi.js';\nimport type { Disposable } from '../utils/Disposable.js';\n\n/**\n * One visible cell on a reel — the thing that actually draws.\n *\n * `ReelSymbol` is the abstract base class. Subclass it to pick a rendering\n * technology (`SpriteSymbol`, `AnimatedSpriteSymbol`, `SpineSymbol`, or a\n * custom class of your own). The reel set pools instances aggressively:\n * one instance is reused many times as it scrolls off one identity and on\n * to another, so implementations must never assume \"I was just created\".\n *\n * Required lifecycle hooks:\n *\n * - `onActivate(symbolId)` — the pool just handed me a new identity. Swap\n * texture, restart animations, bring myself out of any \"ended\" pose.\n * - `onDeactivate()` — I am about to be pooled. Pause animations, clear\n * listeners, leave myself in a clean state for the next activation.\n * - `playWin()` — the spotlight is celebrating me. Return a promise that\n * resolves when the one-shot animation is done.\n * - `stopAnimation()` — spotlight is over, return to idle.\n * - `resize(w, h)` — the reel's cell size changed (on every symbol swap).\n * Store the dimensions and reposition internal children. Forgetting\n * this is the single most common \"why do my symbols scatter\" bug.\n *\n * ```\n * create → activate(symbolId) → [playWin / stopAnimation]\n * → deactivate\n * → activate(newId) → ...\n * ```\n *\n * There's no hidden GC. Hold resources? Override `onDestroy()`.\n */\nexport abstract class ReelSymbol implements Disposable {\n /** The PixiJS container that holds this symbol's visual. */\n public readonly view: Container;\n\n private _symbolId: string = '';\n private _isDestroyed = false;\n\n constructor() {\n this.view = new Container();\n }\n\n get symbolId(): string {\n return this._symbolId;\n }\n\n get isDestroyed(): boolean {\n return this._isDestroyed;\n }\n\n /**\n * Activate the symbol with a new identity. Called when the symbol enters\n * the visible reel or is recycled from the pool. Resets container\n * transform / filter state for parity with deactivate().\n */\n activate(symbolId: string): void {\n this._symbolId = symbolId;\n this.view.visible = true;\n this.view.alpha = 1;\n this.view.scale.set(1, 1);\n this.view.rotation = 0;\n this.view.filters = null;\n this.view.zIndex = 0;\n this.onActivate(symbolId);\n }\n\n /**\n * Deactivate the symbol before returning it to the pool. Stops\n * animations, hides the view, and resets container transform / filter\n * state so subclass decorations don't leak across recycles.\n */\n deactivate(): void {\n this.stopAnimation();\n this.onDeactivate();\n this._symbolId = '';\n this.view.visible = false;\n this.view.alpha = 1;\n this.view.scale.set(1, 1);\n this.view.rotation = 0;\n this.view.filters = null;\n this.view.zIndex = 0;\n }\n\n /** Pool reset — aliases deactivate. */\n reset(): void {\n this.deactivate();\n }\n\n destroy(): void {\n if (this._isDestroyed) return;\n this.stopAnimation();\n this.onDeactivate();\n this.onDestroy();\n if (!this.view.destroyed) this.view.destroy({ children: true });\n this._isDestroyed = true;\n }\n\n /** Subclass hook: set up visuals for the given symbolId. */\n protected abstract onActivate(symbolId: string): void;\n\n /** Subclass hook: clean up visuals. */\n protected abstract onDeactivate(): void;\n\n /** Subclass hook: additional cleanup on destroy. */\n protected onDestroy(): void {\n // Override if needed\n }\n\n /** Play the win/highlight animation for this symbol. Resolves when complete. */\n abstract playWin(): Promise<void>;\n\n /** Immediately stop any running animation and return to idle. */\n abstract stopAnimation(): void;\n\n /** Resize the symbol's visual to fit the given dimensions. */\n abstract resize(width: number, height: number): void;\n\n /**\n * Lifecycle hook: the owning reel has started spinning.\n * Default: no-op. Override (e.g. SpineReelSymbol.autoPlayBlur) to swap to\n * a blur animation automatically.\n */\n onReelSpinStart(): void {}\n\n /**\n * Lifecycle hook: the owning reel is about to stop (just before bounce).\n * Default: no-op.\n */\n onReelSpinEnd(): void {}\n\n /**\n * Lifecycle hook: the owning reel has landed on its final symbols.\n * Default: no-op. Override (e.g. SpineReelSymbol.autoPlayLanding) to fire\n * a landing animation concurrently with the bounce.\n */\n onReelLanded(): void {}\n}\n","import { ReelSymbol } from './ReelSymbol.js';\n\n// Spine types imported dynamically — this is an optional peer dependency\nlet SpineClass: any = null;\n\nasync function loadSpine(): Promise<void> {\n try {\n const spineModule = await import('@esotericsoftware/spine-pixi-v8');\n SpineClass = spineModule.Spine;\n } catch {\n // Spine not available — SpineSymbol will throw on construction\n }\n}\n\n// Attempt to load Spine on module init\nloadSpine();\n\nexport interface SpineSymbolOptions {\n /** Map of symbolId → SkeletonData. */\n skeletonDataMap: Record<string, any>;\n /** Default animation name to play in idle. Default: 'idle'. */\n idleAnimation?: string;\n /** Animation name to play on win. Default: 'win'. */\n winAnimation?: string;\n /** Default skin name. Default: 'default'. */\n defaultSkin?: string;\n}\n\n/**\n * Symbol implementation using Spine 2D skeletal animation.\n *\n * Requires `@esotericsoftware/spine-pixi-v8` as an optional peer dependency.\n * If Spine is not installed, constructing a SpineSymbol will throw.\n */\nexport class SpineSymbol extends ReelSymbol {\n private _spine: any = null;\n private _skeletonDataMap: Record<string, any>;\n private _idleAnimation: string;\n private _winAnimation: string;\n private _defaultSkin: string;\n private _winResolve: (() => void) | null = null;\n private _currentSkeletonKey: string = '';\n\n constructor(options: SpineSymbolOptions) {\n super();\n if (!SpineClass) {\n throw new Error(\n 'SpineSymbol requires @esotericsoftware/spine-pixi-v8 to be installed. ' +\n 'Install it with: npm install @esotericsoftware/spine-pixi-v8',\n );\n }\n this._skeletonDataMap = options.skeletonDataMap;\n this._idleAnimation = options.idleAnimation ?? 'idle';\n this._winAnimation = options.winAnimation ?? 'win';\n this._defaultSkin = options.defaultSkin ?? 'default';\n }\n\n protected onActivate(symbolId: string): void {\n const skeletonData = this._skeletonDataMap[symbolId];\n if (!skeletonData) return;\n\n // Reuse existing spine if same skeleton data\n if (this._currentSkeletonKey !== symbolId) {\n if (this._spine) {\n this.view.removeChild(this._spine);\n this._spine.destroy();\n }\n this._spine = new SpineClass({ skeletonData });\n this.view.addChild(this._spine);\n this._currentSkeletonKey = symbolId;\n }\n\n // Set to idle\n if (this._spine.skeleton.data.findSkin(this._defaultSkin)) {\n this._spine.skeleton.setSkinByName(this._defaultSkin);\n this._spine.skeleton.setSlotsToSetupPose();\n }\n if (this._spine.skeleton.data.findAnimation(this._idleAnimation)) {\n this._spine.state.setAnimation(0, this._idleAnimation, true);\n }\n }\n\n protected onDeactivate(): void {\n if (this._spine) {\n this._spine.state.clearListeners();\n this._spine.state.clearTracks();\n }\n // Settle any pending playWin so awaiters don't hang when the symbol is\n // recycled mid-animation. Mirrors the same fix in SpineReelSymbol.\n if (this._winResolve) {\n const r = this._winResolve;\n this._winResolve = null;\n r();\n }\n }\n\n async playWin(): Promise<void> {\n if (!this._spine) return;\n if (!this._spine.skeleton.data.findAnimation(this._winAnimation)) return;\n\n return new Promise<void>((resolve) => {\n this._winResolve = resolve;\n const entry = this._spine.state.setAnimation(0, this._winAnimation, false);\n this._spine.state.addListener({\n complete: (trackEntry: any) => {\n if (trackEntry === entry) {\n this._spine.state.clearListeners();\n // Return to idle\n if (this._spine.skeleton.data.findAnimation(this._idleAnimation)) {\n this._spine.state.setAnimation(0, this._idleAnimation, true);\n }\n this._winResolve = null;\n resolve();\n }\n },\n });\n });\n }\n\n stopAnimation(): void {\n if (!this._spine) return;\n this._spine.state.clearListeners();\n if (this._spine.skeleton.data.findAnimation(this._idleAnimation)) {\n this._spine.state.setAnimation(0, this._idleAnimation, true);\n }\n if (this._winResolve) {\n this._winResolve();\n this._winResolve = null;\n }\n }\n\n resize(width: number, height: number): void {\n if (!this._spine) return;\n const bounds = this._spine.getBounds();\n if (bounds.width > 0 && bounds.height > 0) {\n this._spine.scale.set(\n width / bounds.width,\n height / bounds.height,\n );\n }\n }\n\n protected override onDestroy(): void {\n if (this._spine) {\n this._spine.state.clearListeners();\n this._spine.destroy();\n this._spine = null;\n }\n if (this._winResolve) {\n const r = this._winResolve;\n this._winResolve = null;\n r();\n }\n }\n}\n"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"CascadeAnticipationPhase.d.ts","sourceRoot":"","sources":["../../src/cascade/CascadeAnticipationPhase.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,SAAS,EAAE,MAAM,6BAA6B,CAAC;AAExD;;;;;;;;;;;GAWG;AACH,qBAAa,wBAAyB,SAAQ,SAAS,CAAC,IAAI,CAAC;IAC3D,QAAQ,CAAC,IAAI,kBAAkB;IAC/B,QAAQ,CAAC,SAAS,QAAQ;IAE1B,OAAO,CAAC,MAAM,CAAgC;IAC9C,OAAO,CAAC,MAAM,CAAK;IAEnB,SAAS,CAAC,OAAO,IAAI,IAAI;IA2BzB,MAAM,CAAC,QAAQ,EAAE,MAAM,GAAG,IAAI;IAE9B,SAAS,CAAC,MAAM,IAAI,IAAI;CAKzB"}
1
+ {"version":3,"file":"CascadeAnticipationPhase.d.ts","sourceRoot":"","sources":["../../src/cascade/CascadeAnticipationPhase.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,SAAS,EAAE,MAAM,6BAA6B,CAAC;AAExD;;;;;;;;;;;GAWG;AACH,qBAAa,wBAAyB,SAAQ,SAAS,CAAC,IAAI,CAAC;IAC3D,QAAQ,CAAC,IAAI,kBAAkB;IAC/B,QAAQ,CAAC,SAAS,QAAQ;IAE1B,OAAO,CAAC,MAAM,CAAgC;IAC9C,OAAO,CAAC,MAAM,CAAK;IAEnB,SAAS,CAAC,OAAO,IAAI,IAAI;IA2BzB,MAAM,CAAC,QAAQ,EAAE,MAAM,GAAG,IAAI;IAE9B,SAAS,CAAC,MAAM,IAAI,IAAI;CAKzB"}
@@ -1,4 +1,45 @@
1
1
  import { Container, Ticker } from 'pixi.js';
2
+ /**
3
+ * Options accepted by `reelSet.spin(options?)`. All fields are optional —
4
+ * passing nothing reproduces the legacy "every reel spins" behaviour.
5
+ */
6
+ export interface SpinOptions {
7
+ /**
8
+ * Phase chain selector for this spin.
9
+ * `'cascade'` requires `.cascade(...)` on the builder.
10
+ */
11
+ mode?: 'standard' | 'cascade';
12
+ /**
13
+ * Reel indices to HOLD this spin. Held reels skip START / SPIN / STOP
14
+ * entirely and stay on whatever symbols they're currently showing.
15
+ * They count as already-landed for the `spin:allLanded` resolver — only
16
+ * non-held reels actually animate.
17
+ *
18
+ * Use cases:
19
+ * - Hold & Win respins (most reels held, one or two reroll)
20
+ * - Sticky / expanding wilds during a feature spin
21
+ * - Bonus respin where the trigger column stays in place
22
+ *
23
+ * Notes:
24
+ * - `setResult(grid)` still expects a full `reelCount`-length grid;
25
+ * entries at held indices are ignored. Pass anything (including
26
+ * the held reels' current visible rows) — the engine doesn't read
27
+ * held columns.
28
+ * - `setAnticipation([...])` silently filters held indices.
29
+ * - `setStopDelays([...])` entries at held indices are ignored.
30
+ * - The resolved `SpinResult.symbols` is the full visible grid AFTER
31
+ * the spin lands — held reels contribute their unchanged rows,
32
+ * non-held reels contribute their landed rows.
33
+ * - No `spin:reelLanded` / `spin:stopping` event fires for held reels.
34
+ * - Big-symbol blocks crossing held into non-held reels are not
35
+ * supported — the engine doesn't reposition or reshape held reels
36
+ * to accommodate them. Author results that keep big symbols inside
37
+ * a contiguous run of non-held reels.
38
+ * - Indices outside `[0, reelCount)` and duplicate entries are silently
39
+ * filtered.
40
+ */
41
+ holdReels?: number[];
42
+ }
2
43
  /** Timing and animation profile for a speed mode. */
3
44
  export interface SpeedProfile {
4
45
  readonly name: string;
@@ -29,7 +70,41 @@ export interface SymbolData {
29
70
  weight: number;
30
71
  /** Display layering order. Higher = in front. */
31
72
  zIndex?: number;
32
- /** If true, this symbol renders above the reel mask (for oversized animations). */
73
+ /**
74
+ * If true, the engine parents this symbol's view to
75
+ * `viewport.unmaskedContainer` instead of the reel's masked container —
76
+ * the symbol renders above the reel mask, useful for oversized win
77
+ * animations (expanding wilds, splash frames) that should not be
78
+ * clipped at the cell boundary.
79
+ *
80
+ * **Coordinate space:** when unmasked, the engine sets the view's X to
81
+ * `reel.container.x` and adds `reel.container.y` to the view's Y so
82
+ * the at-rest position matches the reel's grid cell.
83
+ *
84
+ * **Motion limitation:** `ReelMotion` writes `view.y` in reel-local
85
+ * coordinates. While the reel is spinning, an unmasked symbol on the
86
+ * strip will appear shifted vertically by the reel's offset (the
87
+ * `reel.container.y` translation is only applied on activate, not on
88
+ * every motion frame). Treat `unmask: true` as a *landed-state* flag —
89
+ * it is correct at rest and during static frames, but not designed to
90
+ * stay visually accurate while the reel is spinning. If you need a
91
+ * mid-spin "stays visible above mask" overlay, use a cell pin instead.
92
+ *
93
+ * **Pyramid layouts not supported:** when any reel has a non-zero
94
+ * `offsetY` (pyramid / trapezoid offsets), `motion.snapToGrid()` and
95
+ * `motion.displace()` will write reel-local Y to the unmasked view —
96
+ * shifting the rendered position by `reel.container.y`. The builder
97
+ * throws at config time if both conditions are present. Use cell pins
98
+ * for above-mask overlays on pyramid slots.
99
+ *
100
+ * **Mask-strategy auto-pick:** when any registered symbol sets
101
+ * `unmask: true` and `symbolGap.x > 0`, the builder switches the
102
+ * default `RectMaskStrategy` to `SharedRectMaskStrategy` so that
103
+ * neighboring (masked) symbols don't get clipped at the column gap
104
+ * next to the unmasked overlay. Passing `.maskStrategy(...)`
105
+ * explicitly always wins. For symbols that need to overlap across
106
+ * reel boundaries while unmasked, prefer `SharedRectMaskStrategy`.
107
+ */
33
108
  unmask?: boolean;
34
109
  /**
35
110
  * Footprint in cells. Default `{ w: 1, h: 1 }`. When `w * h > 1` this
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/config/types.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/config/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,EAAE,MAAM,SAAS,CAAC;AAEjD;;;GAGG;AACH,MAAM,WAAW,WAAW;IAC1B;;;OAGG;IACH,IAAI,CAAC,EAAE,UAAU,GAAG,SAAS,CAAC;IAE9B;;;;;;;;;;;;;;;;;;;;;;;;;;;;OA4BG;IACH,SAAS,CAAC,EAAE,MAAM,EAAE,CAAC;CACtB;AAED,qDAAqD;AACrD,MAAM,WAAW,YAAY;IAC3B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,uDAAuD;IACvD,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,2CAA2C;IAC3C,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,+CAA+C;IAC/C,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,+CAA+C;IAC/C,QAAQ,CAAC,iBAAiB,EAAE,MAAM,CAAC;IACnC,2CAA2C;IAC3C,QAAQ,CAAC,cAAc,EAAE,MAAM,CAAC;IAChC,8CAA8C;IAC9C,QAAQ,CAAC,cAAc,EAAE,MAAM,CAAC;IAChC,wEAAwE;IACxE,QAAQ,CAAC,gBAAgB,CAAC,EAAE,MAAM,CAAC;IACnC,yEAAyE;IACzE,QAAQ,CAAC,gBAAgB,CAAC,EAAE,MAAM,CAAC;IACnC,yDAAyD;IACzD,QAAQ,CAAC,oBAAoB,CAAC,EAAE,MAAM,CAAC;IACvC,oEAAoE;IACpE,QAAQ,CAAC,eAAe,CAAC,EAAE,MAAM,CAAC;CACnC;AAED,qCAAqC;AACrC,MAAM,WAAW,UAAU;IACzB,sEAAsE;IACtE,MAAM,EAAE,MAAM,CAAC;IACf,iDAAiD;IACjD,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;OAkCG;IACH,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB;;;;;OAKG;IACH,IAAI,CAAC,EAAE;QAAE,CAAC,EAAE,MAAM,CAAC;QAAC,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;CACjC;AAED,gEAAgE;AAChE,MAAM,MAAM,UAAU,GAAG,KAAK,GAAG,QAAQ,GAAG,QAAQ,CAAC;AAErD;;;GAGG;AACH,MAAM,WAAW,eAAe;IAC9B,8DAA8D;IAC9D,OAAO,EAAE,MAAM,CAAC;IAChB,8DAA8D;IAC9D,OAAO,EAAE,MAAM,CAAC;IAChB;;;OAGG;IACH,eAAe,EAAE,MAAM,CAAC;CACzB;AAED,8CAA8C;AAC9C,MAAM,WAAW,cAAc;IAC7B,8BAA8B;IAC9B,SAAS,EAAE,MAAM,CAAC;IAClB;;;OAGG;IACH,WAAW,EAAE,MAAM,CAAC;IACpB;;;;OAIG;IACH,kBAAkB,CAAC,EAAE,MAAM,EAAE,CAAC;IAC9B,8BAA8B;IAC9B,WAAW,EAAE,MAAM,CAAC;IACpB,0EAA0E;IAC1E,YAAY,EAAE,MAAM,CAAC;IACrB;;;;OAIG;IACH,gBAAgB,CAAC,EAAE,MAAM,EAAE,CAAC;IAC5B;;;OAGG;IACH,UAAU,CAAC,EAAE,UAAU,CAAC;IACxB,oDAAoD;IACpD,SAAS,CAAC,EAAE;QAAE,CAAC,EAAE,MAAM,CAAC;QAAC,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;IACrC,6EAA6E;IAC7E,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB;;;;;OAKG;IACH,SAAS,CAAC,EAAE,eAAe,CAAC;CAC7B;AAED,iDAAiD;AACjD,MAAM,WAAW,gBAAgB;IAC/B,YAAY,EAAE,MAAM,CAAC;IACrB,YAAY,EAAE,MAAM,CAAC;CACtB;AAED,kDAAkD;AAClD,MAAM,MAAM,WAAW,GAAG,MAAM,GAAG,WAAW,CAAC;AAE/C,2CAA2C;AAC3C,MAAM,WAAW,eAAe;IAC9B,IAAI,EAAE,WAAW,CAAC;IAClB,eAAe,EAAE,MAAM,CAAC;IACxB,cAAc,EAAE,MAAM,CAAC;IACvB,iBAAiB,EAAE,MAAM,CAAC;CAC3B;AAED,+BAA+B;AAC/B,MAAM,WAAW,cAAc;IAC7B,IAAI,EAAE,MAAM,CAAC;CACd;AAED,MAAM,MAAM,YAAY,GAAG,eAAe,GAAG,cAAc,CAAC;AAE5D,mCAAmC;AACnC,MAAM,MAAM,MAAM,CAAC,CAAC,IAAI,CAAC,EAAE,EAAE,CAAC;AAE9B,0BAA0B;AAC1B,MAAM,WAAW,QAAQ;IACvB,CAAC,EAAE,MAAM,CAAC;IACV,CAAC,EAAE,MAAM,CAAC;CACX;AAED;;;;;;GAMG;AACH,MAAM,WAAW,UAAU;IACzB,qDAAqD;IACrD,CAAC,EAAE,MAAM,CAAC;IACV,oDAAoD;IACpD,CAAC,EAAE,MAAM,CAAC;IACV,uDAAuD;IACvD,KAAK,EAAE,MAAM,CAAC;IACd,yDAAyD;IACzD,MAAM,EAAE,MAAM,CAAC;CAChB;AAED;;;;;;;GAOG;AACH,MAAM,WAAW,cAAc;IAC7B,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED;;;;;;;;;;;;;;GAcG;AACH,MAAM,WAAW,GAAG;IAClB,4DAA4D;IAC5D,KAAK,EAAE,aAAa,CAAC,cAAc,CAAC,CAAC;IACrC,8DAA8D;IAC9D,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,6DAA6D;IAC7D,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,mEAAmE;IACnE,EAAE,CAAC,EAAE,MAAM,CAAC;CACb;AAED,gDAAgD;AAChD,MAAM,WAAW,UAAU;IACzB,IAAI,EAAE,SAAS,CAAC;IAChB,QAAQ,EAAE,QAAQ,CAAC;CACpB;AAED;;;;GAIG;AACH,MAAM,WAAW,sBAAsB;IACrC,SAAS,EAAE,MAAM,CAAC;IAClB,WAAW,EAAE,MAAM,CAAC;IACpB,WAAW,EAAE,MAAM,CAAC;IACpB,YAAY,EAAE,MAAM,CAAC;IACrB,SAAS,EAAE;QAAE,CAAC,EAAE,MAAM,CAAC;QAAC,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;IACpC,aAAa,EAAE,MAAM,CAAC;IACtB,kBAAkB,CAAC,EAAE,MAAM,EAAE,CAAC;IAC9B,gBAAgB,CAAC,EAAE,MAAM,EAAE,CAAC;IAC5B,UAAU,EAAE,UAAU,CAAC;IACvB,SAAS,CAAC,EAAE,eAAe,CAAC;CAC7B;AAED,4DAA4D;AAC5D,MAAM,WAAW,qBAAqB;IACpC,IAAI,EAAE,sBAAsB,CAAC;IAC7B,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,UAAU,CAAC,CAAC;IACpC,MAAM,EAAE,GAAG,CAAC,MAAM,EAAE,YAAY,CAAC,CAAC;IAClC,YAAY,EAAE,MAAM,CAAC;IACrB,MAAM,EAAE,YAAY,CAAC;IACrB,MAAM,EAAE,MAAM,CAAC;CAChB"}
@@ -202,6 +202,36 @@ export declare class Reel implements Disposable {
202
202
  * end of every stop sequence.
203
203
  */
204
204
  snapToGrid(): void;
205
+ /**
206
+ * Swap the symbol at a single visible row in-place, without restarting
207
+ * the spin or rebuilding the rest of the strip.
208
+ *
209
+ * Useful for live presentation effects at rest — converting a wild
210
+ * after a cascade pop, swapping to a sticky variant after a win —
211
+ * without going through the full `placeSymbols` / `setResult` paths.
212
+ *
213
+ * The symbol's `zIndex`, parent (masked vs unmasked), and visual state
214
+ * are reset by `_replaceSymbol` so callers don't need to follow up
215
+ * with `refreshZIndex`. The motion layer is **not** snapped — call
216
+ * `snapToGrid()` separately if you need to re-grid.
217
+ *
218
+ * Throws if:
219
+ * - the reel is currently moving (`speed !== 0` or `isStopping`).
220
+ * A mid-spin swap would be overwritten by the next wrap/stop frame
221
+ * anyway; the fail-loud throw spares the caller the silent loss.
222
+ * - `visibleRow` is out of `[0, visibleRows)`.
223
+ * - `symbolId` is not registered.
224
+ * - the row is a non-anchor cell of an existing big-symbol block.
225
+ * - the row currently holds the anchor of a big-symbol block — big
226
+ * blocks span multiple cells (and possibly reels) and require
227
+ * `placeSymbols` + the cross-reel OCCUPIED coordinator.
228
+ * - `symbolId` itself is a big symbol — same reason.
229
+ *
230
+ * Pin overlap is **not** detected at this layer (Reel doesn't see the
231
+ * pin map). Use `ReelSet.setSymbolAt(col, row, id)` for the safe
232
+ * caller-facing surface that also throws on pinned cells.
233
+ */
234
+ setSymbolAt(visibleRow: number, symbolId: string): void;
205
235
  /** Place symbols immediately at target positions (for skip/turbo). */
206
236
  placeSymbols(symbolIds: string[]): void;
207
237
  /**
@@ -221,6 +251,14 @@ export declare class Reel implements Disposable {
221
251
  * slot and migrates pins atomically.
222
252
  */
223
253
  reshape(newVisibleRows: number, newSymbolHeight: number, bufferAbove: number, bufferBelow: number): void;
254
+ /**
255
+ * Compute the canonical zIndex for a single symbol view at a given
256
+ * array index. Centralizes the formula used by both `refreshZIndex`
257
+ * (full rescan) and the per-swap activate path (so newly placed
258
+ * symbols land with their correct zIndex without the caller needing
259
+ * to remember to call `refreshZIndex` afterwards).
260
+ */
261
+ private _computeSymbolZIndex;
224
262
  /**
225
263
  * Recompute `zIndex` for every symbol in the reel.
226
264
  *
@@ -229,11 +267,45 @@ export declare class Reel implements Disposable {
229
267
  * render in front of top-row symbols and any symbol with a higher
230
268
  * configured base zIndex (e.g. wild, bonus) renders above its neighbors.
231
269
  *
232
- * Called automatically after wraps, snaps, and direct placement. Call it
233
- * manually after mutating `symbolsData.zIndex` at runtime.
270
+ * Called automatically after wraps, snaps, and direct placement. Also
271
+ * called inline by `_replaceSymbol` for the single newly-placed symbol —
272
+ * so consumers who swap one symbol at a time (via the public APIs that
273
+ * funnel into `_replaceSymbol`) get correct layering for free, no
274
+ * manual `refreshZIndex` required. Call it manually after mutating
275
+ * `symbolsData.zIndex` at runtime.
234
276
  */
235
277
  refreshZIndex(): void;
236
278
  destroy(): void;
279
+ /**
280
+ * Whether the symbol with this id has `unmask: true` in its data — i.e.
281
+ * its view should be parented to `viewport.unmaskedContainer` to render
282
+ * above the reel mask.
283
+ */
284
+ private _isUnmasked;
285
+ /**
286
+ * Pick the right parent container for a symbol view based on its
287
+ * `unmask` flag. Unmasked symbols sit in `viewport.unmaskedContainer`
288
+ * (above the reel mask); everything else lives in this reel's own
289
+ * container (which is itself inside `viewport.maskedContainer`).
290
+ */
291
+ private _parentForSymbolId;
292
+ /**
293
+ * Position a symbol view at a given reel-local Y, choosing X and any
294
+ * parent-translation offset based on whether the symbol is unmasked.
295
+ *
296
+ * Unmasked views live in `viewport.unmaskedContainer` (at viewport
297
+ * (0,0)), so we add `reel.container.x` and `reel.container.y` to keep
298
+ * the at-rest cell position aligned with the reel column. Masked views
299
+ * live in `this.container`, so reel-local coords map directly.
300
+ */
301
+ private _placeSymbolView;
302
+ /**
303
+ * Convert a view's current y back to reel-local coords. The view may
304
+ * be parented to either `this.container` (already reel-local) or
305
+ * `viewport.unmaskedContainer` (viewport-local — needs the reel offset
306
+ * subtracted).
307
+ */
308
+ private _toReelLocalY;
237
309
  private _setupSymbolPositions;
238
310
  private _onSymbolWrapped;
239
311
  private _replaceSymbol;
@@ -1 +1 @@
1
- {"version":3,"file":"Reel.d.ts","sourceRoot":"","sources":["../../src/core/Reel.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"Reel.d.ts","sourceRoot":"","sources":["../../src/core/Reel.ts"],"names":[],"mappings":"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"}