import { Container, Graphics, Ticker } from 'pixi.js'; import { EventEmitter } from '../events/EventEmitter.js'; import { ReelSet } from '../core/ReelSet.js'; import { ReelSymbol } from '../symbols/ReelSymbol.js'; import { SymbolRegistry } from '../symbols/SymbolRegistry.js'; import { SpeedProfile, SymbolData } from '../config/types.js'; import { Disposable } from '../utils/Disposable.js'; import { Direction, Orientation } from '../core/ReelAxis.js'; import { HwPhase } from './HoldAndWinState.js'; import { HoldAndWinBoardEvents, HwCell, HwCoin, HwRespinResult } from './HwTypes.js'; /** Internal config produced by {@link HoldAndWinBuilder.build}. */ export interface HoldAndWinBoardConfig { cols: number; rows: number; cell: number; gap: number; emptyId: string; respins: number; configurator: (registry: SymbolRegistry) => void; weights: Record | null; symbolData: Record> | null; baseProfile: SpeedProfile; stagger: (reel: number, cell: number) => number; anticipateWhen: ((state: { locked: number; capacity: number; respinsLeft: number; }) => boolean) | null; chrome: ((g: Graphics, size: number) => void) | null; /** Travel axis for each cell's own strip. See `HoldAndWinBuilder.axis`. */ orientation?: Orientation; direction?: Direction; ticker: Ticker; rng: (() => number) | null; } /** * A Hold & Win board: a grid of independently spinning cells plus the round * choreography every H&W game repeats - spin the free cells, lock the hits, * reset-or-decrement the respin counter, detect the full board. * * It composes two collaborators: a `BoardGrid` (the generic "board of reels" * mechanism - geometry, instances, spinning) and a `HoldAndWinState` (the pure * single-source reducer - ledger, counter, phase). The board is the * mediator: it drives the reels, reports each landing to the reducer, and * replays the reducer's decided effects onto {@link events}. * * It deliberately owns nothing about *value*. Coins are opaque `{ cell, id, data }` * - `id` picks the registered art, `data` is the game layer's to read and mutate. * Adders, doublers, collectors and flights are game design, expressed through * three openings rather than board features: {@link events}, {@link symbolAt} * (the live `ReelSymbol` instance) and {@link cellBounds}/{@link cellCenter} * (pixel geometry for flights). * * ```ts * const board = new HoldAndWinBuilder<{ value: number }>() * .grid(5, 3).cellSize(72, { gap: 4 }) * .symbols((r) => r.register('coin', CoinSymbol, COIN_TRIGGER)) * .weights({ coin: 1, empty: 3 }).respins(3).ticker(app.ticker) * .build(); * * board.events.on('coin:locked', ({ coin }) => hud.add(coin.data.value)); * board.enter(triggerCoins); * while (true) { * const round = await server.respin(board.lockedCoins); * const result = await board.respin(round.hits); * if (result.done) break; // game animates between rounds * } * ``` */ export declare class HoldAndWinBoard implements Disposable { readonly events: EventEmitter>; readonly cols: number; readonly rows: number; private readonly _grid; private readonly _state; private readonly _emptyId; private readonly _anticipateWhen; constructor(cfg: HoldAndWinBoardConfig); get container(): Container; get capacity(): number; get respinsLeft(): number; get lockedCoins(): HwCoin[]; get isFull(): boolean; get freeCells(): HwCell[]; /** Where the feature is right now: idle (no feature), active, or spinning. */ get phase(): HwPhase; cellBounds(cell: HwCell): { x: number; y: number; width: number; height: number; }; cellCenter(cell: HwCell): { x: number; y: number; }; /** Live symbol instance currently shown in a cell. */ symbolAt(cell: HwCell): ReelSymbol; /** The cell's underlying 1×1 ReelSet, for driving one cell directly. */ reelAt(cell: HwCell): ReelSet; /** * Rewrite a **locked** cell's coin in place - coin → jackpot, mini → major, * raise a tier - without disturbing any other cell. The ledger entry is * rewritten so `lockedCoins` and totals stay correct. Throws on a free cell. * Returns the new live symbol instance. * * Throws if called while a wave is in flight - `await respin()` first. To * upgrade a coin in reaction to its own `coin:locked`, defer the swap until * the awaited `respin()` resolves rather than swapping inside the listener. */ setSymbolAt(cell: HwCell, id: string, data?: TData): ReelSymbol; /** Activate the feature with the trigger coins. Seeds land locked, instantly. */ enter(seed: HwCoin[]): void; /** * Spin every free cell; `hits` land (and lock) their coins, all other spinning * cells land empty. Resolves once the wave has landed and the counter is * resolved. The game layer drives pacing between rounds. */ respin(hits: HwCoin[]): Promise>; /** * Remove locked coins - the collect moment. Clears the cells (they become * free again) and returns the released coins; the flight itself is game-layer * animation, started from `cellCenter()` or the `coin:released` event. */ release(cells: HwCell[]): HwCoin[]; /** * Fast-forward whatever is spinning: every in-flight cell is slammed to its * landed position, then `feature:skip` fires so the game layer can cut its own * flights short. The normal landing → `coin:locked` → `feature:end` flow still * resolves; this only removes the waiting. Returns the number of cells that * were in flight. */ skip(): number; /** Clear the board back to idle. Fires `feature:reset` (not `coin:released`). */ reset(): void; get isDestroyed(): boolean; destroy(): void; /** Emit each reducer-decided effect and fire the visual side effects. */ private _apply; private _anticipating; } //# sourceMappingURL=HoldAndWinBoard.d.ts.map