pixi-reels 1.3.0 → 1.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 (40) hide show
  1. package/CHANGELOG.md +6 -0
  2. package/dist/{ReelSymbol-BxoiwQ-r.cjs → ReelSymbol-BJGylIyA.cjs} +2 -2
  3. package/dist/{ReelSymbol-BxoiwQ-r.cjs.map → ReelSymbol-BJGylIyA.cjs.map} +1 -1
  4. package/dist/{ReelSymbol-C9QQ30ER.js → ReelSymbol-DBbmNOIa.js} +2 -2
  5. package/dist/{ReelSymbol-C9QQ30ER.js.map → ReelSymbol-DBbmNOIa.js.map} +1 -1
  6. package/dist/{SpineSymbol-DAqBuFX2.cjs → SpineSymbol-C6V3FScO.cjs} +2 -2
  7. package/dist/{SpineSymbol-DAqBuFX2.cjs.map → SpineSymbol-C6V3FScO.cjs.map} +1 -1
  8. package/dist/{SpineSymbol-C_pacfc4.js → SpineSymbol-w7zxDNo_.js} +2 -2
  9. package/dist/{SpineSymbol-C_pacfc4.js.map → SpineSymbol-w7zxDNo_.js.map} +1 -1
  10. package/dist/core/Reel.d.ts +7 -0
  11. package/dist/core/Reel.d.ts.map +1 -1
  12. package/dist/{debug-CtfTdj2g.js → debug-C1xsPOVH.js} +11 -8
  13. package/dist/debug-C1xsPOVH.js.map +1 -0
  14. package/dist/debug-CWou81X6.cjs +4 -0
  15. package/dist/debug-CWou81X6.cjs.map +1 -0
  16. package/dist/horizontal/HorizontalReel.d.ts.map +1 -1
  17. package/dist/index.cjs +1 -1
  18. package/dist/index.cjs.map +1 -1
  19. package/dist/index.d.ts +4 -0
  20. package/dist/index.d.ts.map +1 -1
  21. package/dist/index.js +266 -47
  22. package/dist/index.js.map +1 -1
  23. package/dist/snapshot/SpinTextureCache.d.ts +149 -0
  24. package/dist/snapshot/SpinTextureCache.d.ts.map +1 -0
  25. package/dist/snapshot/StaticSpinSymbol.d.ts +123 -0
  26. package/dist/snapshot/StaticSpinSymbol.d.ts.map +1 -0
  27. package/dist/spin/phases/StartPhase.d.ts.map +1 -1
  28. package/dist/spine/SpineReelSymbol.d.ts.map +1 -1
  29. package/dist/spine.cjs +1 -1
  30. package/dist/spine.cjs.map +1 -1
  31. package/dist/spine.js +4 -4
  32. package/dist/spine.js.map +1 -1
  33. package/dist/symbols/ReelSymbol.d.ts +14 -4
  34. package/dist/symbols/ReelSymbol.d.ts.map +1 -1
  35. package/dist/testing.cjs +1 -1
  36. package/dist/testing.js +2 -2
  37. package/package.json +1 -1
  38. package/dist/debug-Cr9Zn29z.cjs +0 -4
  39. package/dist/debug-Cr9Zn29z.cjs.map +0 -1
  40. package/dist/debug-CtfTdj2g.js.map +0 -1
package/CHANGELOG.md CHANGED
@@ -1,5 +1,11 @@
1
1
  # pixi-reels
2
2
 
3
+ ## 1.4.0
4
+
5
+ ### Minor Changes
6
+
7
+ - [#186](https://github.com/schmooky/pixi-reels/pull/186) [`b6d1649`](https://github.com/schmooky/pixi-reels/commit/b6d1649ec8102f64063479e81dacd5ad920606e8) Thanks [@igaming-bulochka](https://github.com/igaming-bulochka)! - Add: static / motion-blurred snapshot spinning — spin cached textures instead of live symbols. New `SpinTextureCache` captures any symbol into a per-symbolId `RenderTexture` (or accepts hand-authored textures via `setStatic` / `setBlurred`, which always win and are never destroyed by the cache) and bakes a motion-blur variant in a one-time `BlurFilter` pass — no filters run during the spin. The smear follows the reel's travel axis: vertical by default, `blur: { axis: 'x' }` for a `HorizontalReel` strip. New `StaticSpinSymbol` wraps any `ReelSymbol` (Spine included): while the reel spins it deactivates the inner symbol and shows the cached snapshot, crossfading crisp→blurred over `blurRampMs`; symbols wrapping in mid-spin only retarget a sprite texture; on land the live symbol is reactivated. `prewarmSpinTextures()` bakes all ids up front so the first spin never hitches. Engine: `onReelSpinStart` now also fires (with a new `joinedMidSpin` arg) on symbols installed while a reel is already spinning, spin start/end notifications reach buffer rows, a slam-stopped `StartPhase` no longer skips `notifySpinStart`, and `HorizontalReel` now fires the symbol spin hooks across its conveyor (`onReelSpinStart` at `spin()` and on mid-spin feeds; `onReelSpinEnd` at `setResult()` so the visible deceleration runs crisp and the landing window feeds in live — what lands is never blurred; `onReelLanded` on land) — fixing Spine `autoPlayBlur` gaps where mid-spin, slammed, or horizontal-strip symbols stayed on `idle`. Spin-state hooks must now be idempotent; `SpineReelSymbol.playBlur()` no longer restarts an already-running blur loop, and `SpineReelSymbol` applies the idle pose immediately on activation (`spine.update(0)`) so same-frame renders and snapshot captures see the posed skeleton instead of nothing.
8
+
3
9
  ## 1.3.0
4
10
 
5
11
  ### Minor Changes
@@ -1,2 +1,2 @@
1
- let e=require(`pixi.js`);var t=require(`gsap`).gsap;function n(e){t=e}function r(){return t}var i=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(){}async playDestroy(e){let t=this.view,n=t.pivot.x,i=t.pivot.y,a=t.x,o=t.y,s=t.getLocalBounds(),c=s.x+s.width/2,l=s.y+s.height/2;t.pivot.set(c,l),t.x=a+(c-n),t.y=o+(l-i);let u=e?.delay??0,d=e?.signal,f=()=>{t.alpha=0,t.scale.set(0,0)};if(d?.aborted){f(),t.pivot.set(n,i),t.x=a,t.y=o,t.scale.set(1,1),t.alpha=0;return}await new Promise(e=>{let n=r().timeline({onComplete:()=>{d&&d.removeEventListener(`abort`,i),e()},delay:u}).to(t.scale,{x:1.1,y:1.1,duration:.06,ease:`back.out(2.5)`}).to(t.scale,{x:0,y:0,duration:.14,ease:`power3.in`},`<+=0.04`).to(t,{alpha:0,duration:.14,ease:`power3.in`},`<`),i=()=>{n.kill(),f(),e()};d&&d.addEventListener(`abort`,i,{once:!0})}),t.pivot.set(n,i),t.x=a,t.y=o,t.scale.set(1,1)}onReelSpinStart(){}onReelSpinEnd(){}onReelLanded(){}};Object.defineProperty(exports,`n`,{enumerable:!0,get:function(){return r}}),Object.defineProperty(exports,`r`,{enumerable:!0,get:function(){return n}}),Object.defineProperty(exports,`t`,{enumerable:!0,get:function(){return i}});
2
- //# sourceMappingURL=ReelSymbol-BxoiwQ-r.cjs.map
1
+ let e=require(`pixi.js`);var t=require(`gsap`).gsap;function n(e){t=e}function r(){return t}var i=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(){}async playDestroy(e){let t=this.view,n=t.pivot.x,i=t.pivot.y,a=t.x,o=t.y,s=t.getLocalBounds(),c=s.x+s.width/2,l=s.y+s.height/2;t.pivot.set(c,l),t.x=a+(c-n),t.y=o+(l-i);let u=e?.delay??0,d=e?.signal,f=()=>{t.alpha=0,t.scale.set(0,0)};if(d?.aborted){f(),t.pivot.set(n,i),t.x=a,t.y=o,t.scale.set(1,1),t.alpha=0;return}await new Promise(e=>{let n=r().timeline({onComplete:()=>{d&&d.removeEventListener(`abort`,i),e()},delay:u}).to(t.scale,{x:1.1,y:1.1,duration:.06,ease:`back.out(2.5)`}).to(t.scale,{x:0,y:0,duration:.14,ease:`power3.in`},`<+=0.04`).to(t,{alpha:0,duration:.14,ease:`power3.in`},`<`),i=()=>{n.kill(),f(),e()};d&&d.addEventListener(`abort`,i,{once:!0})}),t.pivot.set(n,i),t.x=a,t.y=o,t.scale.set(1,1)}onReelSpinStart(e){}onReelSpinEnd(){}onReelLanded(){}};Object.defineProperty(exports,`n`,{enumerable:!0,get:function(){return r}}),Object.defineProperty(exports,`r`,{enumerable:!0,get:function(){return n}}),Object.defineProperty(exports,`t`,{enumerable:!0,get:function(){return i}});
2
+ //# sourceMappingURL=ReelSymbol-BJGylIyA.cjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"ReelSymbol-BxoiwQ-r.cjs","names":[],"sources":["../src/utils/gsapRef.ts","../src/symbols/ReelSymbol.ts"],"sourcesContent":["import { gsap as defaultGsap } from 'gsap';\n\n/**\n * The gsap instance every phase, motion tween, and cascade animation in\n * the engine reads from.\n *\n * **Why this indirection exists:** under tools that resolve modules through\n * symlinked workspaces (vite + a locally-linked pixi-reels, pnpm dev\n * setups, esbuild plugin chains), the gsap import inside the lib's\n * compiled `dist/index.js` and the gsap import in the consumer's source\n * can resolve to *different module instances*. each with its own root\n * timeline. The consumer drives one, the lib's tweens live on the other,\n * and reels stall at progress 0.\n *\n * The fix: every internal animation site reads gsap through `getGsap()`\n * instead of importing the `'gsap'` module directly. The builder method\n * `ReelSetBuilder.gsap(myGsap)` rebinds the singleton so the consumer's\n * gsap is the one the engine uses.\n *\n * Defaults to the gsap import resolved at lib-load time, so consumers\n * who don't hit the dual-instance trap don't have to do anything.\n */\nlet currentGsap: typeof defaultGsap = defaultGsap;\n\n/**\n * Replace the gsap instance the engine drives. Call this BEFORE\n * `ReelSetBuilder.build()` (the builder does this for you when you call\n * `.gsap(instance)`).\n *\n * Process-global: there's only one bound instance at a time. If you\n * build several ReelSets with different gsap instances, the last\n * `setGsap` call wins.\n */\nexport function setGsap(g: typeof defaultGsap): void {\n currentGsap = g;\n}\n\n/** The currently-bound gsap instance. */\nexport function getGsap(): typeof defaultGsap {\n return currentGsap;\n}\n","import { Container } from 'pixi.js';\nimport type { Disposable } from '../utils/Disposable.js';\nimport { getGsap } from '../utils/gsapRef.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 * Play the cascade-destruction animation for this symbol. Called by\n * consumers (typically via `reelSet.destroySymbols(...)`) to disintegrate\n * a winning cell before the next cascade refill drops fresh symbols in.\n *\n * Default implementation: brief scale-up \"charge\" then implode (scale 0\n * + spin + fade), squishing around the symbol's bounding-box CENTER\n * regardless of the view's anchor. Total ~320 ms. The view is left at\n * `alpha: 0` (destroyed); position / pivot are restored so pool reuse\n * via `_replaceSymbol`'s same-id fast path doesn't inherit a stale\n * pivot offset.\n *\n * Override in subclasses for art-appropriate destruction. e.g. a\n * Spine symbol can play its `disintegration` track here, or a sprite\n * symbol can swap to a shatter atlas. The promise must resolve when\n * the symbol is no longer visible.\n *\n * Default animation: a snappy \"poof\". tiny anticipation pop (~60 ms)\n * then a fast implode to `scale: 0` + `alpha: 0` (~140 ms), centered on\n * the symbol's bounds. ~200 ms total. No rotation. designed to read\n * cleanly under win-cluster pacing without competing with the win\n * presenter.\n *\n * `opts.delay`. seconds to wait before the animation starts. Use to\n * stagger a cluster of winners (e.g. `i * 0.015`).\n * `opts.signal`. abort signal. If aborted (now or mid-animation), the\n * tween is killed and the view is snapped to its destroyed pose\n * (`alpha: 0`, transform restored). The promise resolves normally. abort\n * means \"skip to the end,\" not \"fail\". Subclasses that override this\n * method MUST honor the signal or document why they can't (e.g. a Spine\n * `disintegration` track is uninterruptible).\n */\n async playDestroy(opts?: { delay?: number; signal?: AbortSignal }): Promise<void> {\n const view = this.view;\n // Capture original transform so pool reuse sees a clean state.\n const originalPivotX = view.pivot.x;\n const originalPivotY = view.pivot.y;\n const originalX = view.x;\n const originalY = view.y;\n\n // Pivot to bounds-center so the scale collapses around the visual\n // centre instead of the view's (0,0) corner. and compensate position\n // so the symbol doesn't visibly jump when the pivot moves.\n const bounds = view.getLocalBounds();\n const cx = bounds.x + bounds.width / 2;\n const cy = bounds.y + bounds.height / 2;\n view.pivot.set(cx, cy);\n view.x = originalX + (cx - originalPivotX);\n view.y = originalY + (cy - originalPivotY);\n\n const delay = opts?.delay ?? 0;\n const signal = opts?.signal;\n\n const snapDestroyed = (): void => {\n view.alpha = 0;\n view.scale.set(0, 0);\n };\n\n // Pre-abort: skip the tween entirely and snap to the destroyed pose.\n if (signal?.aborted) {\n snapDestroyed();\n view.pivot.set(originalPivotX, originalPivotY);\n view.x = originalX;\n view.y = originalY;\n view.scale.set(1, 1);\n view.alpha = 0;\n return;\n }\n\n await new Promise<void>((resolve) => {\n const tl = getGsap()\n .timeline({ onComplete: () => {\n if (signal) signal.removeEventListener('abort', onAbort);\n resolve();\n }, delay })\n // Brief anticipation pop. small upscale, ~60 ms, with overshoot\n // so the implode reads as a release. No rotation.\n .to(view.scale, { x: 1.1, y: 1.1, duration: 0.06, ease: 'back.out(2.5)' })\n // Snap implode. scale -> 0 + alpha -> 0 together, snappy ease-in\n // so the symbol collapses into the cell centre and is gone.\n .to(view.scale, { x: 0, y: 0, duration: 0.14, ease: 'power3.in' }, '<+=0.04')\n .to(view, { alpha: 0, duration: 0.14, ease: 'power3.in' }, '<');\n\n const onAbort = (): void => {\n tl.kill();\n snapDestroyed();\n resolve();\n };\n if (signal) signal.addEventListener('abort', onAbort, { once: true });\n });\n\n // Restore transform. alpha stays 0 (the symbol IS destroyed). Scale\n // restored to 1 so pool reuse via `_replaceSymbol`'s same-id fast path\n // doesn't inherit a stale 0× scale; _replaceSymbol also resets scale\n // explicitly but a defensive restore here makes the destroyed cell\n // observably \"ready to be re-skinned\" between calls.\n view.pivot.set(originalPivotX, originalPivotY);\n view.x = originalX;\n view.y = originalY;\n view.scale.set(1, 1);\n }\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"],"mappings":"yBAsBA,IAAI,kBAAkC,KAWtC,SAAgB,EAAQ,EAA6B,CACnD,EAAc,EAIhB,SAAgB,GAA8B,CAC5C,OAAO,ECLT,IAAsB,EAAtB,KAAuD,CAErD,KAEA,UAA4B,GAC5B,aAAuB,GAEvB,aAAc,CACZ,KAAK,KAAO,IAAI,EAAA,UAGlB,IAAI,UAAmB,CACrB,OAAO,KAAK,UAGd,IAAI,aAAuB,CACzB,OAAO,KAAK,aAQd,SAAS,EAAwB,CAC/B,KAAK,UAAY,EACjB,KAAK,KAAK,QAAU,GACpB,KAAK,KAAK,MAAQ,EAClB,KAAK,KAAK,MAAM,IAAI,EAAG,EAAE,CACzB,KAAK,KAAK,SAAW,EACrB,KAAK,KAAK,QAAU,KACpB,KAAK,KAAK,OAAS,EACnB,KAAK,WAAW,EAAS,CAQ3B,YAAmB,CACjB,KAAK,eAAe,CACpB,KAAK,cAAc,CACnB,KAAK,UAAY,GACjB,KAAK,KAAK,QAAU,GACpB,KAAK,KAAK,MAAQ,EAClB,KAAK,KAAK,MAAM,IAAI,EAAG,EAAE,CACzB,KAAK,KAAK,SAAW,EACrB,KAAK,KAAK,QAAU,KACpB,KAAK,KAAK,OAAS,EAIrB,OAAc,CACZ,KAAK,YAAY,CAGnB,SAAgB,CACV,AAKJ,KAAK,gBAJL,KAAK,eAAe,CACpB,KAAK,cAAc,CACnB,KAAK,WAAW,CACX,KAAK,KAAK,WAAW,KAAK,KAAK,QAAQ,CAAE,SAAU,GAAM,CAAC,CAC3C,IAUtB,WAA4B,EA6C5B,MAAM,YAAY,EAAgE,CAChF,IAAM,EAAO,KAAK,KAEZ,EAAiB,EAAK,MAAM,EAC5B,EAAiB,EAAK,MAAM,EAC5B,EAAY,EAAK,EACjB,EAAY,EAAK,EAKjB,EAAS,EAAK,gBAAgB,CAC9B,EAAK,EAAO,EAAI,EAAO,MAAQ,EAC/B,EAAK,EAAO,EAAI,EAAO,OAAS,EACtC,EAAK,MAAM,IAAI,EAAI,EAAG,CACtB,EAAK,EAAI,GAAa,EAAK,GAC3B,EAAK,EAAI,GAAa,EAAK,GAE3B,IAAM,EAAQ,GAAM,OAAS,EACvB,EAAS,GAAM,OAEf,MAA4B,CAChC,EAAK,MAAQ,EACb,EAAK,MAAM,IAAI,EAAG,EAAE,EAItB,GAAI,GAAQ,QAAS,CACnB,GAAe,CACf,EAAK,MAAM,IAAI,EAAgB,EAAe,CAC9C,EAAK,EAAI,EACT,EAAK,EAAI,EACT,EAAK,MAAM,IAAI,EAAG,EAAE,CACpB,EAAK,MAAQ,EACb,OAGF,MAAM,IAAI,QAAe,GAAY,CACnC,IAAM,EAAK,GAAS,CACjB,SAAS,CAAE,eAAkB,CACxB,GAAQ,EAAO,oBAAoB,QAAS,EAAQ,CACxD,GAAS,EACR,QAAO,CAAC,CAGV,GAAG,EAAK,MAAO,CAAE,EAAG,IAAK,EAAG,IAAK,SAAU,IAAM,KAAM,gBAAiB,CAAC,CAGzE,GAAG,EAAK,MAAO,CAAE,EAAG,EAAG,EAAG,EAAG,SAAU,IAAM,KAAM,YAAa,CAAE,UAAU,CAC5E,GAAG,EAAM,CAAE,MAAO,EAAG,SAAU,IAAM,KAAM,YAAa,CAAE,IAAI,CAE3D,MAAsB,CAC1B,EAAG,MAAM,CACT,GAAe,CACf,GAAS,EAEP,GAAQ,EAAO,iBAAiB,QAAS,EAAS,CAAE,KAAM,GAAM,CAAC,EACrE,CAOF,EAAK,MAAM,IAAI,EAAgB,EAAe,CAC9C,EAAK,EAAI,EACT,EAAK,EAAI,EACT,EAAK,MAAM,IAAI,EAAG,EAAE,CAQtB,iBAAwB,EAMxB,eAAsB,EAOtB,cAAqB"}
1
+ {"version":3,"file":"ReelSymbol-BJGylIyA.cjs","names":[],"sources":["../src/utils/gsapRef.ts","../src/symbols/ReelSymbol.ts"],"sourcesContent":["import { gsap as defaultGsap } from 'gsap';\n\n/**\n * The gsap instance every phase, motion tween, and cascade animation in\n * the engine reads from.\n *\n * **Why this indirection exists:** under tools that resolve modules through\n * symlinked workspaces (vite + a locally-linked pixi-reels, pnpm dev\n * setups, esbuild plugin chains), the gsap import inside the lib's\n * compiled `dist/index.js` and the gsap import in the consumer's source\n * can resolve to *different module instances*. each with its own root\n * timeline. The consumer drives one, the lib's tweens live on the other,\n * and reels stall at progress 0.\n *\n * The fix: every internal animation site reads gsap through `getGsap()`\n * instead of importing the `'gsap'` module directly. The builder method\n * `ReelSetBuilder.gsap(myGsap)` rebinds the singleton so the consumer's\n * gsap is the one the engine uses.\n *\n * Defaults to the gsap import resolved at lib-load time, so consumers\n * who don't hit the dual-instance trap don't have to do anything.\n */\nlet currentGsap: typeof defaultGsap = defaultGsap;\n\n/**\n * Replace the gsap instance the engine drives. Call this BEFORE\n * `ReelSetBuilder.build()` (the builder does this for you when you call\n * `.gsap(instance)`).\n *\n * Process-global: there's only one bound instance at a time. If you\n * build several ReelSets with different gsap instances, the last\n * `setGsap` call wins.\n */\nexport function setGsap(g: typeof defaultGsap): void {\n currentGsap = g;\n}\n\n/** The currently-bound gsap instance. */\nexport function getGsap(): typeof defaultGsap {\n return currentGsap;\n}\n","import { Container } from 'pixi.js';\nimport type { Disposable } from '../utils/Disposable.js';\nimport { getGsap } from '../utils/gsapRef.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 * Play the cascade-destruction animation for this symbol. Called by\n * consumers (typically via `reelSet.destroySymbols(...)`) to disintegrate\n * a winning cell before the next cascade refill drops fresh symbols in.\n *\n * Default implementation: brief scale-up \"charge\" then implode (scale 0\n * + spin + fade), squishing around the symbol's bounding-box CENTER\n * regardless of the view's anchor. Total ~320 ms. The view is left at\n * `alpha: 0` (destroyed); position / pivot are restored so pool reuse\n * via `_replaceSymbol`'s same-id fast path doesn't inherit a stale\n * pivot offset.\n *\n * Override in subclasses for art-appropriate destruction. e.g. a\n * Spine symbol can play its `disintegration` track here, or a sprite\n * symbol can swap to a shatter atlas. The promise must resolve when\n * the symbol is no longer visible.\n *\n * Default animation: a snappy \"poof\". tiny anticipation pop (~60 ms)\n * then a fast implode to `scale: 0` + `alpha: 0` (~140 ms), centered on\n * the symbol's bounds. ~200 ms total. No rotation. designed to read\n * cleanly under win-cluster pacing without competing with the win\n * presenter.\n *\n * `opts.delay`. seconds to wait before the animation starts. Use to\n * stagger a cluster of winners (e.g. `i * 0.015`).\n * `opts.signal`. abort signal. If aborted (now or mid-animation), the\n * tween is killed and the view is snapped to its destroyed pose\n * (`alpha: 0`, transform restored). The promise resolves normally. abort\n * means \"skip to the end,\" not \"fail\". Subclasses that override this\n * method MUST honor the signal or document why they can't (e.g. a Spine\n * `disintegration` track is uninterruptible).\n */\n async playDestroy(opts?: { delay?: number; signal?: AbortSignal }): Promise<void> {\n const view = this.view;\n // Capture original transform so pool reuse sees a clean state.\n const originalPivotX = view.pivot.x;\n const originalPivotY = view.pivot.y;\n const originalX = view.x;\n const originalY = view.y;\n\n // Pivot to bounds-center so the scale collapses around the visual\n // centre instead of the view's (0,0) corner. and compensate position\n // so the symbol doesn't visibly jump when the pivot moves.\n const bounds = view.getLocalBounds();\n const cx = bounds.x + bounds.width / 2;\n const cy = bounds.y + bounds.height / 2;\n view.pivot.set(cx, cy);\n view.x = originalX + (cx - originalPivotX);\n view.y = originalY + (cy - originalPivotY);\n\n const delay = opts?.delay ?? 0;\n const signal = opts?.signal;\n\n const snapDestroyed = (): void => {\n view.alpha = 0;\n view.scale.set(0, 0);\n };\n\n // Pre-abort: skip the tween entirely and snap to the destroyed pose.\n if (signal?.aborted) {\n snapDestroyed();\n view.pivot.set(originalPivotX, originalPivotY);\n view.x = originalX;\n view.y = originalY;\n view.scale.set(1, 1);\n view.alpha = 0;\n return;\n }\n\n await new Promise<void>((resolve) => {\n const tl = getGsap()\n .timeline({ onComplete: () => {\n if (signal) signal.removeEventListener('abort', onAbort);\n resolve();\n }, delay })\n // Brief anticipation pop. small upscale, ~60 ms, with overshoot\n // so the implode reads as a release. No rotation.\n .to(view.scale, { x: 1.1, y: 1.1, duration: 0.06, ease: 'back.out(2.5)' })\n // Snap implode. scale -> 0 + alpha -> 0 together, snappy ease-in\n // so the symbol collapses into the cell centre and is gone.\n .to(view.scale, { x: 0, y: 0, duration: 0.14, ease: 'power3.in' }, '<+=0.04')\n .to(view, { alpha: 0, duration: 0.14, ease: 'power3.in' }, '<');\n\n const onAbort = (): void => {\n tl.kill();\n snapDestroyed();\n resolve();\n };\n if (signal) signal.addEventListener('abort', onAbort, { once: true });\n });\n\n // Restore transform. alpha stays 0 (the symbol IS destroyed). Scale\n // restored to 1 so pool reuse via `_replaceSymbol`'s same-id fast path\n // doesn't inherit a stale 0× scale; _replaceSymbol also resets scale\n // explicitly but a defensive restore here makes the destroyed cell\n // observably \"ready to be re-skinned\" between calls.\n view.pivot.set(originalPivotX, originalPivotY);\n view.x = originalX;\n view.y = originalY;\n view.scale.set(1, 1);\n }\n\n /**\n * Lifecycle hook: the owning reel is spinning.\n * Default: no-op. Override (e.g. SpineReelSymbol.autoPlayBlur,\n * StaticSpinSymbol) to swap to a spin presentation automatically.\n *\n * Fired on every strip symbol (visible AND buffer rows) when the reel\n * enters the spin phase, and again with `joinedMidSpin: true` on each\n * symbol freshly installed while the reel is already spinning (pool\n * recycling wipes symbol state, so a wrapped-in symbol can't know the\n * reel is moving without this). Implementations MUST be idempotent.\n * the same instance can be notified more than once per spin.\n *\n * @param joinedMidSpin true when this symbol was installed into a reel\n * already at speed (skip start-of-spin transitions like blur ramps).\n */\n onReelSpinStart(_joinedMidSpin?: boolean): 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"],"mappings":"yBAsBA,IAAI,kBAAkC,KAWtC,SAAgB,EAAQ,EAA6B,CACnD,EAAc,EAIhB,SAAgB,GAA8B,CAC5C,OAAO,ECLT,IAAsB,EAAtB,KAAuD,CAErD,KAEA,UAA4B,GAC5B,aAAuB,GAEvB,aAAc,CACZ,KAAK,KAAO,IAAI,EAAA,UAGlB,IAAI,UAAmB,CACrB,OAAO,KAAK,UAGd,IAAI,aAAuB,CACzB,OAAO,KAAK,aAQd,SAAS,EAAwB,CAC/B,KAAK,UAAY,EACjB,KAAK,KAAK,QAAU,GACpB,KAAK,KAAK,MAAQ,EAClB,KAAK,KAAK,MAAM,IAAI,EAAG,EAAE,CACzB,KAAK,KAAK,SAAW,EACrB,KAAK,KAAK,QAAU,KACpB,KAAK,KAAK,OAAS,EACnB,KAAK,WAAW,EAAS,CAQ3B,YAAmB,CACjB,KAAK,eAAe,CACpB,KAAK,cAAc,CACnB,KAAK,UAAY,GACjB,KAAK,KAAK,QAAU,GACpB,KAAK,KAAK,MAAQ,EAClB,KAAK,KAAK,MAAM,IAAI,EAAG,EAAE,CACzB,KAAK,KAAK,SAAW,EACrB,KAAK,KAAK,QAAU,KACpB,KAAK,KAAK,OAAS,EAIrB,OAAc,CACZ,KAAK,YAAY,CAGnB,SAAgB,CACV,AAKJ,KAAK,gBAJL,KAAK,eAAe,CACpB,KAAK,cAAc,CACnB,KAAK,WAAW,CACX,KAAK,KAAK,WAAW,KAAK,KAAK,QAAQ,CAAE,SAAU,GAAM,CAAC,CAC3C,IAUtB,WAA4B,EA6C5B,MAAM,YAAY,EAAgE,CAChF,IAAM,EAAO,KAAK,KAEZ,EAAiB,EAAK,MAAM,EAC5B,EAAiB,EAAK,MAAM,EAC5B,EAAY,EAAK,EACjB,EAAY,EAAK,EAKjB,EAAS,EAAK,gBAAgB,CAC9B,EAAK,EAAO,EAAI,EAAO,MAAQ,EAC/B,EAAK,EAAO,EAAI,EAAO,OAAS,EACtC,EAAK,MAAM,IAAI,EAAI,EAAG,CACtB,EAAK,EAAI,GAAa,EAAK,GAC3B,EAAK,EAAI,GAAa,EAAK,GAE3B,IAAM,EAAQ,GAAM,OAAS,EACvB,EAAS,GAAM,OAEf,MAA4B,CAChC,EAAK,MAAQ,EACb,EAAK,MAAM,IAAI,EAAG,EAAE,EAItB,GAAI,GAAQ,QAAS,CACnB,GAAe,CACf,EAAK,MAAM,IAAI,EAAgB,EAAe,CAC9C,EAAK,EAAI,EACT,EAAK,EAAI,EACT,EAAK,MAAM,IAAI,EAAG,EAAE,CACpB,EAAK,MAAQ,EACb,OAGF,MAAM,IAAI,QAAe,GAAY,CACnC,IAAM,EAAK,GAAS,CACjB,SAAS,CAAE,eAAkB,CACxB,GAAQ,EAAO,oBAAoB,QAAS,EAAQ,CACxD,GAAS,EACR,QAAO,CAAC,CAGV,GAAG,EAAK,MAAO,CAAE,EAAG,IAAK,EAAG,IAAK,SAAU,IAAM,KAAM,gBAAiB,CAAC,CAGzE,GAAG,EAAK,MAAO,CAAE,EAAG,EAAG,EAAG,EAAG,SAAU,IAAM,KAAM,YAAa,CAAE,UAAU,CAC5E,GAAG,EAAM,CAAE,MAAO,EAAG,SAAU,IAAM,KAAM,YAAa,CAAE,IAAI,CAE3D,MAAsB,CAC1B,EAAG,MAAM,CACT,GAAe,CACf,GAAS,EAEP,GAAQ,EAAO,iBAAiB,QAAS,EAAS,CAAE,KAAM,GAAM,CAAC,EACrE,CAOF,EAAK,MAAM,IAAI,EAAgB,EAAe,CAC9C,EAAK,EAAI,EACT,EAAK,EAAI,EACT,EAAK,MAAM,IAAI,EAAG,EAAE,CAkBtB,gBAAgB,EAAgC,EAMhD,eAAsB,EAOtB,cAAqB"}
@@ -72,11 +72,11 @@ var a = class {
72
72
  d && d.addEventListener("abort", r, { once: !0 });
73
73
  }), t.pivot.set(n, r), t.x = a, t.y = o, t.scale.set(1, 1);
74
74
  }
75
- onReelSpinStart() {}
75
+ onReelSpinStart(e) {}
76
76
  onReelSpinEnd() {}
77
77
  onReelLanded() {}
78
78
  };
79
79
  //#endregion
80
80
  export { i as n, r, a as t };
81
81
 
82
- //# sourceMappingURL=ReelSymbol-C9QQ30ER.js.map
82
+ //# sourceMappingURL=ReelSymbol-DBbmNOIa.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"ReelSymbol-C9QQ30ER.js","names":[],"sources":["../src/utils/gsapRef.ts","../src/symbols/ReelSymbol.ts"],"sourcesContent":["import { gsap as defaultGsap } from 'gsap';\n\n/**\n * The gsap instance every phase, motion tween, and cascade animation in\n * the engine reads from.\n *\n * **Why this indirection exists:** under tools that resolve modules through\n * symlinked workspaces (vite + a locally-linked pixi-reels, pnpm dev\n * setups, esbuild plugin chains), the gsap import inside the lib's\n * compiled `dist/index.js` and the gsap import in the consumer's source\n * can resolve to *different module instances*. each with its own root\n * timeline. The consumer drives one, the lib's tweens live on the other,\n * and reels stall at progress 0.\n *\n * The fix: every internal animation site reads gsap through `getGsap()`\n * instead of importing the `'gsap'` module directly. The builder method\n * `ReelSetBuilder.gsap(myGsap)` rebinds the singleton so the consumer's\n * gsap is the one the engine uses.\n *\n * Defaults to the gsap import resolved at lib-load time, so consumers\n * who don't hit the dual-instance trap don't have to do anything.\n */\nlet currentGsap: typeof defaultGsap = defaultGsap;\n\n/**\n * Replace the gsap instance the engine drives. Call this BEFORE\n * `ReelSetBuilder.build()` (the builder does this for you when you call\n * `.gsap(instance)`).\n *\n * Process-global: there's only one bound instance at a time. If you\n * build several ReelSets with different gsap instances, the last\n * `setGsap` call wins.\n */\nexport function setGsap(g: typeof defaultGsap): void {\n currentGsap = g;\n}\n\n/** The currently-bound gsap instance. */\nexport function getGsap(): typeof defaultGsap {\n return currentGsap;\n}\n","import { Container } from 'pixi.js';\nimport type { Disposable } from '../utils/Disposable.js';\nimport { getGsap } from '../utils/gsapRef.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 * Play the cascade-destruction animation for this symbol. Called by\n * consumers (typically via `reelSet.destroySymbols(...)`) to disintegrate\n * a winning cell before the next cascade refill drops fresh symbols in.\n *\n * Default implementation: brief scale-up \"charge\" then implode (scale 0\n * + spin + fade), squishing around the symbol's bounding-box CENTER\n * regardless of the view's anchor. Total ~320 ms. The view is left at\n * `alpha: 0` (destroyed); position / pivot are restored so pool reuse\n * via `_replaceSymbol`'s same-id fast path doesn't inherit a stale\n * pivot offset.\n *\n * Override in subclasses for art-appropriate destruction. e.g. a\n * Spine symbol can play its `disintegration` track here, or a sprite\n * symbol can swap to a shatter atlas. The promise must resolve when\n * the symbol is no longer visible.\n *\n * Default animation: a snappy \"poof\". tiny anticipation pop (~60 ms)\n * then a fast implode to `scale: 0` + `alpha: 0` (~140 ms), centered on\n * the symbol's bounds. ~200 ms total. No rotation. designed to read\n * cleanly under win-cluster pacing without competing with the win\n * presenter.\n *\n * `opts.delay`. seconds to wait before the animation starts. Use to\n * stagger a cluster of winners (e.g. `i * 0.015`).\n * `opts.signal`. abort signal. If aborted (now or mid-animation), the\n * tween is killed and the view is snapped to its destroyed pose\n * (`alpha: 0`, transform restored). The promise resolves normally. abort\n * means \"skip to the end,\" not \"fail\". Subclasses that override this\n * method MUST honor the signal or document why they can't (e.g. a Spine\n * `disintegration` track is uninterruptible).\n */\n async playDestroy(opts?: { delay?: number; signal?: AbortSignal }): Promise<void> {\n const view = this.view;\n // Capture original transform so pool reuse sees a clean state.\n const originalPivotX = view.pivot.x;\n const originalPivotY = view.pivot.y;\n const originalX = view.x;\n const originalY = view.y;\n\n // Pivot to bounds-center so the scale collapses around the visual\n // centre instead of the view's (0,0) corner. and compensate position\n // so the symbol doesn't visibly jump when the pivot moves.\n const bounds = view.getLocalBounds();\n const cx = bounds.x + bounds.width / 2;\n const cy = bounds.y + bounds.height / 2;\n view.pivot.set(cx, cy);\n view.x = originalX + (cx - originalPivotX);\n view.y = originalY + (cy - originalPivotY);\n\n const delay = opts?.delay ?? 0;\n const signal = opts?.signal;\n\n const snapDestroyed = (): void => {\n view.alpha = 0;\n view.scale.set(0, 0);\n };\n\n // Pre-abort: skip the tween entirely and snap to the destroyed pose.\n if (signal?.aborted) {\n snapDestroyed();\n view.pivot.set(originalPivotX, originalPivotY);\n view.x = originalX;\n view.y = originalY;\n view.scale.set(1, 1);\n view.alpha = 0;\n return;\n }\n\n await new Promise<void>((resolve) => {\n const tl = getGsap()\n .timeline({ onComplete: () => {\n if (signal) signal.removeEventListener('abort', onAbort);\n resolve();\n }, delay })\n // Brief anticipation pop. small upscale, ~60 ms, with overshoot\n // so the implode reads as a release. No rotation.\n .to(view.scale, { x: 1.1, y: 1.1, duration: 0.06, ease: 'back.out(2.5)' })\n // Snap implode. scale -> 0 + alpha -> 0 together, snappy ease-in\n // so the symbol collapses into the cell centre and is gone.\n .to(view.scale, { x: 0, y: 0, duration: 0.14, ease: 'power3.in' }, '<+=0.04')\n .to(view, { alpha: 0, duration: 0.14, ease: 'power3.in' }, '<');\n\n const onAbort = (): void => {\n tl.kill();\n snapDestroyed();\n resolve();\n };\n if (signal) signal.addEventListener('abort', onAbort, { once: true });\n });\n\n // Restore transform. alpha stays 0 (the symbol IS destroyed). Scale\n // restored to 1 so pool reuse via `_replaceSymbol`'s same-id fast path\n // doesn't inherit a stale 0× scale; _replaceSymbol also resets scale\n // explicitly but a defensive restore here makes the destroyed cell\n // observably \"ready to be re-skinned\" between calls.\n view.pivot.set(originalPivotX, originalPivotY);\n view.x = originalX;\n view.y = originalY;\n view.scale.set(1, 1);\n }\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"],"mappings":";;;AAsBA,IAAI,IAAkC;AAWtC,SAAgB,EAAQ,GAA6B;AACnD,KAAc;;AAIhB,SAAgB,IAA8B;AAC5C,QAAO;;;;ACLT,IAAsB,IAAtB,MAAuD;CAErD;CAEA,YAA4B;CAC5B,eAAuB;CAEvB,cAAc;AACZ,OAAK,OAAO,IAAI,GAAW;;CAG7B,IAAI,WAAmB;AACrB,SAAO,KAAK;;CAGd,IAAI,cAAuB;AACzB,SAAO,KAAK;;CAQd,SAAS,GAAwB;AAQ/B,EAPA,KAAK,YAAY,GACjB,KAAK,KAAK,UAAU,IACpB,KAAK,KAAK,QAAQ,GAClB,KAAK,KAAK,MAAM,IAAI,GAAG,EAAE,EACzB,KAAK,KAAK,WAAW,GACrB,KAAK,KAAK,UAAU,MACpB,KAAK,KAAK,SAAS,GACnB,KAAK,WAAW,EAAS;;CAQ3B,aAAmB;AASjB,EARA,KAAK,eAAe,EACpB,KAAK,cAAc,EACnB,KAAK,YAAY,IACjB,KAAK,KAAK,UAAU,IACpB,KAAK,KAAK,QAAQ,GAClB,KAAK,KAAK,MAAM,IAAI,GAAG,EAAE,EACzB,KAAK,KAAK,WAAW,GACrB,KAAK,KAAK,UAAU,MACpB,KAAK,KAAK,SAAS;;CAIrB,QAAc;AACZ,OAAK,YAAY;;CAGnB,UAAgB;AACV,EAKJ,KAAK,kBAJL,KAAK,eAAe,EACpB,KAAK,cAAc,EACnB,KAAK,WAAW,EACX,KAAK,KAAK,aAAW,KAAK,KAAK,QAAQ,EAAE,UAAU,IAAM,CAAC,EAC3C;;CAUtB,YAA4B;CA6C5B,MAAM,YAAY,GAAgE;EAChF,IAAM,IAAO,KAAK,MAEZ,IAAiB,EAAK,MAAM,GAC5B,IAAiB,EAAK,MAAM,GAC5B,IAAY,EAAK,GACjB,IAAY,EAAK,GAKjB,IAAS,EAAK,gBAAgB,EAC9B,IAAK,EAAO,IAAI,EAAO,QAAQ,GAC/B,IAAK,EAAO,IAAI,EAAO,SAAS;AAGtC,EAFA,EAAK,MAAM,IAAI,GAAI,EAAG,EACtB,EAAK,IAAI,KAAa,IAAK,IAC3B,EAAK,IAAI,KAAa,IAAK;EAE3B,IAAM,IAAQ,GAAM,SAAS,GACvB,IAAS,GAAM,QAEf,UAA4B;AAEhC,GADA,EAAK,QAAQ,GACb,EAAK,MAAM,IAAI,GAAG,EAAE;;AAItB,MAAI,GAAQ,SAAS;AAMnB,GALA,GAAe,EACf,EAAK,MAAM,IAAI,GAAgB,EAAe,EAC9C,EAAK,IAAI,GACT,EAAK,IAAI,GACT,EAAK,MAAM,IAAI,GAAG,EAAE,EACpB,EAAK,QAAQ;AACb;;AAiCF,EA9BA,MAAM,IAAI,SAAe,MAAY;GACnC,IAAM,IAAK,GAAS,CACjB,SAAS;IAAE,kBAAkB;AAE5B,KADI,KAAQ,EAAO,oBAAoB,SAAS,EAAQ,EACxD,GAAS;;IACR;IAAO,CAAC,CAGV,GAAG,EAAK,OAAO;IAAE,GAAG;IAAK,GAAG;IAAK,UAAU;IAAM,MAAM;IAAiB,CAAC,CAGzE,GAAG,EAAK,OAAO;IAAE,GAAG;IAAG,GAAG;IAAG,UAAU;IAAM,MAAM;IAAa,EAAE,UAAU,CAC5E,GAAG,GAAM;IAAE,OAAO;IAAG,UAAU;IAAM,MAAM;IAAa,EAAE,IAAI,EAE3D,UAAsB;AAG1B,IAFA,EAAG,MAAM,EACT,GAAe,EACf,GAAS;;AAEX,GAAI,KAAQ,EAAO,iBAAiB,SAAS,GAAS,EAAE,MAAM,IAAM,CAAC;IACrE,EAOF,EAAK,MAAM,IAAI,GAAgB,EAAe,EAC9C,EAAK,IAAI,GACT,EAAK,IAAI,GACT,EAAK,MAAM,IAAI,GAAG,EAAE;;CAQtB,kBAAwB;CAMxB,gBAAsB;CAOtB,eAAqB"}
1
+ {"version":3,"file":"ReelSymbol-DBbmNOIa.js","names":[],"sources":["../src/utils/gsapRef.ts","../src/symbols/ReelSymbol.ts"],"sourcesContent":["import { gsap as defaultGsap } from 'gsap';\n\n/**\n * The gsap instance every phase, motion tween, and cascade animation in\n * the engine reads from.\n *\n * **Why this indirection exists:** under tools that resolve modules through\n * symlinked workspaces (vite + a locally-linked pixi-reels, pnpm dev\n * setups, esbuild plugin chains), the gsap import inside the lib's\n * compiled `dist/index.js` and the gsap import in the consumer's source\n * can resolve to *different module instances*. each with its own root\n * timeline. The consumer drives one, the lib's tweens live on the other,\n * and reels stall at progress 0.\n *\n * The fix: every internal animation site reads gsap through `getGsap()`\n * instead of importing the `'gsap'` module directly. The builder method\n * `ReelSetBuilder.gsap(myGsap)` rebinds the singleton so the consumer's\n * gsap is the one the engine uses.\n *\n * Defaults to the gsap import resolved at lib-load time, so consumers\n * who don't hit the dual-instance trap don't have to do anything.\n */\nlet currentGsap: typeof defaultGsap = defaultGsap;\n\n/**\n * Replace the gsap instance the engine drives. Call this BEFORE\n * `ReelSetBuilder.build()` (the builder does this for you when you call\n * `.gsap(instance)`).\n *\n * Process-global: there's only one bound instance at a time. If you\n * build several ReelSets with different gsap instances, the last\n * `setGsap` call wins.\n */\nexport function setGsap(g: typeof defaultGsap): void {\n currentGsap = g;\n}\n\n/** The currently-bound gsap instance. */\nexport function getGsap(): typeof defaultGsap {\n return currentGsap;\n}\n","import { Container } from 'pixi.js';\nimport type { Disposable } from '../utils/Disposable.js';\nimport { getGsap } from '../utils/gsapRef.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 * Play the cascade-destruction animation for this symbol. Called by\n * consumers (typically via `reelSet.destroySymbols(...)`) to disintegrate\n * a winning cell before the next cascade refill drops fresh symbols in.\n *\n * Default implementation: brief scale-up \"charge\" then implode (scale 0\n * + spin + fade), squishing around the symbol's bounding-box CENTER\n * regardless of the view's anchor. Total ~320 ms. The view is left at\n * `alpha: 0` (destroyed); position / pivot are restored so pool reuse\n * via `_replaceSymbol`'s same-id fast path doesn't inherit a stale\n * pivot offset.\n *\n * Override in subclasses for art-appropriate destruction. e.g. a\n * Spine symbol can play its `disintegration` track here, or a sprite\n * symbol can swap to a shatter atlas. The promise must resolve when\n * the symbol is no longer visible.\n *\n * Default animation: a snappy \"poof\". tiny anticipation pop (~60 ms)\n * then a fast implode to `scale: 0` + `alpha: 0` (~140 ms), centered on\n * the symbol's bounds. ~200 ms total. No rotation. designed to read\n * cleanly under win-cluster pacing without competing with the win\n * presenter.\n *\n * `opts.delay`. seconds to wait before the animation starts. Use to\n * stagger a cluster of winners (e.g. `i * 0.015`).\n * `opts.signal`. abort signal. If aborted (now or mid-animation), the\n * tween is killed and the view is snapped to its destroyed pose\n * (`alpha: 0`, transform restored). The promise resolves normally. abort\n * means \"skip to the end,\" not \"fail\". Subclasses that override this\n * method MUST honor the signal or document why they can't (e.g. a Spine\n * `disintegration` track is uninterruptible).\n */\n async playDestroy(opts?: { delay?: number; signal?: AbortSignal }): Promise<void> {\n const view = this.view;\n // Capture original transform so pool reuse sees a clean state.\n const originalPivotX = view.pivot.x;\n const originalPivotY = view.pivot.y;\n const originalX = view.x;\n const originalY = view.y;\n\n // Pivot to bounds-center so the scale collapses around the visual\n // centre instead of the view's (0,0) corner. and compensate position\n // so the symbol doesn't visibly jump when the pivot moves.\n const bounds = view.getLocalBounds();\n const cx = bounds.x + bounds.width / 2;\n const cy = bounds.y + bounds.height / 2;\n view.pivot.set(cx, cy);\n view.x = originalX + (cx - originalPivotX);\n view.y = originalY + (cy - originalPivotY);\n\n const delay = opts?.delay ?? 0;\n const signal = opts?.signal;\n\n const snapDestroyed = (): void => {\n view.alpha = 0;\n view.scale.set(0, 0);\n };\n\n // Pre-abort: skip the tween entirely and snap to the destroyed pose.\n if (signal?.aborted) {\n snapDestroyed();\n view.pivot.set(originalPivotX, originalPivotY);\n view.x = originalX;\n view.y = originalY;\n view.scale.set(1, 1);\n view.alpha = 0;\n return;\n }\n\n await new Promise<void>((resolve) => {\n const tl = getGsap()\n .timeline({ onComplete: () => {\n if (signal) signal.removeEventListener('abort', onAbort);\n resolve();\n }, delay })\n // Brief anticipation pop. small upscale, ~60 ms, with overshoot\n // so the implode reads as a release. No rotation.\n .to(view.scale, { x: 1.1, y: 1.1, duration: 0.06, ease: 'back.out(2.5)' })\n // Snap implode. scale -> 0 + alpha -> 0 together, snappy ease-in\n // so the symbol collapses into the cell centre and is gone.\n .to(view.scale, { x: 0, y: 0, duration: 0.14, ease: 'power3.in' }, '<+=0.04')\n .to(view, { alpha: 0, duration: 0.14, ease: 'power3.in' }, '<');\n\n const onAbort = (): void => {\n tl.kill();\n snapDestroyed();\n resolve();\n };\n if (signal) signal.addEventListener('abort', onAbort, { once: true });\n });\n\n // Restore transform. alpha stays 0 (the symbol IS destroyed). Scale\n // restored to 1 so pool reuse via `_replaceSymbol`'s same-id fast path\n // doesn't inherit a stale 0× scale; _replaceSymbol also resets scale\n // explicitly but a defensive restore here makes the destroyed cell\n // observably \"ready to be re-skinned\" between calls.\n view.pivot.set(originalPivotX, originalPivotY);\n view.x = originalX;\n view.y = originalY;\n view.scale.set(1, 1);\n }\n\n /**\n * Lifecycle hook: the owning reel is spinning.\n * Default: no-op. Override (e.g. SpineReelSymbol.autoPlayBlur,\n * StaticSpinSymbol) to swap to a spin presentation automatically.\n *\n * Fired on every strip symbol (visible AND buffer rows) when the reel\n * enters the spin phase, and again with `joinedMidSpin: true` on each\n * symbol freshly installed while the reel is already spinning (pool\n * recycling wipes symbol state, so a wrapped-in symbol can't know the\n * reel is moving without this). Implementations MUST be idempotent.\n * the same instance can be notified more than once per spin.\n *\n * @param joinedMidSpin true when this symbol was installed into a reel\n * already at speed (skip start-of-spin transitions like blur ramps).\n */\n onReelSpinStart(_joinedMidSpin?: boolean): 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"],"mappings":";;;AAsBA,IAAI,IAAkC;AAWtC,SAAgB,EAAQ,GAA6B;AACnD,KAAc;;AAIhB,SAAgB,IAA8B;AAC5C,QAAO;;;;ACLT,IAAsB,IAAtB,MAAuD;CAErD;CAEA,YAA4B;CAC5B,eAAuB;CAEvB,cAAc;AACZ,OAAK,OAAO,IAAI,GAAW;;CAG7B,IAAI,WAAmB;AACrB,SAAO,KAAK;;CAGd,IAAI,cAAuB;AACzB,SAAO,KAAK;;CAQd,SAAS,GAAwB;AAQ/B,EAPA,KAAK,YAAY,GACjB,KAAK,KAAK,UAAU,IACpB,KAAK,KAAK,QAAQ,GAClB,KAAK,KAAK,MAAM,IAAI,GAAG,EAAE,EACzB,KAAK,KAAK,WAAW,GACrB,KAAK,KAAK,UAAU,MACpB,KAAK,KAAK,SAAS,GACnB,KAAK,WAAW,EAAS;;CAQ3B,aAAmB;AASjB,EARA,KAAK,eAAe,EACpB,KAAK,cAAc,EACnB,KAAK,YAAY,IACjB,KAAK,KAAK,UAAU,IACpB,KAAK,KAAK,QAAQ,GAClB,KAAK,KAAK,MAAM,IAAI,GAAG,EAAE,EACzB,KAAK,KAAK,WAAW,GACrB,KAAK,KAAK,UAAU,MACpB,KAAK,KAAK,SAAS;;CAIrB,QAAc;AACZ,OAAK,YAAY;;CAGnB,UAAgB;AACV,EAKJ,KAAK,kBAJL,KAAK,eAAe,EACpB,KAAK,cAAc,EACnB,KAAK,WAAW,EACX,KAAK,KAAK,aAAW,KAAK,KAAK,QAAQ,EAAE,UAAU,IAAM,CAAC,EAC3C;;CAUtB,YAA4B;CA6C5B,MAAM,YAAY,GAAgE;EAChF,IAAM,IAAO,KAAK,MAEZ,IAAiB,EAAK,MAAM,GAC5B,IAAiB,EAAK,MAAM,GAC5B,IAAY,EAAK,GACjB,IAAY,EAAK,GAKjB,IAAS,EAAK,gBAAgB,EAC9B,IAAK,EAAO,IAAI,EAAO,QAAQ,GAC/B,IAAK,EAAO,IAAI,EAAO,SAAS;AAGtC,EAFA,EAAK,MAAM,IAAI,GAAI,EAAG,EACtB,EAAK,IAAI,KAAa,IAAK,IAC3B,EAAK,IAAI,KAAa,IAAK;EAE3B,IAAM,IAAQ,GAAM,SAAS,GACvB,IAAS,GAAM,QAEf,UAA4B;AAEhC,GADA,EAAK,QAAQ,GACb,EAAK,MAAM,IAAI,GAAG,EAAE;;AAItB,MAAI,GAAQ,SAAS;AAMnB,GALA,GAAe,EACf,EAAK,MAAM,IAAI,GAAgB,EAAe,EAC9C,EAAK,IAAI,GACT,EAAK,IAAI,GACT,EAAK,MAAM,IAAI,GAAG,EAAE,EACpB,EAAK,QAAQ;AACb;;AAiCF,EA9BA,MAAM,IAAI,SAAe,MAAY;GACnC,IAAM,IAAK,GAAS,CACjB,SAAS;IAAE,kBAAkB;AAE5B,KADI,KAAQ,EAAO,oBAAoB,SAAS,EAAQ,EACxD,GAAS;;IACR;IAAO,CAAC,CAGV,GAAG,EAAK,OAAO;IAAE,GAAG;IAAK,GAAG;IAAK,UAAU;IAAM,MAAM;IAAiB,CAAC,CAGzE,GAAG,EAAK,OAAO;IAAE,GAAG;IAAG,GAAG;IAAG,UAAU;IAAM,MAAM;IAAa,EAAE,UAAU,CAC5E,GAAG,GAAM;IAAE,OAAO;IAAG,UAAU;IAAM,MAAM;IAAa,EAAE,IAAI,EAE3D,UAAsB;AAG1B,IAFA,EAAG,MAAM,EACT,GAAe,EACf,GAAS;;AAEX,GAAI,KAAQ,EAAO,iBAAiB,SAAS,GAAS,EAAE,MAAM,IAAM,CAAC;IACrE,EAOF,EAAK,MAAM,IAAI,GAAgB,EAAe,EAC9C,EAAK,IAAI,GACT,EAAK,IAAI,GACT,EAAK,MAAM,IAAI,GAAG,EAAE;;CAkBtB,gBAAgB,GAAgC;CAMhD,gBAAsB;CAOtB,eAAqB"}
@@ -1,2 +1,2 @@
1
- const e=require(`./ReelSymbol-BxoiwQ-r.cjs`);var t=null,n=(async()=>{try{t=(await import(`@esotericsoftware/spine-pixi-v8`)).Spine}catch{}})();function r(){return n}var i=class extends e.t{_spine=null;_skeletonDataMap;_idleAnimation;_winAnimation;_defaultSkin;_winResolve=null;_currentSkeletonKey=``;_strict;constructor(e){if(super(),!t)throw Error(`SpineSymbol could not use @esotericsoftware/spine-pixi-v8. Either it is not installed (npm install @esotericsoftware/spine-pixi-v8), or its dynamic import has not resolved yet on a cold start — await whenSpineReady() before constructing SpineSymbols.`);this._skeletonDataMap=e.skeletonDataMap,this._idleAnimation=e.idleAnimation??`idle`,this._winAnimation=e.winAnimation??`win`,this._defaultSkin=e.defaultSkin??`default`,this._strict=e.strict??!1}onActivate(e){let n=this._skeletonDataMap[e];if(n){if(this._currentSkeletonKey!==e&&(this._spine&&(this.view.removeChild(this._spine),this._spine.destroy()),this._spine=new t({skeletonData:n}),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);else if(this._strict)throw Error(`SpineSymbol(strict): idle animation '${this._idleAnimation}' not found on skeleton '${e}'.`)}}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){if(!this._spine.skeleton.data.findAnimation(this._winAnimation)){if(this._strict)throw Error(`SpineSymbol(strict): win animation '${this._winAnimation}' not found on skeleton '${this._currentSkeletonKey}'.`);return}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 r}}),Object.defineProperty(exports,`t`,{enumerable:!0,get:function(){return i}});
2
- //# sourceMappingURL=SpineSymbol-DAqBuFX2.cjs.map
1
+ const e=require(`./ReelSymbol-BJGylIyA.cjs`);var t=null,n=(async()=>{try{t=(await import(`@esotericsoftware/spine-pixi-v8`)).Spine}catch{}})();function r(){return n}var i=class extends e.t{_spine=null;_skeletonDataMap;_idleAnimation;_winAnimation;_defaultSkin;_winResolve=null;_currentSkeletonKey=``;_strict;constructor(e){if(super(),!t)throw Error(`SpineSymbol could not use @esotericsoftware/spine-pixi-v8. Either it is not installed (npm install @esotericsoftware/spine-pixi-v8), or its dynamic import has not resolved yet on a cold start — await whenSpineReady() before constructing SpineSymbols.`);this._skeletonDataMap=e.skeletonDataMap,this._idleAnimation=e.idleAnimation??`idle`,this._winAnimation=e.winAnimation??`win`,this._defaultSkin=e.defaultSkin??`default`,this._strict=e.strict??!1}onActivate(e){let n=this._skeletonDataMap[e];if(n){if(this._currentSkeletonKey!==e&&(this._spine&&(this.view.removeChild(this._spine),this._spine.destroy()),this._spine=new t({skeletonData:n}),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);else if(this._strict)throw Error(`SpineSymbol(strict): idle animation '${this._idleAnimation}' not found on skeleton '${e}'.`)}}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){if(!this._spine.skeleton.data.findAnimation(this._winAnimation)){if(this._strict)throw Error(`SpineSymbol(strict): win animation '${this._winAnimation}' not found on skeleton '${this._currentSkeletonKey}'.`);return}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 r}}),Object.defineProperty(exports,`t`,{enumerable:!0,get:function(){return i}});
2
+ //# sourceMappingURL=SpineSymbol-C6V3FScO.cjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"SpineSymbol-DAqBuFX2.cjs","names":[],"sources":["../src/symbols/SpineSymbol.ts"],"sourcesContent":["import { ReelSymbol } from './ReelSymbol.js';\n\n// Spine types imported dynamically. this is an optional peer dependency\nlet SpineClass: any = null;\n\n// Kick off the optional import at module init and keep the promise so callers\n// can wait for it. Without this, constructing a SpineSymbol on a cold start\n// (before the dynamic import resolves) throws the \"not installed\" error even\n// when Spine IS installed — it just hadn't finished loading yet.\nconst spineReady: Promise<void> = (async () => {\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/**\n * Resolves once the optional `@esotericsoftware/spine-pixi-v8` import has\n * settled (whether or not it was installed). Await this before constructing\n * `SpineSymbol`s if you build the reel set immediately on app start, so a cold\n * load can't throw the misleading \"not installed\" error.\n */\nexport function whenSpineReady(): Promise<void> {\n return spineReady;\n}\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 * Fail loud on unmapped animations. When `true`, activating or winning with\n * a configured animation name that the skeleton doesn't define throws instead\n * of silently showing nothing (catches typos like `ide` vs `idle`). Default\n * `false` to preserve the lenient behavior. See ADR 011.\n */\n strict?: boolean;\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 private _strict: boolean;\n\n constructor(options: SpineSymbolOptions) {\n super();\n if (!SpineClass) {\n throw new Error(\n 'SpineSymbol could not use @esotericsoftware/spine-pixi-v8. Either it is ' +\n 'not installed (npm install @esotericsoftware/spine-pixi-v8), or its ' +\n 'dynamic import has not resolved yet on a cold start — await ' +\n 'whenSpineReady() before constructing SpineSymbols.',\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 this._strict = options.strict ?? false;\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 } else if (this._strict) {\n throw new Error(\n `SpineSymbol(strict): idle animation '${this._idleAnimation}' not found ` +\n `on skeleton '${symbolId}'.`,\n );\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)) {\n if (this._strict) {\n throw new Error(\n `SpineSymbol(strict): win animation '${this._winAnimation}' not found ` +\n `on skeleton '${this._currentSkeletonKey}'.`,\n );\n }\n return;\n }\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":"6CAGA,IAAI,EAAkB,KAMhB,GAA6B,SAAY,CAC7C,GAAI,CAEF,GADoB,MAAM,OAAO,oCACR,WACnB,MAGN,CAQJ,SAAgB,GAAgC,CAC9C,OAAO,EA2BT,IAAa,EAAb,cAAiC,EAAA,CAAW,CAC1C,OAAsB,KACtB,iBACA,eACA,cACA,aACA,YAA2C,KAC3C,oBAAsC,GACtC,QAEA,YAAY,EAA6B,CAEvC,GADA,OAAO,CACH,CAAC,EACH,MAAU,MACR,6PAID,CAEH,KAAK,iBAAmB,EAAQ,gBAChC,KAAK,eAAiB,EAAQ,eAAiB,OAC/C,KAAK,cAAgB,EAAQ,cAAgB,MAC7C,KAAK,aAAe,EAAQ,aAAe,UAC3C,KAAK,QAAU,EAAQ,QAAU,GAGnC,WAAqB,EAAwB,CAC3C,IAAM,EAAe,KAAK,iBAAiB,GACtC,KAkBL,IAfI,KAAK,sBAAwB,IAC3B,KAAK,SACP,KAAK,KAAK,YAAY,KAAK,OAAO,CAClC,KAAK,OAAO,SAAS,EAEvB,KAAK,OAAS,IAAI,EAAW,CAAE,eAAc,CAAC,CAC9C,KAAK,KAAK,SAAS,KAAK,OAAO,CAC/B,KAAK,oBAAsB,GAIzB,KAAK,OAAO,SAAS,KAAK,SAAS,KAAK,aAAa,GACvD,KAAK,OAAO,SAAS,cAAc,KAAK,aAAa,CACrD,KAAK,OAAO,SAAS,qBAAqB,EAExC,KAAK,OAAO,SAAS,KAAK,cAAc,KAAK,eAAe,CAC9D,KAAK,OAAO,MAAM,aAAa,EAAG,KAAK,eAAgB,GAAK,SACnD,KAAK,QACd,MAAU,MACR,wCAAwC,KAAK,eAAe,2BAC5C,EAAS,IAC1B,EAIL,cAA+B,CAO7B,GANI,KAAK,SACP,KAAK,OAAO,MAAM,gBAAgB,CAClC,KAAK,OAAO,MAAM,aAAa,EAI7B,KAAK,YAAa,CACpB,IAAM,EAAI,KAAK,YACf,KAAK,YAAc,KACnB,GAAG,EAIP,MAAM,SAAyB,CACxB,QAAK,OACV,IAAI,CAAC,KAAK,OAAO,SAAS,KAAK,cAAc,KAAK,cAAc,CAAE,CAChE,GAAI,KAAK,QACP,MAAU,MACR,uCAAuC,KAAK,cAAc,2BAC1C,KAAK,oBAAoB,IAC1C,CAEH,OAGF,OAAO,IAAI,QAAe,GAAY,CACpC,KAAK,YAAc,EACnB,IAAM,EAAQ,KAAK,OAAO,MAAM,aAAa,EAAG,KAAK,cAAe,GAAM,CAC1E,KAAK,OAAO,MAAM,YAAY,CAC5B,SAAW,GAAoB,CACzB,IAAe,IACjB,KAAK,OAAO,MAAM,gBAAgB,CAE9B,KAAK,OAAO,SAAS,KAAK,cAAc,KAAK,eAAe,EAC9D,KAAK,OAAO,MAAM,aAAa,EAAG,KAAK,eAAgB,GAAK,CAE9D,KAAK,YAAc,KACnB,GAAS,GAGd,CAAC,EACF,EAGJ,eAAsB,CACf,KAAK,SACV,KAAK,OAAO,MAAM,gBAAgB,CAC9B,KAAK,OAAO,SAAS,KAAK,cAAc,KAAK,eAAe,EAC9D,KAAK,OAAO,MAAM,aAAa,EAAG,KAAK,eAAgB,GAAK,CAE9D,AAEE,KAAK,eADL,KAAK,aAAa,CACC,OAIvB,OAAO,EAAe,EAAsB,CAC1C,GAAI,CAAC,KAAK,OAAQ,OAClB,IAAM,EAAS,KAAK,OAAO,WAAW,CAClC,EAAO,MAAQ,GAAK,EAAO,OAAS,GACtC,KAAK,OAAO,MAAM,IAChB,EAAQ,EAAO,MACf,EAAS,EAAO,OACjB,CAIL,WAAqC,CAMnC,GALA,AAGE,KAAK,UAFL,KAAK,OAAO,MAAM,gBAAgB,CAClC,KAAK,OAAO,SAAS,CACP,MAEZ,KAAK,YAAa,CACpB,IAAM,EAAI,KAAK,YACf,KAAK,YAAc,KACnB,GAAG"}
1
+ {"version":3,"file":"SpineSymbol-C6V3FScO.cjs","names":[],"sources":["../src/symbols/SpineSymbol.ts"],"sourcesContent":["import { ReelSymbol } from './ReelSymbol.js';\n\n// Spine types imported dynamically. this is an optional peer dependency\nlet SpineClass: any = null;\n\n// Kick off the optional import at module init and keep the promise so callers\n// can wait for it. Without this, constructing a SpineSymbol on a cold start\n// (before the dynamic import resolves) throws the \"not installed\" error even\n// when Spine IS installed — it just hadn't finished loading yet.\nconst spineReady: Promise<void> = (async () => {\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/**\n * Resolves once the optional `@esotericsoftware/spine-pixi-v8` import has\n * settled (whether or not it was installed). Await this before constructing\n * `SpineSymbol`s if you build the reel set immediately on app start, so a cold\n * load can't throw the misleading \"not installed\" error.\n */\nexport function whenSpineReady(): Promise<void> {\n return spineReady;\n}\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 * Fail loud on unmapped animations. When `true`, activating or winning with\n * a configured animation name that the skeleton doesn't define throws instead\n * of silently showing nothing (catches typos like `ide` vs `idle`). Default\n * `false` to preserve the lenient behavior. See ADR 011.\n */\n strict?: boolean;\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 private _strict: boolean;\n\n constructor(options: SpineSymbolOptions) {\n super();\n if (!SpineClass) {\n throw new Error(\n 'SpineSymbol could not use @esotericsoftware/spine-pixi-v8. Either it is ' +\n 'not installed (npm install @esotericsoftware/spine-pixi-v8), or its ' +\n 'dynamic import has not resolved yet on a cold start — await ' +\n 'whenSpineReady() before constructing SpineSymbols.',\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 this._strict = options.strict ?? false;\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 } else if (this._strict) {\n throw new Error(\n `SpineSymbol(strict): idle animation '${this._idleAnimation}' not found ` +\n `on skeleton '${symbolId}'.`,\n );\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)) {\n if (this._strict) {\n throw new Error(\n `SpineSymbol(strict): win animation '${this._winAnimation}' not found ` +\n `on skeleton '${this._currentSkeletonKey}'.`,\n );\n }\n return;\n }\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":"6CAGA,IAAI,EAAkB,KAMhB,GAA6B,SAAY,CAC7C,GAAI,CAEF,GADoB,MAAM,OAAO,oCACR,WACnB,MAGN,CAQJ,SAAgB,GAAgC,CAC9C,OAAO,EA2BT,IAAa,EAAb,cAAiC,EAAA,CAAW,CAC1C,OAAsB,KACtB,iBACA,eACA,cACA,aACA,YAA2C,KAC3C,oBAAsC,GACtC,QAEA,YAAY,EAA6B,CAEvC,GADA,OAAO,CACH,CAAC,EACH,MAAU,MACR,6PAID,CAEH,KAAK,iBAAmB,EAAQ,gBAChC,KAAK,eAAiB,EAAQ,eAAiB,OAC/C,KAAK,cAAgB,EAAQ,cAAgB,MAC7C,KAAK,aAAe,EAAQ,aAAe,UAC3C,KAAK,QAAU,EAAQ,QAAU,GAGnC,WAAqB,EAAwB,CAC3C,IAAM,EAAe,KAAK,iBAAiB,GACtC,KAkBL,IAfI,KAAK,sBAAwB,IAC3B,KAAK,SACP,KAAK,KAAK,YAAY,KAAK,OAAO,CAClC,KAAK,OAAO,SAAS,EAEvB,KAAK,OAAS,IAAI,EAAW,CAAE,eAAc,CAAC,CAC9C,KAAK,KAAK,SAAS,KAAK,OAAO,CAC/B,KAAK,oBAAsB,GAIzB,KAAK,OAAO,SAAS,KAAK,SAAS,KAAK,aAAa,GACvD,KAAK,OAAO,SAAS,cAAc,KAAK,aAAa,CACrD,KAAK,OAAO,SAAS,qBAAqB,EAExC,KAAK,OAAO,SAAS,KAAK,cAAc,KAAK,eAAe,CAC9D,KAAK,OAAO,MAAM,aAAa,EAAG,KAAK,eAAgB,GAAK,SACnD,KAAK,QACd,MAAU,MACR,wCAAwC,KAAK,eAAe,2BAC5C,EAAS,IAC1B,EAIL,cAA+B,CAO7B,GANI,KAAK,SACP,KAAK,OAAO,MAAM,gBAAgB,CAClC,KAAK,OAAO,MAAM,aAAa,EAI7B,KAAK,YAAa,CACpB,IAAM,EAAI,KAAK,YACf,KAAK,YAAc,KACnB,GAAG,EAIP,MAAM,SAAyB,CACxB,QAAK,OACV,IAAI,CAAC,KAAK,OAAO,SAAS,KAAK,cAAc,KAAK,cAAc,CAAE,CAChE,GAAI,KAAK,QACP,MAAU,MACR,uCAAuC,KAAK,cAAc,2BAC1C,KAAK,oBAAoB,IAC1C,CAEH,OAGF,OAAO,IAAI,QAAe,GAAY,CACpC,KAAK,YAAc,EACnB,IAAM,EAAQ,KAAK,OAAO,MAAM,aAAa,EAAG,KAAK,cAAe,GAAM,CAC1E,KAAK,OAAO,MAAM,YAAY,CAC5B,SAAW,GAAoB,CACzB,IAAe,IACjB,KAAK,OAAO,MAAM,gBAAgB,CAE9B,KAAK,OAAO,SAAS,KAAK,cAAc,KAAK,eAAe,EAC9D,KAAK,OAAO,MAAM,aAAa,EAAG,KAAK,eAAgB,GAAK,CAE9D,KAAK,YAAc,KACnB,GAAS,GAGd,CAAC,EACF,EAGJ,eAAsB,CACf,KAAK,SACV,KAAK,OAAO,MAAM,gBAAgB,CAC9B,KAAK,OAAO,SAAS,KAAK,cAAc,KAAK,eAAe,EAC9D,KAAK,OAAO,MAAM,aAAa,EAAG,KAAK,eAAgB,GAAK,CAE9D,AAEE,KAAK,eADL,KAAK,aAAa,CACC,OAIvB,OAAO,EAAe,EAAsB,CAC1C,GAAI,CAAC,KAAK,OAAQ,OAClB,IAAM,EAAS,KAAK,OAAO,WAAW,CAClC,EAAO,MAAQ,GAAK,EAAO,OAAS,GACtC,KAAK,OAAO,MAAM,IAChB,EAAQ,EAAO,MACf,EAAS,EAAO,OACjB,CAIL,WAAqC,CAMnC,GALA,AAGE,KAAK,UAFL,KAAK,OAAO,MAAM,gBAAgB,CAClC,KAAK,OAAO,SAAS,CACP,MAEZ,KAAK,YAAa,CACpB,IAAM,EAAI,KAAK,YACf,KAAK,YAAc,KACnB,GAAG"}
@@ -1,4 +1,4 @@
1
- import { t as e } from "./ReelSymbol-C9QQ30ER.js";
1
+ import { t as e } from "./ReelSymbol-DBbmNOIa.js";
2
2
  //#region src/symbols/SpineSymbol.ts
3
3
  var t = null, n = (async () => {
4
4
  try {
@@ -67,4 +67,4 @@ var i = class extends e {
67
67
  //#endregion
68
68
  export { r as n, i as t };
69
69
 
70
- //# sourceMappingURL=SpineSymbol-C_pacfc4.js.map
70
+ //# sourceMappingURL=SpineSymbol-w7zxDNo_.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"SpineSymbol-C_pacfc4.js","names":[],"sources":["../src/symbols/SpineSymbol.ts"],"sourcesContent":["import { ReelSymbol } from './ReelSymbol.js';\n\n// Spine types imported dynamically. this is an optional peer dependency\nlet SpineClass: any = null;\n\n// Kick off the optional import at module init and keep the promise so callers\n// can wait for it. Without this, constructing a SpineSymbol on a cold start\n// (before the dynamic import resolves) throws the \"not installed\" error even\n// when Spine IS installed — it just hadn't finished loading yet.\nconst spineReady: Promise<void> = (async () => {\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/**\n * Resolves once the optional `@esotericsoftware/spine-pixi-v8` import has\n * settled (whether or not it was installed). Await this before constructing\n * `SpineSymbol`s if you build the reel set immediately on app start, so a cold\n * load can't throw the misleading \"not installed\" error.\n */\nexport function whenSpineReady(): Promise<void> {\n return spineReady;\n}\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 * Fail loud on unmapped animations. When `true`, activating or winning with\n * a configured animation name that the skeleton doesn't define throws instead\n * of silently showing nothing (catches typos like `ide` vs `idle`). Default\n * `false` to preserve the lenient behavior. See ADR 011.\n */\n strict?: boolean;\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 private _strict: boolean;\n\n constructor(options: SpineSymbolOptions) {\n super();\n if (!SpineClass) {\n throw new Error(\n 'SpineSymbol could not use @esotericsoftware/spine-pixi-v8. Either it is ' +\n 'not installed (npm install @esotericsoftware/spine-pixi-v8), or its ' +\n 'dynamic import has not resolved yet on a cold start — await ' +\n 'whenSpineReady() before constructing SpineSymbols.',\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 this._strict = options.strict ?? false;\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 } else if (this._strict) {\n throw new Error(\n `SpineSymbol(strict): idle animation '${this._idleAnimation}' not found ` +\n `on skeleton '${symbolId}'.`,\n );\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)) {\n if (this._strict) {\n throw new Error(\n `SpineSymbol(strict): win animation '${this._winAnimation}' not found ` +\n `on skeleton '${this._currentSkeletonKey}'.`,\n );\n }\n return;\n }\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
+ {"version":3,"file":"SpineSymbol-w7zxDNo_.js","names":[],"sources":["../src/symbols/SpineSymbol.ts"],"sourcesContent":["import { ReelSymbol } from './ReelSymbol.js';\n\n// Spine types imported dynamically. this is an optional peer dependency\nlet SpineClass: any = null;\n\n// Kick off the optional import at module init and keep the promise so callers\n// can wait for it. Without this, constructing a SpineSymbol on a cold start\n// (before the dynamic import resolves) throws the \"not installed\" error even\n// when Spine IS installed — it just hadn't finished loading yet.\nconst spineReady: Promise<void> = (async () => {\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/**\n * Resolves once the optional `@esotericsoftware/spine-pixi-v8` import has\n * settled (whether or not it was installed). Await this before constructing\n * `SpineSymbol`s if you build the reel set immediately on app start, so a cold\n * load can't throw the misleading \"not installed\" error.\n */\nexport function whenSpineReady(): Promise<void> {\n return spineReady;\n}\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 * Fail loud on unmapped animations. When `true`, activating or winning with\n * a configured animation name that the skeleton doesn't define throws instead\n * of silently showing nothing (catches typos like `ide` vs `idle`). Default\n * `false` to preserve the lenient behavior. See ADR 011.\n */\n strict?: boolean;\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 private _strict: boolean;\n\n constructor(options: SpineSymbolOptions) {\n super();\n if (!SpineClass) {\n throw new Error(\n 'SpineSymbol could not use @esotericsoftware/spine-pixi-v8. Either it is ' +\n 'not installed (npm install @esotericsoftware/spine-pixi-v8), or its ' +\n 'dynamic import has not resolved yet on a cold start — await ' +\n 'whenSpineReady() before constructing SpineSymbols.',\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 this._strict = options.strict ?? false;\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 } else if (this._strict) {\n throw new Error(\n `SpineSymbol(strict): idle animation '${this._idleAnimation}' not found ` +\n `on skeleton '${symbolId}'.`,\n );\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)) {\n if (this._strict) {\n throw new Error(\n `SpineSymbol(strict): win animation '${this._winAnimation}' not found ` +\n `on skeleton '${this._currentSkeletonKey}'.`,\n );\n }\n return;\n }\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":";;AAGA,IAAI,IAAkB,MAMhB,KAA6B,YAAY;AAC7C,KAAI;AAEF,OADoB,MAAM,OAAO,oCACR;SACnB;IAGN;AAQJ,SAAgB,IAAgC;AAC9C,QAAO;;AA2BT,IAAa,IAAb,cAAiC,EAAW;CAC1C,SAAsB;CACtB;CACA;CACA;CACA;CACA,cAA2C;CAC3C,sBAAsC;CACtC;CAEA,YAAY,GAA6B;AAEvC,MADA,OAAO,EACH,CAAC,EACH,OAAU,MACR,6PAID;AAMH,EAJA,KAAK,mBAAmB,EAAQ,iBAChC,KAAK,iBAAiB,EAAQ,iBAAiB,QAC/C,KAAK,gBAAgB,EAAQ,gBAAgB,OAC7C,KAAK,eAAe,EAAQ,eAAe,WAC3C,KAAK,UAAU,EAAQ,UAAU;;CAGnC,WAAqB,GAAwB;EAC3C,IAAM,IAAe,KAAK,iBAAiB;AACtC,SAkBL;OAfI,KAAK,wBAAwB,MAC3B,KAAK,WACP,KAAK,KAAK,YAAY,KAAK,OAAO,EAClC,KAAK,OAAO,SAAS,GAEvB,KAAK,SAAS,IAAI,EAAW,EAAE,iBAAc,CAAC,EAC9C,KAAK,KAAK,SAAS,KAAK,OAAO,EAC/B,KAAK,sBAAsB,IAIzB,KAAK,OAAO,SAAS,KAAK,SAAS,KAAK,aAAa,KACvD,KAAK,OAAO,SAAS,cAAc,KAAK,aAAa,EACrD,KAAK,OAAO,SAAS,qBAAqB,GAExC,KAAK,OAAO,SAAS,KAAK,cAAc,KAAK,eAAe,CAC9D,MAAK,OAAO,MAAM,aAAa,GAAG,KAAK,gBAAgB,GAAK;YACnD,KAAK,QACd,OAAU,MACR,wCAAwC,KAAK,eAAe,2BAC5C,EAAS,IAC1B;;;CAIL,eAA+B;AAO7B,MANI,KAAK,WACP,KAAK,OAAO,MAAM,gBAAgB,EAClC,KAAK,OAAO,MAAM,aAAa,GAI7B,KAAK,aAAa;GACpB,IAAM,IAAI,KAAK;AAEf,GADA,KAAK,cAAc,MACnB,GAAG;;;CAIP,MAAM,UAAyB;AACxB,WAAK,QACV;OAAI,CAAC,KAAK,OAAO,SAAS,KAAK,cAAc,KAAK,cAAc,EAAE;AAChE,QAAI,KAAK,QACP,OAAU,MACR,uCAAuC,KAAK,cAAc,2BAC1C,KAAK,oBAAoB,IAC1C;AAEH;;AAGF,UAAO,IAAI,SAAe,MAAY;AACpC,SAAK,cAAc;IACnB,IAAM,IAAQ,KAAK,OAAO,MAAM,aAAa,GAAG,KAAK,eAAe,GAAM;AAC1E,SAAK,OAAO,MAAM,YAAY,EAC5B,WAAW,MAAoB;AAC7B,KAAI,MAAe,MACjB,KAAK,OAAO,MAAM,gBAAgB,EAE9B,KAAK,OAAO,SAAS,KAAK,cAAc,KAAK,eAAe,IAC9D,KAAK,OAAO,MAAM,aAAa,GAAG,KAAK,gBAAgB,GAAK,EAE9D,KAAK,cAAc,MACnB,GAAS;OAGd,CAAC;KACF;;;CAGJ,gBAAsB;AACf,OAAK,WACV,KAAK,OAAO,MAAM,gBAAgB,EAC9B,KAAK,OAAO,SAAS,KAAK,cAAc,KAAK,eAAe,IAC9D,KAAK,OAAO,MAAM,aAAa,GAAG,KAAK,gBAAgB,GAAK,EAE9D,AAEE,KAAK,iBADL,KAAK,aAAa,EACC;;CAIvB,OAAO,GAAe,GAAsB;AAC1C,MAAI,CAAC,KAAK,OAAQ;EAClB,IAAM,IAAS,KAAK,OAAO,WAAW;AACtC,EAAI,EAAO,QAAQ,KAAK,EAAO,SAAS,KACtC,KAAK,OAAO,MAAM,IAChB,IAAQ,EAAO,OACf,IAAS,EAAO,OACjB;;CAIL,YAAqC;AAMnC,MALA,AAGE,KAAK,YAFL,KAAK,OAAO,MAAM,gBAAgB,EAClC,KAAK,OAAO,SAAS,EACP,OAEZ,KAAK,aAAa;GACpB,IAAM,IAAI,KAAK;AAEf,GADA,KAAK,cAAc,MACnB,GAAG"}
@@ -170,6 +170,13 @@ export declare class Reel implements Disposable {
170
170
  private _symbolGapX;
171
171
  private _isDestroyed;
172
172
  private _isStopping;
173
+ /**
174
+ * True between `notifySpinStart()` and `notifySpinEnd()`. While set,
175
+ * `_replaceSymbol` fires `onReelSpinStart(true)` on each freshly
176
+ * installed symbol so it can join the spin presentation (pool recycling
177
+ * wipes per-instance state, so the symbol can't know on its own).
178
+ */
179
+ private _spinPresentationActive;
173
180
  private _isNudging;
174
181
  /**
175
182
  * Symbol-id queue consulted by `_onSymbolWrapped` during a nudge. Each
@@ -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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"}
@@ -1,4 +1,4 @@
1
- import { n as e, r as t, t as n } from "./ReelSymbol-C9QQ30ER.js";
1
+ import { n as e, r as t, t as n } from "./ReelSymbol-DBbmNOIa.js";
2
2
  import { Container as r, Graphics as i } from "pixi.js";
3
3
  //#region src/events/EventEmitter.ts
4
4
  var a = class {
@@ -144,6 +144,7 @@ var a = class {
144
144
  _symbolGapX;
145
145
  _isDestroyed = !1;
146
146
  _isStopping = !1;
147
+ _spinPresentationActive = !1;
147
148
  _isNudging = !1;
148
149
  _nudgeQueue = null;
149
150
  _nudgeTween = null;
@@ -230,10 +231,12 @@ var a = class {
230
231
  t === null ? this._occupancy[e] = null : this._occupancy[e] = { anchorRow: t };
231
232
  }
232
233
  notifySpinStart() {
233
- for (let e = this._bufferAbove; e < this._bufferAbove + this._visibleRows; e++) this.symbols[e].onReelSpinStart();
234
+ this._spinPresentationActive = !0;
235
+ for (let e = 0; e < this.symbols.length; e++) this.symbols[e].onReelSpinStart();
234
236
  }
235
237
  notifySpinEnd() {
236
- for (let e = this._bufferAbove; e < this._bufferAbove + this._visibleRows; e++) this.symbols[e].onReelSpinEnd();
238
+ this._spinPresentationActive = !1;
239
+ for (let e = 0; e < this.symbols.length; e++) this.symbols[e].onReelSpinEnd();
237
240
  }
238
241
  notifyLanded() {
239
242
  for (let e = this._bufferAbove; e < this._bufferAbove + this._visibleRows; e++) this.symbols[e].onReelLanded();
@@ -455,18 +458,18 @@ var a = class {
455
458
  if (r) {
456
459
  this._releaseOccupiedStub(n);
457
460
  let r = this._symbolFactory.acquire(t), a = this._isUnmasked(t);
458
- r.resize(this._symbolWidth, this._symbolHeight), this._placeSymbolView(r.view, i, a), r.view.alpha = 1, r.view.scale.set(1, 1), r.view.zIndex = this._computeSymbolZIndex(t, e), this._parentForSymbolId(t).addChild(r.view), this.symbols[e] = r, this.events.emit("symbol:created", t, e);
461
+ r.resize(this._symbolWidth, this._symbolHeight), this._placeSymbolView(r.view, i, a), r.view.alpha = 1, r.view.scale.set(1, 1), r.view.zIndex = this._computeSymbolZIndex(t, e), this._parentForSymbolId(t).addChild(r.view), this.symbols[e] = r, this._spinPresentationActive && r.onReelSpinStart(!0), this.events.emit("symbol:created", t, e);
459
462
  return;
460
463
  }
461
464
  if (n.symbolId === t) {
462
465
  n.view.alpha = 1, n.view.scale.set(1, 1), n.view.rotation = 0, n.view.filters = null, n.view.zIndex = this._computeSymbolZIndex(t, e);
463
466
  let r = this._parentForSymbolId(t);
464
- n.view.parent !== r && r.addChild(n.view), this._placeSymbolView(n.view, i, this._isUnmasked(t));
467
+ n.view.parent !== r && r.addChild(n.view), this._placeSymbolView(n.view, i, this._isUnmasked(t)), this._spinPresentationActive && n.onReelSpinStart(!0);
465
468
  return;
466
469
  }
467
470
  this._symbolFactory.release(n);
468
471
  let a = this._symbolFactory.acquire(t), o = this._isUnmasked(t);
469
- a.resize(this._symbolWidth, this._symbolHeight), this._placeSymbolView(a.view, i, o), a.view.alpha = 1, a.view.scale.set(1, 1), a.view.zIndex = this._computeSymbolZIndex(t, e), this._parentForSymbolId(t).addChild(a.view), this.symbols[e] = a, this.events.emit("symbol:created", t, e);
472
+ a.resize(this._symbolWidth, this._symbolHeight), this._placeSymbolView(a.view, i, o), a.view.alpha = 1, a.view.scale.set(1, 1), a.view.zIndex = this._computeSymbolZIndex(t, e), this._parentForSymbolId(t).addChild(a.view), this.symbols[e] = a, this._spinPresentationActive && a.onReelSpinStart(!0), this.events.emit("symbol:created", t, e);
470
473
  }
471
474
  _acquireOccupiedStub() {
472
475
  for (let e of this._occupiedStubs) if (!e.view.parent) return e;
@@ -644,7 +647,7 @@ var a = class {
644
647
  }
645
648
  update(e) {}
646
649
  onSkip() {
647
- this._kill(), this._reel.speed = this._speed.spinSpeed;
650
+ this._kill(), this._reel.speed = this._speed.spinSpeed, this._reel.notifySpinStart();
648
651
  }
649
652
  _kill() {
650
653
  this._delayedCall &&= (this._delayedCall.kill(), null), this._tween &&= (this._tween.kill(), null);
@@ -3008,4 +3011,4 @@ function oe(e, t) {
3008
3011
  //#endregion
3009
3012
  export { p as C, c as D, f as E, a as O, g as S, m as T, k as _, Q as a, S as b, ie as c, F as d, P as f, A as g, j as h, oe as i, W as l, M as m, K as n, X as o, N as p, G as r, Z as s, $ as t, L as u, O as v, h as w, x, D as y };
3010
3013
 
3011
- //# sourceMappingURL=debug-CtfTdj2g.js.map
3014
+ //# sourceMappingURL=debug-C1xsPOVH.js.map