Skip to main content

Free Rounds

Free rounds are bonus rounds funded by the operator. They are managed entirely by the Kalamba SDK Wrapper — the wrapper detects them in RGS responses, restricts the bet, injects the required payload into play requests and informs the Game Client about lifecycle changes. The Game Client does not talk to the RGS about free rounds directly.

The FreeRound shape

type FreeRound = {
conf: {
base: number
multiplier: number
numAwarded: number
}
data: {
numPlayed: number
win: number
numLeft: number
}
id: string
rejectable: boolean
skippable: boolean
status: 'PENDING' | 'ACTIVE' | 'FINISHED'
type: string
}

conf describes the awarded package (the fixed bet and the number of rounds), data describes the progress so far. rejectable and skippable tell whether the player is allowed to decline or skip the offer.

Lifecycle

Free rounds arrive in the freeRounds array of the open game and play responses. The wrapper inspects the first entry after the game reports playReady (following open game) and after every playCycleEnd, and acts on its status:

  • PENDING — the player has an offer waiting. The wrapper sends freeRoundsOffer with the FreeRound payload.
  • ACTIVE — free rounds are running. The wrapper activates them: it remembers the free round id, injects { freeRoundId } into every play request sent to the RGS, and sends restricted legalBets and bet events matching the free round configuration (conf.base / conf.multiplier). While free rounds are active the wrapper also stops deducting the bet from the displayed balance on playCycleStart.
  • FINISHED — the package has been played out. The wrapper sends freeRoundsComplete with the final FreeRound payload (including data.win), stops injecting freeRoundId, and restores the original legalBets and default bet from the open game response.

Info

While free rounds are active, the Game Client MAY send freeRoundsPopup (no payload) to request the current session state. The wrapper answers with freeRoundsInfo carrying the current FreeRound.

sdk.send('freeRoundsPopup')

Player actions

The Game Client notifies the wrapper about the player's decision on an offer by sending freeRounds with a FreeRoundsAction payload:

type FreeRoundsAction = {
action: 'ACCEPT' | 'DECLINE' | 'SKIP'
id: string
}

sdk.send('freeRounds', { action: 'ACCEPT', id: freeRound.id })

DECLINE MUST only be offered to the player when rejectable is true, and SKIP only when skippable is true. On ACCEPT the wrapper activates the free rounds once the RGS confirms the action with freeRoundsResponse. If the RGS fails to process the action, the wrapper reports it like any other RGS error (see Error Handling).

showFreeRounds

By default (showFreeRounds: true) the wrapper renders its own free rounds popups for the offer, info and complete stages — see Wrapper Features.

If the Game Client wants to render these screens itself, it MUST send showFreeRounds: false in sdk.configure and MUST handle the freeRoundsOffer, freeRoundsInfo and freeRoundsComplete events on its own, including sending the freeRounds action for the offer.

sdk.on('freeRoundsOffer', freeRound => {
game.showFreeRoundsOffer(freeRound)
})

sdk.on('freeRoundsComplete', freeRound => {
game.showFreeRoundsSummary(freeRound)
})

Minimal Game Client requirements

Even with the wrapper popups enabled, every Game Client MUST:

  • React to legalBets and bet events at any time — the wrapper uses them to lock the bet to the free round configuration on activation and to restore it on completion (see Reacting to Events).
  • Treat the play request payload transparently — the freeRoundId is injected by the wrapper, the Game Client MUST NOT add, remove or depend on it.

No other free-rounds-specific logic is required in the Game Client.