Skip to content

Slot Game Responses

This guide explains how to read the scenario field returned by placeBet for slot games. The shapes used below are published as @hizi.io/slot-types — install it to type-check your scenario handling on the frontend.

Overview

Slot game databases are generated by the slot engine that ships inside hizi-engine-creator (the creator's playGame() produces the IResult objects that get stored as scenarios). At runtime, gameResult.scenario contains exactly that structure.

For single-result spins, the scenario is a single IResult object. For multi-result spins (cascades, hold & win), the scenario is an array of IResult objects — one per step.

The IResult Structure

typescript
interface IResult {
  featureName?: string;
  board: TBoard;
  wins?: IWin[];
  winAmount: number;
  symbolSubstitutions?: ISymbolSubstitutionData[];
  duplicatingSymbolData?: IDuplicatingSymbolData[];
  stuckSymbols?: TStuckSymbols;
  bookSpinChosenSymbol?: string;
  bookSpinWin?: IBookSpinWin;
  spinMultiplierTotal?: number;
  multiplierCollectionData?: IMultiplierCollectionData[];
  cascadeData?: ICascadeData;
  boardStates?: IBoardState[];
  holdAndWinData?: IHoldAndWinData;
  wheelWins?: IWheelWin[];
  randomFeatureEntry?: string;
  featureMultiplier?: number;
  collectorTargetData?: ICollectorTargetData[];
  collectorLevelData?: ICollectorLevelData;
  collectedSpinMultiplierData?: ICollectedSpinMultiplierData;
  reelRows?: number[];
  cascadeMultiplierValue?: number;
  featureRoundId?: number;
  spinInfo?: ISpinInfo[]; // consolidated rounds only — see Feature progression below
  nextFeature?: string; // consolidated rounds only — see Feature progression below
}

Most fields are optional and only present when the corresponding feature is active. A minimal base game result without features contains just board, wins, and winAmount.

Reading the Board

board is a 2D array indexed by column (reel), then row (top to bottom):

typescript
type TBoard = Array<Array<string>>;
typescript
const scenario = gameResult.scenario as IResult;

// A 5×3 slot: 5 columns, 3 rows each
// scenario.board[0] = ['cherry', 'lemon', 'bar']     ← reel 0
// scenario.board[1] = ['seven', 'cherry', 'cherry']   ← reel 1
// ...

for (let col = 0; col < scenario.board.length; col++) {
  for (let row = 0; row < scenario.board[col].length; row++) {
    const symbol = scenario.board[col][row];
    // Render symbol at grid position (col, row)
  }
}

Reading Wins

wins is an array of IWin objects. Each describes one winning combination:

typescript
interface IWin {
  mode: 'winline' | 'ways' | 'scatter' | 'cluster' | 'unconditional' | 'collector';
  positions: Array<{ column: number; row: number }>;
  winMultiplier?: number;
  featureWin?: { featureName: string; featureCount: number };
  overrideWins?: boolean;
  symbolMultiplierTotal?: number;
  spinMultiplierValue?: number;
  multipayCount?: number;
  collectedSymbols?: ICollectedSymbol[];
}

Highlighting winning positions

typescript
if (scenario.wins) {
  for (const win of scenario.wins) {
    for (const pos of win.positions) {
      // Highlight the symbol at (pos.column, pos.row)
    }
    console.log(`${win.mode} win: ${win.winMultiplier}x`);
  }
}

Win modes

ModeDescription
winlineConsecutive matching symbols along a configured payline
waysMatching symbols in adjacent columns (left-to-right or both ways)
scatterMatching symbols anywhere on the board
clusterAdjacent matching symbols forming a connected group
unconditionalAwards that fire on every spin regardless of symbols
collectorCollector symbols gathering target symbol values

Feature triggers

A win can trigger bonus features via featureWin:

typescript
for (const win of scenario.wins ?? []) {
  if (win.featureWin) {
    console.log(`Triggered ${win.featureWin.featureName} × ${win.featureWin.featureCount}`);
  }
}

featureWin only tells you what triggered. To track progression — which feature a spin belongs to, how many spins remain, what plays next — read spinInfo / nextFeature / currentFeature. Where those live depends on the round type; see Feature progression for the unified accessor that works for both. For consolidated slot rounds they are baked onto the scenario IResult (scenario.spinInfo, scenario.nextFeature, scenario.featureName); otherwise they come from engineData.

Override wins

When overrideWins is set on a win, only the highest-paying override-win on the spin is paid; all other wins on that spin are suppressed. Use this to detect wins that should be rendered exclusively (e.g. a jackpot scatter that hides the line wins).

Multipay scatters

multipayCount is set on scatter wins when the configured award uses multipay. The win's winMultiplier already includes the multipay scaling — multipayCount is just the multiplier so you can show "2× scatter pay" alongside the win.

Win Multipliers

A win's payout (winMultiplier) can be modified by several multipliers, each surfaced on the win or the spin so you can render the breakdown without recomputing it:

FieldWhereMeaning
win.winMultiplierper-winFinal win value, after every multiplier on this win has been applied
win.symbolMultiplierTotalper-winCombined multiplier from symbols-with-multipliers in this win (uses the slot's symbolMultiplierBehaviour: highest / multiply / add). Only present when > 1
win.spinMultiplierValueper-winA spin-multiplier award contributed by this win (e.g. multiplier symbols on the board). Aggregated across all wins into result.spinMultiplierTotal
result.spinMultiplierTotalper-spinSum of every win's spinMultiplierValue on this spin, applied to result.winAmount. Only present when > 0
result.cascadeMultiplierValueper-spinFinal cascade multiplier value after all cascade steps. Only present when cascadeMultiplier is configured
result.featureMultiplierper-spinEffective feature-level Win Multiplier applied to this spin's wins. Source-agnostic — set whether it came from featureConfig.multiplier (static), randomMultiplierOptions (rolled once per run), or perSpinRandomMultiplierOptions (rolled fresh every spin). Only present when ≠ 1×. See Win Multiplier

winAmount is the final value the spin paid out; the multipliers above are a breakdown for display purposes — multiplying them yourself will not always reproduce winAmount exactly because rounding happens at intermediate steps.

Symbol Substitutions and Duplications

When the slot config has symbolSubstitutions (e.g. wilds turning into specific paying symbols) or symbols with duplicatingCount (a symbol that copies itself onto another position), the engine reports what happened so you can animate the transformation:

typescript
interface ISymbolSubstitutionData {
  position: { column: number; row: number };
  originalSymbol: string;
  newSymbol: string;
}

interface IDuplicatingSymbolData {
  sourcePosition: { column: number; row: number };
  targetPosition: { column: number; row: number };
  symbol: string;
}
typescript
for (const sub of scenario.symbolSubstitutions ?? []) {
  // Animate sub.originalSymbol turning into sub.newSymbol at sub.position
}
for (const dup of scenario.duplicatingSymbolData ?? []) {
  // Animate dup.symbol jumping from dup.sourcePosition to dup.targetPosition
}

scenario.board is always the final post-transformation board. Use boardStates (see Board States) when you need the pre-transformation board too.

Cascades / Tumbles

When cascades are enabled, cascadeData tracks the full tumble sequence within a single spin:

typescript
interface ICascadeData {
  count: number; // Number of cascade iterations (0 = no cascades)
  steps: ICascadeStep[]; // Each step of the cascade
}

interface ICascadeStep {
  inputBoard?: TBoard; // Board before transformations
  board: TBoard; // Board at this step
  wins: IWin[]; // Wins found at this step
  winAmount: number; // Win from this step
  cascadeMultiplier?: number; // Active multiplier on this step (only when cascadeMultiplier is configured)
}

When the slot config has cascadeMultiplier enabled, the multiplier ramps up across cascade steps. Each step's cascadeMultiplier is the value applied to that step's wins; the final value is also surfaced on the spin's top-level cascadeMultiplierValue.

typescript
if (scenario.cascadeData) {
  for (let i = 0; i < scenario.cascadeData.steps.length; i++) {
    const step = scenario.cascadeData.steps[i];
    // step.board = board state at this cascade step
    // step.wins  = wins found at this step
    // step.winAmount = payout from this step
    // Animate: remove winning symbols, drop new ones, show next board
  }
}

Step 0 is the initial evaluation. Steps 1+ are cascade iterations after winning symbols were removed and new symbols dropped in.

TIP

When cascade is active with multi-result scenarios, each placeBet call returns one step. Use scenarioInfo.inProgress to check if more cascade steps follow.

Hold & Win

holdAndWinData tracks the state of a hold-and-win (respin) feature:

typescript
interface IHoldAndWinData {
  multipliers: (number | null)[][]; // Payout multiplier at each position
  symbols?: (string | null)[][]; // Symbol at each stuck position
  livesRemaining: number; // Remaining respins
  totalMultiplier: number; // Sum of all placed multipliers
  ended: boolean; // Feature complete?
}
typescript
if (scenario.holdAndWinData) {
  const hw = scenario.holdAndWinData;
  // Render the grid: non-null entries in hw.multipliers are stuck symbols
  for (let col = 0; col < hw.multipliers.length; col++) {
    for (let row = 0; row < hw.multipliers[col].length; row++) {
      if (hw.multipliers[col][row] !== null) {
        // Show stuck symbol with its multiplier value
      }
    }
  }
  console.log(`Lives: ${hw.livesRemaining}, Total: ${hw.totalMultiplier}x`);
}

Hold & Win results are bundled as a multi-result scenario — the trigger spin and all H&W respins come as sequential steps.

Sticky Symbols

During features with sticky symbols, stuckSymbols tracks which positions are locked:

typescript
type TStuckSymbols = Array<Array<string | null>>;
// Same shape as board - null means the position is not stuck
typescript
if (scenario.stuckSymbols) {
  for (let col = 0; col < scenario.stuckSymbols.length; col++) {
    for (let row = 0; row < scenario.stuckSymbols[col].length; row++) {
      if (scenario.stuckSymbols[col][row] !== null) {
        // This position has a locked symbol that persists across spins
      }
    }
  }
}

Multiplier Collection

multiplierCollectionData tracks accumulated multiplier progress within a feature:

typescript
interface IMultiplierCollectionData {
  symbol: string; // Symbol being collected
  count: number; // Total collected across all spins
  multiplier: number; // Current active multiplier
  target: 'winline' | 'ways' | 'scatter' | 'cluster' | 'bookwinline' | 'totalwin';
}
typescript
if (scenario.multiplierCollectionData) {
  for (const mc of scenario.multiplierCollectionData) {
    console.log(`${mc.symbol}: collected ${mc.count}, multiplier ${mc.multiplier}x on ${mc.target} wins`);
  }
}

Collected Spin Multiplier

For games like Gates of Olympus where multiplier symbols accumulate within a cascade or feature:

typescript
interface ICollectedSpinMultiplierData {
  collectedMultiplier: number; // Accumulated multiplier across spins
  appliedMultiplier: number; // Multiplier applied to this spin's wins (0 = not applied)
}

Win Multiplier

Every feature spin can carry a Win Multiplier from one of three sources, all exposed on the result via the same featureMultiplier field — so consumers don't need to branch on the source:

SourceConfigBehaviour
StaticfeatureConfig.multiplierFlat multiplier applied to every spin of every run of the feature.
Run-level randomfeatureConfig.randomMultiplierOptionsEngine rolls one value from the weighted list at the start of each feature run; the same value applies to every spin of that run.
Per-spin randomfeatureConfig.perSpinRandomMultiplierOptionsEngine rolls a fresh value from the weighted list on every individual feature spin, so the multiplier varies spin-to-spin within one run. Cascades within a single spin keep that spin's value (cascades are intra-spin).

Precedence when more than one is configured: per-spin > run-level > static.

typescript
if (scenario.featureMultiplier !== undefined) {
  // e.g. show "5x" alongside the feature wins
  console.log(`Feature multiplier: ${scenario.featureMultiplier}x`);
}

The field is omitted when the effective multiplier is 1× (no feature multiplier configured, or base spins).

Per-spin random and the DB shape. Per-spin random forces the feature into the consolidated/grouped DB path, the same way randomMultiplierOptions does — outcomes can't be pre-baked when the multiplier is rolled at runtime, so all spins of the run land in a single grouped scenario entry. Configurations that need each freespin to come from an independent scenario table should use multiple feature configs with different static multipliers and route entries through a player-choice or weighted award instead.

Hold-and-Win. Per-spin random is ignored on Hold-and-Win features — H&W settles cumulatively at the end of the respin sequence, where a per-spin pick has nowhere meaningful to land. H&W falls back to the run-level pick (or static), and featureMultiplier reflects that single applied value.

Random-entry preview spins. When a base spin is replaced by a feature-style preview via featureRandomEntryConfig, the preview spin picks its own multiplier (per-spin if configured on the substituted feature, otherwise run-level) independently of any surrounding feature run.

Wheel Features

wheelWins contains results from wheel bonus spins:

typescript
interface IWheelWin {
  selectedSegment: number; // Index of the landing segment
  winAmount?: number; // Cash prize (if any)
  featureAward?: IFeatureWin; // Feature triggered (if any)
}

Board States

boardStates provides named snapshots showing intermediate board transformations:

typescript
interface IBoardState {
  name: string; // e.g. 'Generated', 'After Sticky', 'Book Expanded'
  board: TBoard;
}

These are useful for animating board transformations step by step — for example, showing the raw generated board before expanding wilds or applying sticky symbols.

Book Spins

During book-spin features:

  • bookSpinChosenSymbol — the expanding symbol selected for the feature
  • bookSpinWin — additional win data from the book expansion:
typescript
interface IBookSpinWin {
  symbolWinAmount: number; // Win from the chosen expanding symbol
  totalWin: number; // Total book spin win multiplier
}

Collector Features

When collector mechanics are active, the spin reports per-symbol target values as well as the player's level progression:

typescript
interface ICollectorTargetData {
  symbol: string; // Target symbol on the board
  position: { column: number; row: number };
  value: number; // Weighted random value assigned to this target instance
}

interface ICollectedSymbol {
  symbol: string; // Target symbol gathered by the collector
  position: { column: number; row: number };
  value: number; // Payout value assigned to this target
}

interface ICollectorLevelData {
  currentLevel: number; // 0-based index into the levels array
  collectedCount: number; // Collected in the current level (resets on level up)
  totalCollected: number; // Total across all levels in this feature
}
  • scenario.collectorTargetData lists the values rolled for each target symbol present on the board this spin (independent of whether they were gathered).
  • win.collectedSymbols (on collector mode wins) lists the targets actually gathered by a collector win and the value awarded for each.
  • scenario.collectorLevelData is the level-progression state after this spin, when the slot has a collectorLevelConfig.
typescript
if (scenario.collectorLevelData) {
  const cl = scenario.collectorLevelData;
  console.log(`Level ${cl.currentLevel}, collected ${cl.collectedCount} this level, ${cl.totalCollected} total`);
}

Megaways (Variable Reel Heights)

When the slot has megaways configured, each reel generates a random number of rows per spin. The chosen heights are reported on reelRows so you can size the grid before rendering symbols:

typescript
if (scenario.reelRows) {
  // scenario.reelRows[col] = number of rows on reel col for this spin
  // scenario.board[col].length matches scenario.reelRows[col]
}

reelRows is only present when the active config (base or feature) has megaways enabled.

Random Feature Entry

If the slot config has featureRandomEntryConfig, a base spin can be replaced with a feature-style spin (the feature's symbols, awards, and multiplier are applied). The spin's randomFeatureEntry carries the name of the feature whose config was used:

typescript
if (scenario.randomFeatureEntry) {
  console.log(`This spin used the "${scenario.randomFeatureEntry}" feature config`);
}

A random-entry spin can also queue additional spins of the same feature (when the feature's count > 1) — those queued spins are normal feature spins and have featureName set instead.

Feature Run Identification

featureRoundId is a 1-based counter that distinguishes separate runs of the same feature within one round. If a feature triggers, completes, then re-triggers from a base spin or wheel award later in the round, the second run gets featureRoundId: 2:

typescript
// Group feature spins by run
const runs = new Map<string, IResult[]>();
for (const r of allResults) {
  if (r.featureName && r.featureRoundId !== undefined) {
    const key = `${r.featureName}#${r.featureRoundId}`;
    if (!runs.has(key)) runs.set(key, []);
    runs.get(key)!.push(r);
  }
}

Hold-and-win in particular uses this to count respins within a single trigger: every step of the run carries the same featureRoundId, so filtering on featureName + featureRoundId gives you the spins of one respin sequence.

Single vs Multi-Result Scenarios

Single-result (most spins): gameResult.scenario is one IResult object.

Multi-result (cascades, H&W): gameResult.scenario is an array of IResult objects, and engineData.scenarioInfo.inProgress is true. Each placeBet call returns the next step in the sequence.

typescript
// Check if this is a multi-result scenario
if (gameResult.engineData.scenarioInfo.inProgress) {
  // Current step index
  const step = gameResult.engineData.scenarioInfo.currentScenarioIndex;
  // More steps to come — call placeBet({ backendURL, token })
}

Minimal Example

typescript
import { placeBet, IGameResult } from '@hizi.io/engine-sdk';

// Define your scenario type matching hizi engine slot's IResult
interface ISlotScenario {
  board: string[][];
  wins?: Array<{
    mode: string;
    positions: Array<{ column: number; row: number }>;
    winMultiplier?: number;
    featureWin?: { featureName: string; featureCount: number };
  }>;
  winAmount: number;
  cascadeData?: {
    count: number;
    steps: Array<{ board: string[][]; wins: any[]; winAmount: number }>;
  };
  holdAndWinData?: {
    multipliers: (number | null)[][];
    livesRemaining: number;
    totalMultiplier: number;
    ended: boolean;
  };
  featureMultiplier?: number;
  // ... other optional fields as needed
}

const response = await placeBet({ backendURL, token, stake });
if (response.success) {
  const { scenario, engineData, totalWin } = response.result.result;
  const slot = scenario as ISlotScenario;

  // Render the board
  renderBoard(slot.board);

  // Highlight wins
  for (const win of slot.wins ?? []) {
    highlightPositions(win.positions);
  }

  // Handle cascades
  if (slot.cascadeData) {
    for (const step of slot.cascadeData.steps) {
      await animateCascade(step);
    }
  }

  // Continue if round is in progress
  if (engineData.inProgress) {
    await placeBet({ backendURL, token });
  }
}

Supported Features

A complete reference of every mechanic the slot engine can model. The "Example" column points to a recognisable commercial slot using the same mechanic — useful shorthand when discussing configurations with designers. The configuration field shows the relevant key on ISlotConfig, ISymbol, IAwardTrigger, or IFeatureConfig.

Grid & reels

MechanicConfigExampleNotes
Fixed gridcolumns, rowsStarburst (NetEnt)Standard fixed grid.
Variable reel heights (Megaways)megawaysBonanza Megaways (Big Time Gaming)Each reel rolls a random number of rows per spin within [minRows, maxRows]; per-reel ranges via reelRowRange.
Fixed reel strips (reelsets)reelSetConfigMechanical-style NetEnt slotsBoard generation picks a window of rows consecutive symbols from each strip; multiple reelsets can be weighted against each other. When enabled, per-symbol reel weights and stacking are bypassed.

Symbols

MechanicConfigExampleNotes
Wildssymbol.isWild20 Super Hot (EGT)Substitutes for paying symbols.
Expanding wildssymbol.isExpandingWildStarburst (NetEnt)Wild expands to fill its reel.
Wild scatterssymbol.isWildScatterBook of Ra (Novomatic)Wild that also counts toward scatter awards (per-award opt-in via canIncludeWildScatters).
Per-reel symbol weightingsymbol.reelWeights[]virtually every modern slotDifferent probability per reel.
Reel spacingsymbol.reelSpacinghigh-volatility scatter symbolsMinimum stop distance between occurrences on a reel.
Symbol stackingsymbol.minStack
symbol.maxStack
symbol.averageStack
Buffalo (Aristocrat)A symbol either appears alone or in a contiguous group on a reel; averageStack skews stack sizes toward a target.
Symbol multiplierssymbol.multiplier
symbolMultiplierBehaviour
Sweet Bonanza (Pragmatic Play)Multiplies the win when this symbol is part of it. Combination across multiple multiplier symbols: highest / multiply / add.
Multi-position symbolssymbol.winlineCount"double-wide" symbols in various Pragmatic Play / BTG titlesA symbol counts as N adjacent positions for win length.
Duplicating symbolssymbol.duplicatingCountvarious "cloning wild" mechanicsSymbol clones itself onto another position before evaluation.
Symbol substitutionssymbolSubstitutions[]Mystery Symbols (Fishin' Frenzy series — Reel Time Gaming)One symbol transforms into another via weighted pick. uniformSubstitution makes all source occurrences resolve to the same target; noSubstitutionWeighting lets the source survive untransformed.

Win evaluation modes

MechanicConfigExampleNotes
Paylinesaward.mode: 'winline'
winlines[]
Book of Ra (Novomatic)Configured payline patterns.
Ways paysaward.mode: 'ways'243 Ways (Microgaming)Adjacent-reel matching, no payline patterns.
Scatter paysaward.mode: 'scatter'universalN matching symbols anywhere on the board.
Multipay scatteraward.multipayvariousScatter pays floor(count / N) times for big counts.
Cluster paysaward.mode: 'cluster'
adjacency
Aloha! Cluster Pays (NetEnt), Reactoonz (Play'n GO)Connected groups of matching symbols. adjacency is fourWays (default) or eightWays.
Unconditional awardsaward.mode: 'unconditional'bonus picks, "always pays" mechanicsFires every spin regardless of board state.
Collector awardsaward.mode: 'collector'
collectsSymbols
Money Train (Relax Gaming)A collector symbol gathers values from target symbols on the board into one win.
Override winsaward.overrideWinsjackpot lines, super-wild winsOnly the highest override-win pays — suppresses other wins on the spin.
After-cascade evaluationaward.afterCascade
award.cascadePersist
variousAward is evaluated only on the final post-cascade board (or its symbols persist through cascades).
Range-based scatter triggersaward.maxCountbonus tiers (e.g. 3/4/5 scatters → different awards)Award only triggers when the symbol count is within [count, maxCount].

Cascades (tumble mechanics)

MechanicConfigExampleNotes
Cascading winscascadeGonzo's Quest (NetEnt)Winning symbols are removed; remaining drop down; new symbols fill from the top; repeats until no win.
Progressive cascade multipliercascadeMultiplierSweet Bonanza (Pragmatic Play), Sugar Rush (Pragmatic Play)Multiplier ramps up across cascade steps.
First-step substitutions onlycascadeSubstitutionvariousSubstitutions apply only on the first cascade step (default is every step).

Bonus features

MechanicConfigExampleNotes
Free spinsfeatureConfig.type: 'freespins'universalStandard bonus spins.
Bonus spins (lower priority)featureConfig.type: 'bonusspins'Book of Holla: Bonus Spins (Hoelle Games)Only run when no free-spins feature has remaining spins.
Wheel featuresfeatureConfig.type: 'wheel'Mega Moolah (Microgaming), Wheel of Fortune (IGT)Single weighted segment pick; segment can pay cash, trigger another feature, or both.
Hold and Win (respin)featureConfig.holdAndWinLightning Link (Aristocrat)Coin symbols stick; respin counter resets on every hit (lives); ends on grid-full or 0 lives. fullScreenBonus, symbolWeights, blankWeight, payouts shape the maths.
Book Spins (expanding feature symbol)featureConfig.bookSpinSymbolAllowListBook of Ra (Novomatic), Book of Dead (Play'n GO)One symbol from the allow-list is rolled at feature start; on each spin it expands to fill all positions and pays as a winline regardless of position.
Random feature entryfeatureRandomEntryConfig
featureRandomEntryBaseWeighting
random hold-and-win drops on a base spinWeighted chance to replace a base spin with a feature-style spin (using the feature's full config); can also queue additional feature spins.
Static feature multiplierfeatureConfig.multiplier"wins multiplied by 3 during free spins"Flat multiplier on every win during the feature. Surfaced on the result via featureMultiplier.
Random feature multiplier (run-level)featureConfig.randomMultiplierOptionsRandom Multiplier Free Spins variantsEngine rolls one multiplier from a weighted list at the start of each feature run; applies to the entire run. Surfaced via featureMultiplier. Forces the feature into a grouped DB entry.
Per-spin random feature multiplierfeatureConfig.perSpinRandomMultiplierOptions"every freespin a fresh global multiplier" variantsEngine rolls a fresh multiplier from the weighted list on every individual feature spin, so the multiplier varies spin-to-spin within one run. Overrides both multiplier and randomMultiplierOptions. Regular feature spins only — Hold-and-Win and wheel features fall back to the run-level pick. Surfaced via featureMultiplier. Forces the feature into a grouped DB entry.

Sticky mechanics (feature-scoped)

All fields below live on IFeatureConfig (featureConfig.X).

MechanicConfigExampleNotes
Sticky wilds / sticky symbolsstickySymbolsDead or Alive (NetEnt)Listed symbols persist on the board across feature spins once they land.
Sticky winsstickyWinsvariousAll symbols involved in any winning combination become sticky.
Sticky winlinesstickyWinlinesvariousSymbols on a winning payline become sticky.
Sticky symbol limitstickySymbolLimitvariousCaps how many symbols can be sticky simultaneously.
Sticky overwrite rulesstickySymbolOverwriteRulesupgrading stuck symbols (low → high)A new sticky can replace an existing stuck symbol per defined replacement rule.

Multiplier mechanics (per-feature)

All fields below live on IFeatureConfig (featureConfig.X).

MechanicConfigExampleNotes
Multiplier collectionmultiplierCollectionConfigsvarious "multiplier symbol counter" free-spins featuresCounts collected symbols across all feature spins; thresholds map count → active multiplier; targets a specific win type (winline/ways/scatter/cluster/bookwinline/totalwin).
Collected spin multipliercollectedSpinMultiplierGates of Olympus (Pragmatic Play)Spin multiplier values accumulate across feature spins (add = sum, multiply = product); application is always or onHit.

Collector mechanics

MechanicConfigExampleNotes
Target value poolcollectorTargetValuesMoney Train (Relax Gaming)Weighted random values assigned to each target symbol type when a collector evaluation runs.
Collector level progressioncollectorLevelConfigBig Bass Bonanza (Pragmatic Play)Collector symbols progress through ranked levels. Each level has its own multiplier, can grant additionalSpins, and can transition to a different feature via nextFeature. Shared between base game and features.

Triggers, awards & round control

MechanicConfigExampleNotes
Feature awards from winsawardResult.featureAwarduniversal scatter-bonus triggersA win awards featureCount spins of a named feature.
Spin multiplier awardsawardResult.spinMultiplierGates of Olympus (Pragmatic Play) — board multiplier symbolsA win contributes a spin-multiplier value, summed into result.spinMultiplierTotal and applied to the spin's win amount.
Pot awards (combination-aware)potAwards3 Pots of Egypt (3 Oaks) — combined trigger tablesMultiple scatter groups evaluated together against one outcome table; allows correlated triggers and shared probability budgets.
Progression countersprogressionCounters
awardResult.progressionAwards
various "meter/charge/level-up" mechanicsWins increment named counters; counter at 1.0 awards configured features; can be stakeSpecific for separate counters per stake.
Random-choice / player-choice award resolutionprogressionCounter.onComplete.typevarious player-choice bonus picksWhen a counter completes, the awards are either rolled randomly or surfaced as a player choice via the SDK.
Max payout capmaxPayout
consolidateRounds
Pragmatic Play 5,000× / 25,000× max-win capWhen cumulative win reaches the cap the round ends immediately.
Consolidated roundsconsolidateRoundsbundled bonus/H&W gamesBundles all feature spins into the base scenario instead of separate feature DBs. Required for maxPayout.
Scenario variety capmaxScenariosPerEntry(generator-level tuning)Caps stored scenarios per unique entry — higher = more variety, larger DB.
Zero-win scenario capmaxScenariosPerZeroWinEntry(dead-spin tuning, default 2000)Separate, larger cap for 0-win spins (~60% of volume) so dead-spin variety isn't starved.
Loose scenario caplooseScenarioCap(default on in the creator)Pools the scenario cap across outcomes that share win + feature spins + progression but differ only in cosmetic tags — keeps scenarios.jsonl small on the large sims needed to reach jackpot-odds wins, without changing entries or weights.

Provably Fair Verification

When a slot round runs with config.rng === 'pf', every spin's outcome is fully determined by the RNG draws committed to in the round's pf block. Use pfVerify to have the engine replay the round from its revealed seeds and compare the result to what you recorded live.

Request

The slot replay needs the opening stake and any featureToBuy it used. Each continuation step in the live round (wager-feature decisions, random-choice or player-choice awards) is replayed by passing the matching playerChoiceIndex in actions. Free-spin / feature cascades that resolve internally — no player choice — need no entry in actions; the engine cascades them itself.

typescript
import { pfVerify } from '@hizi.io/engine-sdk';

const reply = await pfVerify({
  backendURL,
  token,
  serverSeed: pf.revealedServerSeed,
  clientSeed: pf.clientSeed,
  stake: 100,
  featureToBuy: 'freespin',     // only if the round opened on a buy feature
  actions: [
    { playerChoiceIndex: 1 },   // first wager-feature / playerChoice resolution
    { playerChoiceIndex: 0 },   // second
  ],
});

Reading the response

rngData mirrors the live round's pf.rngData row-for-row — same pf:start-end ids, same nonces[], same values[]. steps[] is the per-step IGameResult chain that placeBet returned in order; the terminal step is steps[steps.length - 1]. Compare against your recorded round:

typescript
if (!reply.success) {
  // handle reply.error
  return;
}
const { rngData, steps } = reply.result;

// 1. RNG audit
const audited = rngData.length === pf.rngData.length &&
  rngData.every((row, i) => deepEqual(row, pf.rngData[i]));

// 2. Scenario / totalWin (terminal step)
const terminal = steps[steps.length - 1];
const resultOk = deepEqual(terminal.scenario, lastLiveResult.scenario) &&
  terminal.totalWin === lastLiveResult.totalWin;

If both line up the engine couldn't have rigged the outcome — every draw and every cascade trace back to (serverSeed, clientSeed, your actions).

Next Steps