diff options
Diffstat (limited to 'building_guide')
| -rw-r--r-- | building_guide/README.md | 27 | ||||
| -rw-r--r-- | building_guide/admin.md | 3 | ||||
| -rw-r--r-- | building_guide/bank.md | 3 | ||||
| -rw-r--r-- | building_guide/hacking.md | 50 | ||||
| -rw-r--r-- | building_guide/pai_gow.md | 149 | ||||
| -rw-r--r-- | building_guide/rooms.md | 347 | ||||
| -rw-r--r-- | building_guide/video_poker.md | 134 |
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). |
