Skip to content
Jan Kluka edited this page Jul 30, 2026 · 6 revisions

Casino

Four solo games played against the house — Slots, Roulette, Dice and Crash.

Every wager the house wins is destroyed. The Casino is a currency sink: it exists to take currency out of an inflated economy, never to put currency into it. That is the opposite of the Coinflip addon, which is player-versus-player and simply moves currency between two players.

Addon — drop X-Prison-Casino-<version>.jar into plugins/X-Prison/addons/ and restart. Requires the Currency module to be enabled. Requires X-Prison 2026.3.2.0 or newer.


Files

File Purpose
plugins/X-Prison/addons/Casino/casino.yml Games, odds, wager limits, house edge
plugins/X-Prison/addons/Casino/casino-gui.yml Every menu — slots, items, titles, animation targets
plugins/X-Prison/addons/Casino/casino-messages.yml Every message, raw MiniMessage

How the house edge works

Each game works out a fair payout multiplier from its own odds, then scales it down by house-edge. An edge of 0.05 means players get back 95% of what perfectly fair odds would pay.

Game Fair multiplier
Slots the payout configured for the symbol combination that landed
Roulette pockets ÷ winning-pockets for the bet that was backed
Dice 100 ÷ win chance
Crash baked into the crash-point distribution itself

The resulting return to player (RTP) is printed in the console when each game is enabled, and listed by /casino rtp.

The money-printer guard

A game configured with an RTP of 100% or more pays out more than it takes in — it would create currency instead of destroying it. The Casino refuses to enable such a game and prints an error naming it, rather than quietly running it. Lower its payouts or raise its house-edge and reload.

This is why house-edge: 0 is not a valid setup for Dice or Crash: with no edge they return exactly 100%, and they will not start.


Games

Slots

Every reel is drawn from the same weighted symbol pool. A symbol's chance is its weight divided by the total of all weights.

slots:
  reels: 3
  symbols:
    CHERRY: 40
    SEVEN: 1
  payouts:
    all-matching:      # every reel showing the same symbol
      CHERRY: 2.0
      SEVEN: 15000.0
    two-matching: 1.2  # exactly two reels match; 0 disables it

How each symbol looks is configured separately, in casino-gui.yml under result.slots.symbols.

The RTP is computed by enumerating every possible combination exactly. If your table has more combinations than can be checked (symbols raised to the power of reels), the game refuses to load and asks you to reduce one of the two.

Roulette

One pocket is drawn from 0 to pockets - 1. Each bet declares how many of those pockets win it.

roulette:
  pockets: 37
  default-bet: "red"
  bets:
    red:
      winning-pockets: 18     # an 18-in-37 chance
      payout: 2.0555          # fair, because 37 ÷ 18 = 2.0555
    green:
      winning-pockets: 1
      payout: 37.0

Dice

The player picks a win chance; the payout scales inversely with it. A 5% shot pays roughly twenty times the stake, a 90% shot barely more than the stake.

dice:
  min-chance: 1
  max-chance: 95      # must stay below 100, or the game could not lose
  default-chance: 50

Crash

A multiplier climbs from 1.00x. Cash out before it crashes or lose the wager.

crash:
  max-multiplier: 100.0
  tick-interval: 4      # ticks between steps — lower is faster and tenser
  growth-per-tick: 0.04

Crash is the only game that settles while its menu is open, which makes it the only one that can be interrupted. Two safeguards cover that:

  • Closing the menu cashes out at the multiplier that was on screen. Logging out, dying or walking through a portal can never strand a wager.
  • A server crash refunds the wager. Open rounds are recorded in the database and refunded on the next startup, with a line in the console saying how many.

Wagering and limits

defaults:
  currencies: [ "Tokens" ]   # which currencies may be wagered
  min-wager: 1000
  max-wager: 0               # 0 = no upper limit
  house-edge: 0.05
  cooldown-seconds: 0
  permission: ""             # extra permission on top of xprison.casino.game.<id>

Anything under defaults can be overridden per game under games.<id>.

limits:
  daily-net-loss-cap: 0      # stop a player once they are this far down today; 0 = no cap
  bypass-permission: "xprison.casino.bypass.limits"

The daily tally is kept in memory and survives a relog, so reconnecting cannot reset a player's limit. It does reset when the server restarts, and at midnight in the server's timezone.

All amounts are exact BigDecimal values end to end — OP-scale balances above 10²⁴ wager and pay out without losing a single digit.


Nothing shown to a player is hardcoded

Every piece of text a player can see comes from a config file. Menus and their items live in casino-gui.yml, messages in casino-messages.yml, and the small fragments the plugin has to substitute live under display in casino.yml:

display:
  unlimited: "<gray>Unlimited"     # shown wherever a wager has no upper limit
  selected: "<green><bold>✔"       # marks the bet or chance a player has picked
  not-selected: ""
  hidden: "<dark_gray>???"         # stands in for the Crash multiplier while it is secret

Each game also takes a display-name, used everywhere the game is named — menus, messages, broadcasts and /casino stats:

games:
  slots:
    display-name: "<gradient:#f5d76e:#e08f2c>Slots</gradient>"

Roulette bets take one too, so red can read as anything you like.

All of it is raw MiniMessage, so gradients, hover and click tags work throughout.


Sounds

Every cue is configurable, and any of them can be silenced with enabled: false or sound: none.

sounds:
  menu-open:    { enabled: true, sound: BLOCK_BARREL_OPEN,   volume: 0.5, pitch: 1.4 }
  click:        { enabled: true, sound: UI_BUTTON_CLICK,     volume: 0.5, pitch: 1.6 }
  wager-change: { enabled: true, sound: BLOCK_NOTE_BLOCK_HAT, volume: 0.6, pitch: 1.8 }
  select:       { enabled: true, sound: BLOCK_NOTE_BLOCK_PLING, volume: 0.6, pitch: 1.5 }
  denied:       { enabled: true, sound: BLOCK_NOTE_BLOCK_BASS, volume: 0.5, pitch: 0.7 }
  round-start:  { enabled: true, sound: BLOCK_LEVER_CLICK,   volume: 0.9, pitch: 0.8 }
  reel-tick:    { enabled: true, sound: BLOCK_NOTE_BLOCK_HAT, volume: 0.3, pitch: 1.2 }
  win:          { enabled: true, sound: ENTITY_PLAYER_LEVELUP, volume: 0.8, pitch: 1.4 }
  big-win:      { enabled: true, sound: UI_TOAST_CHALLENGE_COMPLETE, volume: 1.0, pitch: 1.0 }
  loss:         { enabled: true, sound: ENTITY_VILLAGER_NO,  volume: 0.7, pitch: 0.9 }
  push:         { enabled: true, sound: BLOCK_NOTE_BLOCK_BELL, volume: 0.6, pitch: 1.0 }
  crash-tick:   { enabled: true, sound: BLOCK_NOTE_BLOCK_HAT, volume: 0.4, pitch: 1.0 }
  crash-cashout:{ enabled: true, sound: ENTITY_PLAYER_LEVELUP, volume: 0.9, pitch: 1.6 }
  crash-bust:   { enabled: true, sound: ENTITY_GENERIC_EXPLODE, volume: 0.7, pitch: 1.3 }

Two cues climb in pitch rather than repeating flatly: reel-tick rises as the reels slow down, and crash-tick rises with the multiplier. denied is what a click that could not do anything sounds like — already at the wager cap, or only one currency to switch between.

big-win replaces win whenever a payout reaches broadcast.big-win-threshold.


Animation

animation:
  enabled: true
  duration-ticks: 40
  frame-interval-ticks: 2
  skippable: true
  remember-skip-choice: true

The round is already settled before its menu opens — the animation is pure display. Skipping it, closing the menu, or logging out mid-spin cannot change or lose the result.

With remember-skip-choice: true, clicking the skip button also skips future animations for that player. /casino skip toggles it back on.


Commands

Command Permission What it does
/casino xprison.casino.use Opens the casino hub
/casino <game> [wager] xprison.casino.game.<game> Opens that game's bet menu
/casino open <player> xprison.menu.other Opens the hub on another player's screen (NPC support)
/casino stats [player] xprison.casino.use An overall line plus one line per game
/casino top [currency] xprison.casino.use Players who are up on the house
/casino skip xprison.casino.use Toggles animation skipping
/casino rtp xprison.casino.admin Prints each game's configured return to player
/casino reload xprison.casino.reload Reloads all three config files

The command name and its aliases are set in casino.yml under command.


Placeholders

Requires PlaceholderAPI.

Placeholder Value
%xcasino_wins% Rounds won
%xcasino_losses% Rounds lost
%xcasino_rounds% Rounds played
%xcasino_wagered% Total wagered
%xcasino_net% Net result against the house
%xcasino_biggest_win% Largest single payout
%xcasino_net_<currency>% Net result in a specific currency
%xcasino_wagered_<currency>% Total wagered in a specific currency
%xcasino_biggest_win_<currency>% Largest payout in a specific currency
%xcasino_rtp_<game>% A game's configured return to player
%xcasino_min_wager_<game>% A game's minimum wager
%xcasino_<stat>_game_<game>% Any of the stats above, scoped to one game — e.g. %xcasino_net_game_crash%

Stats storage

stats:
  enabled: true
  retention-days: 30    # prune round history older than this on startup; 0 = keep forever

Rounds are written to the X-Prison database as an audit trail, and running totals are kept per player, per currency and per game — which is what lets /casino stats show an overall line followed by one line for each game, and what the _game_ placeholders read from. Amounts are stored as text rather than numbers, because SQLite has no exact decimal type and would round OP-scale wagers.


Broadcasts

broadcast:
  big-win-threshold: 10000000   # announce payouts at or above this; 0 = never

The message itself is big_win_broadcast in casino-messages.yml.


Adding your own game

The Casino is built around a CasinoGame interface. A new game is one class, one registry line and one config block — no existing code is edited. Developers can also listen for CasinoWagerEvent (cancellable, with a mutable amount) and CasinoRoundSettledEvent.

XPrison Logo

General

Modules

Default Configs

Enchant Configs — Passive

Enchant Configs — Currency Rewards

Enchant Configs — Key & Item Rewards

Enchant Configs — Area of Effect

Enchant Configs — Multipliers

Enchant Configs — Templates

Enchant Configs — Addons

Addons

Support

For Developers

Others

Clone this wiki locally