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.
- package/LICENSE +28 -0
- package/README.md +167 -0
- package/dist/SpineSymbol-B40TevSr.cjs +2 -0
- package/dist/SpineSymbol-B40TevSr.cjs.map +1 -0
- package/dist/SpineSymbol-D2jhCzFW.js +82 -0
- package/dist/SpineSymbol-D2jhCzFW.js.map +1 -0
- package/dist/config/SpeedPresets.d.ts +49 -0
- package/dist/config/SpeedPresets.d.ts.map +1 -0
- package/dist/config/defaults.d.ts +12 -0
- package/dist/config/defaults.d.ts.map +1 -0
- package/dist/config/types.d.ts +93 -0
- package/dist/config/types.d.ts.map +1 -0
- package/dist/core/Reel.d.ts +106 -0
- package/dist/core/Reel.d.ts.map +1 -0
- package/dist/core/ReelMotion.d.ts +43 -0
- package/dist/core/ReelMotion.d.ts.map +1 -0
- package/dist/core/ReelSet.d.ts +101 -0
- package/dist/core/ReelSet.d.ts.map +1 -0
- package/dist/core/ReelSetBuilder.d.ts +102 -0
- package/dist/core/ReelSetBuilder.d.ts.map +1 -0
- package/dist/core/ReelViewport.d.ts +43 -0
- package/dist/core/ReelViewport.d.ts.map +1 -0
- package/dist/core/StopSequencer.d.ts +26 -0
- package/dist/core/StopSequencer.d.ts.map +1 -0
- package/dist/debug/debug.d.ts +65 -0
- package/dist/debug/debug.d.ts.map +1 -0
- package/dist/events/EventEmitter.d.ts +25 -0
- package/dist/events/EventEmitter.d.ts.map +1 -0
- package/dist/events/ReelEvents.d.ts +40 -0
- package/dist/events/ReelEvents.d.ts.map +1 -0
- package/dist/frame/FrameBuilder.d.ts +50 -0
- package/dist/frame/FrameBuilder.d.ts.map +1 -0
- package/dist/frame/OffsetCalculator.d.ts +19 -0
- package/dist/frame/OffsetCalculator.d.ts.map +1 -0
- package/dist/frame/RandomSymbolProvider.d.ts +27 -0
- package/dist/frame/RandomSymbolProvider.d.ts.map +1 -0
- package/dist/index.cjs +5 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.ts +51 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +1454 -0
- package/dist/index.js.map +1 -0
- package/dist/pool/ObjectPool.d.ts +36 -0
- package/dist/pool/ObjectPool.d.ts.map +1 -0
- package/dist/speed/SpeedManager.d.ts +38 -0
- package/dist/speed/SpeedManager.d.ts.map +1 -0
- package/dist/spin/SpinController.d.ts +71 -0
- package/dist/spin/SpinController.d.ts.map +1 -0
- package/dist/spin/modes/CascadeMode.d.ts +16 -0
- package/dist/spin/modes/CascadeMode.d.ts.map +1 -0
- package/dist/spin/modes/ImmediateMode.d.ts +10 -0
- package/dist/spin/modes/ImmediateMode.d.ts.map +1 -0
- package/dist/spin/modes/SpinningMode.d.ts +18 -0
- package/dist/spin/modes/SpinningMode.d.ts.map +1 -0
- package/dist/spin/modes/StandardMode.d.ts +10 -0
- package/dist/spin/modes/StandardMode.d.ts.map +1 -0
- package/dist/spin/phases/AnticipationPhase.d.ts +24 -0
- package/dist/spin/phases/AnticipationPhase.d.ts.map +1 -0
- package/dist/spin/phases/PhaseFactory.d.ts +21 -0
- package/dist/spin/phases/PhaseFactory.d.ts.map +1 -0
- package/dist/spin/phases/ReelPhase.d.ts +39 -0
- package/dist/spin/phases/ReelPhase.d.ts.map +1 -0
- package/dist/spin/phases/SpinPhase.d.ts +25 -0
- package/dist/spin/phases/SpinPhase.d.ts.map +1 -0
- package/dist/spin/phases/StartPhase.d.ts +26 -0
- package/dist/spin/phases/StartPhase.d.ts.map +1 -0
- package/dist/spin/phases/StopPhase.d.ts +37 -0
- package/dist/spin/phases/StopPhase.d.ts.map +1 -0
- package/dist/spine/SpineReelSymbol.d.ts +111 -0
- package/dist/spine/SpineReelSymbol.d.ts.map +1 -0
- package/dist/spine/index.d.ts +5 -0
- package/dist/spine/index.d.ts.map +1 -0
- package/dist/spine.cjs +2 -0
- package/dist/spine.cjs.map +1 -0
- package/dist/spine.js +123 -0
- package/dist/spine.js.map +1 -0
- package/dist/spotlight/SymbolSpotlight.d.ts +70 -0
- package/dist/spotlight/SymbolSpotlight.d.ts.map +1 -0
- package/dist/symbols/AnimatedSpriteSymbol.d.ts +30 -0
- package/dist/symbols/AnimatedSpriteSymbol.d.ts.map +1 -0
- package/dist/symbols/ReelSymbol.d.ts +84 -0
- package/dist/symbols/ReelSymbol.d.ts.map +1 -0
- package/dist/symbols/SpineSymbol.d.ts +34 -0
- package/dist/symbols/SpineSymbol.d.ts.map +1 -0
- package/dist/symbols/SpriteSymbol.d.ts +29 -0
- package/dist/symbols/SpriteSymbol.d.ts.map +1 -0
- package/dist/symbols/SymbolFactory.d.ts +20 -0
- package/dist/symbols/SymbolFactory.d.ts.map +1 -0
- package/dist/symbols/SymbolRegistry.d.ts +25 -0
- package/dist/symbols/SymbolRegistry.d.ts.map +1 -0
- package/dist/testing/FakeTicker.d.ts +45 -0
- package/dist/testing/FakeTicker.d.ts.map +1 -0
- package/dist/testing/HeadlessSymbol.d.ts +28 -0
- package/dist/testing/HeadlessSymbol.d.ts.map +1 -0
- package/dist/testing/index.d.ts +5 -0
- package/dist/testing/index.d.ts.map +1 -0
- package/dist/testing/testHarness.d.ts +70 -0
- package/dist/testing/testHarness.d.ts.map +1 -0
- package/dist/utils/Disposable.d.ts +10 -0
- package/dist/utils/Disposable.d.ts.map +1 -0
- package/dist/utils/TickerRef.d.ts +30 -0
- package/dist/utils/TickerRef.d.ts.map +1 -0
- package/package.json +86 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","names":[],"sources":["../src/events/EventEmitter.ts","../src/core/ReelMotion.ts","../src/core/StopSequencer.ts","../src/spin/modes/StandardMode.ts","../src/core/Reel.ts","../src/core/ReelViewport.ts","../src/spin/phases/ReelPhase.ts","../src/spin/phases/StartPhase.ts","../src/spin/phases/SpinPhase.ts","../src/spin/phases/StopPhase.ts","../src/spin/phases/AnticipationPhase.ts","../src/spin/phases/PhaseFactory.ts","../src/utils/TickerRef.ts","../src/spin/SpinController.ts","../src/speed/SpeedManager.ts","../src/spotlight/SymbolSpotlight.ts","../src/core/ReelSet.ts","../src/config/defaults.ts","../src/config/SpeedPresets.ts","../src/symbols/SymbolRegistry.ts","../src/pool/ObjectPool.ts","../src/symbols/SymbolFactory.ts","../src/frame/RandomSymbolProvider.ts","../src/frame/FrameBuilder.ts","../src/frame/OffsetCalculator.ts","../src/core/ReelSetBuilder.ts","../src/symbols/SpriteSymbol.ts","../src/symbols/AnimatedSpriteSymbol.ts","../src/spin/modes/CascadeMode.ts","../src/spin/modes/ImmediateMode.ts","../src/debug/debug.ts","../src/testing/FakeTicker.ts","../src/testing/HeadlessSymbol.ts","../src/testing/testHarness.ts"],"sourcesContent":["type Listener = (...args: any[]) => void;\n\ninterface ListenerEntry {\n fn: Listener;\n context: unknown;\n once: boolean;\n}\n\n/**\n * Lightweight typed event emitter with zero dependencies.\n *\n * Usage:\n * ```ts\n * interface MyEvents {\n * 'foo': [x: number, y: string];\n * 'bar': [];\n * }\n * const emitter = new EventEmitter<MyEvents>();\n * emitter.on('foo', (x, y) => console.log(x, y));\n * emitter.emit('foo', 42, 'hello');\n * ```\n */\nexport class EventEmitter<TEvents extends Record<string, unknown[]>> {\n private _listeners = new Map<keyof TEvents, ListenerEntry[]>();\n\n on<K extends keyof TEvents>(\n event: K,\n fn: (...args: TEvents[K]) => void,\n context?: unknown,\n ): this {\n return this._add(event, fn as Listener, context, false);\n }\n\n once<K extends keyof TEvents>(\n event: K,\n fn: (...args: TEvents[K]) => void,\n context?: unknown,\n ): this {\n return this._add(event, fn as Listener, context, true);\n }\n\n off<K extends keyof TEvents>(\n event: K,\n fn?: (...args: TEvents[K]) => void,\n context?: unknown,\n ): this {\n const entries = this._listeners.get(event);\n if (!entries) return this;\n\n if (!fn) {\n this._listeners.delete(event);\n return this;\n }\n\n const filtered = entries.filter(\n (e) => e.fn !== fn || (context !== undefined && e.context !== context),\n );\n if (filtered.length === 0) {\n this._listeners.delete(event);\n } else {\n this._listeners.set(event, filtered);\n }\n return this;\n }\n\n emit<K extends keyof TEvents>(event: K, ...args: TEvents[K]): boolean {\n const entries = this._listeners.get(event);\n if (!entries || entries.length === 0) return false;\n\n // Snapshot to allow mutations during iteration\n const snapshot = entries.slice();\n for (const entry of snapshot) {\n if (entry.once) {\n this.off(event, entry.fn as any, entry.context);\n }\n entry.fn.apply(entry.context, args);\n }\n return true;\n }\n\n removeAllListeners(event?: keyof TEvents): this {\n if (event !== undefined) {\n this._listeners.delete(event);\n } else {\n this._listeners.clear();\n }\n return this;\n }\n\n listenerCount(event: keyof TEvents): number {\n return this._listeners.get(event)?.length ?? 0;\n }\n\n private _add(\n event: keyof TEvents,\n fn: Listener,\n context: unknown,\n once: boolean,\n ): this {\n let entries = this._listeners.get(event);\n if (!entries) {\n entries = [];\n this._listeners.set(event, entries);\n }\n entries.push({ fn, context, once });\n return this;\n }\n}\n","import type { ReelSymbol } from '../symbols/ReelSymbol.js';\n\n/**\n * The physics of one reel — move symbols down, wrap them around.\n *\n * Every frame, `ReelMotion.update(delta)` adds `delta` to each symbol's\n * Y coordinate. A symbol whose position falls off the bottom wraps back\n * to the top (and vice versa — reels can run upward). Each wrap fires\n * the `_onSymbolWrapped` callback so the owning `Reel` can ask the\n * `FrameBuilder` for the next identity to paint on it.\n *\n * Maintains the invariant that `_symbols[0]` is always the visually\n * topmost symbol and `_symbols[N-1]` is always the bottommost. On each\n * wrap, the wrapping symbol is moved to the front (or back) of the array\n * so the ordering stays consistent with the grid. `snapToGrid` and the\n * visible window selection rely on this.\n */\nexport class ReelMotion {\n private _symbolHeight: number;\n private _symbolGapY: number;\n private _slotHeight: number;\n private _minY: number;\n private _maxY: number;\n\n constructor(\n private _symbols: ReelSymbol[],\n symbolHeight: number,\n symbolGapY: number,\n private _bufferAbove: number,\n visibleRows: number,\n private _onSymbolWrapped: (symbol: ReelSymbol, arrayIndex: number, direction: 'up' | 'down') => void,\n ) {\n this._symbolHeight = symbolHeight;\n this._symbolGapY = symbolGapY;\n this._slotHeight = symbolHeight + symbolGapY;\n this._maxY = (visibleRows + 1) * this._slotHeight;\n this._minY = -(this._bufferAbove + 1) * this._slotHeight;\n }\n\n /**\n * Move all symbols by deltaY pixels (positive = downward).\n * At most one wrap per call (deltaY is capped at half a symbol by the\n * spinning mode, so a single symbol can cross the boundary per tick).\n */\n displace(deltaY: number): void {\n if (deltaY === 0) return;\n for (const symbol of this._symbols) {\n symbol.view.y += deltaY;\n }\n if (deltaY > 0) {\n this._wrapBottomToTop();\n } else {\n this._wrapTopToBottom();\n }\n }\n\n /** Snap all symbols to their correct grid positions (array index = visual row). */\n snapToGrid(): void {\n for (let i = 0; i < this._symbols.length; i++) {\n const targetY = (i - this._bufferAbove) * this._slotHeight;\n this._symbols[i].view.y = targetY;\n }\n }\n\n /** Position all symbols above the visible area (for cascade mode start). */\n setToTopPosition(): void {\n for (let i = 0; i < this._symbols.length; i++) {\n this._symbols[i].view.y = this._minY - (this._symbols.length - i) * this._slotHeight;\n }\n }\n\n /** Get the correct Y position for a symbol at a given row. */\n getRowY(row: number): number {\n return (row - this._bufferAbove) * this._slotHeight;\n }\n\n get slotHeight(): number {\n return this._slotHeight;\n }\n\n private _wrapBottomToTop(): void {\n const lastIdx = this._symbols.length - 1;\n const lastSymbol = this._symbols[lastIdx];\n if (lastSymbol.view.y < this._maxY) return;\n const firstSymbol = this._symbols[0];\n lastSymbol.view.y = firstSymbol.view.y - this._slotHeight;\n // Maintain array order: last symbol becomes the new first.\n this._symbols.pop();\n this._symbols.unshift(lastSymbol);\n this._onSymbolWrapped(lastSymbol, 0, 'up');\n }\n\n private _wrapTopToBottom(): void {\n const firstSymbol = this._symbols[0];\n if (firstSymbol.view.y >= this._minY) return;\n const lastSymbol = this._symbols[this._symbols.length - 1];\n firstSymbol.view.y = lastSymbol.view.y + this._slotHeight;\n // Maintain array order: first symbol becomes the new last.\n this._symbols.shift();\n this._symbols.push(firstSymbol);\n this._onSymbolWrapped(firstSymbol, this._symbols.length - 1, 'down');\n }\n}\n","/**\n * The \"what do I land on\" queue for one reel.\n *\n * When a reel enters its stop phase, the `SpinController` loads the\n * target frame (the exact list of symbol ids that should appear on\n * screen, top-to-bottom, including the off-screen buffers). As the reel\n * keeps scrolling downward during deceleration, every `ReelMotion` wrap\n * event asks this sequencer for the next symbol — and it hands them back\n * from the END of the frame first, because new symbols arrive at the\n * top of a reel scrolling downward.\n *\n * After the last symbol is consumed the reel lands, and what you see on\n * screen matches the loaded frame exactly.\n */\nexport class StopSequencer {\n private _frame: string[] = [];\n private _remaining: number = 0;\n\n /** Load a target frame in top-to-bottom order. */\n setFrame(frame: string[]): void {\n this._frame = [...frame];\n this._remaining = this._frame.length;\n }\n\n /** Deliver the next symbol (consumed from the end of the frame). */\n next(): string {\n if (this._remaining > 0) {\n this._remaining--;\n return this._frame[this._remaining];\n }\n return this._frame[0] ?? '';\n }\n\n get hasRemaining(): boolean {\n return this._remaining > 0;\n }\n\n get remaining(): number {\n return this._remaining;\n }\n\n reset(): void {\n this._frame = [];\n this._remaining = 0;\n }\n}\n","import type { SpinningMode } from './SpinningMode.js';\n\n/**\n * Standard top-to-bottom reel spinning.\n * Symbols scroll downward at constant speed, wrapping around.\n */\nexport class StandardMode implements SpinningMode {\n readonly name = 'standard';\n\n computeDeltaY(symbolHeight: number, speed: number, deltaMs: number): number {\n const raw = (symbolHeight * speed * deltaMs) / 1000;\n // Cap at half symbol height to prevent skipping\n return Math.min(raw, symbolHeight / 2);\n }\n}\n","import { Container } from 'pixi.js';\nimport type { Disposable } from '../utils/Disposable.js';\nimport type { ReelSymbol } from '../symbols/ReelSymbol.js';\nimport type { SymbolFactory } from '../symbols/SymbolFactory.js';\nimport type { SymbolData } from '../config/types.js';\nimport { ReelMotion } from './ReelMotion.js';\nimport { StopSequencer } from './StopSequencer.js';\nimport { EventEmitter } from '../events/EventEmitter.js';\nimport type { ReelEvents } from '../events/ReelEvents.js';\nimport type { RandomSymbolProvider } from '../frame/RandomSymbolProvider.js';\nimport type { ReelViewport } from './ReelViewport.js';\nimport type { SpinningMode } from '../spin/modes/SpinningMode.js';\nimport { StandardMode } from '../spin/modes/StandardMode.js';\n\nexport interface ReelConfig {\n reelIndex: number;\n visibleRows: number;\n bufferAbove: number;\n bufferBelow: number;\n symbolWidth: number;\n symbolHeight: number;\n symbolGapX: number;\n symbolGapY: number;\n symbolsData: Record<string, SymbolData>;\n initialSymbols: string[];\n}\n\n/**\n * One vertical column of a slot board.\n *\n * A `Reel` owns:\n * - the `ReelSymbol[]` currently on screen (a small buffer above the\n * visible rows + the visible rows + a small buffer below — so symbols\n * can fade in from off-screen cleanly)\n * - the `ReelMotion` that adds a Y delta each tick and wraps symbols\n * that scroll off the ends\n * - a `StopSequencer` — the queue of target symbols the reel still has\n * to land on before it can stop\n *\n * You generally do not touch a `Reel` directly. Drive the `ReelSet` and\n * let it fan out. Reels are exposed on `reelSet.reels` so you can read\n * the current grid (`reel.getSymbolAt(row)`) or listen to per-reel\n * events (`phase:enter`, `landed`, `symbol:created`, ...).\n */\nexport class Reel implements Disposable {\n public readonly container: Container;\n public readonly events: EventEmitter<ReelEvents>;\n public readonly reelIndex: number;\n\n /** Current symbols in order (top buffer → visible → bottom buffer). */\n public symbols: ReelSymbol[];\n\n /** Current spin speed (pixels per frame). Set by phases. */\n public speed: number = 0;\n\n /** Current spinning mode. */\n public spinningMode: SpinningMode = new StandardMode();\n\n public readonly motion: ReelMotion;\n public readonly stopSequencer: StopSequencer;\n\n private _symbolFactory: SymbolFactory;\n private _randomProvider: RandomSymbolProvider;\n private _viewport: ReelViewport;\n private _symbolsData: Record<string, SymbolData>;\n private _visibleRows: number;\n private _bufferAbove: number;\n private _symbolWidth: number;\n private _symbolHeight: number;\n private _isDestroyed = false;\n private _isStopping = false;\n\n constructor(\n config: ReelConfig,\n symbolFactory: SymbolFactory,\n randomProvider: RandomSymbolProvider,\n viewport: ReelViewport,\n ) {\n this.reelIndex = config.reelIndex;\n this._symbolFactory = symbolFactory;\n this._randomProvider = randomProvider;\n this._viewport = viewport;\n this._symbolsData = config.symbolsData;\n this._visibleRows = config.visibleRows;\n this._bufferAbove = config.bufferAbove;\n this._symbolWidth = config.symbolWidth;\n this._symbolHeight = config.symbolHeight;\n this.events = new EventEmitter<ReelEvents>();\n this.stopSequencer = new StopSequencer();\n\n // Create container positioned at the reel's X column. Sortable so that\n // per-symbol zIndex (set from symbolData.zIndex + visual row) controls\n // render order — bottom-row symbols render in front, and flagged \"big\"\n // symbols like wild/bonus can override to render above neighbors.\n this.container = new Container();\n this.container.sortableChildren = true;\n this.container.x = config.reelIndex * (config.symbolWidth + config.symbolGapX);\n\n // Create initial symbols\n this.symbols = config.initialSymbols.map((symbolId, row) => {\n const symbol = symbolFactory.acquire(symbolId);\n symbol.resize(config.symbolWidth, config.symbolHeight);\n return symbol;\n });\n\n // Create motion handler\n this.motion = new ReelMotion(\n this.symbols,\n config.symbolHeight,\n config.symbolGapY,\n config.bufferAbove,\n config.visibleRows,\n (symbol, row, direction) => this._onSymbolWrapped(symbol, row, direction),\n );\n\n // Position symbols on grid and add to containers\n this._setupSymbolPositions(config);\n }\n\n get isDestroyed(): boolean {\n return this._isDestroyed;\n }\n\n get isStopping(): boolean {\n return this._isStopping;\n }\n\n set isStopping(value: boolean) {\n this._isStopping = value;\n }\n\n get bufferAbove(): number {\n return this._bufferAbove;\n }\n\n get bufferBelow(): number {\n return this.symbols.length - this._bufferAbove - this._visibleRows;\n }\n\n get visibleRows(): number {\n return this._visibleRows;\n }\n\n /** Update reel for one frame. Called by SpinController via ticker. */\n update(deltaMs: number): void {\n if (this.speed === 0) return;\n\n const deltaY = this.spinningMode.computeDeltaY(\n this.motion.slotHeight,\n this.speed,\n deltaMs,\n );\n\n if (deltaY !== 0) {\n this.motion.displace(deltaY);\n }\n }\n\n /** Set the target frame for stopping. */\n setStopFrame(frame: string[]): void {\n this.stopSequencer.setFrame(frame);\n }\n\n /** Get visible symbol IDs (top to bottom, excluding buffers). */\n getVisibleSymbols(): string[] {\n const result: string[] = [];\n for (let i = this._bufferAbove; i < this._bufferAbove + this._visibleRows; i++) {\n result.push(this.symbols[i].symbolId);\n }\n return result;\n }\n\n /** Get symbol at a visible row (0-indexed from top visible). */\n getSymbolAt(visibleRow: number): ReelSymbol {\n return this.symbols[this._bufferAbove + visibleRow];\n }\n\n /** Notify all visible symbols that the reel has started spinning. */\n notifySpinStart(): void {\n for (let i = this._bufferAbove; i < this._bufferAbove + this._visibleRows; i++) {\n this.symbols[i].onReelSpinStart();\n }\n }\n\n /** Notify all visible symbols that the reel is about to stop (just before bounce). */\n notifySpinEnd(): void {\n for (let i = this._bufferAbove; i < this._bufferAbove + this._visibleRows; i++) {\n this.symbols[i].onReelSpinEnd();\n }\n }\n\n /** Notify all visible symbols that the reel has landed on its target. */\n notifyLanded(): void {\n for (let i = this._bufferAbove; i < this._bufferAbove + this._visibleRows; i++) {\n this.symbols[i].onReelLanded();\n }\n }\n\n /** Snap all symbols to grid. */\n snapToGrid(): void {\n this.motion.snapToGrid();\n this.refreshZIndex();\n }\n\n /** Place symbols immediately at target positions (for skip/turbo). */\n placeSymbols(symbolIds: string[]): void {\n const totalSlots = this.symbols.length;\n for (let i = 0; i < totalSlots; i++) {\n const targetId =\n i < this._bufferAbove\n ? this._randomProvider.next(true)\n : i < this._bufferAbove + symbolIds.length\n ? symbolIds[i - this._bufferAbove]\n : this._randomProvider.next(true);\n\n this._replaceSymbol(i, targetId);\n }\n this.motion.snapToGrid();\n this.refreshZIndex();\n }\n\n /**\n * Recompute `zIndex` for every symbol in the reel.\n *\n * Formula: `symbolData.zIndex ?? 0` (scaled by 100 to leave room for row\n * ordering), plus the symbol's current array index — so bottom-row symbols\n * render in front of top-row symbols and any symbol with a higher\n * configured base zIndex (e.g. wild, bonus) renders above its neighbors.\n *\n * Called automatically after wraps, snaps, and direct placement. Call it\n * manually after mutating `symbolsData.zIndex` at runtime.\n */\n refreshZIndex(): void {\n for (let i = 0; i < this.symbols.length; i++) {\n const symbol = this.symbols[i];\n const base = this._symbolsData[symbol.symbolId]?.zIndex ?? 0;\n symbol.view.zIndex = base * 100 + i;\n }\n }\n\n destroy(): void {\n if (this._isDestroyed) return;\n for (const symbol of this.symbols) {\n this._symbolFactory.release(symbol);\n }\n this.symbols = [];\n this.events.removeAllListeners();\n this.container.destroy({ children: true });\n this._isDestroyed = true;\n this.events.emit('destroyed');\n }\n\n private _setupSymbolPositions(config: ReelConfig): void {\n for (let i = 0; i < this.symbols.length; i++) {\n const symbol = this.symbols[i];\n const y = (i - config.bufferAbove) * (config.symbolHeight + config.symbolGapY);\n symbol.view.y = y;\n symbol.view.x = 0;\n\n // All symbols go into the reel's own container\n this.container.addChild(symbol.view);\n }\n // Add the reel container to the viewport's masked area\n this._viewport.maskedContainer.addChild(this.container);\n }\n\n private _onSymbolWrapped(symbol: ReelSymbol, row: number, direction: 'up' | 'down'): void {\n let newSymbolId: string;\n if (this._isStopping && this.stopSequencer.hasRemaining) {\n newSymbolId = this.stopSequencer.next();\n } else {\n newSymbolId = this._randomProvider.next();\n }\n\n this._replaceSymbol(this.symbols.indexOf(symbol), newSymbolId);\n // Array was rearranged by ReelMotion (pop+unshift or shift+push), so the\n // array index of every remaining symbol changed — refresh all zIndexes.\n this.refreshZIndex();\n }\n\n private _replaceSymbol(index: number, newSymbolId: string): void {\n const oldSymbol = this.symbols[index];\n\n // Even if same symbolId, always reset visual state (alpha, scale, zIndex)\n if (oldSymbol.symbolId === newSymbolId) {\n oldSymbol.view.alpha = 1;\n oldSymbol.view.scale.set(1, 1);\n oldSymbol.view.zIndex = 0;\n return;\n }\n\n const parent = oldSymbol.view.parent;\n const y = oldSymbol.view.y;\n\n this._symbolFactory.release(oldSymbol);\n const newSymbol = this._symbolFactory.acquire(newSymbolId);\n newSymbol.resize(this._symbolWidth, this._symbolHeight);\n newSymbol.view.y = y;\n newSymbol.view.x = 0;\n newSymbol.view.alpha = 1;\n newSymbol.view.scale.set(1, 1);\n newSymbol.view.zIndex = 0;\n\n if (parent) {\n parent.addChild(newSymbol.view);\n }\n\n this.symbols[index] = newSymbol;\n this.events.emit('symbol:created', newSymbolId, index);\n }\n}\n","import { Container, Graphics } from 'pixi.js';\nimport type { Disposable } from '../utils/Disposable.js';\n\n/**\n * The clipping window + layering tricks for a reel set.\n *\n * The viewport is the \"looking-glass\" of the slot: a rectangle the size\n * of the visible grid with a PixiJS mask so symbols scrolling above or\n * below the visible rows are hidden. It also provides three stacking\n * layers so win animations can break out of the mask:\n *\n * - `maskedContainer` — the normal place for reels. Clipped to the\n * visible area so buffer rows never leak.\n * - `unmaskedContainer` — rendered on top of the mask. Use for a symbol\n * whose celebration animation expands beyond its cell (a big expanding\n * wild, a splash frame).\n * - `spotlightContainer` — above everything else. Win spotlight lifts\n * winning symbols here temporarily so dim overlay + bounce don't clip.\n *\n * `dimOverlay` is a semi-transparent rectangle the spotlight fades in\n * behind the promoted winners to visually push the losers into the\n * background.\n */\nexport class ReelViewport extends Container implements Disposable {\n public readonly maskedContainer: Container;\n public readonly unmaskedContainer: Container;\n public readonly spotlightContainer: Container;\n public readonly dimOverlay: Graphics;\n\n private _mask: Graphics;\n private _isDestroyed = false;\n\n constructor(\n width: number,\n height: number,\n position: { x: number; y: number } = { x: 0, y: 0 },\n ) {\n super();\n this.x = position.x;\n this.y = position.y;\n\n // Create mask graphic\n this._mask = new Graphics();\n this._mask.rect(0, 0, width, height).fill({ color: 0xffffff });\n\n // Masked container — main symbol area\n this.maskedContainer = new Container();\n this.maskedContainer.sortableChildren = true;\n this.maskedContainer.addChild(this._mask);\n this.maskedContainer.mask = this._mask;\n this.addChild(this.maskedContainer);\n\n // Unmasked container — for symbols with unmask flag\n this.unmaskedContainer = new Container();\n this.unmaskedContainer.sortableChildren = true;\n this.addChild(this.unmaskedContainer);\n\n // Dim overlay — for win animations\n this.dimOverlay = new Graphics();\n this.dimOverlay.rect(0, 0, width, height).fill({ color: 0x000000, alpha: 0.5 });\n this.dimOverlay.visible = false;\n this.addChild(this.dimOverlay);\n\n // Spotlight container — promoted symbols render above everything\n this.spotlightContainer = new Container();\n this.spotlightContainer.sortableChildren = true;\n this.addChild(this.spotlightContainer);\n }\n\n get isDestroyed(): boolean {\n return this._isDestroyed;\n }\n\n /** Show the dim overlay with given opacity. */\n showDim(alpha: number = 0.5): void {\n this.dimOverlay.alpha = alpha;\n this.dimOverlay.visible = true;\n }\n\n /** Hide the dim overlay. */\n hideDim(): void {\n this.dimOverlay.visible = false;\n }\n\n /** Update mask size (e.g., for Megaways variable rows). */\n updateMaskSize(width: number, height: number): void {\n this._mask.clear();\n this._mask.rect(0, 0, width, height).fill({ color: 0xffffff });\n }\n\n destroy(): void {\n if (this._isDestroyed) return;\n this._isDestroyed = true;\n super.destroy({ children: true });\n }\n}\n","import type { Reel } from '../../core/Reel.js';\nimport type { SpeedProfile } from '../../config/types.js';\n\n/**\n * Abstract base for reel spin phases.\n *\n * Each phase represents one stage of the spin lifecycle:\n * START → SPIN → ANTICIPATION → STOP.\n *\n * Phases are entered and exited by SpinController, and can be skipped\n * if marked as skippable and the user triggers skip/slam-stop.\n *\n * @typeParam TConfig - Phase-specific configuration type.\n */\nexport abstract class ReelPhase<TConfig = void> {\n abstract readonly name: string;\n abstract readonly skippable: boolean;\n\n protected _reel: Reel;\n protected _speed: SpeedProfile;\n protected _resolve: (() => void) | null = null;\n protected _isActive = false;\n\n constructor(reel: Reel, speed: SpeedProfile) {\n this._reel = reel;\n this._speed = speed;\n }\n\n get reel(): Reel {\n return this._reel;\n }\n\n get isActive(): boolean {\n return this._isActive;\n }\n\n /** Enter the phase. Returns a promise that resolves when the phase is complete. */\n async run(config: TConfig): Promise<void> {\n this._isActive = true;\n this._reel.events.emit('phase:enter', this.name);\n\n return new Promise<void>((resolve) => {\n this._resolve = () => {\n this._isActive = false;\n this._reel.events.emit('phase:exit', this.name);\n resolve();\n };\n this.onEnter(config);\n });\n }\n\n /** Skip the phase immediately (if skippable). */\n skip(): void {\n if (!this.skippable || !this._isActive) return;\n this.onSkip();\n this._complete();\n }\n\n /** Force-complete the phase regardless of skippable flag. */\n forceComplete(): void {\n if (!this._isActive) return;\n this.onSkip();\n this._complete();\n }\n\n /** Called each frame while the phase is active. */\n abstract update(deltaMs: number): void;\n\n /** Subclass: set up the phase (start tweens, set speed, etc). */\n protected abstract onEnter(config: TConfig): void;\n\n /** Subclass: clean up when skipped or force-completed. */\n protected abstract onSkip(): void;\n\n /** Call when the phase naturally completes. */\n protected _complete(): void {\n if (this._resolve) {\n const resolve = this._resolve;\n this._resolve = null;\n resolve();\n }\n }\n}\n","import { gsap } from 'gsap';\nimport { ReelPhase } from './ReelPhase.js';\nimport type { SpinningMode } from '../modes/SpinningMode.js';\n\nexport interface StartPhaseConfig {\n /** Spinning mode to set on enter. */\n spinningMode: SpinningMode;\n /** Delay before this reel starts (for staggered start). */\n delay?: number;\n}\n\n/**\n * Accelerates the reel from rest to full spin speed.\n *\n * Optionally performs a brief step-back (reel reverses a tiny amount) before\n * accelerating upward, giving the classic slot machine \"pull\" feel.\n */\nexport class StartPhase extends ReelPhase<StartPhaseConfig> {\n readonly name = 'start';\n readonly skippable = true;\n\n private _tween: gsap.core.Timeline | null = null;\n private _delayedCall: gsap.core.Tween | null = null;\n\n protected onEnter(config: StartPhaseConfig): void {\n const reel = this._reel;\n const speed = this._speed;\n const delay = config.delay ?? 0;\n\n reel.spinningMode = config.spinningMode;\n reel.speed = 0;\n\n if (delay > 0) {\n this._delayedCall = gsap.delayedCall(delay / 1000, () => this._launch());\n } else {\n this._launch();\n }\n }\n\n private _launch(): void {\n this._delayedCall = null;\n const reel = this._reel;\n const speed = this._speed;\n const accelDuration = (speed.accelerationDuration ?? 300) / 1000;\n const accelEase = speed.accelerationEase ?? 'power2.in';\n\n this._tween = gsap.timeline();\n\n // Step-back: brief reverse to give a \"pull\" before launch.\n if (speed.bounceDistance > 0) {\n this._tween.to(reel, {\n speed: -2,\n duration: 0.05,\n ease: 'power1.out',\n });\n }\n\n this._tween.to(reel, {\n speed: speed.spinSpeed,\n duration: accelDuration,\n ease: accelEase,\n onComplete: () => {\n reel.notifySpinStart();\n this._complete();\n },\n });\n }\n\n update(_deltaMs: number): void {\n // Motion is driven by reel.speed, updated by Reel.update()\n }\n\n protected onSkip(): void {\n this._kill();\n this._reel.speed = this._speed.spinSpeed;\n }\n\n private _kill(): void {\n if (this._delayedCall) {\n this._delayedCall.kill();\n this._delayedCall = null;\n }\n if (this._tween) {\n this._tween.kill();\n this._tween = null;\n }\n }\n}\n","import { ReelPhase } from './ReelPhase.js';\n\nexport interface SpinPhaseConfig {\n /** Minimum time to spin before allowing stop. Overrides speed profile if set. */\n minimumSpinTime?: number;\n}\n\n/**\n * Continuous spinning at constant speed.\n *\n * Runs until externally resolved (when setResult arrives). Tracks minimum\n * spin time via ticker accumulation so it behaves consistently when the tab\n * is hidden (no reliance on wall-clock performance.now()).\n */\nexport class SpinPhase extends ReelPhase<SpinPhaseConfig> {\n readonly name = 'spin';\n readonly skippable = false;\n\n private _elapsed = 0;\n private _minTime = 0;\n private _readyToStop = false;\n\n protected onEnter(config: SpinPhaseConfig): void {\n this._elapsed = 0;\n this._minTime = config.minimumSpinTime ?? this._speed.minimumSpinTime ?? 500;\n this._readyToStop = false;\n }\n\n update(deltaMs: number): void {\n this._elapsed += deltaMs;\n if (this._readyToStop && this._elapsed >= this._minTime) {\n this._complete();\n }\n }\n\n /** Signal that this phase should end (called by SpinController when result arrives). */\n resolve(): void {\n this._readyToStop = true;\n if (this._elapsed >= this._minTime) {\n this._complete();\n }\n }\n\n protected onSkip(): void {\n // SpinPhase is not skippable.\n }\n}\n","import { gsap } from 'gsap';\nimport { ReelPhase } from './ReelPhase.js';\n\nexport interface StopPhaseConfig {\n /** Target symbols for this reel (full frame including buffers, top-to-bottom). */\n targetFrame: string[];\n /** Delay before this reel starts stopping (for staggered stop). */\n delay?: number;\n}\n\n/**\n * Stops the reel on the target frame with a weighted, slot-machine feel.\n *\n * Sequence:\n * 1. Wait for the staggered delay.\n * 2. Keep spinning at full speed with `isStopping` flagged. The target frame\n * is loaded into the StopSequencer; each wrap event at the top of the\n * reel pulls the next frame symbol — so targets arrive in the visible\n * area naturally, carrying the full momentum of the spin.\n * 3. When the sequencer is exhausted, snap to grid and bounce:\n * - overshoot downward by `bounceDistance` with `power1.out`\n * - settle back upward with `power1.out`\n * Both legs share a duration for a symmetric, weighty landing.\n */\nexport class StopPhase extends ReelPhase<StopPhaseConfig> {\n readonly name = 'stop';\n readonly skippable = true;\n\n private _config: StopPhaseConfig | null = null;\n private _delayTween: gsap.core.Tween | null = null;\n private _bounceTween: gsap.core.Timeline | null = null;\n private _stage: 'delay' | 'spinning' | 'bouncing' | 'done' = 'delay';\n private _baseY = 0;\n\n protected onEnter(config: StopPhaseConfig): void {\n this._config = config;\n this._stage = 'delay';\n this._baseY = this._reel.container.y;\n\n const delay = (config.delay ?? 0) / 1000;\n if (delay > 0) {\n this._delayTween = gsap.delayedCall(delay, () => this._beginSpinOut());\n } else {\n this._beginSpinOut();\n }\n }\n\n private _beginSpinOut(): void {\n if (!this._config) return;\n const reel = this._reel;\n const speed = this._speed;\n\n reel.setStopFrame(this._config.targetFrame);\n reel.isStopping = true;\n // Restore full spin speed — anticipation or other phases may have lowered\n // it. Weighty stops need full momentum through the final frame.\n reel.speed = speed.spinSpeed;\n\n this._stage = 'spinning';\n }\n\n update(_deltaMs: number): void {\n if (this._stage !== 'spinning') return;\n // Sequencer consumes one symbol per wrap via Reel._onSymbolWrapped.\n // When it's empty, the target frame is fully placed — time to land.\n if (!this._reel.stopSequencer.hasRemaining) {\n this._landAndBounce();\n }\n }\n\n private _landAndBounce(): void {\n const reel = this._reel;\n const speed = this._speed;\n\n reel.speed = 0;\n reel.isStopping = false;\n reel.snapToGrid();\n reel.notifySpinEnd();\n reel.notifyLanded();\n\n const bounceDistance = speed.bounceDistance;\n if (bounceDistance <= 0) {\n this._stage = 'done';\n this._complete();\n return;\n }\n\n const legDuration = (speed.bounceDuration ?? 600) / 2000; // half of total, in seconds\n this._stage = 'bouncing';\n this._bounceTween = gsap.timeline();\n this._bounceTween.to(reel.container, {\n y: this._baseY + bounceDistance,\n duration: legDuration,\n ease: 'power1.out',\n });\n this._bounceTween.to(reel.container, {\n y: this._baseY,\n duration: legDuration,\n ease: 'power1.out',\n onComplete: () => {\n this._stage = 'done';\n this._complete();\n },\n });\n }\n\n protected onSkip(): void {\n this._killTweens();\n const reel = this._reel;\n reel.speed = 0;\n reel.isStopping = false;\n\n if (this._stage !== 'done' && this._config) {\n const bufferAbove = reel.bufferAbove;\n const visible = reel.visibleRows;\n reel.placeSymbols(this._config.targetFrame.slice(bufferAbove, bufferAbove + visible));\n }\n reel.snapToGrid();\n reel.container.y = this._baseY;\n reel.notifySpinEnd();\n reel.notifyLanded();\n this._stage = 'done';\n }\n\n private _killTweens(): void {\n if (this._delayTween) {\n this._delayTween.kill();\n this._delayTween = null;\n }\n if (this._bounceTween) {\n this._bounceTween.kill();\n this._bounceTween = null;\n }\n }\n}\n","import { gsap } from 'gsap';\nimport { ReelPhase } from './ReelPhase.js';\n\nexport interface AnticipationPhaseConfig {\n /** Duration override in ms. Uses speed profile anticipationDelay if not set. */\n duration?: number;\n /** Speed multiplier during anticipation. Default: 0.3 (30% of spin speed). */\n speedMultiplier?: number;\n}\n\n/**\n * Anticipation phase: slow-down tease before a reel stops.\n *\n * Decelerates to a fraction of spin speed, holds for a duration, then hands\n * off to StopPhase. StopPhase resets speed to full spin speed at the start\n * of its spin-out stage, so leaving speed low here is fine.\n */\nexport class AnticipationPhase extends ReelPhase<AnticipationPhaseConfig> {\n readonly name = 'anticipation';\n readonly skippable = true;\n\n private _tween: gsap.core.Timeline | null = null;\n\n protected onEnter(config: AnticipationPhaseConfig): void {\n const reel = this._reel;\n const speed = this._speed;\n const duration = (config.duration ?? speed.anticipationDelay) / 1000;\n const targetSpeed = speed.spinSpeed * (config.speedMultiplier ?? 0.3);\n\n if (duration <= 0) {\n this._complete();\n return;\n }\n\n this._tween = gsap.timeline();\n\n this._tween.to(reel, {\n speed: targetSpeed,\n duration: duration * 0.35,\n ease: 'power2.out',\n });\n this._tween.to({}, { duration: duration * 0.65, onComplete: () => this._complete() });\n }\n\n update(_deltaMs: number): void {\n // Driven by GSAP tweens\n }\n\n protected onSkip(): void {\n this._kill();\n this._reel.speed = this._speed.spinSpeed;\n }\n\n private _kill(): void {\n if (this._tween) {\n this._tween.kill();\n this._tween = null;\n }\n }\n}\n","import type { Reel } from '../../core/Reel.js';\nimport type { SpeedProfile } from '../../config/types.js';\nimport { ReelPhase } from './ReelPhase.js';\nimport { StartPhase } from './StartPhase.js';\nimport { SpinPhase } from './SpinPhase.js';\nimport { StopPhase } from './StopPhase.js';\nimport { AnticipationPhase } from './AnticipationPhase.js';\n\ntype PhaseConstructor<T extends ReelPhase<any> = ReelPhase<any>> =\n new (reel: Reel, speed: SpeedProfile) => T;\n\n/**\n * Factory for creating reel phase instances.\n *\n * Ships with all four default phases pre-registered.\n * Users can override any phase by registering a custom constructor.\n */\nexport class PhaseFactory {\n private _registry = new Map<string, PhaseConstructor>();\n\n constructor() {\n // Register defaults\n this._registry.set('start', StartPhase);\n this._registry.set('spin', SpinPhase);\n this._registry.set('stop', StopPhase);\n this._registry.set('anticipation', AnticipationPhase);\n }\n\n /** Register or override a phase type. */\n register<T extends ReelPhase<any>>(name: string, PhaseClass: PhaseConstructor<T>): void {\n this._registry.set(name, PhaseClass);\n }\n\n /** Create a phase instance for a reel. */\n create<T extends ReelPhase<any> = ReelPhase<any>>(\n name: string,\n reel: Reel,\n speed: SpeedProfile,\n ): T {\n const PhaseClass = this._registry.get(name);\n if (!PhaseClass) {\n throw new Error(\n `Phase '${name}' not registered. Available: ${[...this._registry.keys()].join(', ')}`,\n );\n }\n return new PhaseClass(reel, speed) as T;\n }\n\n has(name: string): boolean {\n return this._registry.has(name);\n }\n}\n","import type { Ticker } from 'pixi.js';\nimport type { Disposable } from './Disposable.js';\n\ntype TickerCallback = (ticker: Ticker) => void;\n\n/**\n * Safe wrapper around PixiJS Ticker subscriptions.\n *\n * Solves the #1 memory leak in the original library: dangling ticker callbacks.\n * When `destroy()` is called, ALL registered callbacks are automatically\n * removed from the ticker.\n *\n * Usage:\n * ```ts\n * const ref = new TickerRef(app.ticker);\n * ref.add((ticker) => reel.update(ticker));\n * // Later:\n * ref.destroy(); // all callbacks removed\n * ```\n */\nexport class TickerRef implements Disposable {\n private _callbacks: TickerCallback[] = [];\n private _isDestroyed = false;\n\n constructor(private _ticker: Ticker) {}\n\n get isDestroyed(): boolean {\n return this._isDestroyed;\n }\n\n add(fn: TickerCallback): void {\n if (this._isDestroyed) return;\n this._callbacks.push(fn);\n this._ticker.add(fn);\n }\n\n remove(fn: TickerCallback): void {\n const idx = this._callbacks.indexOf(fn);\n if (idx !== -1) {\n this._callbacks.splice(idx, 1);\n this._ticker.remove(fn);\n }\n }\n\n destroy(): void {\n if (this._isDestroyed) return;\n for (const fn of this._callbacks) {\n this._ticker.remove(fn);\n }\n this._callbacks.length = 0;\n this._isDestroyed = true;\n }\n}\n","import type { Ticker } from 'pixi.js';\nimport type { Reel } from '../core/Reel.js';\nimport type { SpeedProfile } from '../config/types.js';\nimport type { SpeedManager } from '../speed/SpeedManager.js';\nimport type { FrameBuilder } from '../frame/FrameBuilder.js';\nimport type { SpinResult } from '../events/ReelEvents.js';\nimport { EventEmitter } from '../events/EventEmitter.js';\nimport type { ReelSetEvents } from '../events/ReelEvents.js';\nimport { PhaseFactory } from './phases/PhaseFactory.js';\nimport type { SpinPhase } from './phases/SpinPhase.js';\nimport type { ReelPhase } from './phases/ReelPhase.js';\nimport type { StartPhaseConfig } from './phases/StartPhase.js';\nimport type { StopPhaseConfig } from './phases/StopPhase.js';\nimport type { AnticipationPhaseConfig } from './phases/AnticipationPhase.js';\nimport type { SpinningMode } from './modes/SpinningMode.js';\nimport { StandardMode } from './modes/StandardMode.js';\nimport type { Disposable } from '../utils/Disposable.js';\nimport { TickerRef } from '../utils/TickerRef.js';\n\n/**\n * The conductor of a spin.\n *\n * A reel set has many moving parts; the `SpinController` is the single\n * brain that drives them in time. On `spin()` it walks every reel through\n * its phase state machine (`StartPhase` → `SpinPhase` → optional\n * `AnticipationPhase` → `StopPhase`), applies the per-reel staggered\n * delays from the `SpeedProfile`, and resolves a promise when the last\n * reel lands (or the spin is skipped).\n *\n * It does not draw anything — drawing lives on `Reel` and `ReelSymbol`.\n * It does not decide outcomes — that's `setResult(grid)` coming in from\n * your game code. Its one job is timing.\n *\n * Every interesting moment fires on the event bus:\n * `spin:start`, `spin:allStarted`, `spin:stopping`, `spin:reelLanded`,\n * `spin:allLanded`, `spin:complete`, `skip:requested`, `skip:completed`.\n */\nexport class SpinController implements Disposable {\n private _reels: Reel[];\n private _speedManager: SpeedManager;\n private _frameBuilder: FrameBuilder;\n private _phaseFactory: PhaseFactory;\n private _events: EventEmitter<ReelSetEvents>;\n private _tickerRef: TickerRef;\n private _spinningMode: SpinningMode;\n\n private _isSpinning = false;\n private _spinStartTime = 0;\n private _resultSymbols: string[][] | null = null;\n private _anticipationReels: number[] = [];\n private _stopDelayOverride: number[] | null = null;\n private _activePhases: Map<number, ReelPhase<any>> = new Map();\n private _landedReels = new Set<number>();\n private _wasSkipped = false;\n private _isDestroyed = false;\n private _currentSpinResolve: ((result: SpinResult) => void) | null = null;\n /** Incremented on each new spin. If a callback sees a stale generation, it no-ops. */\n private _spinGeneration = 0;\n\n constructor(\n reels: Reel[],\n speedManager: SpeedManager,\n frameBuilder: FrameBuilder,\n phaseFactory: PhaseFactory,\n events: EventEmitter<ReelSetEvents>,\n ticker: Ticker,\n spinningMode?: SpinningMode,\n ) {\n this._reels = reels;\n this._speedManager = speedManager;\n this._frameBuilder = frameBuilder;\n this._phaseFactory = phaseFactory;\n this._events = events;\n this._tickerRef = new TickerRef(ticker);\n this._spinningMode = spinningMode ?? new StandardMode();\n\n this._tickerRef.add((ticker) => this._onTick(ticker));\n }\n\n get isSpinning(): boolean {\n return this._isSpinning;\n }\n\n get isDestroyed(): boolean {\n return this._isDestroyed;\n }\n\n async spin(): Promise<SpinResult> {\n if (this._isSpinning) {\n throw new Error('Cannot start a new spin while one is in progress.');\n }\n\n this._isSpinning = true;\n this._wasSkipped = false;\n this._spinStartTime = performance.now();\n this._resultSymbols = null;\n this._anticipationReels = [];\n this._stopDelayOverride = null;\n this._landedReels.clear();\n this._activePhases.clear();\n this._spinGeneration++;\n\n const generation = this._spinGeneration;\n const speed = this._speedManager.active;\n\n this._events.emit('spin:start');\n\n const resultPromise = new Promise<SpinResult>((resolve) => {\n this._currentSpinResolve = resolve;\n });\n\n for (let i = 0; i < this._reels.length; i++) {\n this._startReel(i, speed, generation);\n }\n\n return resultPromise;\n }\n\n setResult(symbols: string[][]): void {\n if (!this._isSpinning) return;\n this._resultSymbols = symbols;\n this._tryBeginStopSequence();\n }\n\n setAnticipation(reelIndices: number[]): void {\n this._anticipationReels = reelIndices;\n }\n\n /**\n * Override the per-reel stop delay (in ms). Pass one value per reel.\n * When set, these replace the staggered `reelIndex * speed.stopDelay`\n * pattern for the current spin. Cleared at the start of each new spin.\n */\n setStopDelays(delays: number[]): void {\n this._stopDelayOverride = [...delays];\n }\n\n skip(): void {\n if (!this._isSpinning) return;\n\n this._wasSkipped = true;\n this._events.emit('skip:requested');\n\n for (const [, phase] of this._activePhases) {\n phase.forceComplete();\n }\n this._activePhases.clear();\n\n this._spinGeneration++;\n\n if (this._resultSymbols) {\n for (let i = 0; i < this._reels.length; i++) {\n if (this._landedReels.has(i)) continue;\n const reel = this._reels[i];\n reel.speed = 0;\n reel.isStopping = false;\n reel.placeSymbols(this._resultSymbols[i]);\n this._markLanded(i);\n }\n } else {\n for (let i = 0; i < this._reels.length; i++) {\n if (this._landedReels.has(i)) continue;\n const reel = this._reels[i];\n reel.speed = 0;\n reel.isStopping = false;\n reel.snapToGrid();\n this._markLanded(i);\n }\n }\n\n this._events.emit('skip:completed');\n }\n\n destroy(): void {\n if (this._isDestroyed) return;\n this._tickerRef.destroy();\n this._activePhases.clear();\n this._isDestroyed = true;\n }\n\n // ── Internal ──────────────────────────────────────────\n\n private async _startReel(reelIndex: number, speed: SpeedProfile, generation: number): Promise<void> {\n if (generation !== this._spinGeneration) return;\n\n const reel = this._reels[reelIndex];\n\n // START → SPIN: chain via phase.run() promises (no busy-polling).\n const startPhase = this._phaseFactory.create<any>('start', reel, speed);\n this._activePhases.set(reelIndex, startPhase);\n await startPhase.run({\n spinningMode: this._spinningMode,\n delay: reelIndex * speed.spinDelay,\n } as StartPhaseConfig);\n\n if (generation !== this._spinGeneration) return;\n\n const spinPhase = this._phaseFactory.create<SpinPhase>('spin', reel, speed);\n this._activePhases.set(reelIndex, spinPhase);\n const spinDone = spinPhase.run({});\n\n let allSpinning = true;\n for (let i = 0; i < this._reels.length; i++) {\n const phase = this._activePhases.get(i);\n if (!phase || phase.name !== 'spin') { allSpinning = false; break; }\n }\n if (allSpinning) {\n this._events.emit('spin:allStarted');\n this._tryBeginStopSequence();\n }\n\n await spinDone;\n if (generation !== this._spinGeneration) return;\n\n // SpinPhase resolved (result arrived). Run ANTICIPATION (if requested) then STOP.\n const stopDelay = this._stopDelayFor(reelIndex, speed);\n const targetFrame = this._frameFor(reelIndex);\n\n if (this._anticipationReels.includes(reelIndex) && speed.anticipationDelay > 0) {\n this._events.emit('spin:stopping', reelIndex);\n const anticipationPhase = this._phaseFactory.create<any>('anticipation', reel, speed);\n this._activePhases.set(reelIndex, anticipationPhase);\n await anticipationPhase.run({} as AnticipationPhaseConfig);\n if (generation !== this._spinGeneration) return;\n } else {\n this._events.emit('spin:stopping', reelIndex);\n }\n\n const stopPhase = this._phaseFactory.create<any>('stop', reel, speed);\n this._activePhases.set(reelIndex, stopPhase);\n await stopPhase.run({ targetFrame, delay: stopDelay } as StopPhaseConfig);\n if (generation !== this._spinGeneration) return;\n\n this._markLanded(reelIndex);\n }\n\n private _stopDelayFor(reelIndex: number, speed: SpeedProfile): number {\n if (this._stopDelayOverride) {\n return this._stopDelayOverride[reelIndex] ?? 0;\n }\n return reelIndex * speed.stopDelay;\n }\n\n private _cachedFrames: string[][] | null = null;\n\n private _frameFor(reelIndex: number): string[] {\n if (!this._cachedFrames) return [];\n return this._cachedFrames[reelIndex];\n }\n\n private _tryBeginStopSequence(): void {\n if (!this._resultSymbols) return;\n\n for (let i = 0; i < this._reels.length; i++) {\n const phase = this._activePhases.get(i);\n if (!phase || phase.name !== 'spin') return;\n }\n\n // Build and cache frames using each reel's actual buffer/visible config.\n // Reels may differ in buffer size; build each independently.\n const frames: string[][] = [];\n for (let i = 0; i < this._reels.length; i++) {\n const reel = this._reels[i];\n frames.push(\n this._frameBuilder.build(\n i,\n reel.visibleRows,\n reel.bufferAbove,\n reel.bufferBelow,\n this._resultSymbols[i],\n ),\n );\n }\n this._cachedFrames = frames;\n\n // Resolve all SpinPhases; each reel's _startReel awaits its own spinDone,\n // then independently runs ANTICIPATION/STOP.\n for (let i = 0; i < this._reels.length; i++) {\n const spinPhase = this._activePhases.get(i) as SpinPhase;\n if (spinPhase?.resolve) spinPhase.resolve();\n }\n }\n\n private _markLanded(reelIndex: number): void {\n if (this._landedReels.has(reelIndex)) return;\n this._landedReels.add(reelIndex);\n\n const reel = this._reels[reelIndex];\n const symbols = reel.getVisibleSymbols();\n reel.events.emit('landed', symbols);\n this._events.emit('spin:reelLanded', reelIndex, symbols);\n\n if (this._landedReels.size === this._reels.length) {\n this._finishSpin();\n }\n }\n\n private _finishSpin(): void {\n const result: SpinResult = {\n symbols: this._reels.map((r) => r.getVisibleSymbols()),\n wasSkipped: this._wasSkipped,\n duration: performance.now() - this._spinStartTime,\n };\n\n this._isSpinning = false;\n this._activePhases.clear();\n this._cachedFrames = null;\n\n this._events.emit('spin:allLanded', result);\n this._events.emit('spin:complete', result);\n\n if (this._currentSpinResolve) {\n this._currentSpinResolve(result);\n this._currentSpinResolve = null;\n }\n }\n\n private _onTick(ticker: Ticker): void {\n if (!this._isSpinning) return;\n\n const deltaMs = ticker.deltaMS;\n for (const reel of this._reels) {\n reel.update(deltaMs);\n }\n for (const phase of this._activePhases.values()) {\n if (phase.isActive) {\n phase.update(deltaMs);\n }\n }\n }\n}\n","import type { SpeedProfile } from '../config/types.js';\n\n/**\n * The tempo of your reels, as named presets.\n *\n * A `SpeedProfile` is a bundle of timings — how long the wind-up takes,\n * how fast the reel scrolls at full speed, how deep the landing bounce\n * is, which GSAP easing drives each transition. `SpeedManager` holds\n * those profiles by name and tracks which one is active.\n *\n * Built-in profiles: `normal` (default), `turbo`, `superTurbo`. Add your\n * own via `reelSet.speed.addProfile('cinematic', {...})`. Switch at\n * runtime with `reelSet.setSpeed('turbo')`.\n *\n * Speed changes take effect on the next spin — mid-spin switching\n * is deliberately not supported to keep animation state simple.\n */\nexport class SpeedManager {\n private _profiles = new Map<string, SpeedProfile>();\n private _activeName: string;\n private _active: SpeedProfile;\n\n constructor(profiles: Map<string, SpeedProfile>, initialSpeed: string) {\n for (const [name, profile] of profiles) {\n this._profiles.set(name, profile);\n }\n const initial = this._profiles.get(initialSpeed);\n if (!initial) {\n throw new Error(\n `Speed profile '${initialSpeed}' not found. Available: ${[...this._profiles.keys()].join(', ')}`,\n );\n }\n this._activeName = initialSpeed;\n this._active = initial;\n }\n\n /** The currently active speed profile. */\n get active(): Readonly<SpeedProfile> {\n return this._active;\n }\n\n /** Name of the currently active speed profile. */\n get activeName(): string {\n return this._activeName;\n }\n\n /** Switch to a different named speed profile. */\n set(name: string): { previous: SpeedProfile; current: SpeedProfile } {\n const profile = this._profiles.get(name);\n if (!profile) {\n throw new Error(\n `Speed profile '${name}' not found. Available: ${[...this._profiles.keys()].join(', ')}`,\n );\n }\n const previous = this._active;\n this._activeName = name;\n this._active = profile;\n return { previous, current: profile };\n }\n\n /** Add or replace a speed profile. */\n addProfile(name: string, profile: SpeedProfile): void {\n this._profiles.set(name, profile);\n }\n\n /** Get a profile by name, or undefined if not found. */\n getProfile(name: string): SpeedProfile | undefined {\n return this._profiles.get(name);\n }\n\n /** All registered profile names. */\n get profileNames(): string[] {\n return [...this._profiles.keys()];\n }\n}\n","import type { Reel } from '../core/Reel.js';\nimport type { ReelViewport } from '../core/ReelViewport.js';\nimport type { SymbolPosition } from '../events/ReelEvents.js';\nimport type { ReelSymbol } from '../symbols/ReelSymbol.js';\nimport type { Disposable } from '../utils/Disposable.js';\n\nexport interface SpotlightOptions {\n /** Opacity of the dim overlay (0-1). Default: 0.5. */\n dimAmount?: number;\n /** Whether to play win animation on spotlighted symbols. Default: true. */\n playWinAnimation?: boolean;\n /** Whether to re-parent symbols above the mask. Default: true. */\n promoteAboveMask?: boolean;\n}\n\nexport interface WinLine {\n positions: SymbolPosition[];\n}\n\nexport interface CycleOptions extends SpotlightOptions {\n /** Milliseconds to display each win line. Default: 2000. */\n displayDuration?: number;\n /** Milliseconds between lines. Default: 300. */\n gapDuration?: number;\n /** Number of cycles (-1 for infinite). Default: 1. */\n cycles?: number;\n}\n\ninterface PromotedSymbol {\n symbol: ReelSymbol;\n originalParent: any;\n position: SymbolPosition;\n}\n\n/**\n * The \"we just won\" visual primitive.\n *\n * The spotlight is what turns a landed grid into a celebration. Given a\n * list of winning cell positions, it:\n *\n * 1. Fades in the dim overlay behind everything (everything that is\n * not winning visually sinks into the background).\n * 2. Re-parents each winning `ReelSymbol` into the viewport's\n * spotlight layer so its animation isn't clipped by the reel mask.\n * 3. Calls `playWin()` on each winner (your symbol class's one-shot).\n * 4. When you call `hide()` or the cycle ends, it puts every symbol\n * back where it came from and removes the dim overlay.\n *\n * Two modes:\n * - `show(positions, options)` — one-shot. Cell highlight + promote +\n * play win. Returns when the animation fully ends.\n * - `cycle(lines, options)` — iterate multiple win lines with a\n * configurable per-line duration and gap, optionally repeating.\n *\n * Win detection is NOT part of this. pixi-reels never computes wins —\n * your server / game code decides which cells are winners and passes\n * them here. See [ADR 007](../../docs/adr/007-scope.md).\n */\nexport class SymbolSpotlight implements Disposable {\n private _reels: Reel[];\n private _viewport: ReelViewport;\n private _promoted: PromotedSymbol[] = [];\n private _isActive = false;\n private _isDestroyed = false;\n private _cycleAbort: AbortController | null = null;\n\n constructor(reels: Reel[], viewport: ReelViewport) {\n this._reels = reels;\n this._viewport = viewport;\n }\n\n get isActive(): boolean {\n return this._isActive;\n }\n\n get isDestroyed(): boolean {\n return this._isDestroyed;\n }\n\n /** Show spotlight on specific positions. */\n async show(positions: SymbolPosition[], options: SpotlightOptions = {}): Promise<void> {\n this.hide(); // Clear any existing spotlight\n\n const {\n dimAmount = 0.5,\n playWinAnimation = true,\n promoteAboveMask = true,\n } = options;\n\n this._isActive = true;\n\n // Show dim overlay\n this._viewport.showDim(dimAmount);\n\n // Promote symbols\n const winPromises: Promise<void>[] = [];\n\n for (const pos of positions) {\n const reel = this._reels[pos.reelIndex];\n if (!reel) continue;\n\n const symbol = reel.getSymbolAt(pos.rowIndex);\n if (!symbol) continue;\n\n const originalParent = symbol.view.parent;\n this._promoted.push({ symbol, originalParent, position: pos });\n\n if (promoteAboveMask) {\n // Re-parent to spotlight container (above mask)\n const globalPos = symbol.view.getGlobalPosition();\n this._viewport.spotlightContainer.addChild(symbol.view);\n const localPos = this._viewport.spotlightContainer.toLocal(globalPos);\n symbol.view.x = localPos.x;\n symbol.view.y = localPos.y;\n }\n\n if (playWinAnimation) {\n winPromises.push(symbol.playWin());\n }\n }\n\n if (winPromises.length > 0) {\n await Promise.all(winPromises);\n }\n }\n\n /** Hide the spotlight and return symbols to their original positions. */\n hide(): void {\n // Cancel any running cycle\n if (this._cycleAbort) {\n this._cycleAbort.abort();\n this._cycleAbort = null;\n }\n\n // Return promoted symbols\n for (const { symbol, originalParent } of this._promoted) {\n if (originalParent && symbol.view.parent !== originalParent) {\n const globalPos = symbol.view.getGlobalPosition();\n originalParent.addChild(symbol.view);\n const localPos = originalParent.toLocal(globalPos);\n symbol.view.x = localPos.x;\n symbol.view.y = localPos.y;\n }\n symbol.stopAnimation();\n }\n this._promoted = [];\n\n // Hide dim overlay\n this._viewport.hideDim();\n this._isActive = false;\n }\n\n /**\n * Cycle through win lines, showing each for a duration.\n * Returns when all cycles complete or when hide() is called.\n */\n async cycle(winLines: WinLine[], options: CycleOptions = {}): Promise<void> {\n const {\n displayDuration = 2000,\n gapDuration = 300,\n cycles = 1,\n } = options;\n\n if (winLines.length === 0) return;\n\n this._cycleAbort = new AbortController();\n const signal = this._cycleAbort.signal;\n\n let cycleCount = 0;\n while (cycles === -1 || cycleCount < cycles) {\n for (const line of winLines) {\n if (signal.aborted) return;\n await this.show(line.positions, options);\n await this._wait(displayDuration, signal);\n if (signal.aborted) return;\n this.hide();\n await this._wait(gapDuration, signal);\n }\n cycleCount++;\n }\n }\n\n destroy(): void {\n if (this._isDestroyed) return;\n this.hide();\n this._isDestroyed = true;\n }\n\n private _wait(ms: number, signal: AbortSignal): Promise<void> {\n return new Promise((resolve) => {\n if (signal.aborted) {\n resolve();\n return;\n }\n const timer = setTimeout(resolve, ms);\n signal.addEventListener('abort', () => {\n clearTimeout(timer);\n resolve();\n }, { once: true });\n });\n }\n}\n","import { Container } from 'pixi.js';\nimport type { Ticker } from 'pixi.js';\nimport type { Disposable } from '../utils/Disposable.js';\nimport type { SpeedProfile, ReelSetInternalConfig } from '../config/types.js';\nimport { EventEmitter } from '../events/EventEmitter.js';\nimport type { ReelSetEvents, SpinResult, SymbolPosition } from '../events/ReelEvents.js';\nimport { Reel } from './Reel.js';\nimport { ReelViewport } from './ReelViewport.js';\nimport { SpinController } from '../spin/SpinController.js';\nimport { SpeedManager } from '../speed/SpeedManager.js';\nimport { SymbolSpotlight, type SpotlightOptions, type WinLine, type CycleOptions } from '../spotlight/SymbolSpotlight.js';\nimport type { SymbolFactory } from '../symbols/SymbolFactory.js';\nimport type { FrameBuilder } from '../frame/FrameBuilder.js';\nimport type { PhaseFactory } from '../spin/phases/PhaseFactory.js';\nimport type { SpinningMode } from '../spin/modes/SpinningMode.js';\n\nexport interface ReelSetParams {\n config: ReelSetInternalConfig;\n reels: Reel[];\n viewport: ReelViewport;\n symbolFactory: SymbolFactory;\n frameBuilder: FrameBuilder;\n phaseFactory: PhaseFactory;\n spinningMode: SpinningMode;\n}\n\n/**\n * The whole slot board as one object.\n *\n * A `ReelSet` is a PixiJS `Container` that owns every reel, the spin\n * controller, the speed manager, and the win spotlight. You `addChild` it\n * to your stage and then drive it from the four public verbs below:\n *\n * - `spin()` — start the reels moving, returns a promise that resolves\n * when every reel has landed (or been slam-stopped)\n * - `setResult(grid)` — tell the reels what to land on; the spin\n * controller consumes this and each reel queues its target symbols\n * - `setAnticipation(reelIndices)` — slow the given reels before they\n * stop, for \"will the third scatter land?\" tension\n * - `skip()` — land immediately; useful for slam-stop UX\n *\n * Everything else is subsystems: `speed`, `spotlight`, `events`, `viewport`.\n * Construction goes through {@link ReelSetBuilder}, never `new ReelSet()`\n * directly — the builder enforces that every required piece is wired.\n *\n * ```ts\n * const reelSet = new ReelSetBuilder()\n * .reels(5).visibleSymbols(3).symbolSize(140, 140)\n * .symbols((r) => r.register('cherry', SpriteSymbol, { textures }))\n * .ticker(app.ticker)\n * .build();\n * app.stage.addChild(reelSet);\n *\n * const spin = reelSet.spin();\n * reelSet.setResult(await server.spin());\n * await spin;\n * ```\n *\n * Teardown cascades: one `reelSet.destroy()` disposes every child.\n */\nexport class ReelSet extends Container implements Disposable {\n private _events = new EventEmitter<ReelSetEvents>();\n private _reels: Reel[];\n private _viewport: ReelViewport;\n private _spinController: SpinController;\n private _speedManager: SpeedManager;\n private _spotlight: SymbolSpotlight;\n private _symbolFactory: SymbolFactory;\n private _isDestroyed = false;\n\n constructor(params: ReelSetParams) {\n super();\n\n this._reels = params.reels;\n this._viewport = params.viewport;\n this._symbolFactory = params.symbolFactory;\n\n // Speed manager\n this._speedManager = new SpeedManager(\n params.config.speeds,\n params.config.initialSpeed,\n );\n\n // Spin controller\n this._spinController = new SpinController(\n params.reels,\n this._speedManager,\n params.frameBuilder,\n params.phaseFactory,\n this._events,\n params.config.ticker,\n params.spinningMode,\n );\n\n // Spotlight\n this._spotlight = new SymbolSpotlight(params.reels, params.viewport);\n\n // Add viewport to display\n this.addChild(this._viewport);\n }\n\n // ─── Event system ──────────────────────────────────────────\n // Uses a dedicated emitter to avoid collision with PixiJS Container events.\n\n /** The event emitter for reel-specific events. */\n get events(): EventEmitter<ReelSetEvents> {\n return this._events;\n }\n\n // ─── Spin API ─────────────────────────────────────────────\n\n /** Start spinning all reels. Returns a promise that resolves when all reels land. */\n async spin(): Promise<SpinResult> {\n return this._spinController.spin();\n }\n\n /** Set the target result symbols. Triggers the stop sequence. */\n setResult(symbols: string[][]): void {\n this._spinController.setResult(symbols);\n }\n\n /** Set which reels should show anticipation before stopping. */\n setAnticipation(reelIndices: number[]): void {\n this._spinController.setAnticipation(reelIndices);\n }\n\n /**\n * Override the per-reel stop delay for the current spin (in ms).\n * Pass one value per reel. Cleared at the start of each new spin.\n *\n * @example\n * // Stagger the last two reels more than the default for dramatic effect:\n * reelSet.setStopDelays([0, 140, 280, 600, 1100]);\n */\n setStopDelays(delays: number[]): void {\n this._spinController.setStopDelays(delays);\n }\n\n /** Skip/slam-stop: immediately land all reels on target. */\n skip(): void {\n this._spinController.skip();\n }\n\n get isSpinning(): boolean {\n return this._spinController.isSpinning;\n }\n\n // ─── Speed API ────────────────────────────────────────────\n\n /** Speed profile manager. */\n get speed(): SpeedManager {\n return this._speedManager;\n }\n\n /** Change speed and emit event. */\n setSpeed(name: string): void {\n const { previous, current } = this._speedManager.set(name);\n this._events.emit('speed:changed', current, previous);\n }\n\n // ─── Spotlight API ────────────────────────────────────────\n\n get spotlight(): SymbolSpotlight {\n return this._spotlight;\n }\n\n // ─── Reel access ──────────────────────────────────────────\n\n /** Get all reels. */\n get reels(): readonly Reel[] {\n return this._reels;\n }\n\n /** Get a reel by index. */\n getReel(index: number): Reel {\n return this._reels[index];\n }\n\n /** Get the viewport. */\n get viewport(): ReelViewport {\n return this._viewport;\n }\n\n // ─── Lifecycle ────────────────────────────────────────────\n\n get isDestroyed(): boolean {\n return this._isDestroyed;\n }\n\n destroy(): void {\n if (this._isDestroyed) return;\n this._isDestroyed = true;\n\n this._spotlight.destroy();\n this._spinController.destroy();\n\n for (const reel of this._reels) {\n reel.destroy();\n }\n\n this._symbolFactory.destroy();\n this._viewport.destroy();\n this._events.emit('destroyed');\n this._events.removeAllListeners();\n\n super.destroy({ children: true });\n }\n}\n","/** Default values applied when not explicitly set by the builder. */\nexport const DEFAULTS = {\n bufferSymbols: 1 as number,\n symbolGap: { x: 0 as number, y: 0 as number },\n initialSpeed: 'normal' as string,\n maxPoolPerKey: 20 as number,\n zIndexStep: 100 as number,\n};\n","import type { SpeedProfile } from './types.js';\n\n/**\n * Built-in speed profiles covering common slot game needs.\n *\n * Bounce values are tuned for the typical 120–200px symbol range. For larger\n * or smaller symbols, register a custom profile or override `bounceDistance`.\n * `bounceDuration` is the total time for the two-leg bounce (down then back).\n */\nexport const SpeedPresets = {\n NORMAL: {\n name: 'normal',\n spinDelay: 100,\n spinSpeed: 30,\n stopDelay: 140,\n anticipationDelay: 450,\n bounceDistance: 56,\n bounceDuration: 600,\n accelerationEase: 'power2.in',\n decelerationEase: 'power2.out',\n accelerationDuration: 300,\n minimumSpinTime: 500,\n },\n TURBO: {\n name: 'turbo',\n spinDelay: 30,\n spinSpeed: 50,\n stopDelay: 0,\n anticipationDelay: 250,\n bounceDistance: 42,\n bounceDuration: 200,\n accelerationEase: 'power2.in',\n decelerationEase: 'power2.out',\n accelerationDuration: 200,\n minimumSpinTime: 300,\n },\n SUPER_TURBO: {\n name: 'superTurbo',\n spinDelay: 0,\n spinSpeed: 80,\n stopDelay: 0,\n anticipationDelay: 0,\n bounceDistance: 14,\n bounceDuration: 120,\n accelerationEase: 'power1.in',\n decelerationEase: 'power1.out',\n accelerationDuration: 50,\n minimumSpinTime: 100,\n },\n} as const satisfies Record<string, SpeedProfile>;\n","import type { ReelSymbol } from './ReelSymbol.js';\n\ntype SymbolConstructor<T extends ReelSymbol = ReelSymbol> = new (options: any) => T;\n\ninterface RegistryEntry {\n SymbolClass: SymbolConstructor;\n options: any;\n}\n\n/**\n * Registry that maps symbolIds to their constructors and options.\n *\n * Used by the builder and SymbolFactory to create symbols on demand.\n */\nexport class SymbolRegistry {\n private _entries = new Map<string, RegistryEntry>();\n\n /**\n * Register a symbol type.\n *\n * ```ts\n * registry.register('cherry', SpriteSymbol, { textures: { cherry: tex } });\n * ```\n */\n register<T extends ReelSymbol>(\n symbolId: string,\n SymbolClass: new (options: any) => T,\n options: T extends { constructor: (options: infer O) => any } ? O : any,\n ): void {\n if (this._entries.has(symbolId)) {\n throw new Error(`Symbol '${symbolId}' is already registered.`);\n }\n this._entries.set(symbolId, { SymbolClass, options });\n }\n\n /** Create a new symbol instance for the given symbolId. */\n create(symbolId: string): ReelSymbol {\n const entry = this._entries.get(symbolId);\n if (!entry) {\n throw new Error(\n `Symbol '${symbolId}' is not registered. Available: ${[...this._entries.keys()].join(', ')}`,\n );\n }\n const symbol = new entry.SymbolClass(entry.options);\n symbol.activate(symbolId);\n return symbol;\n }\n\n has(symbolId: string): boolean {\n return this._entries.has(symbolId);\n }\n\n get symbolIds(): string[] {\n return [...this._entries.keys()];\n }\n\n get size(): number {\n return this._entries.size;\n }\n}\n","import type { Disposable } from '../utils/Disposable.js';\n\n/**\n * Generic object pool for reusing expensive-to-create objects.\n *\n * Reduces GC pressure by recycling objects instead of creating/destroying them each frame.\n * Used internally for ReelSymbol instances and available to game code for trails, particles, etc.\n *\n * @typeParam T - The type of object to pool.\n */\nexport class ObjectPool<T> implements Disposable {\n private _pools = new Map<string, T[]>();\n private _isDestroyed = false;\n\n constructor(\n private _factory: (key: string) => T,\n private _reset?: (item: T) => void,\n private _dispose?: (item: T) => void,\n private _maxPerKey: number = 20,\n ) {}\n\n get isDestroyed(): boolean {\n return this._isDestroyed;\n }\n\n /**\n * Get an object from the pool, or create a new one if the pool is empty.\n */\n acquire(key: string): T {\n const pool = this._pools.get(key);\n if (pool && pool.length > 0) {\n const item = pool.pop()!;\n this._reset?.(item);\n return item;\n }\n return this._factory(key);\n }\n\n /**\n * Return an object to the pool for reuse.\n * If the pool is at capacity, the object is disposed instead.\n */\n release(key: string, item: T): void {\n let pool = this._pools.get(key);\n if (!pool) {\n pool = [];\n this._pools.set(key, pool);\n }\n if (pool.length >= this._maxPerKey) {\n this._dispose?.(item);\n return;\n }\n pool.push(item);\n }\n\n /** Get the number of pooled items for a key. */\n size(key: string): number {\n return this._pools.get(key)?.length ?? 0;\n }\n\n /** Get total pooled items across all keys. */\n get totalSize(): number {\n let total = 0;\n for (const pool of this._pools.values()) {\n total += pool.length;\n }\n return total;\n }\n\n /** Clear all pooled items, calling dispose on each. */\n clear(): void {\n if (this._dispose) {\n for (const pool of this._pools.values()) {\n for (const item of pool) {\n this._dispose(item);\n }\n }\n }\n this._pools.clear();\n }\n\n destroy(): void {\n if (this._isDestroyed) return;\n this.clear();\n this._isDestroyed = true;\n }\n}\n","import type { ReelSymbol } from './ReelSymbol.js';\nimport type { SymbolRegistry } from './SymbolRegistry.js';\nimport { ObjectPool } from '../pool/ObjectPool.js';\n\n/**\n * Creates and pools ReelSymbol instances.\n *\n * Wraps SymbolRegistry for creation and ObjectPool for recycling.\n * Game code should not need to interact with this directly —\n * it's managed by Reel internally.\n */\nexport class SymbolFactory {\n private _pool: ObjectPool<ReelSymbol>;\n\n constructor(\n private _registry: SymbolRegistry,\n maxPoolPerKey: number = 20,\n ) {\n this._pool = new ObjectPool<ReelSymbol>(\n (key: string) => this._registry.create(key),\n (item: ReelSymbol) => item.reset(),\n (item: ReelSymbol) => item.destroy(),\n maxPoolPerKey,\n );\n }\n\n /** Get a symbol (from pool or newly created), activated with symbolId. */\n acquire(symbolId: string): ReelSymbol {\n const symbol = this._pool.acquire(symbolId);\n if (symbol.symbolId !== symbolId) {\n symbol.activate(symbolId);\n }\n return symbol;\n }\n\n /** Return a symbol to the pool. */\n release(symbol: ReelSymbol): void {\n const id = symbol.symbolId;\n symbol.deactivate();\n this._pool.release(id, symbol);\n }\n\n destroy(): void {\n this._pool.destroy();\n }\n}\n","import type { SymbolData } from '../config/types.js';\n\n/**\n * Weighted random symbol selector using binary search on cumulative weights.\n *\n * Supports exclusion lists for symbols that shouldn't appear during\n * spinning or in buffer areas.\n */\nexport class RandomSymbolProvider {\n private _symbols: string[];\n private _weights: number[];\n private _cumulativeWeights: number[] = [];\n private _totalWeight: number = 0;\n private _excludeSpinning = new Set<string>();\n private _excludeBuffer = new Set<string>();\n\n constructor(symbolsData: Record<string, SymbolData>) {\n this._symbols = Object.keys(symbolsData);\n this._weights = this._symbols.map((id) => symbolsData[id].weight);\n this._rebuildWeights();\n }\n\n /** Get a random symbol, optionally excluding buffer-only symbols. */\n next(useBufferExclusion: boolean = false): string {\n const exclude = useBufferExclusion\n ? new Set([...this._excludeSpinning, ...this._excludeBuffer])\n : this._excludeSpinning;\n\n if (exclude.size === 0) {\n return this._pickWeighted();\n }\n\n // Build filtered weights on the fly for exclusions\n let total = 0;\n const filtered: { id: string; cumWeight: number }[] = [];\n for (let i = 0; i < this._symbols.length; i++) {\n if (exclude.has(this._symbols[i])) continue;\n total += this._weights[i];\n filtered.push({ id: this._symbols[i], cumWeight: total });\n }\n\n if (total === 0 || filtered.length === 0) {\n // Fallback: return first non-excluded symbol\n for (const s of this._symbols) {\n if (!exclude.has(s)) return s;\n }\n return this._symbols[0];\n }\n\n const rand = Math.random() * total;\n // Binary search\n let lo = 0;\n let hi = filtered.length - 1;\n while (lo < hi) {\n const mid = (lo + hi) >> 1;\n if (filtered[mid].cumWeight <= rand) {\n lo = mid + 1;\n } else {\n hi = mid;\n }\n }\n return filtered[lo].id;\n }\n\n /** Set symbols to exclude during spinning. */\n setExcludeSpinning(symbolIds: string[]): void {\n this._excludeSpinning = new Set(symbolIds);\n }\n\n /** Set symbols to exclude from buffer (above/below) areas. */\n setExcludeBuffer(symbolIds: string[]): void {\n this._excludeBuffer = new Set(symbolIds);\n }\n\n /** Update weights at runtime (e.g., for different game modes). */\n updateWeights(symbolsData: Record<string, SymbolData>): void {\n this._symbols = Object.keys(symbolsData);\n this._weights = this._symbols.map((id) => symbolsData[id].weight);\n this._rebuildWeights();\n }\n\n private _rebuildWeights(): void {\n this._cumulativeWeights = [];\n this._totalWeight = 0;\n for (const w of this._weights) {\n this._totalWeight += w;\n this._cumulativeWeights.push(this._totalWeight);\n }\n }\n\n private _pickWeighted(): string {\n const rand = Math.random() * this._totalWeight;\n let lo = 0;\n let hi = this._cumulativeWeights.length - 1;\n while (lo < hi) {\n const mid = (lo + hi) >> 1;\n if (this._cumulativeWeights[mid] <= rand) {\n lo = mid + 1;\n } else {\n hi = mid;\n }\n }\n return this._symbols[lo];\n }\n}\n","import type { RandomSymbolProvider } from './RandomSymbolProvider.js';\n\n/** Context passed through the middleware pipeline. */\nexport interface FrameContext {\n /** Reel column index. */\n readonly reelIndex: number;\n /** Total visible rows. */\n readonly visibleRows: number;\n /** Buffer symbols above visible area. */\n readonly bufferAbove: number;\n /** Buffer symbols below visible area. */\n readonly bufferBelow: number;\n /** The symbol array being built (buffer + visible + buffer). Mutable by middleware. */\n symbols: string[];\n /** Target symbols from setResult() (visible rows only), if available. */\n readonly targetSymbols?: string[];\n /** Whether the reel is currently spinning. */\n readonly isSpinning: boolean;\n /** Arbitrary metadata middleware can use to communicate. */\n metadata: Record<string, unknown>;\n}\n\n/** Middleware that participates in frame building. */\nexport interface FrameMiddleware {\n readonly name: string;\n /** Lower priority runs first. */\n readonly priority: number;\n process(context: FrameContext, next: () => void): void;\n}\n\n/**\n * Builds symbol frames using a middleware pipeline.\n *\n * Built-in middleware handles random fill and target placement.\n * Users can inject custom middleware for features like multiplier encoding\n * or triple-prevention.\n */\nexport class FrameBuilder {\n private _middlewares: FrameMiddleware[] = [];\n private _sorted = false;\n\n constructor(private _randomProvider: RandomSymbolProvider) {\n // Add built-in middleware\n this.use(new RandomFillMiddleware(_randomProvider));\n this.use(new TargetPlacementMiddleware());\n }\n\n /** Add a middleware to the pipeline. */\n use(middleware: FrameMiddleware): this {\n this._middlewares.push(middleware);\n this._sorted = false;\n return this;\n }\n\n /** Remove a middleware by name. */\n remove(name: string): this {\n this._middlewares = this._middlewares.filter((m) => m.name !== name);\n return this;\n }\n\n /** Build a frame for a single reel. */\n build(\n reelIndex: number,\n visibleRows: number,\n bufferAbove: number,\n bufferBelow: number,\n targetSymbols?: string[],\n isSpinning: boolean = false,\n ): string[] {\n if (!this._sorted) {\n this._middlewares.sort((a, b) => a.priority - b.priority);\n this._sorted = true;\n }\n\n const totalSlots = bufferAbove + visibleRows + bufferBelow;\n const context: FrameContext = {\n reelIndex,\n visibleRows,\n bufferAbove,\n bufferBelow,\n symbols: new Array<string>(totalSlots).fill(''),\n targetSymbols,\n isSpinning,\n metadata: {},\n };\n\n // Run middleware chain\n let index = 0;\n const next = (): void => {\n if (index < this._middlewares.length) {\n const mw = this._middlewares[index++];\n mw.process(context, next);\n }\n };\n next();\n\n return context.symbols;\n }\n\n /** Build frames for all reels. */\n buildAll(\n reelCount: number,\n visibleRows: number,\n bufferAbove: number,\n bufferBelow: number,\n targetSymbols?: string[][],\n isSpinning: boolean = false,\n ): string[][] {\n return Array.from({ length: reelCount }, (_, reelIndex) =>\n this.build(\n reelIndex,\n visibleRows,\n bufferAbove,\n bufferBelow,\n targetSymbols?.[reelIndex],\n isSpinning,\n ),\n );\n }\n\n get randomProvider(): RandomSymbolProvider {\n return this._randomProvider;\n }\n}\n\n/** Fills empty symbol slots with random symbols. */\nclass RandomFillMiddleware implements FrameMiddleware {\n readonly name = 'random-fill';\n readonly priority = 0;\n\n constructor(private _provider: RandomSymbolProvider) {}\n\n process(context: FrameContext, next: () => void): void {\n for (let i = 0; i < context.symbols.length; i++) {\n if (!context.symbols[i]) {\n const isBuffer =\n i < context.bufferAbove ||\n i >= context.bufferAbove + context.visibleRows;\n context.symbols[i] = this._provider.next(isBuffer);\n }\n }\n next();\n }\n}\n\n/** Places target symbols (from setResult) into the visible area. */\nclass TargetPlacementMiddleware implements FrameMiddleware {\n readonly name = 'target-placement';\n readonly priority = 10;\n\n process(context: FrameContext, next: () => void): void {\n if (context.targetSymbols) {\n for (let row = 0; row < context.targetSymbols.length; row++) {\n const idx = context.bufferAbove + row;\n if (idx < context.symbols.length) {\n context.symbols[idx] = context.targetSymbols[row];\n }\n }\n }\n next();\n }\n}\n","import type { OffsetConfig, TrapezoidConfig } from '../config/types.js';\n\n/**\n * Computes X-position offsets for symbols to create visual effects\n * like trapezoid perspective.\n */\nexport class OffsetCalculator {\n private _offsets: number[][] = [];\n\n constructor(\n private _reelCount: number,\n private _totalRows: number,\n private _symbolWidth: number,\n private _config: OffsetConfig,\n ) {\n this._compute();\n }\n\n /** Get X offset for a specific reel and row. */\n getOffset(reelIndex: number, rowIndex: number): number {\n return this._offsets[reelIndex]?.[rowIndex] ?? 0;\n }\n\n /** Get all offsets as a 2D array [reelIndex][rowIndex]. */\n get offsets(): readonly (readonly number[])[] {\n return this._offsets;\n }\n\n private _compute(): void {\n if (this._config.mode === 'none') {\n this._offsets = Array.from({ length: this._reelCount }, () =>\n new Array(this._totalRows).fill(0),\n );\n return;\n }\n\n // Trapezoid mode\n const config = this._config as TrapezoidConfig;\n const centralIndex = (this._reelCount - 1) / 2;\n this._offsets = [];\n\n for (let reel = 0; reel < this._reelCount; reel++) {\n const relativePos =\n this._reelCount > 1\n ? (reel - centralIndex) / (this._reelCount / 2)\n : 0;\n\n const reelOffsets: number[] = [];\n for (let row = 0; row < this._totalRows; row++) {\n const rowNorm = this._totalRows > 1 ? row / (this._totalRows - 1) : 0.5;\n const topOffset = relativePos * config.widthDifference * config.topWidthFactor;\n const bottomOffset = relativePos * config.widthDifference * config.bottomWidthFactor;\n const offset = topOffset + (bottomOffset - topOffset) * rowNorm;\n reelOffsets.push(offset);\n }\n this._offsets.push(reelOffsets);\n }\n }\n}\n","import type { Ticker } from 'pixi.js';\nimport type { SpeedProfile, SymbolData, OffsetConfig, ReelSetInternalConfig } from '../config/types.js';\nimport { DEFAULTS } from '../config/defaults.js';\nimport { SpeedPresets } from '../config/SpeedPresets.js';\nimport { ReelSet, type ReelSetParams } from './ReelSet.js';\nimport { Reel, type ReelConfig } from './Reel.js';\nimport { ReelViewport } from './ReelViewport.js';\nimport { SymbolRegistry } from '../symbols/SymbolRegistry.js';\nimport { SymbolFactory } from '../symbols/SymbolFactory.js';\nimport { RandomSymbolProvider } from '../frame/RandomSymbolProvider.js';\nimport { FrameBuilder } from '../frame/FrameBuilder.js';\nimport { OffsetCalculator } from '../frame/OffsetCalculator.js';\nimport { PhaseFactory } from '../spin/phases/PhaseFactory.js';\nimport type { SpinningMode } from '../spin/modes/SpinningMode.js';\nimport { StandardMode } from '../spin/modes/StandardMode.js';\nimport type { FrameMiddleware } from '../frame/FrameBuilder.js';\nimport type { ReelSymbol } from '../symbols/ReelSymbol.js';\n\n/**\n * The configurator you call before every reel set.\n *\n * `ReelSetBuilder` is a fluent, chainable builder: every call returns the\n * builder so you can string setup onto one expression. It exists for two\n * reasons — it hides the twenty-odd subsystems you'd otherwise have to\n * wire by hand, and its `.build()` step validates that every required\n * piece is present (throws at construction, not at first spin).\n *\n * Required calls (in any order): `.reels(n)`, `.visibleSymbols(n)`,\n * `.symbolSize(w, h)`, `.symbols((registry) => ...)`, `.ticker(app.ticker)`.\n * Optional: `.symbolGap()`, `.weights()`, `.symbolData()`, `.speed()`,\n * `.bufferSymbols()`, `.offset()`, `.frameMiddleware()`, `.phases()`,\n * `.spinningMode()`.\n *\n * Reduces ~100 lines of manual wiring to ~10 lines of configuration.\n *\n * ```ts\n * const reelSet = new ReelSetBuilder()\n * .reels(5)\n * .visibleSymbols(3)\n * .symbolSize(200, 200)\n * .symbols((r) => {\n * r.register('cherry', SpriteSymbol, { textures: { cherry: tex } });\n * })\n * .weights({ cherry: 20 })\n * .ticker(app.ticker)\n * .build();\n * ```\n */\nexport class ReelSetBuilder {\n private _reelCount?: number;\n private _visibleRows?: number;\n private _symbolWidth?: number;\n private _symbolHeight?: number;\n private _symbolGap = { ...DEFAULTS.symbolGap };\n private _bufferSymbols = DEFAULTS.bufferSymbols;\n private _symbolRegistry = new SymbolRegistry();\n private _weights: Record<string, number> = {};\n private _speeds = new Map<string, SpeedProfile>();\n private _initialSpeed = DEFAULTS.initialSpeed;\n private _offset: OffsetConfig = { mode: 'none' };\n private _ticker?: Ticker;\n private _spinningMode: SpinningMode = new StandardMode();\n private _phaseFactory = new PhaseFactory();\n private _middlewares: FrameMiddleware[] = [];\n private _initialFrame?: string[][];\n private _symbolDataOverrides: Record<string, Partial<SymbolData>> = {};\n\n /** Set number of reel columns. */\n reels(count: number): this {\n this._reelCount = count;\n return this;\n }\n\n /** Set number of visible symbol rows per reel. */\n visibleSymbols(count: number): this {\n this._visibleRows = count;\n return this;\n }\n\n /** Set symbol dimensions in pixels. */\n symbolSize(width: number, height: number): this {\n this._symbolWidth = width;\n this._symbolHeight = height;\n return this;\n }\n\n /** Set gap between symbols. Default: { x: 0, y: 0 }. */\n symbolGap(x: number, y: number): this {\n this._symbolGap = { x, y };\n return this;\n }\n\n /** Set number of buffer symbols above/below visible area. Default: 1. */\n bufferSymbols(count: number): this {\n this._bufferSymbols = count;\n return this;\n }\n\n /** Configure symbols via a registry callback. */\n symbols(configurator: (registry: SymbolRegistry) => void): this {\n configurator(this._symbolRegistry);\n return this;\n }\n\n /** Set weights for random symbol generation. */\n weights(weights: Record<string, number>): this {\n this._weights = weights;\n return this;\n }\n\n /**\n * Per-symbol metadata overrides (zIndex, unmask, or a custom weight that\n * replaces the one from `weights()`). Merged into the final symbolsData map\n * — any field you don't specify falls back to the default.\n *\n * @example\n * .symbolData({\n * wild: { zIndex: 5 }, // render above neighbours\n * bonus: { zIndex: 10, unmask: true }, // render outside the reel mask\n * })\n */\n symbolData(overrides: Record<string, Partial<SymbolData>>): this {\n this._symbolDataOverrides = { ...this._symbolDataOverrides, ...overrides };\n return this;\n }\n\n /** Add a named speed profile. */\n speed(name: string, profile: SpeedProfile): this {\n this._speeds.set(name, profile);\n return this;\n }\n\n /** Set which speed profile to use initially. Default: 'normal'. */\n initialSpeed(name: string): this {\n this._initialSpeed = name;\n return this;\n }\n\n /** Set X-axis offset config (e.g., trapezoid perspective). Default: 'none'. */\n offsetConfig(config: OffsetConfig): this {\n this._offset = config;\n return this;\n }\n\n /** Set the PixiJS ticker for frame updates. */\n ticker(ticker: Ticker): this {\n this._ticker = ticker;\n return this;\n }\n\n /** Set the spinning mode. Default: StandardMode. */\n spinningMode(mode: SpinningMode): this {\n this._spinningMode = mode;\n return this;\n }\n\n /** Add custom frame middleware. */\n frameMiddleware(middleware: FrameMiddleware): this {\n this._middlewares.push(middleware);\n return this;\n }\n\n /** Override default phases. */\n phases(configurator: (factory: PhaseFactory) => void): this {\n configurator(this._phaseFactory);\n return this;\n }\n\n /** Set the initial symbol grid (visible symbols only). */\n initialFrame(frame: string[][]): this {\n this._initialFrame = frame;\n return this;\n }\n\n /** Build the ReelSet. Validates configuration and assembles all internal objects. */\n build(): ReelSet {\n this._validate();\n\n const reelCount = this._reelCount!;\n const visibleRows = this._visibleRows!;\n const symbolWidth = this._symbolWidth!;\n const symbolHeight = this._symbolHeight!;\n const bufferAbove = this._bufferSymbols;\n const bufferBelow = this._bufferSymbols;\n const ticker = this._ticker!;\n\n // Apply default speed if none registered\n if (this._speeds.size === 0) {\n this._speeds.set('normal', SpeedPresets.NORMAL);\n }\n\n // Build symbols data from weights + per-symbol overrides\n const symbolsData: Record<string, SymbolData> = {};\n const symbolIds = this._symbolRegistry.symbolIds;\n for (const id of symbolIds) {\n const override = this._symbolDataOverrides[id] ?? {};\n symbolsData[id] = {\n weight: override.weight ?? this._weights[id] ?? 10,\n zIndex: override.zIndex ?? 1,\n unmask: override.unmask,\n };\n }\n\n // Build internal config\n const config: ReelSetInternalConfig = {\n grid: {\n reelCount,\n visibleRows,\n symbolWidth,\n symbolHeight,\n symbolGap: { ...this._symbolGap },\n bufferSymbols: this._bufferSymbols,\n },\n symbols: symbolsData,\n speeds: this._speeds,\n initialSpeed: this._initialSpeed,\n offset: this._offset,\n ticker,\n };\n\n // Create subsystems\n const symbolFactory = new SymbolFactory(this._symbolRegistry);\n const randomProvider = new RandomSymbolProvider(symbolsData);\n const frameBuilder = new FrameBuilder(randomProvider);\n\n // Add custom middlewares\n for (const mw of this._middlewares) {\n frameBuilder.use(mw);\n }\n\n // Create viewport\n const viewportWidth = reelCount * (symbolWidth + this._symbolGap.x) - this._symbolGap.x;\n const viewportHeight = visibleRows * (symbolHeight + this._symbolGap.y) - this._symbolGap.y;\n const viewport = new ReelViewport(viewportWidth, viewportHeight);\n\n // Create offset calculator\n const totalRows = bufferAbove + visibleRows + bufferBelow;\n const offsetCalc = new OffsetCalculator(\n reelCount,\n totalRows,\n symbolWidth,\n this._offset,\n );\n\n // Create initial frames\n const initialFrames = this._initialFrame\n ? frameBuilder.buildAll(reelCount, visibleRows, bufferAbove, bufferBelow, this._initialFrame)\n : frameBuilder.buildAll(reelCount, visibleRows, bufferAbove, bufferBelow);\n\n // Create reels\n const reels: Reel[] = [];\n for (let reelIndex = 0; reelIndex < reelCount; reelIndex++) {\n const reelConfig: ReelConfig = {\n reelIndex,\n visibleRows,\n bufferAbove,\n bufferBelow,\n symbolWidth,\n symbolHeight,\n symbolGapX: this._symbolGap.x,\n symbolGapY: this._symbolGap.y,\n symbolsData,\n initialSymbols: initialFrames[reelIndex],\n };\n\n const reel = new Reel(reelConfig, symbolFactory, randomProvider, viewport);\n reels.push(reel);\n }\n\n // Build params\n const params: ReelSetParams = {\n config,\n reels,\n viewport,\n symbolFactory,\n frameBuilder,\n phaseFactory: this._phaseFactory,\n spinningMode: this._spinningMode,\n };\n\n return new ReelSet(params);\n }\n\n private _validate(): void {\n const errors: string[] = [];\n\n if (this._reelCount === undefined || this._reelCount <= 0) {\n errors.push('reels() must be called with a positive number.');\n }\n if (this._visibleRows === undefined || this._visibleRows <= 0) {\n errors.push('visibleSymbols() must be called with a positive number.');\n }\n if (this._symbolWidth === undefined || this._symbolHeight === undefined) {\n errors.push('symbolSize() must be called with width and height.');\n }\n if (this._symbolRegistry.size === 0) {\n errors.push('symbols() must register at least one symbol.');\n }\n if (!this._ticker) {\n errors.push('ticker() must be called with a PixiJS Ticker.');\n }\n if (this._speeds.size > 0 && !this._speeds.has(this._initialSpeed)) {\n errors.push(\n `initialSpeed '${this._initialSpeed}' does not match any registered speed profile. ` +\n `Available: ${[...this._speeds.keys()].join(', ')}`,\n );\n }\n\n if (errors.length > 0) {\n throw new Error(`ReelSetBuilder validation failed:\\n - ${errors.join('\\n - ')}`);\n }\n }\n}\n","import { Sprite, type Texture } from 'pixi.js';\nimport { gsap } from 'gsap';\nimport { ReelSymbol } from './ReelSymbol.js';\n\nexport interface SpriteSymbolOptions {\n /** Map of symbolId → Texture. */\n textures: Record<string, Texture>;\n /** Anchor point. Default: { x: 0.5, y: 0.5 }. */\n anchor?: { x: number; y: number };\n}\n\n/**\n * Symbol implementation using a simple PixiJS Sprite.\n * Swaps texture on activate. Win animation is a scale pulse via GSAP.\n */\nexport class SpriteSymbol extends ReelSymbol {\n private _sprite: Sprite;\n private _textures: Record<string, Texture>;\n private _winTween: gsap.core.Tween | null = null;\n\n constructor(options: SpriteSymbolOptions) {\n super();\n this._textures = options.textures;\n const anchor = options.anchor ?? { x: 0, y: 0 };\n this._sprite = new Sprite();\n this._sprite.anchor.set(anchor.x, anchor.y);\n this.view.addChild(this._sprite);\n }\n\n protected onActivate(symbolId: string): void {\n const texture = this._textures[symbolId];\n if (texture) {\n this._sprite.texture = texture;\n }\n }\n\n protected onDeactivate(): void {\n this._killWinTween();\n this._sprite.scale.set(1, 1);\n }\n\n async playWin(): Promise<void> {\n this._killWinTween();\n return new Promise<void>((resolve) => {\n this._winTween = gsap.to(this._sprite.scale, {\n x: 1.15,\n y: 1.15,\n duration: 0.15,\n yoyo: true,\n repeat: 1,\n ease: 'power2.inOut',\n onComplete: resolve,\n });\n });\n }\n\n stopAnimation(): void {\n this._killWinTween();\n this._sprite.scale.set(1, 1);\n }\n\n resize(width: number, height: number): void {\n this._sprite.width = width;\n this._sprite.height = height;\n }\n\n protected override onDestroy(): void {\n this._killWinTween();\n }\n\n private _killWinTween(): void {\n if (this._winTween) {\n this._winTween.kill();\n this._winTween = null;\n }\n }\n}\n","import { AnimatedSprite, type Texture } from 'pixi.js';\nimport { ReelSymbol } from './ReelSymbol.js';\n\nexport interface AnimatedSpriteSymbolOptions {\n /** Map of symbolId → array of frame textures. */\n frames: Record<string, Texture[]>;\n /** Playback speed (frames per second multiplier). Default: 1. */\n animationSpeed?: number;\n /** Anchor point. Default: { x: 0.5, y: 0.5 }. */\n anchor?: { x: number; y: number };\n}\n\n/**\n * Symbol implementation using PixiJS AnimatedSprite.\n * Swaps frame arrays on activate. Win animation plays the full sequence.\n */\nexport class AnimatedSpriteSymbol extends ReelSymbol {\n private _animSprite: AnimatedSprite;\n private _frames: Record<string, Texture[]>;\n private _animationSpeed: number;\n private _winResolve: (() => void) | null = null;\n\n constructor(options: AnimatedSpriteSymbolOptions) {\n super();\n this._frames = options.frames;\n this._animationSpeed = options.animationSpeed ?? 1;\n const anchor = options.anchor ?? { x: 0, y: 0 };\n\n // Start with the first available frame set\n const firstFrames = Object.values(this._frames)[0] ?? [];\n this._animSprite = new AnimatedSprite(firstFrames.length > 0 ? firstFrames : []);\n this._animSprite.anchor.set(anchor.x, anchor.y);\n this._animSprite.animationSpeed = this._animationSpeed;\n this._animSprite.loop = false;\n this.view.addChild(this._animSprite);\n }\n\n protected onActivate(symbolId: string): void {\n const frames = this._frames[symbolId];\n if (frames && frames.length > 0) {\n this._animSprite.textures = frames;\n this._animSprite.gotoAndStop(0);\n }\n }\n\n protected onDeactivate(): void {\n this._animSprite.stop();\n this._winResolve = null;\n }\n\n async playWin(): Promise<void> {\n return new Promise<void>((resolve) => {\n this._winResolve = resolve;\n this._animSprite.loop = false;\n this._animSprite.onComplete = () => {\n this._winResolve = null;\n this._animSprite.onComplete = undefined;\n resolve();\n };\n this._animSprite.gotoAndPlay(0);\n });\n }\n\n stopAnimation(): void {\n this._animSprite.stop();\n this._animSprite.gotoAndStop(0);\n if (this._winResolve) {\n this._winResolve();\n this._winResolve = null;\n }\n }\n\n resize(width: number, height: number): void {\n this._animSprite.width = width;\n this._animSprite.height = height;\n }\n}\n","import type { SpinningMode } from './SpinningMode.js';\n\n/**\n * Cascade/tumble spinning mode.\n * Symbols fall from above with gravity-like acceleration,\n * used for tumble/avalanche mechanics.\n */\nexport class CascadeMode implements SpinningMode {\n readonly name = 'cascade';\n\n private _gravity: number;\n\n /**\n * @param gravity - Gravity acceleration factor. Default: 1.5.\n */\n constructor(gravity: number = 1.5) {\n this._gravity = gravity;\n }\n\n computeDeltaY(symbolHeight: number, speed: number, deltaMs: number): number {\n const raw = (symbolHeight * speed * this._gravity * deltaMs) / 1000;\n return Math.min(raw, symbolHeight);\n }\n}\n","import type { SpinningMode } from './SpinningMode.js';\n\n/**\n * Immediate placement mode for super-turbo skip.\n * Symbols snap to position instantly — no actual spinning animation.\n */\nexport class ImmediateMode implements SpinningMode {\n readonly name = 'immediate';\n\n computeDeltaY(_symbolHeight: number, _speed: number, _deltaMs: number): number {\n // No movement — placement is handled directly by the phase\n return 0;\n }\n}\n","import type { ReelSet } from '../core/ReelSet.js';\nimport type { Reel } from '../core/Reel.js';\n\n/**\n * Debug snapshot — plain JSON representation of the entire reel state.\n *\n * Designed for AI agents that cannot see the canvas.\n * Returns no PixiJS display objects, only serializable data.\n */\nexport interface DebugSnapshot {\n timestamp: number;\n isSpinning: boolean;\n currentSpeed: string;\n availableSpeeds: string[];\n spotlightActive: boolean;\n reelCount: number;\n visibleRows: number;\n reels: DebugReelSnapshot[];\n grid: string[][];\n}\n\nexport interface DebugReelSnapshot {\n index: number;\n speed: number;\n isStopping: boolean;\n allSymbols: { row: number; symbolId: string; y: number }[];\n visibleSymbols: string[];\n}\n\n/**\n * Take a plain-JSON snapshot of the entire reel set state.\n *\n * This is the primary debugging tool for AI agents. The output is\n * a serializable object with no circular references, no PixiJS types.\n *\n * ```ts\n * const state = debugSnapshot(reelSet);\n * console.log(JSON.stringify(state, null, 2));\n * ```\n */\nexport function debugSnapshot(reelSet: ReelSet): DebugSnapshot {\n const reels = reelSet.reels;\n const reelSnapshots: DebugReelSnapshot[] = reels.map((reel: Reel, i: number) => ({\n index: i,\n speed: reel.speed,\n isStopping: reel.isStopping,\n allSymbols: reel.symbols.map((s, row) => ({\n row,\n symbolId: s.symbolId,\n y: Math.round(s.view.y),\n })),\n visibleSymbols: reel.getVisibleSymbols(),\n }));\n\n // Build the visual grid (what a player would see)\n const grid: string[][] = [];\n for (const reelSnap of reelSnapshots) {\n grid.push(reelSnap.visibleSymbols);\n }\n\n return {\n timestamp: Date.now(),\n isSpinning: reelSet.isSpinning,\n currentSpeed: reelSet.speed.activeName,\n availableSpeeds: reelSet.speed.profileNames,\n spotlightActive: reelSet.spotlight.isActive,\n reelCount: reels.length,\n visibleRows: reels[0]?.getVisibleSymbols().length ?? 0,\n reels: reelSnapshots,\n grid,\n };\n}\n\n/**\n * Pretty-print the grid as an ASCII table.\n *\n * ```\n * ┌────────┬────────┬────────┬────────┬────────┐\n * │ cherry │ lemon │ bar │ seven │ cherry │\n * │ plum │ cherry │ wild │ lemon │ orange │\n * │ orange │ bell │ cherry │ plum │ bell │\n * └────────┴────────┴────────┴────────┴────────┘\n * ```\n */\nexport function debugGrid(reelSet: ReelSet): string {\n const snap = debugSnapshot(reelSet);\n const { grid, visibleRows } = snap;\n if (grid.length === 0) return '(empty grid)';\n\n const colWidth = 8;\n const pad = (s: string) => s.slice(0, colWidth).padEnd(colWidth);\n\n const border = (left: string, mid: string, right: string) =>\n left + grid.map(() => '─'.repeat(colWidth)).join(mid) + right;\n\n const lines: string[] = [];\n lines.push(border('┌', '┬', '┐'));\n\n for (let row = 0; row < visibleRows; row++) {\n const cells = grid.map((col) => pad(col[row] ?? '?'));\n lines.push('│' + cells.join('│') + '│');\n }\n\n lines.push(border('└', '┴', '┘'));\n return lines.join('\\n');\n}\n\n/**\n * Enable debug mode: attaches debug utilities to `window.__PIXI_REELS_DEBUG`.\n *\n * After calling this, an AI agent can run in the browser console:\n * ```js\n * __PIXI_REELS_DEBUG.snapshot() // full state JSON\n * __PIXI_REELS_DEBUG.grid() // ASCII grid\n * __PIXI_REELS_DEBUG.log() // console.log the grid\n * ```\n */\nexport function enableDebug(reelSet: ReelSet): void {\n if (typeof window === 'undefined') return;\n\n const debug = {\n reelSet,\n snapshot: () => debugSnapshot(reelSet),\n grid: () => debugGrid(reelSet),\n log: () => {\n const snap = debugSnapshot(reelSet);\n console.log(`[pixi-reels debug] spinning=${snap.isSpinning} speed=${snap.currentSpeed}`);\n console.log(debugGrid(reelSet));\n return snap;\n },\n /** Log every event as it happens */\n trace: () => {\n const events = [\n 'spin:start', 'spin:allStarted', 'spin:stopping',\n 'spin:reelLanded', 'spin:allLanded', 'spin:complete',\n 'skip:requested', 'skip:completed', 'speed:changed',\n 'spotlight:start', 'spotlight:end', 'destroyed',\n ] as const;\n for (const event of events) {\n reelSet.events.on(event as any, (...args: any[]) => {\n console.log(`[pixi-reels] ${event}`, ...args);\n });\n }\n console.log('[pixi-reels debug] tracing enabled for all events');\n },\n };\n\n (window as any).__PIXI_REELS_DEBUG = debug;\n console.log('[pixi-reels] Debug mode enabled. Use __PIXI_REELS_DEBUG.log() to inspect state.');\n}\n","import type { Ticker } from 'pixi.js';\n\ntype TickerCallback = (ticker: Ticker) => void;\n\n/**\n * Minimal drop-in replacement for `PIXI.Ticker` for tests.\n *\n * Exposes the exact surface `pixi-reels` uses internally (`add`, `remove`,\n * `deltaMS`) plus a manual `tick(deltaMs)` method so tests can advance time\n * deterministically without depending on requestAnimationFrame.\n *\n * ```ts\n * const ticker = new FakeTicker();\n * const reelSet = new ReelSetBuilder()\n * .ticker(ticker as unknown as PIXI.Ticker)\n * ...\n * .build();\n *\n * ticker.tick(16); // advance one frame\n * ticker.tickFor(500); // advance 500ms in 16ms frames\n * ```\n */\nexport class FakeTicker {\n public deltaMS = 16;\n public deltaTime = 1;\n public elapsedMS = 0;\n public lastTime = 0;\n public speed = 1;\n public started = false;\n public FPS = 60;\n public minFPS = 10;\n public maxFPS = 0;\n\n private _callbacks: TickerCallback[] = [];\n\n add(fn: TickerCallback): this {\n this._callbacks.push(fn);\n return this;\n }\n\n addOnce(fn: TickerCallback): this {\n const wrapped: TickerCallback = (t) => {\n this.remove(wrapped);\n fn(t);\n };\n return this.add(wrapped);\n }\n\n remove(fn: TickerCallback): this {\n const i = this._callbacks.indexOf(fn);\n if (i !== -1) this._callbacks.splice(i, 1);\n return this;\n }\n\n start(): this {\n this.started = true;\n return this;\n }\n\n stop(): this {\n this.started = false;\n return this;\n }\n\n destroy(): void {\n this._callbacks.length = 0;\n this.started = false;\n }\n\n /** Manually advance time by `deltaMs` milliseconds and fire all listeners. */\n tick(deltaMs = 16): void {\n this.deltaMS = deltaMs;\n this.deltaTime = deltaMs / (1000 / 60);\n this.elapsedMS += deltaMs;\n this.lastTime += deltaMs;\n const snapshot = this._callbacks.slice();\n for (const cb of snapshot) {\n cb(this as unknown as Ticker);\n }\n }\n\n /** Advance time by `totalMs`, chopped into `stepMs` frames. */\n tickFor(totalMs: number, stepMs = 16): void {\n let remaining = totalMs;\n while (remaining > 0) {\n const step = Math.min(stepMs, remaining);\n this.tick(step);\n remaining -= step;\n }\n }\n\n get listenerCount(): number {\n return this._callbacks.length;\n }\n}\n","import { ReelSymbol } from '../symbols/ReelSymbol.js';\n\n/**\n * A `ReelSymbol` implementation that does no rendering at all.\n *\n * It creates a `Container` for its `view` (so the Reel's child-tree logic works)\n * but never paints anything. This lets tests build a real `ReelSet` without a\n * renderer, textures, or assets.\n *\n * ```ts\n * builder.symbols((r) => {\n * for (const id of ['cherry', 'lemon', 'seven']) {\n * r.register(id, HeadlessSymbol, {});\n * }\n * });\n * ```\n */\nexport class HeadlessSymbol extends ReelSymbol {\n private _width = 0;\n private _height = 0;\n\n protected onActivate(_symbolId: string): void {\n // no-op\n }\n\n protected onDeactivate(): void {\n // no-op\n }\n\n async playWin(): Promise<void> {\n // resolve immediately\n }\n\n stopAnimation(): void {\n // no-op\n }\n\n resize(width: number, height: number): void {\n this._width = width;\n this._height = height;\n }\n\n get width(): number {\n return this._width;\n }\n\n get height(): number {\n return this._height;\n }\n}\n","import type { Ticker } from 'pixi.js';\nimport { ReelSetBuilder } from '../core/ReelSetBuilder.js';\nimport type { ReelSet } from '../core/ReelSet.js';\nimport type { SpinResult } from '../events/ReelEvents.js';\nimport { debugSnapshot, debugGrid } from '../debug/debug.js';\nimport { FakeTicker } from './FakeTicker.js';\nimport { HeadlessSymbol } from './HeadlessSymbol.js';\n\nexport interface TestReelSetOptions {\n reels?: number;\n visibleRows?: number;\n symbolIds?: string[];\n weights?: Record<string, number>;\n symbolSize?: { width: number; height: number };\n}\n\nexport interface TestReelSetHandle {\n reelSet: ReelSet;\n ticker: FakeTicker;\n /** Advance the ticker by `ms` milliseconds. */\n advance(ms: number, stepMs?: number): void;\n /** Run one full spin that lands on `grid`. Uses `skip()` for deterministic synchronous completion. */\n spinAndLand(grid: string[][]): Promise<SpinResult>;\n /** Destroy the reel set. */\n destroy(): void;\n}\n\n/**\n * Build a headless `ReelSet` wired to a `FakeTicker`. Ideal for mechanic tests.\n *\n * The returned `ReelSet` uses `HeadlessSymbol` for every registered symbol,\n * so no textures, renderer, or DOM are required.\n *\n * ```ts\n * const { reelSet, spinAndLand } = createTestReelSet({\n * reels: 5, visibleRows: 3,\n * symbolIds: ['cherry', 'seven', 'wild'],\n * });\n *\n * await spinAndLand([\n * ['cherry','cherry','cherry'],\n * ['seven','seven','seven'],\n * ['wild','wild','wild'],\n * ['cherry','cherry','cherry'],\n * ['seven','seven','seven'],\n * ]);\n * ```\n */\nexport function createTestReelSet(opts: TestReelSetOptions = {}): TestReelSetHandle {\n const reels = opts.reels ?? 5;\n const visibleRows = opts.visibleRows ?? 3;\n const symbolIds = opts.symbolIds ?? ['a', 'b', 'c'];\n const weights = opts.weights ?? {};\n const size = opts.symbolSize ?? { width: 100, height: 100 };\n\n const ticker = new FakeTicker();\n\n const builder = new ReelSetBuilder()\n .reels(reels)\n .visibleSymbols(visibleRows)\n .symbolSize(size.width, size.height)\n .ticker(ticker as unknown as Ticker)\n .symbols((registry) => {\n for (const id of symbolIds) {\n registry.register(id, HeadlessSymbol, {});\n }\n });\n\n if (Object.keys(weights).length > 0) {\n builder.weights(weights);\n }\n\n const reelSet = builder.build();\n\n return {\n reelSet,\n ticker,\n advance(ms: number, stepMs = 16) {\n ticker.tickFor(ms, stepMs);\n },\n async spinAndLand(grid: string[][]) {\n return spinAndLand(reelSet, grid);\n },\n destroy() {\n reelSet.destroy();\n ticker.destroy();\n },\n };\n}\n\n/**\n * Deterministically run a spin to a target grid.\n *\n * Internally: `spin() → setResult(grid) → skip()`. The `skip()` path bypasses\n * all async phases and directly places the symbols, so the returned promise\n * resolves on a microtask.\n */\nexport async function spinAndLand(reelSet: ReelSet, grid: string[][]): Promise<SpinResult> {\n const promise = reelSet.spin();\n reelSet.setResult(grid);\n reelSet.skip();\n return promise;\n}\n\n/** Record every occurrence of the given events in order for assertion. */\nexport function captureEvents(\n reelSet: ReelSet,\n names: Array<keyof import('../events/ReelEvents.js').ReelSetEvents>,\n): Array<{ event: string; args: unknown[] }> {\n const log: Array<{ event: string; args: unknown[] }> = [];\n for (const name of names) {\n reelSet.events.on(name, (...args: unknown[]) => {\n log.push({ event: name as string, args });\n });\n }\n return log;\n}\n\n/**\n * Assert that the current visible grid equals `expected`.\n *\n * Throws a readable error showing the full current grid on mismatch.\n */\nexport function expectGrid(reelSet: ReelSet, expected: string[][]): void {\n const actual = debugSnapshot(reelSet).grid;\n const mismatches: string[] = [];\n\n if (actual.length !== expected.length) {\n throw new Error(\n `Grid reel count mismatch: expected ${expected.length} got ${actual.length}\\n${debugGrid(reelSet)}`,\n );\n }\n\n for (let r = 0; r < expected.length; r++) {\n for (let row = 0; row < expected[r].length; row++) {\n if (expected[r][row] !== actual[r][row]) {\n mismatches.push(\n ` reel ${r} row ${row}: expected \"${expected[r][row]}\" got \"${actual[r][row]}\"`,\n );\n }\n }\n }\n\n if (mismatches.length > 0) {\n throw new Error(\n `Grid mismatch:\\n${mismatches.join('\\n')}\\n\\nCurrent grid:\\n${debugGrid(reelSet)}`,\n );\n }\n}\n\n/**\n * Count how many times a given symbol appears in the visible grid.\n * Handy for scatter/wild-count assertions.\n */\nexport function countSymbol(reelSet: ReelSet, symbolId: string): number {\n let n = 0;\n for (const col of debugSnapshot(reelSet).grid) {\n for (const s of col) if (s === symbolId) n++;\n }\n return n;\n}\n"],"mappings":"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"}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { Disposable } from '../utils/Disposable.js';
|
|
2
|
+
/**
|
|
3
|
+
* Generic object pool for reusing expensive-to-create objects.
|
|
4
|
+
*
|
|
5
|
+
* Reduces GC pressure by recycling objects instead of creating/destroying them each frame.
|
|
6
|
+
* Used internally for ReelSymbol instances and available to game code for trails, particles, etc.
|
|
7
|
+
*
|
|
8
|
+
* @typeParam T - The type of object to pool.
|
|
9
|
+
*/
|
|
10
|
+
export declare class ObjectPool<T> implements Disposable {
|
|
11
|
+
private _factory;
|
|
12
|
+
private _reset?;
|
|
13
|
+
private _dispose?;
|
|
14
|
+
private _maxPerKey;
|
|
15
|
+
private _pools;
|
|
16
|
+
private _isDestroyed;
|
|
17
|
+
constructor(_factory: (key: string) => T, _reset?: ((item: T) => void) | undefined, _dispose?: ((item: T) => void) | undefined, _maxPerKey?: number);
|
|
18
|
+
get isDestroyed(): boolean;
|
|
19
|
+
/**
|
|
20
|
+
* Get an object from the pool, or create a new one if the pool is empty.
|
|
21
|
+
*/
|
|
22
|
+
acquire(key: string): T;
|
|
23
|
+
/**
|
|
24
|
+
* Return an object to the pool for reuse.
|
|
25
|
+
* If the pool is at capacity, the object is disposed instead.
|
|
26
|
+
*/
|
|
27
|
+
release(key: string, item: T): void;
|
|
28
|
+
/** Get the number of pooled items for a key. */
|
|
29
|
+
size(key: string): number;
|
|
30
|
+
/** Get total pooled items across all keys. */
|
|
31
|
+
get totalSize(): number;
|
|
32
|
+
/** Clear all pooled items, calling dispose on each. */
|
|
33
|
+
clear(): void;
|
|
34
|
+
destroy(): void;
|
|
35
|
+
}
|
|
36
|
+
//# sourceMappingURL=ObjectPool.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ObjectPool.d.ts","sourceRoot":"","sources":["../../src/pool/ObjectPool.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,wBAAwB,CAAC;AAEzD;;;;;;;GAOG;AACH,qBAAa,UAAU,CAAC,CAAC,CAAE,YAAW,UAAU;IAK5C,OAAO,CAAC,QAAQ;IAChB,OAAO,CAAC,MAAM,CAAC;IACf,OAAO,CAAC,QAAQ,CAAC;IACjB,OAAO,CAAC,UAAU;IAPpB,OAAO,CAAC,MAAM,CAA0B;IACxC,OAAO,CAAC,YAAY,CAAS;gBAGnB,QAAQ,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,CAAC,EAC5B,MAAM,CAAC,GAAE,CAAC,IAAI,EAAE,CAAC,KAAK,IAAI,aAAA,EAC1B,QAAQ,CAAC,GAAE,CAAC,IAAI,EAAE,CAAC,KAAK,IAAI,aAAA,EAC5B,UAAU,GAAE,MAAW;IAGjC,IAAI,WAAW,IAAI,OAAO,CAEzB;IAED;;OAEG;IACH,OAAO,CAAC,GAAG,EAAE,MAAM,GAAG,CAAC;IAUvB;;;OAGG;IACH,OAAO,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC,GAAG,IAAI;IAanC,gDAAgD;IAChD,IAAI,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM;IAIzB,8CAA8C;IAC9C,IAAI,SAAS,IAAI,MAAM,CAMtB;IAED,uDAAuD;IACvD,KAAK,IAAI,IAAI;IAWb,OAAO,IAAI,IAAI;CAKhB"}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { SpeedProfile } from '../config/types.js';
|
|
2
|
+
/**
|
|
3
|
+
* The tempo of your reels, as named presets.
|
|
4
|
+
*
|
|
5
|
+
* A `SpeedProfile` is a bundle of timings — how long the wind-up takes,
|
|
6
|
+
* how fast the reel scrolls at full speed, how deep the landing bounce
|
|
7
|
+
* is, which GSAP easing drives each transition. `SpeedManager` holds
|
|
8
|
+
* those profiles by name and tracks which one is active.
|
|
9
|
+
*
|
|
10
|
+
* Built-in profiles: `normal` (default), `turbo`, `superTurbo`. Add your
|
|
11
|
+
* own via `reelSet.speed.addProfile('cinematic', {...})`. Switch at
|
|
12
|
+
* runtime with `reelSet.setSpeed('turbo')`.
|
|
13
|
+
*
|
|
14
|
+
* Speed changes take effect on the next spin — mid-spin switching
|
|
15
|
+
* is deliberately not supported to keep animation state simple.
|
|
16
|
+
*/
|
|
17
|
+
export declare class SpeedManager {
|
|
18
|
+
private _profiles;
|
|
19
|
+
private _activeName;
|
|
20
|
+
private _active;
|
|
21
|
+
constructor(profiles: Map<string, SpeedProfile>, initialSpeed: string);
|
|
22
|
+
/** The currently active speed profile. */
|
|
23
|
+
get active(): Readonly<SpeedProfile>;
|
|
24
|
+
/** Name of the currently active speed profile. */
|
|
25
|
+
get activeName(): string;
|
|
26
|
+
/** Switch to a different named speed profile. */
|
|
27
|
+
set(name: string): {
|
|
28
|
+
previous: SpeedProfile;
|
|
29
|
+
current: SpeedProfile;
|
|
30
|
+
};
|
|
31
|
+
/** Add or replace a speed profile. */
|
|
32
|
+
addProfile(name: string, profile: SpeedProfile): void;
|
|
33
|
+
/** Get a profile by name, or undefined if not found. */
|
|
34
|
+
getProfile(name: string): SpeedProfile | undefined;
|
|
35
|
+
/** All registered profile names. */
|
|
36
|
+
get profileNames(): string[];
|
|
37
|
+
}
|
|
38
|
+
//# sourceMappingURL=SpeedManager.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"SpeedManager.d.ts","sourceRoot":"","sources":["../../src/speed/SpeedManager.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAEvD;;;;;;;;;;;;;;GAcG;AACH,qBAAa,YAAY;IACvB,OAAO,CAAC,SAAS,CAAmC;IACpD,OAAO,CAAC,WAAW,CAAS;IAC5B,OAAO,CAAC,OAAO,CAAe;gBAElB,QAAQ,EAAE,GAAG,CAAC,MAAM,EAAE,YAAY,CAAC,EAAE,YAAY,EAAE,MAAM;IAcrE,0CAA0C;IAC1C,IAAI,MAAM,IAAI,QAAQ,CAAC,YAAY,CAAC,CAEnC;IAED,kDAAkD;IAClD,IAAI,UAAU,IAAI,MAAM,CAEvB;IAED,iDAAiD;IACjD,GAAG,CAAC,IAAI,EAAE,MAAM,GAAG;QAAE,QAAQ,EAAE,YAAY,CAAC;QAAC,OAAO,EAAE,YAAY,CAAA;KAAE;IAapE,sCAAsC;IACtC,UAAU,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,YAAY,GAAG,IAAI;IAIrD,wDAAwD;IACxD,UAAU,CAAC,IAAI,EAAE,MAAM,GAAG,YAAY,GAAG,SAAS;IAIlD,oCAAoC;IACpC,IAAI,YAAY,IAAI,MAAM,EAAE,CAE3B;CACF"}
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import { Ticker } from 'pixi.js';
|
|
2
|
+
import { Reel } from '../core/Reel.js';
|
|
3
|
+
import { SpeedManager } from '../speed/SpeedManager.js';
|
|
4
|
+
import { FrameBuilder } from '../frame/FrameBuilder.js';
|
|
5
|
+
import { SpinResult, ReelSetEvents } from '../events/ReelEvents.js';
|
|
6
|
+
import { EventEmitter } from '../events/EventEmitter.js';
|
|
7
|
+
import { PhaseFactory } from './phases/PhaseFactory.js';
|
|
8
|
+
import { SpinningMode } from './modes/SpinningMode.js';
|
|
9
|
+
import { Disposable } from '../utils/Disposable.js';
|
|
10
|
+
/**
|
|
11
|
+
* The conductor of a spin.
|
|
12
|
+
*
|
|
13
|
+
* A reel set has many moving parts; the `SpinController` is the single
|
|
14
|
+
* brain that drives them in time. On `spin()` it walks every reel through
|
|
15
|
+
* its phase state machine (`StartPhase` → `SpinPhase` → optional
|
|
16
|
+
* `AnticipationPhase` → `StopPhase`), applies the per-reel staggered
|
|
17
|
+
* delays from the `SpeedProfile`, and resolves a promise when the last
|
|
18
|
+
* reel lands (or the spin is skipped).
|
|
19
|
+
*
|
|
20
|
+
* It does not draw anything — drawing lives on `Reel` and `ReelSymbol`.
|
|
21
|
+
* It does not decide outcomes — that's `setResult(grid)` coming in from
|
|
22
|
+
* your game code. Its one job is timing.
|
|
23
|
+
*
|
|
24
|
+
* Every interesting moment fires on the event bus:
|
|
25
|
+
* `spin:start`, `spin:allStarted`, `spin:stopping`, `spin:reelLanded`,
|
|
26
|
+
* `spin:allLanded`, `spin:complete`, `skip:requested`, `skip:completed`.
|
|
27
|
+
*/
|
|
28
|
+
export declare class SpinController implements Disposable {
|
|
29
|
+
private _reels;
|
|
30
|
+
private _speedManager;
|
|
31
|
+
private _frameBuilder;
|
|
32
|
+
private _phaseFactory;
|
|
33
|
+
private _events;
|
|
34
|
+
private _tickerRef;
|
|
35
|
+
private _spinningMode;
|
|
36
|
+
private _isSpinning;
|
|
37
|
+
private _spinStartTime;
|
|
38
|
+
private _resultSymbols;
|
|
39
|
+
private _anticipationReels;
|
|
40
|
+
private _stopDelayOverride;
|
|
41
|
+
private _activePhases;
|
|
42
|
+
private _landedReels;
|
|
43
|
+
private _wasSkipped;
|
|
44
|
+
private _isDestroyed;
|
|
45
|
+
private _currentSpinResolve;
|
|
46
|
+
/** Incremented on each new spin. If a callback sees a stale generation, it no-ops. */
|
|
47
|
+
private _spinGeneration;
|
|
48
|
+
constructor(reels: Reel[], speedManager: SpeedManager, frameBuilder: FrameBuilder, phaseFactory: PhaseFactory, events: EventEmitter<ReelSetEvents>, ticker: Ticker, spinningMode?: SpinningMode);
|
|
49
|
+
get isSpinning(): boolean;
|
|
50
|
+
get isDestroyed(): boolean;
|
|
51
|
+
spin(): Promise<SpinResult>;
|
|
52
|
+
setResult(symbols: string[][]): void;
|
|
53
|
+
setAnticipation(reelIndices: number[]): void;
|
|
54
|
+
/**
|
|
55
|
+
* Override the per-reel stop delay (in ms). Pass one value per reel.
|
|
56
|
+
* When set, these replace the staggered `reelIndex * speed.stopDelay`
|
|
57
|
+
* pattern for the current spin. Cleared at the start of each new spin.
|
|
58
|
+
*/
|
|
59
|
+
setStopDelays(delays: number[]): void;
|
|
60
|
+
skip(): void;
|
|
61
|
+
destroy(): void;
|
|
62
|
+
private _startReel;
|
|
63
|
+
private _stopDelayFor;
|
|
64
|
+
private _cachedFrames;
|
|
65
|
+
private _frameFor;
|
|
66
|
+
private _tryBeginStopSequence;
|
|
67
|
+
private _markLanded;
|
|
68
|
+
private _finishSpin;
|
|
69
|
+
private _onTick;
|
|
70
|
+
}
|
|
71
|
+
//# sourceMappingURL=SpinController.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"SpinController.d.ts","sourceRoot":"","sources":["../../src/spin/SpinController.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,SAAS,CAAC;AACtC,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,iBAAiB,CAAC;AAE5C,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,0BAA0B,CAAC;AAC7D,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,0BAA0B,CAAC;AAC7D,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,yBAAyB,CAAC;AAC1D,OAAO,EAAE,YAAY,EAAE,MAAM,2BAA2B,CAAC;AACzD,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,yBAAyB,CAAC;AAC7D,OAAO,EAAE,YAAY,EAAE,MAAM,0BAA0B,CAAC;AAMxD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,yBAAyB,CAAC;AAE5D,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,wBAAwB,CAAC;AAGzD;;;;;;;;;;;;;;;;;GAiBG;AACH,qBAAa,cAAe,YAAW,UAAU;IAC/C,OAAO,CAAC,MAAM,CAAS;IACvB,OAAO,CAAC,aAAa,CAAe;IACpC,OAAO,CAAC,aAAa,CAAe;IACpC,OAAO,CAAC,aAAa,CAAe;IACpC,OAAO,CAAC,OAAO,CAA8B;IAC7C,OAAO,CAAC,UAAU,CAAY;IAC9B,OAAO,CAAC,aAAa,CAAe;IAEpC,OAAO,CAAC,WAAW,CAAS;IAC5B,OAAO,CAAC,cAAc,CAAK;IAC3B,OAAO,CAAC,cAAc,CAA2B;IACjD,OAAO,CAAC,kBAAkB,CAAgB;IAC1C,OAAO,CAAC,kBAAkB,CAAyB;IACnD,OAAO,CAAC,aAAa,CAA0C;IAC/D,OAAO,CAAC,YAAY,CAAqB;IACzC,OAAO,CAAC,WAAW,CAAS;IAC5B,OAAO,CAAC,YAAY,CAAS;IAC7B,OAAO,CAAC,mBAAmB,CAA+C;IAC1E,sFAAsF;IACtF,OAAO,CAAC,eAAe,CAAK;gBAG1B,KAAK,EAAE,IAAI,EAAE,EACb,YAAY,EAAE,YAAY,EAC1B,YAAY,EAAE,YAAY,EAC1B,YAAY,EAAE,YAAY,EAC1B,MAAM,EAAE,YAAY,CAAC,aAAa,CAAC,EACnC,MAAM,EAAE,MAAM,EACd,YAAY,CAAC,EAAE,YAAY;IAa7B,IAAI,UAAU,IAAI,OAAO,CAExB;IAED,IAAI,WAAW,IAAI,OAAO,CAEzB;IAEK,IAAI,IAAI,OAAO,CAAC,UAAU,CAAC;IA+BjC,SAAS,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE,GAAG,IAAI;IAMpC,eAAe,CAAC,WAAW,EAAE,MAAM,EAAE,GAAG,IAAI;IAI5C;;;;OAIG;IACH,aAAa,CAAC,MAAM,EAAE,MAAM,EAAE,GAAG,IAAI;IAIrC,IAAI,IAAI,IAAI;IAoCZ,OAAO,IAAI,IAAI;YASD,UAAU;IAsDxB,OAAO,CAAC,aAAa;IAOrB,OAAO,CAAC,aAAa,CAA2B;IAEhD,OAAO,CAAC,SAAS;IAKjB,OAAO,CAAC,qBAAqB;IAiC7B,OAAO,CAAC,WAAW;IAcnB,OAAO,CAAC,WAAW;IAoBnB,OAAO,CAAC,OAAO;CAahB"}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { SpinningMode } from './SpinningMode.js';
|
|
2
|
+
/**
|
|
3
|
+
* Cascade/tumble spinning mode.
|
|
4
|
+
* Symbols fall from above with gravity-like acceleration,
|
|
5
|
+
* used for tumble/avalanche mechanics.
|
|
6
|
+
*/
|
|
7
|
+
export declare class CascadeMode implements SpinningMode {
|
|
8
|
+
readonly name = "cascade";
|
|
9
|
+
private _gravity;
|
|
10
|
+
/**
|
|
11
|
+
* @param gravity - Gravity acceleration factor. Default: 1.5.
|
|
12
|
+
*/
|
|
13
|
+
constructor(gravity?: number);
|
|
14
|
+
computeDeltaY(symbolHeight: number, speed: number, deltaMs: number): number;
|
|
15
|
+
}
|
|
16
|
+
//# sourceMappingURL=CascadeMode.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"CascadeMode.d.ts","sourceRoot":"","sources":["../../../src/spin/modes/CascadeMode.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AAEtD;;;;GAIG;AACH,qBAAa,WAAY,YAAW,YAAY;IAC9C,QAAQ,CAAC,IAAI,aAAa;IAE1B,OAAO,CAAC,QAAQ,CAAS;IAEzB;;OAEG;gBACS,OAAO,GAAE,MAAY;IAIjC,aAAa,CAAC,YAAY,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,MAAM;CAI5E"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { SpinningMode } from './SpinningMode.js';
|
|
2
|
+
/**
|
|
3
|
+
* Immediate placement mode for super-turbo skip.
|
|
4
|
+
* Symbols snap to position instantly — no actual spinning animation.
|
|
5
|
+
*/
|
|
6
|
+
export declare class ImmediateMode implements SpinningMode {
|
|
7
|
+
readonly name = "immediate";
|
|
8
|
+
computeDeltaY(_symbolHeight: number, _speed: number, _deltaMs: number): number;
|
|
9
|
+
}
|
|
10
|
+
//# sourceMappingURL=ImmediateMode.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ImmediateMode.d.ts","sourceRoot":"","sources":["../../../src/spin/modes/ImmediateMode.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AAEtD;;;GAGG;AACH,qBAAa,aAAc,YAAW,YAAY;IAChD,QAAQ,CAAC,IAAI,eAAe;IAE5B,aAAa,CAAC,aAAa,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,MAAM;CAI/E"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Strategy interface for different reel spinning behaviors.
|
|
3
|
+
*
|
|
4
|
+
* Each mode defines how symbols move during a spin frame
|
|
5
|
+
* and how landing is handled.
|
|
6
|
+
*/
|
|
7
|
+
export interface SpinningMode {
|
|
8
|
+
readonly name: string;
|
|
9
|
+
/**
|
|
10
|
+
* Compute the Y displacement for this frame.
|
|
11
|
+
* @param symbolHeight - Height of one symbol in pixels.
|
|
12
|
+
* @param speed - Current spin speed (pixels per frame).
|
|
13
|
+
* @param deltaMs - Time since last frame in milliseconds.
|
|
14
|
+
* @returns Y displacement in pixels.
|
|
15
|
+
*/
|
|
16
|
+
computeDeltaY(symbolHeight: number, speed: number, deltaMs: number): number;
|
|
17
|
+
}
|
|
18
|
+
//# sourceMappingURL=SpinningMode.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"SpinningMode.d.ts","sourceRoot":"","sources":["../../../src/spin/modes/SpinningMode.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,MAAM,WAAW,YAAY;IAC3B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IAEtB;;;;;;OAMG;IACH,aAAa,CAAC,YAAY,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,MAAM,CAAC;CAC7E"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { SpinningMode } from './SpinningMode.js';
|
|
2
|
+
/**
|
|
3
|
+
* Standard top-to-bottom reel spinning.
|
|
4
|
+
* Symbols scroll downward at constant speed, wrapping around.
|
|
5
|
+
*/
|
|
6
|
+
export declare class StandardMode implements SpinningMode {
|
|
7
|
+
readonly name = "standard";
|
|
8
|
+
computeDeltaY(symbolHeight: number, speed: number, deltaMs: number): number;
|
|
9
|
+
}
|
|
10
|
+
//# sourceMappingURL=StandardMode.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"StandardMode.d.ts","sourceRoot":"","sources":["../../../src/spin/modes/StandardMode.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AAEtD;;;GAGG;AACH,qBAAa,YAAa,YAAW,YAAY;IAC/C,QAAQ,CAAC,IAAI,cAAc;IAE3B,aAAa,CAAC,YAAY,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,MAAM;CAK5E"}
|