pixi-reels 0.1.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 (103) hide show
  1. package/LICENSE +28 -0
  2. package/README.md +167 -0
  3. package/dist/SpineSymbol-B40TevSr.cjs +2 -0
  4. package/dist/SpineSymbol-B40TevSr.cjs.map +1 -0
  5. package/dist/SpineSymbol-D2jhCzFW.js +82 -0
  6. package/dist/SpineSymbol-D2jhCzFW.js.map +1 -0
  7. package/dist/config/SpeedPresets.d.ts +49 -0
  8. package/dist/config/SpeedPresets.d.ts.map +1 -0
  9. package/dist/config/defaults.d.ts +12 -0
  10. package/dist/config/defaults.d.ts.map +1 -0
  11. package/dist/config/types.d.ts +93 -0
  12. package/dist/config/types.d.ts.map +1 -0
  13. package/dist/core/Reel.d.ts +106 -0
  14. package/dist/core/Reel.d.ts.map +1 -0
  15. package/dist/core/ReelMotion.d.ts +43 -0
  16. package/dist/core/ReelMotion.d.ts.map +1 -0
  17. package/dist/core/ReelSet.d.ts +101 -0
  18. package/dist/core/ReelSet.d.ts.map +1 -0
  19. package/dist/core/ReelSetBuilder.d.ts +102 -0
  20. package/dist/core/ReelSetBuilder.d.ts.map +1 -0
  21. package/dist/core/ReelViewport.d.ts +43 -0
  22. package/dist/core/ReelViewport.d.ts.map +1 -0
  23. package/dist/core/StopSequencer.d.ts +26 -0
  24. package/dist/core/StopSequencer.d.ts.map +1 -0
  25. package/dist/debug/debug.d.ts +65 -0
  26. package/dist/debug/debug.d.ts.map +1 -0
  27. package/dist/events/EventEmitter.d.ts +25 -0
  28. package/dist/events/EventEmitter.d.ts.map +1 -0
  29. package/dist/events/ReelEvents.d.ts +40 -0
  30. package/dist/events/ReelEvents.d.ts.map +1 -0
  31. package/dist/frame/FrameBuilder.d.ts +50 -0
  32. package/dist/frame/FrameBuilder.d.ts.map +1 -0
  33. package/dist/frame/OffsetCalculator.d.ts +19 -0
  34. package/dist/frame/OffsetCalculator.d.ts.map +1 -0
  35. package/dist/frame/RandomSymbolProvider.d.ts +27 -0
  36. package/dist/frame/RandomSymbolProvider.d.ts.map +1 -0
  37. package/dist/index.cjs +5 -0
  38. package/dist/index.cjs.map +1 -0
  39. package/dist/index.d.ts +51 -0
  40. package/dist/index.d.ts.map +1 -0
  41. package/dist/index.js +1454 -0
  42. package/dist/index.js.map +1 -0
  43. package/dist/pool/ObjectPool.d.ts +36 -0
  44. package/dist/pool/ObjectPool.d.ts.map +1 -0
  45. package/dist/speed/SpeedManager.d.ts +38 -0
  46. package/dist/speed/SpeedManager.d.ts.map +1 -0
  47. package/dist/spin/SpinController.d.ts +71 -0
  48. package/dist/spin/SpinController.d.ts.map +1 -0
  49. package/dist/spin/modes/CascadeMode.d.ts +16 -0
  50. package/dist/spin/modes/CascadeMode.d.ts.map +1 -0
  51. package/dist/spin/modes/ImmediateMode.d.ts +10 -0
  52. package/dist/spin/modes/ImmediateMode.d.ts.map +1 -0
  53. package/dist/spin/modes/SpinningMode.d.ts +18 -0
  54. package/dist/spin/modes/SpinningMode.d.ts.map +1 -0
  55. package/dist/spin/modes/StandardMode.d.ts +10 -0
  56. package/dist/spin/modes/StandardMode.d.ts.map +1 -0
  57. package/dist/spin/phases/AnticipationPhase.d.ts +24 -0
  58. package/dist/spin/phases/AnticipationPhase.d.ts.map +1 -0
  59. package/dist/spin/phases/PhaseFactory.d.ts +21 -0
  60. package/dist/spin/phases/PhaseFactory.d.ts.map +1 -0
  61. package/dist/spin/phases/ReelPhase.d.ts +39 -0
  62. package/dist/spin/phases/ReelPhase.d.ts.map +1 -0
  63. package/dist/spin/phases/SpinPhase.d.ts +25 -0
  64. package/dist/spin/phases/SpinPhase.d.ts.map +1 -0
  65. package/dist/spin/phases/StartPhase.d.ts +26 -0
  66. package/dist/spin/phases/StartPhase.d.ts.map +1 -0
  67. package/dist/spin/phases/StopPhase.d.ts +37 -0
  68. package/dist/spin/phases/StopPhase.d.ts.map +1 -0
  69. package/dist/spine/SpineReelSymbol.d.ts +111 -0
  70. package/dist/spine/SpineReelSymbol.d.ts.map +1 -0
  71. package/dist/spine/index.d.ts +5 -0
  72. package/dist/spine/index.d.ts.map +1 -0
  73. package/dist/spine.cjs +2 -0
  74. package/dist/spine.cjs.map +1 -0
  75. package/dist/spine.js +123 -0
  76. package/dist/spine.js.map +1 -0
  77. package/dist/spotlight/SymbolSpotlight.d.ts +70 -0
  78. package/dist/spotlight/SymbolSpotlight.d.ts.map +1 -0
  79. package/dist/symbols/AnimatedSpriteSymbol.d.ts +30 -0
  80. package/dist/symbols/AnimatedSpriteSymbol.d.ts.map +1 -0
  81. package/dist/symbols/ReelSymbol.d.ts +84 -0
  82. package/dist/symbols/ReelSymbol.d.ts.map +1 -0
  83. package/dist/symbols/SpineSymbol.d.ts +34 -0
  84. package/dist/symbols/SpineSymbol.d.ts.map +1 -0
  85. package/dist/symbols/SpriteSymbol.d.ts +29 -0
  86. package/dist/symbols/SpriteSymbol.d.ts.map +1 -0
  87. package/dist/symbols/SymbolFactory.d.ts +20 -0
  88. package/dist/symbols/SymbolFactory.d.ts.map +1 -0
  89. package/dist/symbols/SymbolRegistry.d.ts +25 -0
  90. package/dist/symbols/SymbolRegistry.d.ts.map +1 -0
  91. package/dist/testing/FakeTicker.d.ts +45 -0
  92. package/dist/testing/FakeTicker.d.ts.map +1 -0
  93. package/dist/testing/HeadlessSymbol.d.ts +28 -0
  94. package/dist/testing/HeadlessSymbol.d.ts.map +1 -0
  95. package/dist/testing/index.d.ts +5 -0
  96. package/dist/testing/index.d.ts.map +1 -0
  97. package/dist/testing/testHarness.d.ts +70 -0
  98. package/dist/testing/testHarness.d.ts.map +1 -0
  99. package/dist/utils/Disposable.d.ts +10 -0
  100. package/dist/utils/Disposable.d.ts.map +1 -0
  101. package/dist/utils/TickerRef.d.ts +30 -0
  102. package/dist/utils/TickerRef.d.ts.map +1 -0
  103. package/package.json +86 -0
@@ -0,0 +1,84 @@
1
+ import { Container } from 'pixi.js';
2
+ import { Disposable } from '../utils/Disposable.js';
3
+ /**
4
+ * One visible cell on a reel — the thing that actually draws.
5
+ *
6
+ * `ReelSymbol` is the abstract base class. Subclass it to pick a rendering
7
+ * technology (`SpriteSymbol`, `AnimatedSpriteSymbol`, `SpineSymbol`, or a
8
+ * custom class of your own). The reel set pools instances aggressively:
9
+ * one instance is reused many times as it scrolls off one identity and on
10
+ * to another, so implementations must never assume "I was just created".
11
+ *
12
+ * Required lifecycle hooks:
13
+ *
14
+ * - `onActivate(symbolId)` — the pool just handed me a new identity. Swap
15
+ * texture, restart animations, bring myself out of any "ended" pose.
16
+ * - `onDeactivate()` — I am about to be pooled. Pause animations, clear
17
+ * listeners, leave myself in a clean state for the next activation.
18
+ * - `playWin()` — the spotlight is celebrating me. Return a promise that
19
+ * resolves when the one-shot animation is done.
20
+ * - `stopAnimation()` — spotlight is over, return to idle.
21
+ * - `resize(w, h)` — the reel's cell size changed (on every symbol swap).
22
+ * Store the dimensions and reposition internal children. Forgetting
23
+ * this is the single most common "why do my symbols scatter" bug.
24
+ *
25
+ * ```
26
+ * create → activate(symbolId) → [playWin / stopAnimation]
27
+ * → deactivate
28
+ * → activate(newId) → ...
29
+ * ```
30
+ *
31
+ * There's no hidden GC. Hold resources? Override `onDestroy()`.
32
+ */
33
+ export declare abstract class ReelSymbol implements Disposable {
34
+ /** The PixiJS container that holds this symbol's visual. */
35
+ readonly view: Container;
36
+ private _symbolId;
37
+ private _isDestroyed;
38
+ constructor();
39
+ get symbolId(): string;
40
+ get isDestroyed(): boolean;
41
+ /**
42
+ * Activate the symbol with a new identity.
43
+ * Called when the symbol enters the visible reel or is recycled from the pool.
44
+ */
45
+ activate(symbolId: string): void;
46
+ /**
47
+ * Deactivate the symbol before returning it to the pool.
48
+ * Stops any running animations and hides the view.
49
+ */
50
+ deactivate(): void;
51
+ /** Pool reset — aliases deactivate. */
52
+ reset(): void;
53
+ destroy(): void;
54
+ /** Subclass hook: set up visuals for the given symbolId. */
55
+ protected abstract onActivate(symbolId: string): void;
56
+ /** Subclass hook: clean up visuals. */
57
+ protected abstract onDeactivate(): void;
58
+ /** Subclass hook: additional cleanup on destroy. */
59
+ protected onDestroy(): void;
60
+ /** Play the win/highlight animation for this symbol. Resolves when complete. */
61
+ abstract playWin(): Promise<void>;
62
+ /** Immediately stop any running animation and return to idle. */
63
+ abstract stopAnimation(): void;
64
+ /** Resize the symbol's visual to fit the given dimensions. */
65
+ abstract resize(width: number, height: number): void;
66
+ /**
67
+ * Lifecycle hook: the owning reel has started spinning.
68
+ * Default: no-op. Override (e.g. SpineReelSymbol.autoPlayBlur) to swap to
69
+ * a blur animation automatically.
70
+ */
71
+ onReelSpinStart(): void;
72
+ /**
73
+ * Lifecycle hook: the owning reel is about to stop (just before bounce).
74
+ * Default: no-op.
75
+ */
76
+ onReelSpinEnd(): void;
77
+ /**
78
+ * Lifecycle hook: the owning reel has landed on its final symbols.
79
+ * Default: no-op. Override (e.g. SpineReelSymbol.autoPlayLanding) to fire
80
+ * a landing animation concurrently with the bounce.
81
+ */
82
+ onReelLanded(): void;
83
+ }
84
+ //# sourceMappingURL=ReelSymbol.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ReelSymbol.d.ts","sourceRoot":"","sources":["../../src/symbols/ReelSymbol.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,SAAS,CAAC;AACpC,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,wBAAwB,CAAC;AAEzD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AACH,8BAAsB,UAAW,YAAW,UAAU;IACpD,4DAA4D;IAC5D,SAAgB,IAAI,EAAE,SAAS,CAAC;IAEhC,OAAO,CAAC,SAAS,CAAc;IAC/B,OAAO,CAAC,YAAY,CAAS;;IAM7B,IAAI,QAAQ,IAAI,MAAM,CAErB;IAED,IAAI,WAAW,IAAI,OAAO,CAEzB;IAED;;;OAGG;IACH,QAAQ,CAAC,QAAQ,EAAE,MAAM,GAAG,IAAI;IAMhC;;;OAGG;IACH,UAAU,IAAI,IAAI;IAOlB,uCAAuC;IACvC,KAAK,IAAI,IAAI;IAIb,OAAO,IAAI,IAAI;IAQf,4DAA4D;IAC5D,SAAS,CAAC,QAAQ,CAAC,UAAU,CAAC,QAAQ,EAAE,MAAM,GAAG,IAAI;IAErD,uCAAuC;IACvC,SAAS,CAAC,QAAQ,CAAC,YAAY,IAAI,IAAI;IAEvC,oDAAoD;IACpD,SAAS,CAAC,SAAS,IAAI,IAAI;IAI3B,gFAAgF;IAChF,QAAQ,CAAC,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC;IAEjC,iEAAiE;IACjE,QAAQ,CAAC,aAAa,IAAI,IAAI;IAE9B,8DAA8D;IAC9D,QAAQ,CAAC,MAAM,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,IAAI;IAEpD;;;;OAIG;IACH,eAAe,IAAI,IAAI;IAEvB;;;OAGG;IACH,aAAa,IAAI,IAAI;IAErB;;;;OAIG;IACH,YAAY,IAAI,IAAI;CACrB"}
@@ -0,0 +1,34 @@
1
+ import { ReelSymbol } from './ReelSymbol.js';
2
+ export interface SpineSymbolOptions {
3
+ /** Map of symbolId → SkeletonData. */
4
+ skeletonDataMap: Record<string, any>;
5
+ /** Default animation name to play in idle. Default: 'idle'. */
6
+ idleAnimation?: string;
7
+ /** Animation name to play on win. Default: 'win'. */
8
+ winAnimation?: string;
9
+ /** Default skin name. Default: 'default'. */
10
+ defaultSkin?: string;
11
+ }
12
+ /**
13
+ * Symbol implementation using Spine 2D skeletal animation.
14
+ *
15
+ * Requires `@esotericsoftware/spine-pixi-v8` as an optional peer dependency.
16
+ * If Spine is not installed, constructing a SpineSymbol will throw.
17
+ */
18
+ export declare class SpineSymbol extends ReelSymbol {
19
+ private _spine;
20
+ private _skeletonDataMap;
21
+ private _idleAnimation;
22
+ private _winAnimation;
23
+ private _defaultSkin;
24
+ private _winResolve;
25
+ private _currentSkeletonKey;
26
+ constructor(options: SpineSymbolOptions);
27
+ protected onActivate(symbolId: string): void;
28
+ protected onDeactivate(): void;
29
+ playWin(): Promise<void>;
30
+ stopAnimation(): void;
31
+ resize(width: number, height: number): void;
32
+ protected onDestroy(): void;
33
+ }
34
+ //# sourceMappingURL=SpineSymbol.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"SpineSymbol.d.ts","sourceRoot":"","sources":["../../src/symbols/SpineSymbol.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAiB7C,MAAM,WAAW,kBAAkB;IACjC,sCAAsC;IACtC,eAAe,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;IACrC,+DAA+D;IAC/D,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,qDAAqD;IACrD,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,6CAA6C;IAC7C,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED;;;;;GAKG;AACH,qBAAa,WAAY,SAAQ,UAAU;IACzC,OAAO,CAAC,MAAM,CAAa;IAC3B,OAAO,CAAC,gBAAgB,CAAsB;IAC9C,OAAO,CAAC,cAAc,CAAS;IAC/B,OAAO,CAAC,aAAa,CAAS;IAC9B,OAAO,CAAC,YAAY,CAAS;IAC7B,OAAO,CAAC,WAAW,CAA6B;IAChD,OAAO,CAAC,mBAAmB,CAAc;gBAE7B,OAAO,EAAE,kBAAkB;IAcvC,SAAS,CAAC,UAAU,CAAC,QAAQ,EAAE,MAAM,GAAG,IAAI;IAyB5C,SAAS,CAAC,YAAY,IAAI,IAAI;IAQxB,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC;IAuB9B,aAAa,IAAI,IAAI;IAYrB,MAAM,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,IAAI;cAWxB,SAAS,IAAI,IAAI;CAOrC"}
@@ -0,0 +1,29 @@
1
+ import { Texture } from 'pixi.js';
2
+ import { ReelSymbol } from './ReelSymbol.js';
3
+ export interface SpriteSymbolOptions {
4
+ /** Map of symbolId → Texture. */
5
+ textures: Record<string, Texture>;
6
+ /** Anchor point. Default: { x: 0.5, y: 0.5 }. */
7
+ anchor?: {
8
+ x: number;
9
+ y: number;
10
+ };
11
+ }
12
+ /**
13
+ * Symbol implementation using a simple PixiJS Sprite.
14
+ * Swaps texture on activate. Win animation is a scale pulse via GSAP.
15
+ */
16
+ export declare class SpriteSymbol extends ReelSymbol {
17
+ private _sprite;
18
+ private _textures;
19
+ private _winTween;
20
+ constructor(options: SpriteSymbolOptions);
21
+ protected onActivate(symbolId: string): void;
22
+ protected onDeactivate(): void;
23
+ playWin(): Promise<void>;
24
+ stopAnimation(): void;
25
+ resize(width: number, height: number): void;
26
+ protected onDestroy(): void;
27
+ private _killWinTween;
28
+ }
29
+ //# sourceMappingURL=SpriteSymbol.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"SpriteSymbol.d.ts","sourceRoot":"","sources":["../../src/symbols/SpriteSymbol.ts"],"names":[],"mappings":"AAAA,OAAO,EAAU,KAAK,OAAO,EAAE,MAAM,SAAS,CAAC;AAE/C,OAAO,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAE7C,MAAM,WAAW,mBAAmB;IAClC,iCAAiC;IACjC,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAClC,iDAAiD;IACjD,MAAM,CAAC,EAAE;QAAE,CAAC,EAAE,MAAM,CAAC;QAAC,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;CACnC;AAED;;;GAGG;AACH,qBAAa,YAAa,SAAQ,UAAU;IAC1C,OAAO,CAAC,OAAO,CAAS;IACxB,OAAO,CAAC,SAAS,CAA0B;IAC3C,OAAO,CAAC,SAAS,CAAgC;gBAErC,OAAO,EAAE,mBAAmB;IASxC,SAAS,CAAC,UAAU,CAAC,QAAQ,EAAE,MAAM,GAAG,IAAI;IAO5C,SAAS,CAAC,YAAY,IAAI,IAAI;IAKxB,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC;IAe9B,aAAa,IAAI,IAAI;IAKrB,MAAM,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,IAAI;cAKxB,SAAS,IAAI,IAAI;IAIpC,OAAO,CAAC,aAAa;CAMtB"}
@@ -0,0 +1,20 @@
1
+ import { ReelSymbol } from './ReelSymbol.js';
2
+ import { SymbolRegistry } from './SymbolRegistry.js';
3
+ /**
4
+ * Creates and pools ReelSymbol instances.
5
+ *
6
+ * Wraps SymbolRegistry for creation and ObjectPool for recycling.
7
+ * Game code should not need to interact with this directly —
8
+ * it's managed by Reel internally.
9
+ */
10
+ export declare class SymbolFactory {
11
+ private _registry;
12
+ private _pool;
13
+ constructor(_registry: SymbolRegistry, maxPoolPerKey?: number);
14
+ /** Get a symbol (from pool or newly created), activated with symbolId. */
15
+ acquire(symbolId: string): ReelSymbol;
16
+ /** Return a symbol to the pool. */
17
+ release(symbol: ReelSymbol): void;
18
+ destroy(): void;
19
+ }
20
+ //# sourceMappingURL=SymbolFactory.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"SymbolFactory.d.ts","sourceRoot":"","sources":["../../src/symbols/SymbolFactory.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAClD,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AAG1D;;;;;;GAMG;AACH,qBAAa,aAAa;IAItB,OAAO,CAAC,SAAS;IAHnB,OAAO,CAAC,KAAK,CAAyB;gBAG5B,SAAS,EAAE,cAAc,EACjC,aAAa,GAAE,MAAW;IAU5B,0EAA0E;IAC1E,OAAO,CAAC,QAAQ,EAAE,MAAM,GAAG,UAAU;IAQrC,mCAAmC;IACnC,OAAO,CAAC,MAAM,EAAE,UAAU,GAAG,IAAI;IAMjC,OAAO,IAAI,IAAI;CAGhB"}
@@ -0,0 +1,25 @@
1
+ import { ReelSymbol } from './ReelSymbol.js';
2
+ /**
3
+ * Registry that maps symbolIds to their constructors and options.
4
+ *
5
+ * Used by the builder and SymbolFactory to create symbols on demand.
6
+ */
7
+ export declare class SymbolRegistry {
8
+ private _entries;
9
+ /**
10
+ * Register a symbol type.
11
+ *
12
+ * ```ts
13
+ * registry.register('cherry', SpriteSymbol, { textures: { cherry: tex } });
14
+ * ```
15
+ */
16
+ register<T extends ReelSymbol>(symbolId: string, SymbolClass: new (options: any) => T, options: T extends {
17
+ constructor: (options: infer O) => any;
18
+ } ? O : any): void;
19
+ /** Create a new symbol instance for the given symbolId. */
20
+ create(symbolId: string): ReelSymbol;
21
+ has(symbolId: string): boolean;
22
+ get symbolIds(): string[];
23
+ get size(): number;
24
+ }
25
+ //# sourceMappingURL=SymbolRegistry.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"SymbolRegistry.d.ts","sourceRoot":"","sources":["../../src/symbols/SymbolRegistry.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AASlD;;;;GAIG;AACH,qBAAa,cAAc;IACzB,OAAO,CAAC,QAAQ,CAAoC;IAEpD;;;;;;OAMG;IACH,QAAQ,CAAC,CAAC,SAAS,UAAU,EAC3B,QAAQ,EAAE,MAAM,EAChB,WAAW,EAAE,KAAK,OAAO,EAAE,GAAG,KAAK,CAAC,EACpC,OAAO,EAAE,CAAC,SAAS;QAAE,WAAW,EAAE,CAAC,OAAO,EAAE,MAAM,CAAC,KAAK,GAAG,CAAA;KAAE,GAAG,CAAC,GAAG,GAAG,GACtE,IAAI;IAOP,2DAA2D;IAC3D,MAAM,CAAC,QAAQ,EAAE,MAAM,GAAG,UAAU;IAYpC,GAAG,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO;IAI9B,IAAI,SAAS,IAAI,MAAM,EAAE,CAExB;IAED,IAAI,IAAI,IAAI,MAAM,CAEjB;CACF"}
@@ -0,0 +1,45 @@
1
+ import { Ticker } from 'pixi.js';
2
+ type TickerCallback = (ticker: Ticker) => void;
3
+ /**
4
+ * Minimal drop-in replacement for `PIXI.Ticker` for tests.
5
+ *
6
+ * Exposes the exact surface `pixi-reels` uses internally (`add`, `remove`,
7
+ * `deltaMS`) plus a manual `tick(deltaMs)` method so tests can advance time
8
+ * deterministically without depending on requestAnimationFrame.
9
+ *
10
+ * ```ts
11
+ * const ticker = new FakeTicker();
12
+ * const reelSet = new ReelSetBuilder()
13
+ * .ticker(ticker as unknown as PIXI.Ticker)
14
+ * ...
15
+ * .build();
16
+ *
17
+ * ticker.tick(16); // advance one frame
18
+ * ticker.tickFor(500); // advance 500ms in 16ms frames
19
+ * ```
20
+ */
21
+ export declare class FakeTicker {
22
+ deltaMS: number;
23
+ deltaTime: number;
24
+ elapsedMS: number;
25
+ lastTime: number;
26
+ speed: number;
27
+ started: boolean;
28
+ FPS: number;
29
+ minFPS: number;
30
+ maxFPS: number;
31
+ private _callbacks;
32
+ add(fn: TickerCallback): this;
33
+ addOnce(fn: TickerCallback): this;
34
+ remove(fn: TickerCallback): this;
35
+ start(): this;
36
+ stop(): this;
37
+ destroy(): void;
38
+ /** Manually advance time by `deltaMs` milliseconds and fire all listeners. */
39
+ tick(deltaMs?: number): void;
40
+ /** Advance time by `totalMs`, chopped into `stepMs` frames. */
41
+ tickFor(totalMs: number, stepMs?: number): void;
42
+ get listenerCount(): number;
43
+ }
44
+ export {};
45
+ //# sourceMappingURL=FakeTicker.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"FakeTicker.d.ts","sourceRoot":"","sources":["../../src/testing/FakeTicker.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,SAAS,CAAC;AAEtC,KAAK,cAAc,GAAG,CAAC,MAAM,EAAE,MAAM,KAAK,IAAI,CAAC;AAE/C;;;;;;;;;;;;;;;;;GAiBG;AACH,qBAAa,UAAU;IACd,OAAO,SAAM;IACb,SAAS,SAAK;IACd,SAAS,SAAK;IACd,QAAQ,SAAK;IACb,KAAK,SAAK;IACV,OAAO,UAAS;IAChB,GAAG,SAAM;IACT,MAAM,SAAM;IACZ,MAAM,SAAK;IAElB,OAAO,CAAC,UAAU,CAAwB;IAE1C,GAAG,CAAC,EAAE,EAAE,cAAc,GAAG,IAAI;IAK7B,OAAO,CAAC,EAAE,EAAE,cAAc,GAAG,IAAI;IAQjC,MAAM,CAAC,EAAE,EAAE,cAAc,GAAG,IAAI;IAMhC,KAAK,IAAI,IAAI;IAKb,IAAI,IAAI,IAAI;IAKZ,OAAO,IAAI,IAAI;IAKf,8EAA8E;IAC9E,IAAI,CAAC,OAAO,SAAK,GAAG,IAAI;IAWxB,+DAA+D;IAC/D,OAAO,CAAC,OAAO,EAAE,MAAM,EAAE,MAAM,SAAK,GAAG,IAAI;IAS3C,IAAI,aAAa,IAAI,MAAM,CAE1B;CACF"}
@@ -0,0 +1,28 @@
1
+ import { ReelSymbol } from '../symbols/ReelSymbol.js';
2
+ /**
3
+ * A `ReelSymbol` implementation that does no rendering at all.
4
+ *
5
+ * It creates a `Container` for its `view` (so the Reel's child-tree logic works)
6
+ * but never paints anything. This lets tests build a real `ReelSet` without a
7
+ * renderer, textures, or assets.
8
+ *
9
+ * ```ts
10
+ * builder.symbols((r) => {
11
+ * for (const id of ['cherry', 'lemon', 'seven']) {
12
+ * r.register(id, HeadlessSymbol, {});
13
+ * }
14
+ * });
15
+ * ```
16
+ */
17
+ export declare class HeadlessSymbol extends ReelSymbol {
18
+ private _width;
19
+ private _height;
20
+ protected onActivate(_symbolId: string): void;
21
+ protected onDeactivate(): void;
22
+ playWin(): Promise<void>;
23
+ stopAnimation(): void;
24
+ resize(width: number, height: number): void;
25
+ get width(): number;
26
+ get height(): number;
27
+ }
28
+ //# sourceMappingURL=HeadlessSymbol.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"HeadlessSymbol.d.ts","sourceRoot":"","sources":["../../src/testing/HeadlessSymbol.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,0BAA0B,CAAC;AAEtD;;;;;;;;;;;;;;GAcG;AACH,qBAAa,cAAe,SAAQ,UAAU;IAC5C,OAAO,CAAC,MAAM,CAAK;IACnB,OAAO,CAAC,OAAO,CAAK;IAEpB,SAAS,CAAC,UAAU,CAAC,SAAS,EAAE,MAAM,GAAG,IAAI;IAI7C,SAAS,CAAC,YAAY,IAAI,IAAI;IAIxB,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC;IAI9B,aAAa,IAAI,IAAI;IAIrB,MAAM,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,IAAI;IAK3C,IAAI,KAAK,IAAI,MAAM,CAElB;IAED,IAAI,MAAM,IAAI,MAAM,CAEnB;CACF"}
@@ -0,0 +1,5 @@
1
+ export { FakeTicker } from './FakeTicker.js';
2
+ export { HeadlessSymbol } from './HeadlessSymbol.js';
3
+ export { createTestReelSet, spinAndLand, captureEvents, expectGrid, countSymbol, } from './testHarness.js';
4
+ export type { TestReelSetOptions, TestReelSetHandle } from './testHarness.js';
5
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/testing/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAC7C,OAAO,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AACrD,OAAO,EACL,iBAAiB,EACjB,WAAW,EACX,aAAa,EACb,UAAU,EACV,WAAW,GACZ,MAAM,kBAAkB,CAAC;AAC1B,YAAY,EAAE,kBAAkB,EAAE,iBAAiB,EAAE,MAAM,kBAAkB,CAAC"}
@@ -0,0 +1,70 @@
1
+ import { ReelSet } from '../core/ReelSet.js';
2
+ import { SpinResult } from '../events/ReelEvents.js';
3
+ import { FakeTicker } from './FakeTicker.js';
4
+ export interface TestReelSetOptions {
5
+ reels?: number;
6
+ visibleRows?: number;
7
+ symbolIds?: string[];
8
+ weights?: Record<string, number>;
9
+ symbolSize?: {
10
+ width: number;
11
+ height: number;
12
+ };
13
+ }
14
+ export interface TestReelSetHandle {
15
+ reelSet: ReelSet;
16
+ ticker: FakeTicker;
17
+ /** Advance the ticker by `ms` milliseconds. */
18
+ advance(ms: number, stepMs?: number): void;
19
+ /** Run one full spin that lands on `grid`. Uses `skip()` for deterministic synchronous completion. */
20
+ spinAndLand(grid: string[][]): Promise<SpinResult>;
21
+ /** Destroy the reel set. */
22
+ destroy(): void;
23
+ }
24
+ /**
25
+ * Build a headless `ReelSet` wired to a `FakeTicker`. Ideal for mechanic tests.
26
+ *
27
+ * The returned `ReelSet` uses `HeadlessSymbol` for every registered symbol,
28
+ * so no textures, renderer, or DOM are required.
29
+ *
30
+ * ```ts
31
+ * const { reelSet, spinAndLand } = createTestReelSet({
32
+ * reels: 5, visibleRows: 3,
33
+ * symbolIds: ['cherry', 'seven', 'wild'],
34
+ * });
35
+ *
36
+ * await spinAndLand([
37
+ * ['cherry','cherry','cherry'],
38
+ * ['seven','seven','seven'],
39
+ * ['wild','wild','wild'],
40
+ * ['cherry','cherry','cherry'],
41
+ * ['seven','seven','seven'],
42
+ * ]);
43
+ * ```
44
+ */
45
+ export declare function createTestReelSet(opts?: TestReelSetOptions): TestReelSetHandle;
46
+ /**
47
+ * Deterministically run a spin to a target grid.
48
+ *
49
+ * Internally: `spin() → setResult(grid) → skip()`. The `skip()` path bypasses
50
+ * all async phases and directly places the symbols, so the returned promise
51
+ * resolves on a microtask.
52
+ */
53
+ export declare function spinAndLand(reelSet: ReelSet, grid: string[][]): Promise<SpinResult>;
54
+ /** Record every occurrence of the given events in order for assertion. */
55
+ export declare function captureEvents(reelSet: ReelSet, names: Array<keyof import('../events/ReelEvents.js').ReelSetEvents>): Array<{
56
+ event: string;
57
+ args: unknown[];
58
+ }>;
59
+ /**
60
+ * Assert that the current visible grid equals `expected`.
61
+ *
62
+ * Throws a readable error showing the full current grid on mismatch.
63
+ */
64
+ export declare function expectGrid(reelSet: ReelSet, expected: string[][]): void;
65
+ /**
66
+ * Count how many times a given symbol appears in the visible grid.
67
+ * Handy for scatter/wild-count assertions.
68
+ */
69
+ export declare function countSymbol(reelSet: ReelSet, symbolId: string): number;
70
+ //# sourceMappingURL=testHarness.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"testHarness.d.ts","sourceRoot":"","sources":["../../src/testing/testHarness.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,oBAAoB,CAAC;AAClD,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,yBAAyB,CAAC;AAE1D,OAAO,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAG7C,MAAM,WAAW,kBAAkB;IACjC,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,SAAS,CAAC,EAAE,MAAM,EAAE,CAAC;IACrB,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACjC,UAAU,CAAC,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,CAAC;CAChD;AAED,MAAM,WAAW,iBAAiB;IAChC,OAAO,EAAE,OAAO,CAAC;IACjB,MAAM,EAAE,UAAU,CAAC;IACnB,+CAA+C;IAC/C,OAAO,CAAC,EAAE,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3C,sGAAsG;IACtG,WAAW,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,GAAG,OAAO,CAAC,UAAU,CAAC,CAAC;IACnD,4BAA4B;IAC5B,OAAO,IAAI,IAAI,CAAC;CACjB;AAED;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,wBAAgB,iBAAiB,CAAC,IAAI,GAAE,kBAAuB,GAAG,iBAAiB,CAwClF;AAED;;;;;;GAMG;AACH,wBAAsB,WAAW,CAAC,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,EAAE,GAAG,OAAO,CAAC,UAAU,CAAC,CAKzF;AAED,0EAA0E;AAC1E,wBAAgB,aAAa,CAC3B,OAAO,EAAE,OAAO,EAChB,KAAK,EAAE,KAAK,CAAC,MAAM,OAAO,yBAAyB,EAAE,aAAa,CAAC,GAClE,KAAK,CAAC;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,OAAO,EAAE,CAAA;CAAE,CAAC,CAQ3C;AAED;;;;GAIG;AACH,wBAAgB,UAAU,CAAC,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,EAAE,EAAE,GAAG,IAAI,CAyBvE;AAED;;;GAGG;AACH,wBAAgB,WAAW,CAAC,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,GAAG,MAAM,CAMtE"}
@@ -0,0 +1,10 @@
1
+ /**
2
+ * Contract for objects that allocate resources and need cleanup.
3
+ * Every class that subscribes to tickers, creates display objects, or holds references
4
+ * must implement this to prevent memory leaks.
5
+ */
6
+ export interface Disposable {
7
+ destroy(): void;
8
+ readonly isDestroyed: boolean;
9
+ }
10
+ //# sourceMappingURL=Disposable.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Disposable.d.ts","sourceRoot":"","sources":["../../src/utils/Disposable.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,MAAM,WAAW,UAAU;IACzB,OAAO,IAAI,IAAI,CAAC;IAChB,QAAQ,CAAC,WAAW,EAAE,OAAO,CAAC;CAC/B"}
@@ -0,0 +1,30 @@
1
+ import { Ticker } from 'pixi.js';
2
+ import { Disposable } from './Disposable.js';
3
+ type TickerCallback = (ticker: Ticker) => void;
4
+ /**
5
+ * Safe wrapper around PixiJS Ticker subscriptions.
6
+ *
7
+ * Solves the #1 memory leak in the original library: dangling ticker callbacks.
8
+ * When `destroy()` is called, ALL registered callbacks are automatically
9
+ * removed from the ticker.
10
+ *
11
+ * Usage:
12
+ * ```ts
13
+ * const ref = new TickerRef(app.ticker);
14
+ * ref.add((ticker) => reel.update(ticker));
15
+ * // Later:
16
+ * ref.destroy(); // all callbacks removed
17
+ * ```
18
+ */
19
+ export declare class TickerRef implements Disposable {
20
+ private _ticker;
21
+ private _callbacks;
22
+ private _isDestroyed;
23
+ constructor(_ticker: Ticker);
24
+ get isDestroyed(): boolean;
25
+ add(fn: TickerCallback): void;
26
+ remove(fn: TickerCallback): void;
27
+ destroy(): void;
28
+ }
29
+ export {};
30
+ //# sourceMappingURL=TickerRef.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"TickerRef.d.ts","sourceRoot":"","sources":["../../src/utils/TickerRef.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,SAAS,CAAC;AACtC,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAElD,KAAK,cAAc,GAAG,CAAC,MAAM,EAAE,MAAM,KAAK,IAAI,CAAC;AAE/C;;;;;;;;;;;;;;GAcG;AACH,qBAAa,SAAU,YAAW,UAAU;IAI9B,OAAO,CAAC,OAAO;IAH3B,OAAO,CAAC,UAAU,CAAwB;IAC1C,OAAO,CAAC,YAAY,CAAS;gBAET,OAAO,EAAE,MAAM;IAEnC,IAAI,WAAW,IAAI,OAAO,CAEzB;IAED,GAAG,CAAC,EAAE,EAAE,cAAc,GAAG,IAAI;IAM7B,MAAM,CAAC,EAAE,EAAE,cAAc,GAAG,IAAI;IAQhC,OAAO,IAAI,IAAI;CAQhB"}
package/package.json ADDED
@@ -0,0 +1,86 @@
1
+ {
2
+ "name": "pixi-reels",
3
+ "version": "0.1.0",
4
+ "description": "A batteries-included slot machine reel engine for PixiJS v8 — fluent builder, typed events, spin phases, speed modes, win spotlight, and a headless testing harness.",
5
+ "type": "module",
6
+ "main": "./dist/index.cjs",
7
+ "module": "./dist/index.js",
8
+ "types": "./dist/index.d.ts",
9
+ "exports": {
10
+ ".": {
11
+ "import": "./dist/index.js",
12
+ "require": "./dist/index.cjs",
13
+ "types": "./dist/index.d.ts"
14
+ },
15
+ "./spine": {
16
+ "import": "./dist/spine.js",
17
+ "require": "./dist/spine.cjs",
18
+ "types": "./dist/spine.d.ts"
19
+ }
20
+ },
21
+ "files": [
22
+ "dist",
23
+ "README.md",
24
+ "LICENSE",
25
+ "CHANGELOG.md"
26
+ ],
27
+ "sideEffects": false,
28
+ "publishConfig": {
29
+ "access": "public",
30
+ "provenance": true
31
+ },
32
+ "peerDependencies": {
33
+ "@esotericsoftware/spine-pixi-v8": "^4.2.110",
34
+ "gsap": "^3.15.0",
35
+ "pixi.js": "^8.18.1"
36
+ },
37
+ "peerDependenciesMeta": {
38
+ "@esotericsoftware/spine-pixi-v8": {
39
+ "optional": true
40
+ }
41
+ },
42
+ "devDependencies": {
43
+ "@esotericsoftware/spine-pixi-v8": "^4.2.110",
44
+ "gsap": "^3.15.0",
45
+ "pixi.js": "^8.18.1",
46
+ "typescript": "^6.0.3",
47
+ "vite": "^8.0.9",
48
+ "vite-plugin-dts": "^4.5.4",
49
+ "vitest": "^4.1.4"
50
+ },
51
+ "license": "MIT",
52
+ "author": "schmooky",
53
+ "homepage": "https://pixi-reels.dev",
54
+ "bugs": {
55
+ "url": "https://github.com/schmooky/pixi-reels/issues"
56
+ },
57
+ "repository": {
58
+ "type": "git",
59
+ "url": "git+https://github.com/schmooky/pixi-reels.git",
60
+ "directory": "packages/pixi-reels"
61
+ },
62
+ "keywords": [
63
+ "pixi",
64
+ "pixijs",
65
+ "pixi.js",
66
+ "slot",
67
+ "reel",
68
+ "reels",
69
+ "casino",
70
+ "slot-machine",
71
+ "game",
72
+ "gamedev",
73
+ "html5-game",
74
+ "spine",
75
+ "animation",
76
+ "gsap",
77
+ "typescript"
78
+ ],
79
+ "scripts": {
80
+ "build": "vite build",
81
+ "test": "vitest run",
82
+ "test:watch": "vitest",
83
+ "typecheck": "tsc --noEmit",
84
+ "lint": "eslint src/"
85
+ }
86
+ }