Table of contents
Overview
Obviously implementing the logic of a slot game is not rocket science and any decent programmer that has an understanding of the game rules can do it. Still, for slot games (and all gambling games in general) it is crucial that the implementation of the game adheres perfectly to all the rules and it produces valid outcomes at all times.Companies producing slot games normally have to face two problems in order to release a new game title:
- during the game production cycle the design of the mathematics normally takes place at an initial stage and it is decoupled from the implementation of the game logic which is carried out afterwards by a team of developers. Both phases require analysis and extensive testing to make sure that everything is correct and potential problems can occur in any of the two phases. Furthermore even though the game mathematics is correct, it is crucial that the developers have a perfect understanding of all the corresponding rules and implement them correctly.
- the number of possible outcomes and the complexity of the game rules can require a considerable effort to verify that the game implementation and the generation of the outcomes is correct in all cases.
The game engine is a script that processes the games defined in SLOT-IDE and provides the corresponding logic.
It is designed to be simple and flexible in order to allow easy integration in any application or environment that supports javascript:
- if your slot game is written in C++ then you can easily integrate the game engine by using one of the modern javascript engines that are available (such as the excellent google v8). This obviously includes mobile applications (Android and IPhone) where it is possible to embed native code.
- if your slot game is running in a browser then you can have either run the engine server side (see next point) or in the browser (flash games can interact with it through ExternalInterface and HTML5 games can interact natively)
- if you have a game server or you are to implement one then you can easily build one based on the game engine by using one of the server side javascript framework (NodeJS, TeaJS)
The next section describes how the game engine can be used in both scenarios where the outcome is to be generated locally or remotely (such as central-outcome-determination environments) and in both situations where it is remote (as in online games where the client talks to the server via HTTP(s)) or located in the same running environment as the client (as in standalone machines in casinos, AWP etc.), while the section entitled Javascript API describes in detail the only two APIs that are used to interact to the game engine.
Outcome generation
For every game defined in SLOT-IDE the generation of the outcome of a game round depends completely on random numbers.The outcome of a game isn't limited to stoppping the reels and evaluating the wins out of the visible symbols, it may include stopping the reels in the bonus, randomically picking items for wheels-like bonuses, drawing mystery features like multiplier, number of freespins etc. All of tthe above depends uniquely on random numbers.
The game engine is designed so that every random number that is needed to generate the outcome of a game round must be provided externally and asynchronously. Externally means that who is using the game engine is also in charge of providing the random numbers to it through the APIs. Asynchronously means that anytime the game engine needs random numbers the execution will stop and it will continue once the randoms numbers are provided. This allows for three scenarios that altogher cover all possible situations:
- Game client, game engine and RNG all located in the same running environment: this is the case of standalone casino machines and AWPs or any other case where the slot game is developed as a self-consistent application.
- Remote Game engine: this is the common scenario for online games where the client runs in a web browser and communicates to the game server via the HTTP protocol. In most of this cases the random numbers are generated by a RNG or PRNG that is part of the game server.
- Remote game engine, standalone RNG server: this is the case where not only game engine is remote (as if it is part of a game server) but also, because of regolatory or architectural reasons the RNG server is located in standalone (and possibly remote) machine.
As it is described in the section named Javascript API the outcome generation is completely covered by two events (onSpinStart and onPickRandomly) and the random numbers are provided through the corresponding callbacks.
Javascript API
The game engine can be fully controlled by two APIs only: one called bind that allows to attach listeners to all the events fired by the engine, one called invoke that allows to invoke actions on the game engine.As mentioned in the previous sections the game engine comes as a single javascript script. This script needs to be processed before your game script and in the same running context. After having processed both scripts an instance of the game is created and it is available as a static variable named Game.instance. You use this variable to invoke the APIs.
These are the steps that are normally required to setup the game logic:
- process game engine script
- process game script (repeated each time a new instance of the game is to be created)
- register callbacks via bind API
- invoke actions via invoke API
Bind method
The bind method is used to register callbacks for the events that are fired by the game engine. As shown in the example below bind expects two arguments,
the name of the event and a callback function.
Game.instance.bind('spinStart', function(context)
{
...
});
See below for the complete list of events.
Invoke method
The invoke method is used to invoke actions on the game server. As show in the example below invoke expects a first argument as the
name of the action to invoke and a second optional argument that carries contextual information.
Game.instance.invoke('spinStart', context);
See below for the complete list of actions.
Events
Here below comes a description of the individual events fired by the game engine.
Some of the events require a corresponding action to be invoked on the game engine in order to continue the execution. This is the case for the events
related to the generation of the game outcome.
The individual events are listed in the form of eventname(arguments..) where eventname is the name of the event to bind to
and arguments are the arguments passed to the callback when the corresponding event is fired.
config(context)
Fires after the action config is invoked. The callback receives a context object containing the game
configuration (visible window, symbols, reelstrips, pay modes, paylines) as shown in the example below:
{
"symbols": ["LION", "ZEBRA", "GIRAFFE", "PEOPLE", "TREE", "ACE", "KING", "QUEEN", "JACK", ...],
"reels": [
["JACK", "10", "LION", "LION", "LION", "LION", "LION", "QUEEN", ...],
["JACK", "10", "LION", "LION", "LION", "LION", "LION", "QUEEN", ...],
["JACK", "10", "LION", "LION", "LION", "LION", "LION", "QUEEN", ...],
["KING", "JACK", "LION", "LION", "LION", "LION", "LION", "ACE", ...],
["KING", "JACK", "LION", "LION", "LION", "LION", "LION", "QUEEN", ...]
],
"symbolsPay": {
"line": ["LION", "WILD", "ZEBRA", "GIRAFFE", "PEOPLE", "TREE", "ACE", "KING", "QUEEN", ...],
"scatter": ["SCAT"]
},
"wildSymbols": ["WILD"],
"window": {
"rows": 4,
"reels": 5
},
"availablePayLines": [
[0, 0, 0, 0, 0],
[0, 0, 1, 0, 0],
[0, 1, 1, 1, 0],
[0, 1, 2, 1, 0],
[0, 1, 0, 1, 0],
[0, 0, 0, 1, 0],
[0, 1, 0, 0, 0],
[0, 0, 1, 1, 0],
[0, 1, 1, 0, 0],
[0, 0, 2, 0, 0]
]
}
bet(context)
Fires after the action bet is invoked. The callback receives a context object containing
the total bet in credits, the credits bet-per-line, the selected paylines and the maximum win limit in credits (0=unlimited):
{
"total": 5,
"betPerLine": 1,
"paylines": [
[1, 1, 1, 1, 1],
[0, 0, 0, 0, 0],
[2, 2, 2, 2, 2],
[0, 1, 2, 1, 0],
[2, 1, 0, 1, 2]
],
"maxWinCap": 0
}
gameStart()
Fires when a new game round is about to start. The game engine stops the execution and waits for an action called gameStart to be
invoked. When gameStart is invoked the execution is resumed and a new game rounds begins.
spinStart(context)
Fires when the game engine is about to stop the reels and it needs to obtain the corresponding random numbers. After firing this event the game engine stops the execution and
waits for the receiver of the event to provide the reels stop positions. The receiver must do this by:
- generating the needed random numbers (one for each reel)
- setting the stop positions in the context object that is passed to the callback using the generated random numbers
- invoking the action called spinStart passing to it the modified context as an argument
Upon receiving spinStart the game engine resumes executions and stops the reels accordingly. This asynchronous mechanism
allows the outcome of a game round to be generated remotely.
The context object that is passed to the callback contains the reel strips being played in the current spin (base game or bonus), the length of the reels
in the ranges attribute and an empty array in the stops attribute that has to be filled with the reels stop positions corresponding to
the random numbers that have been drawn.
{
"symbols": ["Atkins", "Steak", "Ham", "Drumstick", "Sausage", ...],
"reels": [
["Sausage", "Bacon", "Scale", "Steak", "Sausage", "Scale", ...],
["Eggs", "Drumstick", "Scale", "Drumstick", "Mayonnaise", ...],
["Butter", "Butter", "Scale", "Butter", "Cheese", "Bacon", ...],
["Bacon", "Sausage", "Scale", "Bacon", "Drumstick", "Bacon", ...],
["Drumstick", "Cheese", "Scale", "Drumstick", "Butter", ...]
],
"symbolsPay": {
"line": ["Atkins", "Steak", "Ham", "Drumstick", "Sausage", ...],
"scatter": ["Scale"]
},
"wildSymbols": ["Atkins"],
"stops": [],
"ranges": [32, 32, 32, 32, 32]
}
As an example the following code registers a callback function for the spinStart event, sets the stop positions from random numbers and invokes
the spinStart action in order to resume the game round execution:
function onSpinStart(context)
{
for (var i=0; i<context.ranges.length; i++)
{
var rn = Random.int(context.ranges[i]-1);
stops.push(rn);
}
Game.instance.invoke('spinStart', context);
}
Game.instance.bind('spinStart', onSpinStart);
As you can notice in the example above the RNG that comes with the game engine is used to generate random numbers. This RNG is based on the Mersenne Twister
algorithm and is available through the class called Random. The static method int simply returns a random number in the [0, n] range, where n
is the argument passed to the function, in this case the length of each reel subtracted by one (stop-positions are zero-based).
spinWin(context)
Fires for each win obtained as a result of a reels spin. The callback receives all relevant information
describing the win in the form of a context object containing the following properties:
- what: winning symbol as defined in the paytable
- occurs: number of occurrencies of the winning symbol in the winning combination
- mode: paymode of the winning combination
- pay: number of credits awarded by the winning combination (this represents the actual credits won which for payline wins corresponds to the value from the paytable multiplied by the bet-per-line)
- mpInfo: object containing the multiplier value applied to the win as a result of wild substitutions and the wild substitutions in the winning combinations
- mpBonusInfo: when not null this is an object that specifies the bonus multiplier applied to the win. The object contains a property called bonus whose value is the name of the bonus being
played and a property called mp which carries the bonus multiplier value applied to the win
- context: object that allows to identify which cells of the visible matrix make up the winning combination. This is useful for displaying the win. For payline wins the object contains
the id and the structure of the winning payline. For all other paymodes (scattered, scattered on consecutive reels, scattered on a payline) context is an array of objects that
identify the individual cells of the matrix (each object has two properties called row and reel)
The example below shows the context object describing a payline win:
{
"what":"Drumstick",
"occurs":2,
"mode":"line",
"pay":2,
"mpInfo":{
"mp":1,
"replacements":0
},
"mpBonusInfo":null,
"context":{
"paylineId":0,
"payline":[1,1,1,1,1]
}
}
}
bonusWin(context)
Fires for each win obtained while playing a bonus. This includes both freespins-like bonuses where the underlying bonus game
being played is a slot game and other types of bonuses like pick-a-prize and trails.
The callback receives a context object containing three properties:
- bonus: name of the bonus being played
- pay: number of credits awarded
- isSpinWin: boolean flag indicating whether or not the win is obtained as a result of spinning the results. The flag is set to
true for freespins-like bonuses and to false for other types of bonus
Each win obtained while playing a freespins-like bonus will fire both the spinWin and bonusWin events.
symbolSetInMatrix(context)
This event fires each time a feature of the game requires a symbol of the visible matrix to be replaced by another symbol.
For instance when the
game features and expanding wild this results in all symbols of the reel where a wild is found to be replaced by the wild. In this case the event
is fired as many times as the symbols on the reel being replaced.
The callback receives a context object containing the following properties:
- row: row of the cell corresponding to the symbol being replaced
- reel: reel of the cell corresponding to the symbol being replaced
- oldSymbol: visible symbol in the cell before the replacement
- symbol: new symbol replacing the visible one in the cell
spinTrigger(context)
Fires when as a result of a reels spin a bonus is triggered.
The callback receives a context object containing three properties:
- spins: an array that lists the possible values for the number of spins that will be awarded. Depending of how the bonus is defined, there can be
a random selection for the number of spins to award. In most cases the number of spins is fixed and the array will contain only a single element. If the number of spins
is to be determined randomically then an element of the array will be selected by firing the pickRandomly event
- occurs: number of symbol occurrencies in the combination triggering the bonus
- bonus: name of the bonus being triggered
- trigger: object representing the paytable element that triggered the bonus
The example below shows the context object for a trigger that awards a fixed number of spins.
{
"spins": [{"prob":1,"spins":10}],
"occurs":3,
"bonus":"freespins",
"trigger":{
"occurs":[3,4,5],
"of":"Scale",
"mode":"scatter"
}
}
enterBonus(context)
Fires when a bonus is entered.
The callback receives an object that represents the bonus context as described here
(the object contains the same properties that are available as attributes of this in the functions attached to dynamic bonus properties and from now
on it will be referred to as bonus context).
Here below an example bonus context is shown representing the case where as a result of a scatter win the bonus is entered from the base game and ten free spins are awarded:
{
"triggers": 1,
"played": 0,
"left": 10,
"multiplier": {},
"bonusTriggers": {
"freespins": 1
},
"bonusPlayed": {},
"spins": [{
"spins": 10,
"bonus": "freespins",
"trigger": {
"occurs": [3, 4, 5],
"of": "Scale",
"mode": "scatter"
}
}],
"triggering": {
"bonus": "freespins",
"trigger": {
"occurs": [3, 4, 5],
"of": "Scale",
"mode": "scatter"
}
}
}
For games featuring more than a bonus, the enterBonus event is also fired when a bonus is entered while another bonus is being played. In all
cases the name of the bonus being entered is always the one set in the bonus property of the first element in the spins array (the array
represents the stack of accumulated spins that are left to be played).
playedBonusSpin(context)
Fires after a spin in the bonus has been played. The callback receives the bonus context as an argument.
The name of the bonus being played is the value of the playing property and matches the name of the bonus in the active element
of the accumulated spin stack (first element of the spins array).
The example below shows the bonus context representing the case where one free spin has been played out of the initial ten free spins awarded:
{
"triggers": 1,
"played": 1,
"left": 9,
"bonusTriggers": {
"freespins": 1
},
"bonusPlayed": {
"freespins": {
"count": 1,
}
},
"spins": [{
"spins": 9,
"bonus": "freespins",
"trigger": {
"occurs": [3, 4, 5],
"of": "Scale",
"mode": "scatter"
}
}],
"playing": "freespins",
"state": "main",
"trigger": {
"occurs": [3, 4, 5],
"of": "Scale",
"mode": "scatter"
}
}
playedBonusSpins(context)
Fires when all spins in a bonus have been played and the bonus is over. After this event is fired the bonus is exited and the game round either finishes or
continues in another bonus (in this case enterBonus is fired next).
Here below an example bonus context is shown representing the case where all the ten free spins awarded have been played and the game round is over (the spin stack is empty):
{
"triggers": 1,
"played": 10,
"left": 0,
"bonusTriggers": {
"freespins": 1
},
"bonusPlayed": {
"freespins": {
"count": 10,
}
},
"spins": [],
"playing": "freespins",
"state": "main",
"trigger": {
"occurs": [3, 4, 5],
"of": "Scale",
"mode": "scatter"
}
}
playedSpin(context)
Fires after a spin has been played in the base game or in a bonus.
If the spin is played in a bonus this event is
fired before playedBonusSpin.
The callback receives a context array that contains the visible symbols on each reel:
[
["Bacon", "Drumstick", "Ham"],
["Bacon", "Bacon", "Steak"],
["Mayonnaise", "Eggs", "Drumstick"],
["Bacon", "Bacon", "Ham"],
["Eggs", "Atkins", "Mayonnaise"]
]
pickRandomly(context)
Fires each time the outcome of a mystery feature in a bonus is to be chosen randomically. Just like in spinStart the game engine stops the execution
after firing this event and waits for the receiver of the event to provide the generated outcome (represented in this case
by the item that is chosen randomically). The complete outcome generation of a game round is based on spinStart,
pickRandomly and customTrigger (the last two are only used when required by specific features of the game) and
the asynchronous implementation makes it possible for the outcome to be generated remotely when needed.
The context object passed to the callback contains the following properties:
- scope: type of the mystery feature
- items: list of the possible outcomes for the mystery feature together with the corresponding probabilities
- state: bonus context
The value of the scope property can be one of names that represent the mystery features natively supported by SLOT-IDE:
- mixedTrigger:
- enterState:
- multiplier:
- spins:
- spinState:
- state:
- prize:
- additionalPrize:
Upon receiving pickRandomly the receiver has to:
- generate a random number in the [0,1) range
- use the random number to pick one of the outcomes
- set a property called item in the context object with outcome that was picked
- invoke the pickRandomly action passing to it the context object
What follows is an example that shows the context object in pickRandomly event for a game
that features a bonus with a mystery win multiplier:
{
"scope": "multiplier",
"items": [{
"prob": 0.6,
"multiplier": 2
}, {
"prob": 0.3,
"multiplier": 3
}, {
"prob": 0.1,
"multiplier": 4
}],
"state": {
"prob": 1,
"additionalPrice": 0,
"triggers": 1,
"played": 0,
"left": 10,
"multiplier": {},
"bonusTriggers": {
"freespins": 1
},
"bonusPlayed": {},
"spins": [{
"spins": 10,
"bonus": "freespins",
"trigger": {
"occurs": [3, 4, 5],
"of": "Scale",
"mode": "scatter"
}
}],
"triggering": {
"bonus": "freespins",
"trigger": {
"occurs": [3, 4, 5],
"of": "Scale",
"mode": "scatter"
}
}
}
}
The code below shows how to register and implement a callback for the pickRandomly event:
function onPickRandomly(context)
{
var randProb = Random.perc();
var sumProb = 0.0;
for (var k=0; k<context.items.length; k++)
{
sumProb += context.items[k].prob;
if (sumProb < randProb)
{
context.item = context.items[k];
break;
}
}
Game.instance.invoke('pickRandomly', context);
};
Game.instance.bind('pickRandomly', onPickRandomly);
customTrigger(context)
Fires each time the reels spin in games that feature a bonus that defines a custom trigger.
This event is part of the outcome generation and as for pickRandomly and spinStart the game
engine stops the execution and waits for the receiver of the event to provide the random outcome.
In this case the receiver needs to determine randomically whether or not the bonus is to be triggered based on the probability of the custom trigger
(this comes as a number in the (0,1] range representing the theoretical probability of triggering the bonus). If the value of the random number determines that the custom trigger
must be fired then the receiver must set to true a property of the context object called triggered. Finally the receiver needs to invoke
the action called customTrigger passing the context object to it.
The following example shows how to implement a callback for customTrigger event:
function onCustomTrigger(context)
{
if (Random.perc() < context.trigger.prob)
{
context.triggered = true;
}
Game.instance.invoke('customTrigger', context);
}
Game.instance.bind('pickRandomly', onCustomTrigger);
gameEnd(context)
Fires when the game round is over. The callback receives a context object that contains the win of the game round in credits in a property called win.
Actions
config
This action needs to be invoked just after the game script is processed and the game object is created. When this action is invoked the
game engine fires the config event and provides to the receiver the game configuration.
bet
This action is used to set the bet for the game round(s) that are going to be played in terms of number of paylines and credits bet per line.
The action needs to be called before the very first game round is played and anytime a different value for the number of selected paylines or credits
bet per line needs to be set. The argument passed to the action is an array whose first element specifies the number of paylines and the second
element specifies the credits bet per line.
The following example shows how to select ten paylines and bet five credits on each line:
Game.instance.invoke('bet', [10, 5]);
play
This actions tell the game engine to initiate a new spin of the reels and it is used to start a game round or to play the next available
spin in bonus games.
When a new game round is to be started the game engine fires the gameStart event and waits for the receiver to
invoke the corresponding action.
gameStart
The receiver of gameStart event invokes this action to confirm the starting of a new game round.
spinStart
This action is used to provide the game engine with the reels stop positions. See spinStart event.
pickRandomly
This action is used to provide the game engine with the item that was chosen randomly. See pickRandomly event.
customTrigger
This action is used to tell the game engine whether or not the custom bonus trigger is to be fired. See customTrigger event.
Game.instance.bind('spinStart', function(context)
{
...
});
Game.instance.invoke('spinStart', context);
Events
Here below comes a description of the individual events fired by the game engine.
Some of the events require a corresponding action to be invoked on the game engine in order to continue the execution. This is the case for the events
related to the generation of the game outcome.
The individual events are listed in the form of eventname(arguments..) where eventname is the name of the event to bind to
and arguments are the arguments passed to the callback when the corresponding event is fired.
config(context)
Fires after the action config is invoked. The callback receives a context object containing the game
configuration (visible window, symbols, reelstrips, pay modes, paylines) as shown in the example below:
{
"symbols": ["LION", "ZEBRA", "GIRAFFE", "PEOPLE", "TREE", "ACE", "KING", "QUEEN", "JACK", ...],
"reels": [
["JACK", "10", "LION", "LION", "LION", "LION", "LION", "QUEEN", ...],
["JACK", "10", "LION", "LION", "LION", "LION", "LION", "QUEEN", ...],
["JACK", "10", "LION", "LION", "LION", "LION", "LION", "QUEEN", ...],
["KING", "JACK", "LION", "LION", "LION", "LION", "LION", "ACE", ...],
["KING", "JACK", "LION", "LION", "LION", "LION", "LION", "QUEEN", ...]
],
"symbolsPay": {
"line": ["LION", "WILD", "ZEBRA", "GIRAFFE", "PEOPLE", "TREE", "ACE", "KING", "QUEEN", ...],
"scatter": ["SCAT"]
},
"wildSymbols": ["WILD"],
"window": {
"rows": 4,
"reels": 5
},
"availablePayLines": [
[0, 0, 0, 0, 0],
[0, 0, 1, 0, 0],
[0, 1, 1, 1, 0],
[0, 1, 2, 1, 0],
[0, 1, 0, 1, 0],
[0, 0, 0, 1, 0],
[0, 1, 0, 0, 0],
[0, 0, 1, 1, 0],
[0, 1, 1, 0, 0],
[0, 0, 2, 0, 0]
]
}
bet(context)
Fires after the action bet is invoked. The callback receives a context object containing
the total bet in credits, the credits bet-per-line, the selected paylines and the maximum win limit in credits (0=unlimited):
{
"total": 5,
"betPerLine": 1,
"paylines": [
[1, 1, 1, 1, 1],
[0, 0, 0, 0, 0],
[2, 2, 2, 2, 2],
[0, 1, 2, 1, 0],
[2, 1, 0, 1, 2]
],
"maxWinCap": 0
}
gameStart()
Fires when a new game round is about to start. The game engine stops the execution and waits for an action called gameStart to be
invoked. When gameStart is invoked the execution is resumed and a new game rounds begins.
spinStart(context)
Fires when the game engine is about to stop the reels and it needs to obtain the corresponding random numbers. After firing this event the game engine stops the execution and
waits for the receiver of the event to provide the reels stop positions. The receiver must do this by:
- generating the needed random numbers (one for each reel)
- setting the stop positions in the context object that is passed to the callback using the generated random numbers
- invoking the action called spinStart passing to it the modified context as an argument
Upon receiving spinStart the game engine resumes executions and stops the reels accordingly. This asynchronous mechanism
allows the outcome of a game round to be generated remotely.
The context object that is passed to the callback contains the reel strips being played in the current spin (base game or bonus), the length of the reels
in the ranges attribute and an empty array in the stops attribute that has to be filled with the reels stop positions corresponding to
the random numbers that have been drawn.
{
"symbols": ["Atkins", "Steak", "Ham", "Drumstick", "Sausage", ...],
"reels": [
["Sausage", "Bacon", "Scale", "Steak", "Sausage", "Scale", ...],
["Eggs", "Drumstick", "Scale", "Drumstick", "Mayonnaise", ...],
["Butter", "Butter", "Scale", "Butter", "Cheese", "Bacon", ...],
["Bacon", "Sausage", "Scale", "Bacon", "Drumstick", "Bacon", ...],
["Drumstick", "Cheese", "Scale", "Drumstick", "Butter", ...]
],
"symbolsPay": {
"line": ["Atkins", "Steak", "Ham", "Drumstick", "Sausage", ...],
"scatter": ["Scale"]
},
"wildSymbols": ["Atkins"],
"stops": [],
"ranges": [32, 32, 32, 32, 32]
}
As an example the following code registers a callback function for the spinStart event, sets the stop positions from random numbers and invokes
the spinStart action in order to resume the game round execution:
function onSpinStart(context)
{
for (var i=0; i<context.ranges.length; i++)
{
var rn = Random.int(context.ranges[i]-1);
stops.push(rn);
}
Game.instance.invoke('spinStart', context);
}
Game.instance.bind('spinStart', onSpinStart);
As you can notice in the example above the RNG that comes with the game engine is used to generate random numbers. This RNG is based on the Mersenne Twister
algorithm and is available through the class called Random. The static method int simply returns a random number in the [0, n] range, where n
is the argument passed to the function, in this case the length of each reel subtracted by one (stop-positions are zero-based).
spinWin(context)
Fires for each win obtained as a result of a reels spin. The callback receives all relevant information
describing the win in the form of a context object containing the following properties:
- what: winning symbol as defined in the paytable
- occurs: number of occurrencies of the winning symbol in the winning combination
- mode: paymode of the winning combination
- pay: number of credits awarded by the winning combination (this represents the actual credits won which for payline wins corresponds to the value from the paytable multiplied by the bet-per-line)
- mpInfo: object containing the multiplier value applied to the win as a result of wild substitutions and the wild substitutions in the winning combinations
- mpBonusInfo: when not null this is an object that specifies the bonus multiplier applied to the win. The object contains a property called bonus whose value is the name of the bonus being
played and a property called mp which carries the bonus multiplier value applied to the win
- context: object that allows to identify which cells of the visible matrix make up the winning combination. This is useful for displaying the win. For payline wins the object contains
the id and the structure of the winning payline. For all other paymodes (scattered, scattered on consecutive reels, scattered on a payline) context is an array of objects that
identify the individual cells of the matrix (each object has two properties called row and reel)
The example below shows the context object describing a payline win:
{
"what":"Drumstick",
"occurs":2,
"mode":"line",
"pay":2,
"mpInfo":{
"mp":1,
"replacements":0
},
"mpBonusInfo":null,
"context":{
"paylineId":0,
"payline":[1,1,1,1,1]
}
}
}
bonusWin(context)
Fires for each win obtained while playing a bonus. This includes both freespins-like bonuses where the underlying bonus game
being played is a slot game and other types of bonuses like pick-a-prize and trails.
The callback receives a context object containing three properties:
- bonus: name of the bonus being played
- pay: number of credits awarded
- isSpinWin: boolean flag indicating whether or not the win is obtained as a result of spinning the results. The flag is set to
true for freespins-like bonuses and to false for other types of bonus
Each win obtained while playing a freespins-like bonus will fire both the spinWin and bonusWin events.
symbolSetInMatrix(context)
This event fires each time a feature of the game requires a symbol of the visible matrix to be replaced by another symbol.
For instance when the
game features and expanding wild this results in all symbols of the reel where a wild is found to be replaced by the wild. In this case the event
is fired as many times as the symbols on the reel being replaced.
The callback receives a context object containing the following properties:
- row: row of the cell corresponding to the symbol being replaced
- reel: reel of the cell corresponding to the symbol being replaced
- oldSymbol: visible symbol in the cell before the replacement
- symbol: new symbol replacing the visible one in the cell
spinTrigger(context)
Fires when as a result of a reels spin a bonus is triggered.
The callback receives a context object containing three properties:
- spins: an array that lists the possible values for the number of spins that will be awarded. Depending of how the bonus is defined, there can be
a random selection for the number of spins to award. In most cases the number of spins is fixed and the array will contain only a single element. If the number of spins
is to be determined randomically then an element of the array will be selected by firing the pickRandomly event
- occurs: number of symbol occurrencies in the combination triggering the bonus
- bonus: name of the bonus being triggered
- trigger: object representing the paytable element that triggered the bonus
The example below shows the context object for a trigger that awards a fixed number of spins.
{
"spins": [{"prob":1,"spins":10}],
"occurs":3,
"bonus":"freespins",
"trigger":{
"occurs":[3,4,5],
"of":"Scale",
"mode":"scatter"
}
}
enterBonus(context)
Fires when a bonus is entered.
The callback receives an object that represents the bonus context as described here
(the object contains the same properties that are available as attributes of this in the functions attached to dynamic bonus properties and from now
on it will be referred to as bonus context).
Here below an example bonus context is shown representing the case where as a result of a scatter win the bonus is entered from the base game and ten free spins are awarded:
{
"triggers": 1,
"played": 0,
"left": 10,
"multiplier": {},
"bonusTriggers": {
"freespins": 1
},
"bonusPlayed": {},
"spins": [{
"spins": 10,
"bonus": "freespins",
"trigger": {
"occurs": [3, 4, 5],
"of": "Scale",
"mode": "scatter"
}
}],
"triggering": {
"bonus": "freespins",
"trigger": {
"occurs": [3, 4, 5],
"of": "Scale",
"mode": "scatter"
}
}
}
For games featuring more than a bonus, the enterBonus event is also fired when a bonus is entered while another bonus is being played. In all
cases the name of the bonus being entered is always the one set in the bonus property of the first element in the spins array (the array
represents the stack of accumulated spins that are left to be played).
playedBonusSpin(context)
Fires after a spin in the bonus has been played. The callback receives the bonus context as an argument.
The name of the bonus being played is the value of the playing property and matches the name of the bonus in the active element
of the accumulated spin stack (first element of the spins array).
The example below shows the bonus context representing the case where one free spin has been played out of the initial ten free spins awarded:
{
"triggers": 1,
"played": 1,
"left": 9,
"bonusTriggers": {
"freespins": 1
},
"bonusPlayed": {
"freespins": {
"count": 1,
}
},
"spins": [{
"spins": 9,
"bonus": "freespins",
"trigger": {
"occurs": [3, 4, 5],
"of": "Scale",
"mode": "scatter"
}
}],
"playing": "freespins",
"state": "main",
"trigger": {
"occurs": [3, 4, 5],
"of": "Scale",
"mode": "scatter"
}
}
playedBonusSpins(context)
Fires when all spins in a bonus have been played and the bonus is over. After this event is fired the bonus is exited and the game round either finishes or
continues in another bonus (in this case enterBonus is fired next).
Here below an example bonus context is shown representing the case where all the ten free spins awarded have been played and the game round is over (the spin stack is empty):
{
"triggers": 1,
"played": 10,
"left": 0,
"bonusTriggers": {
"freespins": 1
},
"bonusPlayed": {
"freespins": {
"count": 10,
}
},
"spins": [],
"playing": "freespins",
"state": "main",
"trigger": {
"occurs": [3, 4, 5],
"of": "Scale",
"mode": "scatter"
}
}
playedSpin(context)
Fires after a spin has been played in the base game or in a bonus.
If the spin is played in a bonus this event is
fired before playedBonusSpin.
The callback receives a context array that contains the visible symbols on each reel:
[
["Bacon", "Drumstick", "Ham"],
["Bacon", "Bacon", "Steak"],
["Mayonnaise", "Eggs", "Drumstick"],
["Bacon", "Bacon", "Ham"],
["Eggs", "Atkins", "Mayonnaise"]
]
pickRandomly(context)
Fires each time the outcome of a mystery feature in a bonus is to be chosen randomically. Just like in spinStart the game engine stops the execution
after firing this event and waits for the receiver of the event to provide the generated outcome (represented in this case
by the item that is chosen randomically). The complete outcome generation of a game round is based on spinStart,
pickRandomly and customTrigger (the last two are only used when required by specific features of the game) and
the asynchronous implementation makes it possible for the outcome to be generated remotely when needed.
The context object passed to the callback contains the following properties:
- scope: type of the mystery feature
- items: list of the possible outcomes for the mystery feature together with the corresponding probabilities
- state: bonus context
The value of the scope property can be one of names that represent the mystery features natively supported by SLOT-IDE:
- mixedTrigger:
- enterState:
- multiplier:
- spins:
- spinState:
- state:
- prize:
- additionalPrize:
Upon receiving pickRandomly the receiver has to:
- generate a random number in the [0,1) range
- use the random number to pick one of the outcomes
- set a property called item in the context object with outcome that was picked
- invoke the pickRandomly action passing to it the context object
What follows is an example that shows the context object in pickRandomly event for a game
that features a bonus with a mystery win multiplier:
{
"scope": "multiplier",
"items": [{
"prob": 0.6,
"multiplier": 2
}, {
"prob": 0.3,
"multiplier": 3
}, {
"prob": 0.1,
"multiplier": 4
}],
"state": {
"prob": 1,
"additionalPrice": 0,
"triggers": 1,
"played": 0,
"left": 10,
"multiplier": {},
"bonusTriggers": {
"freespins": 1
},
"bonusPlayed": {},
"spins": [{
"spins": 10,
"bonus": "freespins",
"trigger": {
"occurs": [3, 4, 5],
"of": "Scale",
"mode": "scatter"
}
}],
"triggering": {
"bonus": "freespins",
"trigger": {
"occurs": [3, 4, 5],
"of": "Scale",
"mode": "scatter"
}
}
}
}
The code below shows how to register and implement a callback for the pickRandomly event:
function onPickRandomly(context)
{
var randProb = Random.perc();
var sumProb = 0.0;
for (var k=0; k<context.items.length; k++)
{
sumProb += context.items[k].prob;
if (sumProb < randProb)
{
context.item = context.items[k];
break;
}
}
Game.instance.invoke('pickRandomly', context);
};
Game.instance.bind('pickRandomly', onPickRandomly);
customTrigger(context)
Fires each time the reels spin in games that feature a bonus that defines a custom trigger.
This event is part of the outcome generation and as for pickRandomly and spinStart the game
engine stops the execution and waits for the receiver of the event to provide the random outcome.
In this case the receiver needs to determine randomically whether or not the bonus is to be triggered based on the probability of the custom trigger
(this comes as a number in the (0,1] range representing the theoretical probability of triggering the bonus). If the value of the random number determines that the custom trigger
must be fired then the receiver must set to true a property of the context object called triggered. Finally the receiver needs to invoke
the action called customTrigger passing the context object to it.
The following example shows how to implement a callback for customTrigger event:
function onCustomTrigger(context)
{
if (Random.perc() < context.trigger.prob)
{
context.triggered = true;
}
Game.instance.invoke('customTrigger', context);
}
Game.instance.bind('pickRandomly', onCustomTrigger);
gameEnd(context)
Fires when the game round is over. The callback receives a context object that contains the win of the game round in credits in a property called win.
Actions
config
This action needs to be invoked just after the game script is processed and the game object is created. When this action is invoked the
game engine fires the config event and provides to the receiver the game configuration.
bet
This action is used to set the bet for the game round(s) that are going to be played in terms of number of paylines and credits bet per line.
The action needs to be called before the very first game round is played and anytime a different value for the number of selected paylines or credits
bet per line needs to be set. The argument passed to the action is an array whose first element specifies the number of paylines and the second
element specifies the credits bet per line.
The following example shows how to select ten paylines and bet five credits on each line:
Game.instance.invoke('bet', [10, 5]);
play
This actions tell the game engine to initiate a new spin of the reels and it is used to start a game round or to play the next available
spin in bonus games.
When a new game round is to be started the game engine fires the gameStart event and waits for the receiver to
invoke the corresponding action.
gameStart
The receiver of gameStart event invokes this action to confirm the starting of a new game round.
spinStart
This action is used to provide the game engine with the reels stop positions. See spinStart event.
pickRandomly
This action is used to provide the game engine with the item that was chosen randomly. See pickRandomly event.
customTrigger
This action is used to tell the game engine whether or not the custom bonus trigger is to be fired. See customTrigger event.
{
"symbols": ["LION", "ZEBRA", "GIRAFFE", "PEOPLE", "TREE", "ACE", "KING", "QUEEN", "JACK", ...],
"reels": [
["JACK", "10", "LION", "LION", "LION", "LION", "LION", "QUEEN", ...],
["JACK", "10", "LION", "LION", "LION", "LION", "LION", "QUEEN", ...],
["JACK", "10", "LION", "LION", "LION", "LION", "LION", "QUEEN", ...],
["KING", "JACK", "LION", "LION", "LION", "LION", "LION", "ACE", ...],
["KING", "JACK", "LION", "LION", "LION", "LION", "LION", "QUEEN", ...]
],
"symbolsPay": {
"line": ["LION", "WILD", "ZEBRA", "GIRAFFE", "PEOPLE", "TREE", "ACE", "KING", "QUEEN", ...],
"scatter": ["SCAT"]
},
"wildSymbols": ["WILD"],
"window": {
"rows": 4,
"reels": 5
},
"availablePayLines": [
[0, 0, 0, 0, 0],
[0, 0, 1, 0, 0],
[0, 1, 1, 1, 0],
[0, 1, 2, 1, 0],
[0, 1, 0, 1, 0],
[0, 0, 0, 1, 0],
[0, 1, 0, 0, 0],
[0, 0, 1, 1, 0],
[0, 1, 1, 0, 0],
[0, 0, 2, 0, 0]
]
}
{
"total": 5,
"betPerLine": 1,
"paylines": [
[1, 1, 1, 1, 1],
[0, 0, 0, 0, 0],
[2, 2, 2, 2, 2],
[0, 1, 2, 1, 0],
[2, 1, 0, 1, 2]
],
"maxWinCap": 0
}
{
"symbols": ["Atkins", "Steak", "Ham", "Drumstick", "Sausage", ...],
"reels": [
["Sausage", "Bacon", "Scale", "Steak", "Sausage", "Scale", ...],
["Eggs", "Drumstick", "Scale", "Drumstick", "Mayonnaise", ...],
["Butter", "Butter", "Scale", "Butter", "Cheese", "Bacon", ...],
["Bacon", "Sausage", "Scale", "Bacon", "Drumstick", "Bacon", ...],
["Drumstick", "Cheese", "Scale", "Drumstick", "Butter", ...]
],
"symbolsPay": {
"line": ["Atkins", "Steak", "Ham", "Drumstick", "Sausage", ...],
"scatter": ["Scale"]
},
"wildSymbols": ["Atkins"],
"stops": [],
"ranges": [32, 32, 32, 32, 32]
}
function onSpinStart(context)
{
for (var i=0; i<context.ranges.length; i++)
{
var rn = Random.int(context.ranges[i]-1);
stops.push(rn);
}
Game.instance.invoke('spinStart', context);
}
Game.instance.bind('spinStart', onSpinStart);
{
"what":"Drumstick",
"occurs":2,
"mode":"line",
"pay":2,
"mpInfo":{
"mp":1,
"replacements":0
},
"mpBonusInfo":null,
"context":{
"paylineId":0,
"payline":[1,1,1,1,1]
}
}
}
{
"spins": [{"prob":1,"spins":10}],
"occurs":3,
"bonus":"freespins",
"trigger":{
"occurs":[3,4,5],
"of":"Scale",
"mode":"scatter"
}
}
{
"triggers": 1,
"played": 0,
"left": 10,
"multiplier": {},
"bonusTriggers": {
"freespins": 1
},
"bonusPlayed": {},
"spins": [{
"spins": 10,
"bonus": "freespins",
"trigger": {
"occurs": [3, 4, 5],
"of": "Scale",
"mode": "scatter"
}
}],
"triggering": {
"bonus": "freespins",
"trigger": {
"occurs": [3, 4, 5],
"of": "Scale",
"mode": "scatter"
}
}
}
{
"triggers": 1,
"played": 1,
"left": 9,
"bonusTriggers": {
"freespins": 1
},
"bonusPlayed": {
"freespins": {
"count": 1,
}
},
"spins": [{
"spins": 9,
"bonus": "freespins",
"trigger": {
"occurs": [3, 4, 5],
"of": "Scale",
"mode": "scatter"
}
}],
"playing": "freespins",
"state": "main",
"trigger": {
"occurs": [3, 4, 5],
"of": "Scale",
"mode": "scatter"
}
}
{
"triggers": 1,
"played": 10,
"left": 0,
"bonusTriggers": {
"freespins": 1
},
"bonusPlayed": {
"freespins": {
"count": 10,
}
},
"spins": [],
"playing": "freespins",
"state": "main",
"trigger": {
"occurs": [3, 4, 5],
"of": "Scale",
"mode": "scatter"
}
}
[
["Bacon", "Drumstick", "Ham"],
["Bacon", "Bacon", "Steak"],
["Mayonnaise", "Eggs", "Drumstick"],
["Bacon", "Bacon", "Ham"],
["Eggs", "Atkins", "Mayonnaise"]
]
{
"scope": "multiplier",
"items": [{
"prob": 0.6,
"multiplier": 2
}, {
"prob": 0.3,
"multiplier": 3
}, {
"prob": 0.1,
"multiplier": 4
}],
"state": {
"prob": 1,
"additionalPrice": 0,
"triggers": 1,
"played": 0,
"left": 10,
"multiplier": {},
"bonusTriggers": {
"freespins": 1
},
"bonusPlayed": {},
"spins": [{
"spins": 10,
"bonus": "freespins",
"trigger": {
"occurs": [3, 4, 5],
"of": "Scale",
"mode": "scatter"
}
}],
"triggering": {
"bonus": "freespins",
"trigger": {
"occurs": [3, 4, 5],
"of": "Scale",
"mode": "scatter"
}
}
}
}
function onPickRandomly(context)
{
var randProb = Random.perc();
var sumProb = 0.0;
for (var k=0; k<context.items.length; k++)
{
sumProb += context.items[k].prob;
if (sumProb < randProb)
{
context.item = context.items[k];
break;
}
}
Game.instance.invoke('pickRandomly', context);
};
Game.instance.bind('pickRandomly', onPickRandomly);
function onCustomTrigger(context)
{
if (Random.perc() < context.trigger.prob)
{
context.triggered = true;
}
Game.instance.invoke('customTrigger', context);
}
Game.instance.bind('pickRandomly', onCustomTrigger);
config
This action needs to be invoked just after the game script is processed and the game object is created. When this action is invoked the game engine fires the config event and provides to the receiver the game configuration.bet
This action is used to set the bet for the game round(s) that are going to be played in terms of number of paylines and credits bet per line.The action needs to be called before the very first game round is played and anytime a different value for the number of selected paylines or credits bet per line needs to be set. The argument passed to the action is an array whose first element specifies the number of paylines and the second element specifies the credits bet per line.
The following example shows how to select ten paylines and bet five credits on each line:
Game.instance.invoke('bet', [10, 5]);
play
This actions tell the game engine to initiate a new spin of the reels and it is used to start a game round or to play the next available spin in bonus games.When a new game round is to be started the game engine fires the gameStart event and waits for the receiver to invoke the corresponding action.