aboutsummaryrefslogtreecommitdiff
path: root/building_guide
diff options
context:
space:
mode:
Diffstat (limited to 'building_guide')
-rw-r--r--building_guide/README.md27
-rw-r--r--building_guide/admin.md3
-rw-r--r--building_guide/bank.md3
-rw-r--r--building_guide/hacking.md50
-rw-r--r--building_guide/pai_gow.md149
-rw-r--r--building_guide/rooms.md347
-rw-r--r--building_guide/video_poker.md134
7 files changed, 239 insertions, 474 deletions
diff --git a/building_guide/README.md b/building_guide/README.md
index de70acd..ab08dea 100644
--- a/building_guide/README.md
+++ b/building_guide/README.md
@@ -4,29 +4,4 @@ These guides are for **human world builders** working on the game's content:
rooms, objects, mobs, items, drops, triggers, quests, hazards, casino table
configuration, and similar data-driven world YAML.
-**Do not put engine documentation here.** Anything about Go internals,
-architecture, concurrency, testing, TODOs, roadmaps, or implementation
-assessments belongs in [`../dev_guide/`](../dev_guide/README.md) or in code
-comments next to the code it describes. Keeping the split clean matters:
-builders should be able to read this directory top to bottom without
-knowing the source layout.
-
-| Guide | Covers |
-|---|---|
-| [admin.md](admin.md) | In-game admin commands and the web admin panel |
-| [bank.md](bank.md) | Bank booths and player banking commands |
-| [behaviors.md](behaviors.md) | Gather, talk and shop behaviors |
-| [construction.md](construction.md) | Player-owned housing and furniture |
-| [courses.md](courses.md) | Agility courses |
-| [drops.md](drops.md) | Shared drop tables |
-| [hazards.md](hazards.md) | Environmental hazards |
-| [items.md](items.md) | Items, equipment, food, combinations |
-| [mobs.md](mobs.md) | Mobs, combat stats, shops, task mobs |
-| [objects.md](objects.md) | Objects, interactions, safespots |
-| [pai_gow.md](pai_gow.md) | Pai Gow Poker table configuration |
-| [quests.md](quests.md) | Quest walkthrough example |
-| [recipes.md](recipes.md) | Crafting and recipes |
-| [rooms.md](rooms.md) | Rooms, exits, casino tables, triggers |
-| [tips.md](tips.md) | Building tips |
-| [triggers.md](triggers.md) | Trigger scenarios |
-| [video_poker.md](video_poker.md) | Video poker cabinet configuration |
+LLMs should NOT add new documentation here.
diff --git a/building_guide/admin.md b/building_guide/admin.md
index b29ed00..a99535a 100644
--- a/building_guide/admin.md
+++ b/building_guide/admin.md
@@ -277,8 +277,7 @@ Editor API requests (including undo/redo) execute as one game-owner task:
file writes and their runtime callbacks/reloads cannot interleave with a tick
or player command. Large edits briefly pause gameplay; slow browser uploads
and downloads do not. Requests are limited to 8 MiB and rejected during game
-shutdown. See [Concurrency & durability](../dev_guide/concurrency.md#admin-editor-isolation)
-for the endpoint inventory and developer rules.
+shutdown.
This does not make every edit automatically hot-reloadable. Existing reload
requirements still apply to definition editors and raw Files edits. Use the
diff --git a/building_guide/bank.md b/building_guide/bank.md
index a6eb97f..71c05f0 100644
--- a/building_guide/bank.md
+++ b/building_guide/bank.md
@@ -47,6 +47,3 @@ objects:
| `data/help/bank.yaml` | Help topic for the bank system |
Default bank booth locations: Town Square (room 1), Forge (room 12).
-
-Engine internals (session state, storage layout, command dispatch) are
-documented in [../dev_guide/bank.md](../dev_guide/bank.md).
diff --git a/building_guide/hacking.md b/building_guide/hacking.md
new file mode 100644
index 0000000..dc4372b
--- /dev/null
+++ b/building_guide/hacking.md
@@ -0,0 +1,50 @@
+## Hacking
+
+Hacking minigames are played by jacking into terminal objects. Terminals are YAML objects that are registered in Go's `terminalDefs` map.
+
+**The filename is the ID** (`terminal_basic.yaml` → `terminal_basic`); like all objects, the loader derives the ID from the filename — don't add an `id:` field (see `objects.md`).
+
+### Terminal Object YAML
+
+```yaml
+name: basic terminal
+color: "28"
+description: "A battered terminal with a cracked screen. {28}[Level 1 Hacking]{/}"
+inroom_description: "A {28}basic terminal{/} hums quietly against the wall."
+```
+
+- `id` must match a key in `terminalDefs` (Go code in `internal/game/hacking.go`)
+- `name` is used for matching in the `jack` command
+- `color` controls the display color
+- `description` is shown when examining the terminal
+- `inroom_description` is shown in room listings
+- No `behavior` field — terminals are registered in Go, not YAML behaviors
+
+### Adding a New Terminal
+
+1. Create an object YAML file in `data/objects/terminal_<name>.yaml`
+2. Add an entry to `terminalDefs` in `internal/game/hacking.go`
+3. Optionally create a new minigame implementation in `internal/game/hacking_<name>.go`
+4. Place the terminal object in a room's `objects:` list
+
+### Adding a New Minigame
+
+New minigames implement the `HackingMinigame` interface:
+
+```go
+type HackingMinigame interface {
+ Init(level int) string
+ HandleInput(input string) (output string, done bool, won bool)
+ Name() string
+}
+```
+
+Optionally implement `XPBonuser` for bonus XP:
+
+```go
+type XPBonuser interface {
+ BonusXP() int
+}
+```
+
+
diff --git a/building_guide/pai_gow.md b/building_guide/pai_gow.md
index 93e9594..6aea61d 100644
--- a/building_guide/pai_gow.md
+++ b/building_guide/pai_gow.md
@@ -1,126 +1,35 @@
# Pai Gow Poker tables
-Configure a `casino_tables` entry:
+Add a `casino_tables` entry to a room with `game: paigow`:
```yaml
-- id: fortune
- game: paigow
- max_players: 6
- min_bet: 5
- max_bet: 1000
- betting_ticks: 30
- pai_gow:
- variant: classic # classic, ez, no_push, face_up
- envy: true
- autoset: false # default; true locks every legal arrangement without "set"
- a2345_second_highest: true # default; false makes A2345 the lowest straight
- action_ticks: 60
+casino_tables:
+ - id: fortune
+ game: paigow
+ max_players: 6
+ min_bet: 5
+ max_bet: 1000
+ betting_ticks: 30
+ pai_gow:
+ variant: classic
+ envy: true
+ autoset: false
+ a2345_second_highest: true
+ action_ticks: 60
```
-There are six player seats and one dealer. Each round deals seven packets
-from a fresh 53-card deck, including unused seats. No player banking.
-Side bets come first: `bet 5 fortune` (and, on a Face Up table,
-`bet <amount> pushace`) is placed before the main wager. `bet 20` is the
-main wager that commits the round; when every active player has one, the
-cards are dealt immediately. A lone bettor is dealt as soon as the main
-wager lands, with no timed window. Otherwise the betting window opens and
-expires on `betting_ticks`; anyone who has not bet sits out. `unbet main`,
-`unbet fortune`, `unbet pushace` or `unbet all` returns an undealt wager,
-and `stop` before the deal returns every wager and frees the seat. Both
-wagers use the table's limits; each is also bounded to 500,000,000 credits
-to keep combined payouts below the economy's credit cap even on unlimited
-tables.
-
-`a2345_second_highest` defaults to true and controls whether A-2-3-4-5 is
-the second-highest straight (below TJQKA) or the lowest. When enabled the
-rule is printed on the felt.
-
-## Player hand setting
-
-The seven dealt cards are shown with their position numbers underneath.
-`24` or `set 24` arranges positions 2 and 4 into the two-card low hand;
-`13456` arranges those five positions into the high hand. Spaces and order
-are immaterial; duplicates, out-of-range positions and other counts are
-rejected. Fouls are rejected with a clear warning and leave the hand
-unarranged.
-
-An arrangement is only a preview: the board redraws the five-card high
-hand, a gap, then the two-card low hand (with `HIGH` and `LOW` labels and
-position numbers) and waits. `set` locks the hand and reveals the dealer.
-`house` (`house way`, `set house`) arranges the house way and still needs
-`set`. `autoset on` locks any legal arrangement the moment it is made, so
-the preview and `set` steps are skipped; `autoset off` restores them, and
-`autoset` alone toggles. The table's `autoset` setting only seeds a new
-seat. Timeout or disconnect sets an unfinished hand the house way; a
-committed round cannot be abandoned.
-
-When every seat has set, the dealer reveals and the showdown compares the
-high hands first (your high, then the dealer's high) and then the low hands
-(your low, then the dealer's low). Each hand prints its strength in
-parentheses, bold yellow when it wins its comparison and bold white when it
-loses. The summary names the winner of each hand and then the round.
-
-## Variants
-
-* Classic: win both hands for 0.95:1 profit; lose both to lose; otherwise push.
- Whole-credit profit rounds down, with a minimum of one credit on a win,
- consistent with the existing casino economy. This affects small-bet odds.
-* EZ: the **partially-wild Joker edition**. No commission. Dealer's best
- five-card hand exactly queen-high pushes the main bets, regardless of split.
-* No Push: no commission. When one player hand beats the dealer and the other
- does not, compare the two extra cards dealt after the seven packets. The
- first is the shared player tiebreaker, the second the dealer's. Joker is an
- ace; equal ranks lose to the dealer. Both-win/both-loss results are unchanged.
-* Face Up: no commission; the dealer's seven cards are revealed before
- players set theirs. If the dealer's best five-card hand is exactly
- ace-high the round is an automatic push for every main bet. Requires
- `push_ace_high: 5_15_40` or `5_12_40`, which enables the optional Push
- Ace High side bet (`bet <amount> pushace`, capped at half the main
- wager): dealer ace-high without a Joker 5:1, with a Joker 15:1, both
- sides ace-high 40:1, else lost.
-
-Fortune and Envy are independent of all main-game results, including
-variant pushes. `push_ace_high` on a non-Face-Up table (or a Face Up table
-without it) is rejected by validation.
-
-## Fortune / Envy
-
-Source: [Wizard of Odds Fortune tables](https://wizardofodds.com/games/pai-gow-poker/side-bets/fortune/).
-Of the eight published fixed tables compared there, **table 8** has the
-lowest house edge: 4.4292% before Envy, about 0.08% with six players each
-betting five. These are published theoretical side-bet figures, not a
-simulation of this server. Envy value depends on occupancy and wager size.
-
-| Seven-card holding | Profit odds | Fixed Envy credits |
-|---|---:|---:|
-| Natural seven-card straight flush | 1000:1 | 500 |
-| Royal flush + royal match | 750:1 | 250 |
-| Wild seven-card straight flush | 500:1 | 150 |
-| Five aces | 250:1 | 100 |
-| Royal flush | 150:1 | 50 |
-| Straight flush | 50:1 | 20 |
-| Four of a kind | 25:1 | 5 |
-| Full house | 5:1 | 0 |
-| Flush | 4:1 | 0 |
-| Three of a kind | 3:1 | 0 |
-| Straight | 2:1 | 0 |
-| Three pair | 1:1 | 0 |
-
-Only the highest listed category pays, using all seven cards regardless
-of the actual split. Royal match means suited KQ in the two remaining
-cards. Fortune winning wagers also return their stake. A Fortune wager
-of at least five qualifies for fixed Envy prizes on **other active players**,
-not the bettor, dealer, unused packets or spectators. Multiple qualifying
-hands add. The other player need not have a Fortune bet. A losing Fortune
-stake is still lost when Envy pays.
-
-With `envy: false` (default), quads improve to **30:1**, a custom local
-schedule. The felt displays the effective table.
-
-Variant sources:
-* https://wizardofodds.com/games/ez-pai-gow-poker/partially-wild-joker/
-* https://wizardofodds.com/games/pai-gow-poker/appendix/4/
-* https://wizardofodds.com/games/face-up-pai-gow-poker/
-
-House-way algorithm, settlement internals and Joker art notes are in
-[../dev_guide/pai_gow.md](../dev_guide/pai_gow.md).
+| Field | Type | Default | Notes |
+|---|---|---|---|
+| `variant` | string | `classic` | `classic`, `ez`, `no_push`, or `face_up`. Each is a separate house ruleset. |
+| `envy` | bool | false | Enables Envy prizes and selects the Fortune/Envy pay schedule. |
+| `push_ace_high` | string | — | `face_up` only, and required there. `5_15_40` or `5_12_40`; selects the Push Ace High payout schedule. |
+| `autoset` | bool | false | Seeds the seat default. When true a legal arrangement locks immediately, without a `set` confirmation. Players may toggle it per seat in game. |
+| `action_ticks` | int | 60 | 1–600. |
+| `a2345_second_highest` | bool | true | When true, A-2-3-4-5 is the second-highest straight; when false it is the lowest. |
+
+`max_players` is capped at 6, and `max_bet` at 500,000,000. Each wager is
+bounded by the table's `min_bet`/`max_bet`.
+
+The `id` is internal; players join with `play paigow`. Shared table fields
+(`betting_ticks`, `min_bet`, `max_bet`, and ID rules) are documented under
+[Rooms — Casino Tables](rooms.md#casino-tables).
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
diff --git a/building_guide/video_poker.md b/building_guide/video_poker.md
index 497f64e..349797d 100644
--- a/building_guide/video_poker.md
+++ b/building_guide/video_poker.md
@@ -1,125 +1,27 @@
# Video poker cabinets
-The video poker hall east of Penny Reels (6118) is three rooms. The parlor
-(6192) runs low pay schedules, the high-limit room (6193) normal schedules, and
-the VIP room (6194) full pay schedules. Each holds the same five single-seat
-cabinets: Classic, Ultimate X Single Line, Ultimate X Multi-Hand, Super Times
-Pay, and Hot Roll. Choose a game **inside** a cabinet; schedules are not games.
-The shared table lifecycle adapter provides room routing, wallet settlement,
-disconnect and eviction handling, so author cabinets under `casino_tables`:
+Video poker cabinets are single-seat `casino_tables` entries:
```yaml
casino_tables:
-- id: uxm
- game: video_poker
- max_players: 1
- video_poker:
- cabinet: ultimate_x_multi
- payout: full
+ - id: uxm
+ game: video_poker
+ max_players: 1
+ video_poker:
+ cabinet: ultimate_x_multi
+ payout: full
```
-`cabinet` accepts `classic`, `ultimate_x_single`, `ultimate_x_multi`,
-`super_times_pay`, `hot_roll`. `payout` accepts `low`, `normal`, `full` and is
-the house's setting: players cannot change it. Defaults are classic/full. Do
-not author variant/pay_table/feature/single_line; strict YAML validation
-rejects those obsolete fields. Do not use table wager limits to set coin
-denominations. Each cabinet has one occupant, but multiple cabinets may share a
-room and are addressed by ID. Short room ids (`vp`, `ux`, `uxm`, `stp`, `hr`)
-and cabinet aliases (`poker`, `ultimate_x`, `super_times`, `hot_roll`) keep
-`play` concise.
+| Field | Type | Default | Notes |
+|---|---|---|---|
+| `cabinet` | string | `classic` | `classic`, `ultimate_x_single`, `ultimate_x_multi`, `super_times_pay`, or `hot_roll`. |
+| `payout` | string | `full` | `low`, `normal`, or `full`. The cabinet's house schedule tier, applied to every game selectable on it. Players cannot change it. |
-## Games and quality tiers
+`max_players` is forced to 1. Cabinets are addressed by their `id` (plus a few
+built-in aliases such as `vp`, `ux`, `uxm`, `stp`, and `hr`), so several
+cabinets may share a room. Do not author `variant`, `pay_table`, `feature`, or
+`single_line`; strict YAML validation rejects those obsolete fields. Table
+wager limits do not set coin denominations.
-Each row below lists **low / normal / full** schedules. The authored tier
-applies to all games on that cabinet; selecting a different game preserves it.
-These are published schedules, not interpolated payouts. The tier names rank
-the offered schedules within a game/cabinet; they do not imply equal RTP across
-games. Some classic full schedules exceed 100% with optimal play.
-
-| Game | Classic | UX Single | UX Multi | STP | Hot Roll |
-|---|---|---|---|---|---|
-| Jacks or Better | 7/5, 8/5, 9/6 | 8/5, 8/6, 9/6 | 7/5, 8/5, 8/6 | 7/5, 8/5, 9/6 | 8/5, 9/5, 9/6 |
-| Bonus Poker | 6/5, 7/5, 8/5 | 6/5, 7/5, 8/5 | omitted | 6/5, 7/5, 8/5 | omitted |
-| Bonus Poker Deluxe | 8/5, 9/5, 9/6 | 8/5, 8/6, 9/6 | 7/5, 8/5, 8/6 | omitted | omitted |
-| Double Bonus | 9/6/4, 9/7, 10/7 | 9/6/4, 9/6/5, 9/7 | 9/5/4, 9/6/4, 9/6/5 | 9/6/4, 9/6/5, 9/7 | omitted |
-| Double Double Bonus | 8/5, 9/6, 10/6 | 8/5, 9/5, 9/6 | 8/5, 9/5, 9/6 | 8/5, 8/6, 9/6 | 8/5, 9/5, 9/6 |
-| Deuces Wild | C | U | U | C | omitted |
-| Joker Poker (Kings or Better) | J | J | J | omitted | omitted |
-
-- C: 20/12/10/4/4/3, 25/15/9/4/4/3, 25/15/9/5/3/2.
-- U: 25/16/13/4/3/2, 20/12/10/4/4/3, 25/15/9/4/4/3.
-- J: 50/15/7/5/3, 50/16/7/5/3, 50/17/7/5/3.
-- Double Bonus 9/7 and 10/7 pay five for straights; three-part names explicitly
- identify full-house/flush/straight rates.
-- Bonus Poker has only two sourced UX Multi schedules; it is omitted rather
- than inventing a third tier. Other omissions are deliberately conservative,
- not claims that no physical edition exists.
-
-| Edition | Supported hands |
-|---|---|
-| Classic Jacks or Better | 1, 3, 5, 10, 25, 50, 100 |
-| Other classic games | 1 |
-| Ultimate X Single | 1 |
-| Ultimate X Multi | 3, 5, 10 |
-| Super Times Pay | 1, 3, 5, 10 |
-| Hot Roll | 3, 5, 10 |
-
-## Player controls
-
-- `play <id>` seats you; `game` lists games; `game jacks` (or a full game name)
- selects one. The house sets the payout tier when the cabinet is authored in
- YAML; players cannot change it.
-- While seated at any casino game, casino verbs may be shortened to an
- unambiguous prefix: `b 1000` is `bet 1000`, and `be 1000` also works.
- `stop` and `rules` must be typed in full.
-- `denom 1|10|50` / `denomination` sets credits per coin.
-- `hands` lists counts; `hands <count>` selects one.
-- `bet 1-4` **adds** coins, capped at five per hand. `bet max` loads five.
- Bare `bet` / `rebet` repeats the previous selection, initially five.
-- Loading neither charges nor deals. Explicit bets show the pay table; bare
- bet does not. `deal` charges the full wager and reveals five cards.
-- `531`, `1 3 5`, or `hold 531` **toggles** holds. Repeating the command releases
- those cards; duplicate digits within one command toggle only once.
- `hold none` is not supported. `draw` or seated `d` finishes the hand.
- Away from video poker, `d` retains down movement.
-- `feature on|off` toggles the cabinet's bonus between rounds.
-- `paytable`, `look`, `table rules` redisplay; `rules` opens the cabinet guide.
- `list` retains available-wagers semantics and is not a pay-table command.
-- `stop` leaves between rounds. Disconnect completes the current holds.
-
-## Bonuses
-
-Feature ON requires five base coins per hand. UX and Hot Roll add five coins
-(10 total); STP adds one (6 total). Fees never increase the base payout.
-
-- **Ultimate X:** each line's current multiplier applies to its payout. Enabled
- wins earn next-hand multipliers using the published single/3/5/10-play award
- schedules. Awards never compound. Feature OFF collects existing multipliers
- without earning new ones. `NEXT X` shows awards for the selected hand count.
-- **Super Times Pay:** a pre-deal 1/15 trigger awards a shared multiplier.
- Conditional 2x/3x/4x/5x/8x/10x probabilities are 17/33/16/24/6/4 percent.
-- **Hot Roll:** a pre-deal 1/6 trigger rolls two fair dice; their sum multiplies
- every line. STP and Hot Roll have no next-hand bank.
-
-Ultimate X multiplier banks belong to the **machine**, not the player: another
-occupant inherits them, and they persist on disk under
-`data/casino/video_poker_banks/`.
-
-## Sources
-
-Published schedules, award tables and feature rules:
-
-- https://wizardofodds.com/games/video-poker/tables/ultimate-x-single-line/
-- https://wizardofodds.com/games/video-poker/tables/ultimate-x/
-- https://wizardofodds.com/games/video-poker/tables/super-times-pay/
-- https://wizardofodds.com/games/video-poker/tables/hot-roll/
-- https://wizardofodds.com/games/video-poker/tables/jacks-or-better/
-- https://wizardofodds.com/games/video-poker/tables/bonus-poker/
-- https://wizardofodds.com/games/video-poker/tables/bonus-poker-deluxe/
-- https://wizardofodds.com/games/video-poker/tables/deuces-wild/
-- https://wizardofodds.com/games/video-poker/tables/double-bonus/
-- https://wizardofodds.com/games/video-poker/tables/double-double-bonus/
-- https://www.videopoker.com/games/ (single/multi/hundred-play editions)
-
-Presentation internals and durable-bank transaction details are in
-[../dev_guide/video_poker.md](../dev_guide/video_poker.md).
+Shared table fields and ID rules are documented under
+[Rooms — Casino Tables](rooms.md#casino-tables).