diff options
Diffstat (limited to 'building_guide/rooms.md')
| -rw-r--r-- | building_guide/rooms.md | 347 |
1 files changed, 140 insertions, 207 deletions
diff --git a/building_guide/rooms.md b/building_guide/rooms.md index 635c43c..9cb93b1 100644 --- a/building_guide/rooms.md +++ b/building_guide/rooms.md @@ -234,25 +234,37 @@ Mobs stop wandering while in combat. Dead mobs respawn at their home room. ### Casino Tables -Rooms may host one or more multiplayer casino tables. Players join by typing -`play <game>`; the table ID is an internal room-local identifier and does not -need to be typed. Blackjack and baccarat are the multiplayer table games; -slots run as single-player machines (see below), and older rooms that declare -slots under `casino_tables` are migrated to machines automatically. +Rooms host multiplayer tables under `casino_tables` and single-player slot +machines under `casino_machines`. Players join a table with `play <game>`, and +a machine by its `variant` (or by its bare game name when it is the room's +only machine of that game). Because a table is addressed by its game name, one +room may host at most one table per game; video poker cabinets are addressed +by their `id`. -`max_players`, `betting_ticks`, `min_bet`, and `max_bet` use defaults when omitted. -Game-specific rule configuration is nested alongside the table definition. +Multiplayer table fields (`casino_tables`): -Blackjack tables support sequential player turns and configurable house rules: +| Field | Type | Default | Notes | +|---|---|---|---| +| `id` | string | — | Required. Nonempty, no surrounding whitespace, unique among the room's tables and machines. | +| `game` | string | — | Required. `blackjack`, `baccarat`, `roulette`, `craps`, `paigow`, or `video_poker`. | +| `max_players` | int | 8 | 0–64. Video poker is forced to 1; Pai Gow caps at 6. | +| `betting_ticks` | int | 20 | 0–3600. | +| `min_bet` | int | 1 | Must not exceed `max_bet`. | +| `max_bet` | int | 1000000 | Must be at least `min_bet`. The sentinel `9223372036854775807` means no cap. | + +Game rules are nested in a block named after the game. `id` and `game` are the +only required keys; omitted numeric fields keep their defaults. + +#### Blackjack ```yaml casino_tables: - id: blackjack game: blackjack max_players: 6 - betting_ticks: 20 min_bet: 1 max_bet: 1000 + betting_ticks: 20 blackjack: decks: 8 shuffle_at: 0.25 @@ -268,166 +280,112 @@ casino_tables: split_unlike_ten_values: true ``` -Blackjack supports `hit`, `stand`, `double`, `split`, `insurance`, and -`surrender`. A player blackjack against a dealer blackjack pushes. When the -dealer's up card is an ace, an insurance window opens before anyone plays: -each bettor may type `insurance` to buy cover for half their base bet, and -any other action declines. Once every bettor has decided (or the window times -out), a dealer blackjack settles the round immediately with insurance paying -its configured profit multiplier; otherwise play begins. Split hands can be -configured independently for re-splitting aces and doubling, and split aces -receive one card each with no further actions beyond a configured re-split. - -Two blackjack knobs are player-favourable and on by default, so set them -explicitly when authoring a standard-rules table: - -- `split_unlike_ten_values: true` lets any two 10-valued cards split - (K+Q, 10+J). Casinos vary: some permit any ten-valued pair, others - require identical ranks. -- `double_split_blackjack: true` pays a 3+ card hand that reaches 21 after a - split and double as a blackjack (3:2). Standard play pays 1:1 for any 21 on - a split hand, doubled or not. Both raise the table's actual RTP above the - nominal blackjack payout. - -Baccarat's `banker_payout` (0.95 = a 5% commission) rounds the commission UP: -a winning banker bet too small to carry a whole credit of commission returns -only the stake rather than paying a commission-free 1:1. Payout multipliers -are capped (`blackjack_payout`/`insurance_payout` ≤ 5, `tie_payout` ≤ 10, -commission-table `banker_payout` ≤ 0.99) so a typo cannot silently create an -absurd table. No-commission baccarat variants require `banker_payout: 1` -(or omit it); their compensating exceptions are enforced by the engine. - -### Blackjack rule cards and test tables - -`look blackjack`, `look blackjack table`, or `look <table-id>` displays the -fixture's complete effective rule card (including omitted/default settings, -fixed engine rules, limits, and timers). Joining with `play blackjack` also -shows it privately. No scenery object or room-description text is required: -placing a `casino_tables` entry in any room brings the rule card with it. -Currently only one table of each game is allowed per room; use separate rooms -for different blackjack tables. - -The casino has these test profiles: - -| Room | Table | Decks | Blackjack | Soft 17 | DAS | Maximum hands | Re-split aces | Surrender | -| --- | --- | --- | --- | --- | --- | --- | --- | --- | -| 6110 | Classic | 6 | 3:2 | Stand | Yes | 4 | No | No | -| 6112 | Mercy | 6 | 3:2 | Stand | Yes | 4 | Yes | Late | -| 6114 | Single Deck | 1 | 6:5 | Hit | No | 2 | No | No | -| 6116 | Midnight | 8 | 3:2 | Hit | Yes | 4 | No | No | - -All four disable the nonstandard split-and-double blackjack bonus. Use -`blackjack_payout: 1.5` for 3:2 or `1.2` for 6:5. `max_split_hands: 1` -disables splitting; 2 allows one split, and larger values allow re-splitting -within the cap (aces additionally require `resplit_aces`). - -Common casino variations not currently configurable are restricted double-down -totals (e.g. 9–11 or 10–11 only), hitting split aces, disabling insurance, -and a separate even-money action for a natural against an ace. Doubling is -always for the full original stake, not “double for less.” Late surrender is -supported; early surrender is not. Side bets are not implemented. - -Casino-fidelity caveats: fractional credits round down. Shoe coverage may -trigger an early shuffle, and exhaustion rebuilds the shoe mid-round rather -than guaranteeing a casino-style finite shoe for the whole round. A connected -solo player's action timer also has special grace behavior. None of these -mechanics were changed by adding rule cards. - -At a blackjack table, `bet` uses the configured minimum wager; `bet min`, -`bet max`, and `bet <amount>` select the wager explicitly. - -Card art is loaded from `data/cards.yaml` and is shared by every card game -(blackjack, baccarat). Each of the 52 rank/suit -keys may define Unicode art for sizes 5, 7, and 9. Missing art uses the -generated renderer. Players can choose `option card_size 1`, `5`, `7`, or `9`; -size 1 uses Unicode playing-card symbols. ASCII mode always uses generated -5x5 cards. `option card_style light` adds a white card background; `dark` is -the default style. The same art renders baccarat hands. `option casino_display` -only controls menu verbosity (`detailed` is the default; `compact` shortens -menus and action hints) and never changes card size. - -Baccarat tables play punto banco: every participant bets on the player, the -banker, or a tie, then the dealer plays out both hands by fixed drawing rules -— there are no mid-round player decisions: +| Field | Type | Default | Notes | +|---|---|---|---| +| `decks` | int | 8 | 0–64. | +| `shuffle_at` | float | 0.25 | Shoe fraction; must be greater than 0 and less than 1. | +| `blackjack_payout` | float | 1.5 | Profit multiplier; at most 5. | +| `dealer_hit_soft_17` | bool | false | | +| `max_split_hands` | int | 4 | `1` disables splitting, `2` allows one split, and larger values allow re-splitting up to the cap. | +| `resplit_aces` | bool | false | | +| `double_after_split` | bool | true | | +| `double_split_blackjack` | bool | true | Pays a split and doubled 3+ card 21 as a blackjack. | +| `insurance_payout` | float | 2 | Profit multiplier; at most 5. | +| `surrender` | bool | false | Requires `min_bet` of at least 2. | +| `action_ticks` | int | 20 | 0–3600. | +| `split_unlike_ten_values` | bool | true | Allows any two 10-valued cards to split. | + +#### Baccarat ```yaml casino_tables: - id: baccarat game: baccarat max_players: 6 - betting_ticks: 20 min_bet: 1 max_bet: 1000 + betting_ticks: 20 baccarat: - variant: commission # commission (default), super_6, ez_baccarat + variant: commission decks: 8 shuffle_at: 0.25 - tie_payout: 8 # 8:1 or 9:1 are common - banker_payout: 0.95 # omit or use 1 for no-commission variants - pair_bets: false # player_pair and banker_pair, 11:1 - dragon_7: false # winning three-card Banker 7, 40:1 - panda_8: false # winning three-card Player 8, 25:1 - super_6_bet: false # winning Banker 6, 12:1 -``` - -At a baccarat table, `bet <amount> on player`, `bet <amount> on banker`, and -`bet <amount> on tie` place wagers (the words and amounts may come in any -order, and `min`/`max` work as usual). A bare `bet` repeats the previous -amount and target. Winning player bets pay 1:1; on commission tables winning -banker bets pay `banker_payout`:1 (0.95 is the classic 5% commission); winning tie bets pay -`tie_payout`:1. On a tie, player and banker bets push. When the betting -window closes, both hands are dealt by the standard tableau: naturals (8 or -9) stand immediately, the player hand draws on 0-5, and the banker hand -follows the usual third-card rules. - -#### Baccarat variants, rule cards, and test tables - -- **Commission punto banco:** default 5% commission on Banker winnings; - reduced commission can be configured with `banker_payout` (e.g. 0.98). -- **Super 6 (`super_6`):** no commission; Banker wins pay 1:1 except a - winning Banker 6, with either two or three cards, pays 1:2. -- **EZ Baccarat (`ez_baccarat`):** no commission; Banker wins pay 1:1 except - a winning **three-card** Banker 7 pushes the Banker wager. Player and Tie - wagers still lose on that outcome. A two-card Banker 7 pays normally. - -All variants use the same automatic punto banco drawing tableau. Standard, -mini-, and midi-baccarat mainly differ in seating, limits, dealing pace, and -card handling, not this tableau; they do not need separate settlement engines. -Deck count, cut threshold, limits, seats, and Tie payout remain table-specific. - -Optional wagers default off and may be enabled independently on any variant. -Use `bet 10 on player_pair`, `banker_pair`, `dragon_7`, `panda_8`, or `super_6` -where offered. Pairs mean matching **ranks** of the first two cards (not merely -matching baccarat values); they settle independently of which hand wins and -can win on a tie. Dragon 7, Panda 8, and Super 6 require an outright hand win, -not a tie. Their fixed profit odds are listed in the configuration example. -The separate Super 6 optional bet is not the main Banker's half-pay rule. - -**Current wagering limitation:** one wager per player per round. Optional -bets are selectable wagers, not additions to a simultaneous Player/Banker bet. -All use the table's bet limits. Fractional winnings round down; for exact -5% commissions use multiples of 20 credits, and for half-pay wins use even -stakes. Commission is deducted immediately, not collected later on a ledger. - -`look baccarat`, `look baccarat table`, and `look <table-id>` display the full -rule card, as does joining with `play baccarat`. It lists every supported -setting, including disabled optional bets, automatic drawing rules, rounding, -and operational limits. It belongs to the table and works in any room without -a plaque or special room text. One baccarat table is allowed per room. - -| Room | Table | Variant | Decks | Tie pays | Optional wagers | -| --- | --- | --- | --- | --- | --- | -| 6127 | Harbor | 5% commission | 8 | 8:1 | Player/Banker pair | -| 6128 | Charter | Super 6 | 6 | 9:1 | Pairs, Super 6 | -| 6129 | Crown | EZ Baccarat | 8 | 8:1 | Dragon 7, Panda 8 | - -Not modeled: player squeeze/shoe-passing ceremonies, burn-card rituals, -deferred commission, simultaneous wagers, and other casino-specific side-bet -paytables (such as Dragon Bonus or progressive jackpots). These are not -implied by selecting an implemented variant. - -Single-player machines use `casino_machines` and are separate from multiplayer -tables: + tie_payout: 8 + banker_payout: 0.95 + pair_bets: false + dragon_7: false + panda_8: false + super_6_bet: false +``` + +| Field | Type | Default | Notes | +|---|---|---|---| +| `variant` | string | `commission` | `commission`, `super_6`, or `ez_baccarat`. | +| `decks` | int | 8 | 0–64. | +| `shuffle_at` | float | 0.25 | Must be greater than 0 and less than 1. | +| `tie_payout` | float | 8 | Profit multiplier; at most 10. | +| `banker_payout` | float | 0.95 | Commission tables only; at most 0.99. The `super_6` and `ez_baccarat` variants require 1 (or omit it). | +| `pair_bets` | bool | false | Enables the player and banker pair wagers. | +| `dragon_7` | bool | false | Enables the Dragon 7 wager. | +| `panda_8` | bool | false | Enables the Panda 8 wager. | +| `super_6_bet` | bool | false | Enables the optional Super 6 wager; separate from the `super_6` variant. | + +#### Roulette + +```yaml +casino_tables: + - id: roulette + game: roulette + max_players: 8 + min_bet: 1 + max_bet: 10000 + betting_ticks: 15 + roulette: + double_zero: false + racetrack: false + spin_ticks: 4 +``` + +| Field | Type | Default | Notes | +|---|---|---|---| +| `double_zero` | bool | false | false selects the European 37-pocket wheel; true selects the American 38-pocket wheel. | +| `racetrack` | bool | false | Adds the French call bets; single-zero tables only. | +| `spin_ticks` | int | 3 | 0–3600. | + +#### Craps + +```yaml +casino_tables: + - id: craps + game: craps + max_players: 12 + min_bet: 1 + max_bet: 10000 + craps: + variant: bank + odds: 0 + field_12: 2 +``` + +| Field | Type | Default | Notes | +|---|---|---|---| +| `variant` | string | `bank` | `bank`, `simplified`, `high_point`, `crapless`, `new_york`, `die_rich`, or `diceless`. Each is a separate house edition. | +| `odds` | int | 0 | `0` selects 3-4-5x odds; `1`–`1000000` selects a uniform multiple. Editions without free odds require `0`. | +| `field_12` | int | 2 | `2` or `3`: the field's payout on a 12. | + +#### Pai Gow Poker + +Pai Gow tables are configured under `pai_gow`; see +[Pai Gow Poker tables](pai_gow.md) for every option. + +#### Video poker + +Video poker cabinets are single-seat `casino_tables` entries; see +[Video poker cabinets](video_poker.md) for every option. + +#### Slot machines + +Slots are single-player machines and must be declared under `casino_machines`, +not `casino_tables`: ```yaml casino_machines: @@ -440,57 +398,32 @@ casino_machines: payout: 0.97 ``` +| Field | Type | Default | Notes | +|---|---|---|---| +| `id` | string | — | Required; same rules as table IDs. | +| `game` | string | — | Required; only `slots`. | +| `variant` | string | `huff_and_even_more_puff` | Registered in Go (`slotVariants` in `internal/casino/registry.go`); an unregistered name is rejected. | +| `min_bet` | int | 1 | | +| `max_bet` | int | 1000000 | Must be at least `min_bet`. | +| `spin_ticks` | int | 2 | 0–3600. | +| `payout` | float | 0.97 | Target return-to-player fraction; greater than 0 and at most 1. | + #### Casino configuration validation -Room create/update requests validate casino definitions before writing; startup -validation reports invalid definitions, and game lookup refuses invalid room -casino configurations. Raw file editing is not a validated save endpoint. - -- IDs must be nonempty, have no surrounding whitespace, and be unique across - the room's tables and machines. -- Supported table games are `blackjack`, `baccarat`, `roulette`, `craps`, and - `paigow` ([Pai Gow configuration and variants](pai_gow.md)). Single-seat - `video_poker` cabinets also use the table adapter ([cabinet guide](video_poker.md)). - Roulette configuration basics: `roulette.double_zero` selects the American - 38-pocket wheel (otherwise the European 37-pocket wheel), `roulette.spin_ticks` - defaults to 3, and `roulette.racetrack: true` adds the French call bets on - single-zero tables only. Craps rules and house editions are listed in-game via - `help craps` and `table rules`; slots are machines only - and must be declared under `casino_machines`. A `slots` entry under - `casino_tables` is rejected. Machines use `slots` - with the optional variant `huff_and_even_more_puff`. A machine variant is - registered in Go (`slotVariants` in `internal/casino/registry.go`); naming an - unregistered variant is a validation error. -- A multiplayer table is addressed by its game name (`play blackjack`), so a - room may host only one table per game. -- A machine is addressed by its `variant` when one is declared, so a room may - host several variants of one game, each reached by its own variant name. A - bare game name addresses a machine only when it is the sole machine of that - game (or declares no variant). Two variant-less machines of one game, or two - machines sharing a variant, are rejected as unreachable duplicates. -- Omitted/zero numeric values keep their existing defaults. Negative bets, - timers, seat counts, split counts, decks, and payouts are rejected. -- An explicit `max_bet` must be at least `min_bet`. An omitted or zero - `max_bet` uses the default cap (1,000,000); the explicit sentinel - `9223372036854775807` means no cap. -- A blackjack table that offers `surrender` must set `min_bet` to at least 2: - a surrendered hand returns half the bet, which rounds down to zero below - that (the omitted default of 1 is rejected too). -- Shoe coverage is best-effort, not a hard floor. The shoe reshuffles at the - start of a round when it is smaller than the heuristic round size (eleven - cards per hand--an all-ace hand can exceed that), and `draw()` rebuilds an - exhausted shoe in place. A small `decks` value therefore never invalidates - a table or darkens the room's other casino games. -- `decks` is capped at 64 to bound shoe allocation. An explicit `shuffle_at` - must be greater than zero and less than one. -- Payouts and shuffle fractions must be finite (no NaN/infinity). Slots `payout` - is an RTP fraction greater than zero and at most one; zero defaults to 0.97. -- Unknown casino fields are rejected by the room create/update API. - -Editing a room hot-reconfigures an **idle** table or machine on the next -lookup; an occupied game keeps its live rules until it becomes idle, so a -committed wager is never discarded. Use explicit, modest wager limits: -validation does not yet make currency arithmetic overflow-safe. +Room create/update requests validate casino definitions before writing; +startup validation reports invalid definitions, and game lookup refuses +invalid room casino configurations. Unknown fields are rejected. + +- IDs share one namespace across the room's tables and machines. +- One table per game per room. Machines address by `variant`, so several + variants may share a room; two variant-less machines of one game, or two + machines sharing a variant, are rejected as unreachable. +- Omitted or zero numeric fields keep their defaults; negative bets, timers, + seat counts, split counts, decks, and payouts are rejected. +- Payouts and shuffle fractions must be finite (no NaN or infinity). +- Editing a room hot-reconfigures an **idle** table or machine on the next + lookup; an occupied game keeps its live rules until it becomes idle, so a + committed wager is never discarded. ### Objects — interactive fixtures |
