aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--data/help/craps.yaml11
-rw-r--r--data/help/roulette.yaml6
-rw-r--r--data/mobs/vesta/casino_host.yaml8
-rw-r--r--data/objects/casino_test_portal.yaml2
-rw-r--r--data/rooms/casino/6100.yaml16
-rw-r--r--data/rooms/casino/6101.yaml41
-rw-r--r--data/rooms/casino/6102.yaml47
-rw-r--r--data/rooms/casino/6103.yaml10
-rw-r--r--data/rooms/casino/6104.yaml40
-rw-r--r--data/rooms/casino/6105.yaml12
-rw-r--r--data/rooms/casino/6106.yaml42
-rw-r--r--data/rooms/casino/6107.yaml8
-rw-r--r--data/rooms/casino/6108.yaml32
-rw-r--r--data/rooms/casino/6109.yaml8
-rw-r--r--data/rooms/casino/6110.yaml34
-rw-r--r--data/rooms/casino/6111.yaml8
-rw-r--r--data/rooms/casino/6112.yaml34
-rw-r--r--data/rooms/casino/6113.yaml8
-rw-r--r--data/rooms/casino/6114.yaml34
-rw-r--r--data/rooms/casino/6115.yaml8
-rw-r--r--data/rooms/casino/6116.yaml34
-rw-r--r--data/rooms/casino/6117.yaml8
-rw-r--r--data/rooms/casino/6118.yaml12
-rw-r--r--data/rooms/casino/6119.yaml8
-rw-r--r--data/rooms/casino/6120.yaml11
-rw-r--r--data/rooms/casino/6121.yaml8
-rw-r--r--data/rooms/casino/6122.yaml11
-rw-r--r--data/rooms/casino/6123.yaml8
-rw-r--r--data/rooms/casino/6124.yaml9
-rw-r--r--data/rooms/casino/6125.yaml8
-rw-r--r--data/rooms/casino/6126.yaml9
-rw-r--r--data/rooms/casino/6127.yaml11
-rw-r--r--data/rooms/casino/6128.yaml7
-rw-r--r--data/rooms/casino/6129.yaml7
-rw-r--r--data/rooms/casino/6130.yaml9
-rw-r--r--data/rooms/casino/6131.yaml6
-rw-r--r--data/rooms/casino/6132.yaml7
-rw-r--r--data/rooms/casino/6133.yaml5
-rw-r--r--data/rooms/casino/6134.yaml7
-rw-r--r--data/rooms/casino/6135.yaml5
-rw-r--r--data/rooms/casino/6136.yaml5
-rw-r--r--data/rooms/casino/6137.yaml10
-rw-r--r--data/rooms/casino/6138.yaml6
-rw-r--r--data/rooms/casino/6139.yaml5
-rw-r--r--data/rooms/casino/6140.yaml12
-rw-r--r--data/rooms/casino/6141.yaml6
-rw-r--r--data/rooms/casino/6142.yaml5
-rw-r--r--data/rooms/casino/6143.yaml12
-rw-r--r--data/rooms/casino/6144.yaml6
-rw-r--r--data/rooms/casino/6145.yaml5
-rw-r--r--data/rooms/casino/6146.yaml12
-rw-r--r--data/rooms/casino/6147.yaml6
-rw-r--r--data/rooms/casino/6148.yaml7
-rw-r--r--data/rooms/casino/6149.yaml14
-rw-r--r--data/rooms/casino/6150.yaml6
-rw-r--r--data/rooms/casino/6151.yaml5
-rw-r--r--data/rooms/casino/6152.yaml10
-rw-r--r--data/rooms/casino/6153.yaml6
-rw-r--r--data/rooms/casino/6154.yaml5
-rw-r--r--data/rooms/casino/6155.yaml12
-rw-r--r--data/rooms/casino/6156.yaml6
-rw-r--r--data/rooms/casino/6157.yaml5
-rw-r--r--data/rooms/casino/6158.yaml12
-rw-r--r--data/rooms/casino/6159.yaml6
-rw-r--r--data/rooms/casino/6160.yaml5
-rw-r--r--data/rooms/casino/6161.yaml12
-rw-r--r--data/rooms/casino/6162.yaml6
-rw-r--r--data/rooms/casino/6163.yaml5
-rw-r--r--data/rooms/casino/6164.yaml14
-rw-r--r--data/rooms/casino/6165.yaml6
-rw-r--r--data/rooms/casino/6166.yaml5
-rw-r--r--data/rooms/casino/6167.yaml10
-rw-r--r--data/rooms/casino/6168.yaml6
-rw-r--r--data/rooms/casino/6169.yaml5
-rw-r--r--data/rooms/casino/6170.yaml12
-rw-r--r--data/rooms/casino/6171.yaml6
-rw-r--r--data/rooms/casino/6172.yaml5
-rw-r--r--data/rooms/casino/6173.yaml12
-rw-r--r--data/rooms/casino/6174.yaml6
-rw-r--r--data/rooms/casino/6175.yaml5
-rw-r--r--data/rooms/casino/6176.yaml12
-rw-r--r--data/rooms/casino/6177.yaml6
-rw-r--r--data/rooms/casino/6178.yaml5
-rw-r--r--data/rooms/casino/6179.yaml12
-rw-r--r--data/rooms/casino/6180.yaml6
-rw-r--r--data/rooms/casino/6181.yaml5
-rw-r--r--data/rooms/casino/6182.yaml12
-rw-r--r--data/rooms/casino/6183.yaml2
-rw-r--r--data/rooms/casino/6184.yaml4
-rw-r--r--data/rooms/casino/6185.yaml10
-rw-r--r--data/rooms/casino/6186.yaml2
-rw-r--r--data/rooms/casino/6187.yaml2
-rw-r--r--data/rooms/casino/6188.yaml4
-rw-r--r--data/rooms/casino/6189.yaml6
-rw-r--r--data/rooms/casino/6190.yaml6
-rw-r--r--data/rooms/casino/6191.yaml4
-rw-r--r--internal/casino/baccarat.go6
-rw-r--r--internal/casino/baccarat_variants_test.go13
-rw-r--r--internal/casino/blackjack.go6
-rw-r--r--internal/casino/blackjack_rules_test.go4
-rw-r--r--internal/casino/types.go16
-rw-r--r--internal/game/baccarat_rules_test.go15
-rw-r--r--internal/game/blackjack_rules_test.go7
-rw-r--r--internal/game/casino.go4
-rw-r--r--internal/game/casino_ux_test.go2
-rw-r--r--internal/game/craps_variants_test.go65
-rw-r--r--internal/game/render_baccarat_felt.go111
-rw-r--r--internal/game/render_baccarat_felt_test.go92
-rw-r--r--internal/game/render_blackjack_felt.go137
-rw-r--r--internal/game/render_blackjack_felt_test.go84
-rw-r--r--internal/game/render_roulette.go7
-rw-r--r--internal/game/vesta_world_test.go66
112 files changed, 821 insertions, 815 deletions
diff --git a/data/help/craps.yaml b/data/help/craps.yaml
index f5ce844..3537a80 100644
--- a/data/help/craps.yaml
+++ b/data/help/craps.yaml
@@ -4,11 +4,12 @@ category: Interaction
related: [casino, roulette]
description: |
Craps — multiplayer house editions at the Domita Fortuna casino.
- From single-zero roulette, west reaches Bank Craps. Head north along
- the gallery for Simplified, High Point, Crapless, New York, Die Rich,
- then Diceless. Join with play craps in each room; table rules gives
- that edition's exact targets and payouts. The examples below are BANK
- rules, not a promise that every edition accepts every bet.
+ From single-zero roulette, east reaches Bank Craps. North of the pit
+ waits Simplified, High Point, then Crapless; south waits New York,
+ Die Rich, then Diceless. Join with play craps in each room; table
+ rules gives that edition's exact targets and payouts. The examples
+ below are BANK rules, not a promise that every edition accepts every
+ bet.
play craps Join the tail of the shooting queue
watch craps Watch without betting
diff --git a/data/help/roulette.yaml b/data/help/roulette.yaml
index fae288c..a6f4c23 100644
--- a/data/help/roulette.yaml
+++ b/data/help/roulette.yaml
@@ -3,9 +3,9 @@ category: Interaction
related: [racetrack, casino, blackjack, baccarat, slots]
description: |
Play roulette in the Domita Fortuna casino.
- From the vestibule: north, up, north reaches single zero;
- east from single zero reaches the racetrack;
- another north reaches double zero.
+ Take the stairs up in the vestibule, then east past blackjack and
+ baccarat to single zero; north reaches double zero, south the
+ racetrack, and east the craps pit.
play roulette Join the table
table Show layout, phase, and last result
diff --git a/data/mobs/vesta/casino_host.yaml b/data/mobs/vesta/casino_host.yaml
index 92a887d..f41360a 100644
--- a/data/mobs/vesta/casino_host.yaml
+++ b/data/mobs/vesta/casino_host.yaml
@@ -9,8 +9,10 @@ talk:
nodes:
start:
messages:
- - Welcome to Domita Fortuna. Every game has its own room and posted rules. The upper floors
- are open to visitors but their gaming halls are not operating yet. No membership or wager is needed
- to explore.
+ - 'Welcome to Domita Fortuna. Take the stairs up to the gaming street:
+ blackjack, baccarat, roulette, craps, then slots, one salon each going
+ east. Step north or south inside each salon for its house editions.
+ The bank booth down by the door stores your valuables, and no
+ membership or wager is needed to explore.'
options:
- text: Thank you.
diff --git a/data/objects/casino_test_portal.yaml b/data/objects/casino_test_portal.yaml
index 0132d32..468589c 100644
--- a/data/objects/casino_test_portal.yaml
+++ b/data/objects/casino_test_portal.yaml
@@ -11,4 +11,4 @@ on_use:
weight: 1
- teleport: 6100
messages:
- - 'Testing shortcut: welcome to the Domita casino! Head north to explore the gaming floors.'
+ - 'Testing shortcut: welcome to the Domita casino! Take the stairs up to the gaming street.'
diff --git a/data/rooms/casino/6100.yaml b/data/rooms/casino/6100.yaml
index e822eda..6e77bc6 100644
--- a/data/rooms/casino/6100.yaml
+++ b/data/rooms/casino/6100.yaml
@@ -1,17 +1,19 @@
name: Vestibule
-description: A ceiling of tiny brass suns hangs over polished stone. Icarus Leisure’s emblem repeats on
- every column. Stairs and galleries are open to all visitors.
+description: A ceiling of tiny brass suns hangs over polished stone. Icarus Leisure's emblem repeats on
+ every column. Grand stairs rise to the ground gaming floor.
exits:
south:
room: 5111
- north:
+ up:
room: 6101
mobs:
- id: casino_host
+- id: casino_cashier
objects:
- name: wayboard
- description: 'Grand stair: ground gaming floor above the entrance podium. West: five blackjack salons,
- three baccarat salons. East: five slot rooms. Three upper galleries reserve fifteen future game halls.
- Roulette salons: north, up, north for single zero; east for the racetrack; continue north for double zero.
- All floors are open without a wager.'
+ description: 'Domita Fortuna in one line: take the stairs up to the gaming street — blackjack,
+ baccarat, roulette, craps, slots, one salon each, going east. Each salon keeps its own house
+ editions in the galleries north and south. The bank booth by the door stores your valuables;
+ no wager is needed to explore.'
inroom_description: A wayboard stands where travelers can read it.
+- id: bank_booth
diff --git a/data/rooms/casino/6101.yaml b/data/rooms/casino/6101.yaml
index 7df2c07..fb2838a 100644
--- a/data/rooms/casino/6101.yaml
+++ b/data/rooms/casino/6101.yaml
@@ -1,8 +1,39 @@
-name: Grand Atrium
-description: A ceiling of tiny brass suns hangs over polished stone. Icarus Leisure’s emblem repeats on
- every column. Stairs and galleries are open to all visitors.
+name: Classic Blackjack
+description: The first salon on the gaming street above the entrance podium. A six-deck
+ blackjack table sits under shaded lamps; the house editions wait in the galleries
+ north and south. Look at the table for its rules.
exits:
- south:
+ down:
room: 6100
- up:
+ east:
+ room: 6127
+ north:
room: 6102
+ south:
+ room: 6106
+casino_tables:
+- id: classic
+ game: blackjack
+ max_players: 6
+ betting_ticks: 30
+ min_bet: 10
+ max_bet: 1000
+ blackjack:
+ decks: 6
+ shuffle_at: 0.25
+ blackjack_payout: 1.5
+ dealer_hit_soft_17: false
+ max_split_hands: 4
+ resplit_aces: false
+ double_after_split: true
+ double_split_blackjack: false
+ insurance_payout: 2
+ surrender: false
+ action_ticks: 25
+ split_unlike_ten_values: false
+objects:
+- name: rules plaque
+ description: Play blackjack; bet min, bet max or bet <amount>. Blackjack pays 3:2; insurance 2:1. 6
+ deck(s). Dealer stands on soft 17. No surrender. Double after split allowed. Split 21 pays 1:1; split
+ identical ranks only.
+ inroom_description: A rules plaque stands where travelers can read it.
diff --git a/data/rooms/casino/6102.yaml b/data/rooms/casino/6102.yaml
index 26f80d4..ae39910 100644
--- a/data/rooms/casino/6102.yaml
+++ b/data/rooms/casino/6102.yaml
@@ -1,17 +1,34 @@
-name: Ground Gaming Floor Landing
-description: The entrance stair rises onto a broad podium, the casino’s ground gaming floor. A directory
- divides the floor into card salons west and machine galleries east. Higher stairs lead to future gaming
- floors.
+name: Copper Blackjack
+description: A modest six-deck table for small purses; limits are 1–100 credits. The next house edition
+ waits north. No wager is required to watch or pass through.
exits:
- north:
- room: 6182
- down:
- room: 6101
south:
- room: 6106
- west:
- room: 6115
- east:
- room: 6125
- up:
- room: 6149
+ room: 6101
+ north:
+ room: 6104
+casino_tables:
+- id: copper
+ game: blackjack
+ max_players: 6
+ betting_ticks: 30
+ min_bet: 1
+ max_bet: 100
+ blackjack:
+ decks: 6
+ shuffle_at: 0.25
+ blackjack_payout: 1.5
+ dealer_hit_soft_17: false
+ max_split_hands: 4
+ resplit_aces: false
+ double_after_split: true
+ double_split_blackjack: false
+ insurance_payout: 2
+ surrender: false
+ action_ticks: 40
+ split_unlike_ten_values: false
+objects:
+- name: rules plaque
+ description: Play blackjack; bet min, bet max or bet <amount>. Blackjack pays 3:2; insurance 2:1. 6
+ deck(s). Dealer stands on soft 17. No surrender. Double after split allowed. Split 21 pays 1:1; split
+ identical ranks only.
+ inroom_description: A rules plaque stands where travelers can read it.
diff --git a/data/rooms/casino/6103.yaml b/data/rooms/casino/6103.yaml
deleted file mode 100644
index 2632e65..0000000
--- a/data/rooms/casino/6103.yaml
+++ /dev/null
@@ -1,10 +0,0 @@
-name: South Gaming Rotunda
-description: Deep carpet quiets footsteps between individual gaming salons. Each doorway displays its
- own rules and wager limits.
-exits:
- north:
- room: 6104
- west:
- room: 6107
- east:
- room: 6117
diff --git a/data/rooms/casino/6104.yaml b/data/rooms/casino/6104.yaml
index 38ad8c5..2ec6993 100644
--- a/data/rooms/casino/6104.yaml
+++ b/data/rooms/casino/6104.yaml
@@ -1,12 +1,32 @@
-name: Amber Concourse
-description: Deep carpet quiets footsteps between individual gaming salons. Each doorway displays its
- own rules and wager limits.
+name: Single Deck Salon
+description: One deck, 6:5 naturals, and a dealer who hits soft 17 — the sharpest game in the house.
+ Limits are 25–2500 credits. No wager is required to watch or pass through.
exits:
south:
- room: 6103
- north:
- room: 6105
- west:
- room: 6109
- east:
- room: 6119
+ room: 6102
+casino_tables:
+- id: single
+ game: blackjack
+ max_players: 6
+ betting_ticks: 30
+ min_bet: 25
+ max_bet: 2500
+ blackjack:
+ decks: 1
+ shuffle_at: 0.25
+ blackjack_payout: 1.2
+ dealer_hit_soft_17: true
+ max_split_hands: 2
+ resplit_aces: false
+ double_after_split: false
+ double_split_blackjack: false
+ insurance_payout: 2
+ surrender: false
+ action_ticks: 25
+ split_unlike_ten_values: false
+objects:
+- name: rules plaque
+ description: Play blackjack; bet min, bet max or bet <amount>. Blackjack pays 6:5; insurance 2:1. 1
+ deck(s). Dealer hits soft 17. No surrender. No double after split. Split 21 pays 1:1; split identical
+ ranks only.
+ inroom_description: A rules plaque stands where travelers can read it.
diff --git a/data/rooms/casino/6105.yaml b/data/rooms/casino/6105.yaml
deleted file mode 100644
index 0ebe609..0000000
--- a/data/rooms/casino/6105.yaml
+++ /dev/null
@@ -1,12 +0,0 @@
-name: Central Gaming Concourse
-description: Deep carpet quiets footsteps between individual gaming salons. Each doorway displays its
- own rules and wager limits.
-exits:
- south:
- room: 6104
- north:
- room: 6106
- west:
- room: 6111
- east:
- room: 6121
diff --git a/data/rooms/casino/6106.yaml b/data/rooms/casino/6106.yaml
index 2ba4faf..6cf1345 100644
--- a/data/rooms/casino/6106.yaml
+++ b/data/rooms/casino/6106.yaml
@@ -1,12 +1,34 @@
-name: Marble Concourse
-description: Deep carpet quiets footsteps between individual gaming salons. Each doorway displays its
- own rules and wager limits.
+name: Mercy Blackjack
+description: A forgiving table — late surrender and re-splitting aces are offered here. Limits are
+ 2–500 credits. The high-limit salon waits south. No wager is required to watch or pass through.
exits:
- south:
- room: 6105
north:
- room: 6102
- west:
- room: 6113
- east:
- room: 6123
+ room: 6101
+ south:
+ room: 6108
+casino_tables:
+- id: mercy
+ game: blackjack
+ max_players: 6
+ betting_ticks: 30
+ min_bet: 2
+ max_bet: 500
+ blackjack:
+ decks: 6
+ shuffle_at: 0.25
+ blackjack_payout: 1.5
+ dealer_hit_soft_17: false
+ max_split_hands: 4
+ resplit_aces: true
+ double_after_split: true
+ double_split_blackjack: false
+ insurance_payout: 2
+ surrender: true
+ action_ticks: 25
+ split_unlike_ten_values: false
+objects:
+- name: rules plaque
+ description: Play blackjack; bet min, bet max or bet <amount>. Blackjack pays 3:2; insurance 2:1. 6
+ deck(s). Dealer stands on soft 17. Late surrender and re-splitting aces offered. Double after split allowed. Split 21 pays 1:1; split
+ identical ranks only.
+ inroom_description: A rules plaque stands where travelers can read it.
diff --git a/data/rooms/casino/6107.yaml b/data/rooms/casino/6107.yaml
deleted file mode 100644
index 85fc600..0000000
--- a/data/rooms/casino/6107.yaml
+++ /dev/null
@@ -1,8 +0,0 @@
-name: Copper Blackjack Anteroom
-description: A quiet seating bay separates the concourse from Copper Blackjack. A brass plaque states
- the rules before anyone joins a game.
-exits:
- east:
- room: 6103
- west:
- room: 6108
diff --git a/data/rooms/casino/6108.yaml b/data/rooms/casino/6108.yaml
index 877e219..db7beaa 100644
--- a/data/rooms/casino/6108.yaml
+++ b/data/rooms/casino/6108.yaml
@@ -1,34 +1,34 @@
-name: Copper Blackjack
-description: An individual blackjack salon opens beyond the anteroom. Limits are 1–100 credits. No wager
- is required to watch or pass through.
+name: Midnight Blackjack
+description: Eight decks and a dealer who hits soft 17, under midnight-blue felt. Limits are
+ 100–10000 credits. A prosperous local watches the high-limit action from the rail.
exits:
- east:
- room: 6107
- west:
- room: 6127
+ north:
+ room: 6106
casino_tables:
-- id: copper
+- id: midnight
game: blackjack
max_players: 6
betting_ticks: 30
- min_bet: 1
- max_bet: 100
+ min_bet: 100
+ max_bet: 10000
blackjack:
- decks: 6
+ decks: 8
shuffle_at: 0.25
blackjack_payout: 1.5
- dealer_hit_soft_17: false
+ dealer_hit_soft_17: true
max_split_hands: 4
resplit_aces: false
double_after_split: true
double_split_blackjack: false
insurance_payout: 2
surrender: false
- action_ticks: 40
+ action_ticks: 25
split_unlike_ten_values: false
+mobs:
+- id: casino_patron
objects:
- name: rules plaque
- description: Play blackjack; bet min, bet max or bet <amount>. Blackjack pays 3:2; insurance 2:1. 6
- deck(s). Dealer stands on soft 17. No surrender. Double after split allowed. Split 21 pays 1:1; split
- identical ranks only.
+ description: Play blackjack; bet min, bet max or bet <amount>. Blackjack pays 3:2; insurance 2:1. 8
+ deck(s). Dealer hits soft 17. No surrender. Double after split allowed. Split 21 pays 1:1; split identical
+ ranks only.
inroom_description: A rules plaque stands where travelers can read it.
diff --git a/data/rooms/casino/6109.yaml b/data/rooms/casino/6109.yaml
deleted file mode 100644
index 4e2786c..0000000
--- a/data/rooms/casino/6109.yaml
+++ /dev/null
@@ -1,8 +0,0 @@
-name: Classic Blackjack Anteroom
-description: A quiet seating bay separates the concourse from Classic Blackjack. A brass plaque states
- the rules before anyone joins a game.
-exits:
- east:
- room: 6104
- west:
- room: 6110
diff --git a/data/rooms/casino/6110.yaml b/data/rooms/casino/6110.yaml
deleted file mode 100644
index 5f77c60..0000000
--- a/data/rooms/casino/6110.yaml
+++ /dev/null
@@ -1,34 +0,0 @@
-name: Classic Blackjack
-description: An individual blackjack salon opens beyond the anteroom. Limits are 10–1000 credits. No wager
- is required to watch or pass through.
-exits:
- east:
- room: 6109
- west:
- room: 6130
-casino_tables:
-- id: classic
- game: blackjack
- max_players: 6
- betting_ticks: 30
- min_bet: 10
- max_bet: 1000
- blackjack:
- decks: 6
- shuffle_at: 0.25
- blackjack_payout: 1.5
- dealer_hit_soft_17: false
- max_split_hands: 4
- resplit_aces: false
- double_after_split: true
- double_split_blackjack: false
- insurance_payout: 2
- surrender: false
- action_ticks: 25
- split_unlike_ten_values: false
-objects:
-- name: rules plaque
- description: Play blackjack; bet min, bet max or bet <amount>. Blackjack pays 3:2; insurance 2:1. 6
- deck(s). Dealer stands on soft 17. No surrender. Double after split allowed. Split 21 pays 1:1; split
- identical ranks only.
- inroom_description: A rules plaque stands where travelers can read it.
diff --git a/data/rooms/casino/6111.yaml b/data/rooms/casino/6111.yaml
deleted file mode 100644
index 5f33706..0000000
--- a/data/rooms/casino/6111.yaml
+++ /dev/null
@@ -1,8 +0,0 @@
-name: Mercy Blackjack Anteroom
-description: A quiet seating bay separates the concourse from Mercy Blackjack. A brass plaque states the
- rules before anyone joins a game.
-exits:
- east:
- room: 6105
- west:
- room: 6112
diff --git a/data/rooms/casino/6112.yaml b/data/rooms/casino/6112.yaml
deleted file mode 100644
index a77775d..0000000
--- a/data/rooms/casino/6112.yaml
+++ /dev/null
@@ -1,34 +0,0 @@
-name: Mercy Blackjack
-description: An individual blackjack salon opens beyond the anteroom. Limits are 2–500 credits. No wager
- is required to watch or pass through.
-exits:
- east:
- room: 6111
- west:
- room: 6128
-casino_tables:
-- id: mercy
- game: blackjack
- max_players: 6
- betting_ticks: 30
- min_bet: 2
- max_bet: 500
- blackjack:
- decks: 6
- shuffle_at: 0.25
- blackjack_payout: 1.5
- dealer_hit_soft_17: false
- max_split_hands: 4
- resplit_aces: true
- double_after_split: true
- double_split_blackjack: false
- insurance_payout: 2
- surrender: true
- action_ticks: 25
- split_unlike_ten_values: false
-objects:
-- name: rules plaque
- description: Play blackjack; bet min, bet max or bet <amount>. Blackjack pays 3:2; insurance 2:1. 6
- deck(s). Dealer stands on soft 17. Late surrender and re-splitting aces offered. Double after split allowed. Split 21 pays
- 1:1; split identical ranks only.
- inroom_description: A rules plaque stands where travelers can read it.
diff --git a/data/rooms/casino/6113.yaml b/data/rooms/casino/6113.yaml
deleted file mode 100644
index e1fb79d..0000000
--- a/data/rooms/casino/6113.yaml
+++ /dev/null
@@ -1,8 +0,0 @@
-name: Single Deck Salon Anteroom
-description: A quiet seating bay separates the concourse from Single Deck Salon. A brass plaque states
- the rules before anyone joins a game.
-exits:
- east:
- room: 6106
- west:
- room: 6114
diff --git a/data/rooms/casino/6114.yaml b/data/rooms/casino/6114.yaml
deleted file mode 100644
index 0689249..0000000
--- a/data/rooms/casino/6114.yaml
+++ /dev/null
@@ -1,34 +0,0 @@
-name: Single Deck Salon
-description: An individual blackjack salon opens beyond the anteroom. Limits are 25–2500 credits. No wager
- is required to watch or pass through.
-exits:
- east:
- room: 6113
- west:
- room: 6131
-casino_tables:
-- id: single
- game: blackjack
- max_players: 6
- betting_ticks: 30
- min_bet: 25
- max_bet: 2500
- blackjack:
- decks: 1
- shuffle_at: 0.25
- blackjack_payout: 1.2
- dealer_hit_soft_17: true
- max_split_hands: 2
- resplit_aces: false
- double_after_split: false
- double_split_blackjack: false
- insurance_payout: 2
- surrender: false
- action_ticks: 25
- split_unlike_ten_values: false
-objects:
-- name: rules plaque
- description: Play blackjack; bet min, bet max or bet <amount>. Blackjack pays 6:5; insurance 2:1. 1
- deck(s). Dealer hits soft 17. No surrender. No double after split. Split 21 pays 1:1; split identical
- ranks only.
- inroom_description: A rules plaque stands where travelers can read it.
diff --git a/data/rooms/casino/6115.yaml b/data/rooms/casino/6115.yaml
deleted file mode 100644
index b58fde3..0000000
--- a/data/rooms/casino/6115.yaml
+++ /dev/null
@@ -1,8 +0,0 @@
-name: Midnight Blackjack Anteroom
-description: A quiet seating bay separates the concourse from Midnight Blackjack. A brass plaque states
- the rules before anyone joins a game.
-exits:
- east:
- room: 6102
- west:
- room: 6116
diff --git a/data/rooms/casino/6116.yaml b/data/rooms/casino/6116.yaml
deleted file mode 100644
index 044b106..0000000
--- a/data/rooms/casino/6116.yaml
+++ /dev/null
@@ -1,34 +0,0 @@
-name: Midnight Blackjack
-description: An individual blackjack salon opens beyond the anteroom. Limits are 100–10000 credits. No
- wager is required to watch or pass through.
-exits:
- east:
- room: 6115
- west:
- room: 6129
-casino_tables:
-- id: midnight
- game: blackjack
- max_players: 6
- betting_ticks: 30
- min_bet: 100
- max_bet: 10000
- blackjack:
- decks: 8
- shuffle_at: 0.25
- blackjack_payout: 1.5
- dealer_hit_soft_17: true
- max_split_hands: 4
- resplit_aces: false
- double_after_split: true
- double_split_blackjack: false
- insurance_payout: 2
- surrender: false
- action_ticks: 25
- split_unlike_ten_values: false
-objects:
-- name: rules plaque
- description: Play blackjack; bet min, bet max or bet <amount>. Blackjack pays 3:2; insurance 2:1. 8
- deck(s). Dealer hits soft 17. No surrender. Double after split allowed. Split 21 pays 1:1; split identical
- ranks only.
- inroom_description: A rules plaque stands where travelers can read it.
diff --git a/data/rooms/casino/6117.yaml b/data/rooms/casino/6117.yaml
deleted file mode 100644
index ac51ab5..0000000
--- a/data/rooms/casino/6117.yaml
+++ /dev/null
@@ -1,8 +0,0 @@
-name: Penny Reels Anteroom
-description: A quiet seating bay separates the concourse from Penny Reels. A brass plaque states the rules
- before anyone joins a game.
-exits:
- west:
- room: 6103
- east:
- room: 6118
diff --git a/data/rooms/casino/6118.yaml b/data/rooms/casino/6118.yaml
index 98e6d9d..5ec251b 100644
--- a/data/rooms/casino/6118.yaml
+++ b/data/rooms/casino/6118.yaml
@@ -1,11 +1,13 @@
name: Penny Reels
-description: An individual slots salon opens beyond the anteroom. Limits are 1–25 credits. No wager is
- required to watch or pass through.
+description: A wall of chiming reels opens the machine gallery; limits are 1–25 credits. Higher-stakes
+ machines wait north and south. No wager is required to watch or pass through.
exits:
west:
- room: 6117
- east:
- room: 6132
+ room: 6185
+ north:
+ room: 6120
+ south:
+ room: 6122
casino_machines:
- id: penny_reels
game: slots
diff --git a/data/rooms/casino/6119.yaml b/data/rooms/casino/6119.yaml
deleted file mode 100644
index cdf73e8..0000000
--- a/data/rooms/casino/6119.yaml
+++ /dev/null
@@ -1,8 +0,0 @@
-name: Silver Reels Anteroom
-description: A quiet seating bay separates the concourse from Silver Reels. A brass plaque states the
- rules before anyone joins a game.
-exits:
- west:
- room: 6104
- east:
- room: 6120
diff --git a/data/rooms/casino/6120.yaml b/data/rooms/casino/6120.yaml
index 63b12d3..ed25f49 100644
--- a/data/rooms/casino/6120.yaml
+++ b/data/rooms/casino/6120.yaml
@@ -1,11 +1,10 @@
name: Silver Reels
-description: An individual slots salon opens beyond the anteroom. Limits are 10–500 credits. No wager
- is required to watch or pass through.
+description: Mid-limit reels; wagers 10-500 credits. The high-limit salon waits north.
exits:
- west:
- room: 6119
- east:
- room: 6133
+ south:
+ room: 6118
+ north:
+ room: 6126
casino_machines:
- id: silver_reels
game: slots
diff --git a/data/rooms/casino/6121.yaml b/data/rooms/casino/6121.yaml
deleted file mode 100644
index 511ceb8..0000000
--- a/data/rooms/casino/6121.yaml
+++ /dev/null
@@ -1,8 +0,0 @@
-name: Timber Reels Anteroom
-description: A quiet seating bay separates the concourse from Timber Reels. A brass plaque states the
- rules before anyone joins a game.
-exits:
- west:
- room: 6105
- east:
- room: 6122
diff --git a/data/rooms/casino/6122.yaml b/data/rooms/casino/6122.yaml
index 486256b..9d8027c 100644
--- a/data/rooms/casino/6122.yaml
+++ b/data/rooms/casino/6122.yaml
@@ -1,11 +1,10 @@
name: Timber Reels
-description: An individual slots salon opens beyond the anteroom. Limits are 1–100 credits. No wager is
- required to watch or pass through.
+description: Huff-and-even-more-puff reels; wagers 1-100 credits. Another huff cabinet waits south.
exits:
- west:
- room: 6121
- east:
- room: 6134
+ north:
+ room: 6118
+ south:
+ room: 6124
casino_machines:
- id: timber_reels
game: slots
diff --git a/data/rooms/casino/6123.yaml b/data/rooms/casino/6123.yaml
deleted file mode 100644
index dc3bd2c..0000000
--- a/data/rooms/casino/6123.yaml
+++ /dev/null
@@ -1,8 +0,0 @@
-name: Golden Timber Reels Anteroom
-description: A quiet seating bay separates the concourse from Golden Timber Reels. A brass plaque states
- the rules before anyone joins a game.
-exits:
- west:
- room: 6106
- east:
- room: 6124
diff --git a/data/rooms/casino/6124.yaml b/data/rooms/casino/6124.yaml
index ac0319d..2ef7d00 100644
--- a/data/rooms/casino/6124.yaml
+++ b/data/rooms/casino/6124.yaml
@@ -1,11 +1,8 @@
name: Golden Timber Reels
-description: An individual slots salon opens beyond the anteroom. Limits are 25–2500 credits. No wager
- is required to watch or pass through.
+description: The golden huff cabinet takes larger wagers, 25-2500 credits.
exits:
- west:
- room: 6123
- east:
- room: 6135
+ north:
+ room: 6122
casino_machines:
- id: golden_timber_reels
game: slots
diff --git a/data/rooms/casino/6125.yaml b/data/rooms/casino/6125.yaml
deleted file mode 100644
index 6641421..0000000
--- a/data/rooms/casino/6125.yaml
+++ /dev/null
@@ -1,8 +0,0 @@
-name: Platinum Reels Anteroom
-description: A quiet seating bay separates the concourse from Platinum Reels. A brass plaque states the
- rules before anyone joins a game.
-exits:
- west:
- room: 6102
- east:
- room: 6126
diff --git a/data/rooms/casino/6126.yaml b/data/rooms/casino/6126.yaml
index 272b5ff..4b99a7a 100644
--- a/data/rooms/casino/6126.yaml
+++ b/data/rooms/casino/6126.yaml
@@ -1,11 +1,8 @@
name: Platinum Reels
-description: An individual slots salon opens beyond the anteroom. Limits are 100–10000 credits. No wager
- is required to watch or pass through.
+description: The high-limit reels; wagers 100-10000 credits.
exits:
- west:
- room: 6125
- east:
- room: 6136
+ south:
+ room: 6120
casino_machines:
- id: platinum_reels
game: slots
diff --git a/data/rooms/casino/6127.yaml b/data/rooms/casino/6127.yaml
index b0390b5..4ce355e 100644
--- a/data/rooms/casino/6127.yaml
+++ b/data/rooms/casino/6127.yaml
@@ -1,9 +1,14 @@
name: Harbor Baccarat
-description: Green baize and shaded lamps distinguish this quiet baccarat salon from the neighboring blackjack
- room. A traditional commission baccarat table offers pair bets; look at the table for its rules.
+description: Green baize and shaded lamps. A traditional commission baccarat table offers pair bets; its no-commission editions wait north and south. Look at the table for its rules.
exits:
+ west:
+ room: 6101
east:
- room: 6108
+ room: 6182
+ north:
+ room: 6128
+ south:
+ room: 6129
casino_tables:
- id: harbor_baccarat
game: baccarat
diff --git a/data/rooms/casino/6128.yaml b/data/rooms/casino/6128.yaml
index 35226a2..c7f329f 100644
--- a/data/rooms/casino/6128.yaml
+++ b/data/rooms/casino/6128.yaml
@@ -1,9 +1,8 @@
name: Charter Baccarat
-description: Green baize and shaded lamps distinguish this quiet baccarat salon from the neighboring blackjack
- room. A Super 6 no-commission baccarat table waits here; look at the table for its rules.
+description: A Super 6 no-commission baccarat table; winning Banker sixes pay half, and the separate super_6 wager pays 12:1.
exits:
- east:
- room: 6112
+ south:
+ room: 6127
casino_tables:
- id: charter_baccarat
game: baccarat
diff --git a/data/rooms/casino/6129.yaml b/data/rooms/casino/6129.yaml
index 50c40e4..0aa7fcd 100644
--- a/data/rooms/casino/6129.yaml
+++ b/data/rooms/casino/6129.yaml
@@ -1,9 +1,8 @@
name: Crown Baccarat
-description: Green baize and shaded lamps distinguish this quiet baccarat salon from the neighboring blackjack
- room. An EZ Baccarat no-commission table offers Dragon 7 and Panda 8; look at the table for its rules.
+description: An EZ Baccarat no-commission table; a winning three-card Banker seven pushes Banker bets, and Dragon 7 and Panda 8 are offered.
exits:
- east:
- room: 6116
+ north:
+ room: 6127
casino_tables:
- id: crown_baccarat
game: baccarat
diff --git a/data/rooms/casino/6130.yaml b/data/rooms/casino/6130.yaml
deleted file mode 100644
index 2a3008f..0000000
--- a/data/rooms/casino/6130.yaml
+++ /dev/null
@@ -1,9 +0,0 @@
-name: Cashier Gallery
-description: A long brass grille protects the counting desk; the public bank booth remains outside it.
-exits:
- east:
- room: 6110
-objects:
-- id: bank_booth
-mobs:
-- id: casino_cashier
diff --git a/data/rooms/casino/6131.yaml b/data/rooms/casino/6131.yaml
deleted file mode 100644
index 645c9fb..0000000
--- a/data/rooms/casino/6131.yaml
+++ /dev/null
@@ -1,6 +0,0 @@
-name: Responsible Play Lounge
-description: Quiet chairs face a clock with unusually large numerals. A sign reminds visitors that the
- next hand does not owe them a win.
-exits:
- east:
- room: 6114
diff --git a/data/rooms/casino/6132.yaml b/data/rooms/casino/6132.yaml
deleted file mode 100644
index 34f8756..0000000
--- a/data/rooms/casino/6132.yaml
+++ /dev/null
@@ -1,7 +0,0 @@
-name: South Buffet
-description: A public range lets travelers cook their own food alongside the dining tables.
-exits:
- west:
- room: 6118
-objects:
-- id: cooking_range
diff --git a/data/rooms/casino/6133.yaml b/data/rooms/casino/6133.yaml
deleted file mode 100644
index 9e00c74..0000000
--- a/data/rooms/casino/6133.yaml
+++ /dev/null
@@ -1,5 +0,0 @@
-name: Cloakroom
-description: Numbered hooks and clean benches serve visitors who only came to see the building.
-exits:
- west:
- room: 6120
diff --git a/data/rooms/casino/6134.yaml b/data/rooms/casino/6134.yaml
deleted file mode 100644
index 6bd9f53..0000000
--- a/data/rooms/casino/6134.yaml
+++ /dev/null
@@ -1,7 +0,0 @@
-name: Guest Services
-description: A charger sits beneath a directory of every floor.
-exits:
- west:
- room: 6122
-objects:
-- id: charging_station
diff --git a/data/rooms/casino/6135.yaml b/data/rooms/casino/6135.yaml
deleted file mode 100644
index d1e9745..0000000
--- a/data/rooms/casino/6135.yaml
+++ /dev/null
@@ -1,5 +0,0 @@
-name: Musicians’ Alcove
-description: A mechanical piano waits between performances.
-exits:
- west:
- room: 6124
diff --git a/data/rooms/casino/6136.yaml b/data/rooms/casino/6136.yaml
deleted file mode 100644
index ea7e05b..0000000
--- a/data/rooms/casino/6136.yaml
+++ /dev/null
@@ -1,5 +0,0 @@
-name: Observation Lounge
-description: Tall windows show Domita’s slate roofs far below the illuminated signs.
-exits:
- west:
- room: 6126
diff --git a/data/rooms/casino/6137.yaml b/data/rooms/casino/6137.yaml
deleted file mode 100644
index e8809f7..0000000
--- a/data/rooms/casino/6137.yaml
+++ /dev/null
@@ -1,10 +0,0 @@
-name: First Gallery South Rotunda
-description: The first gallery is freely open for exploration. Completed corridors face unfurnished gaming
- rooms; signs explicitly say that no games operate on this floor yet.
-exits:
- west:
- room: 6138
- east:
- room: 6139
- north:
- room: 6140
diff --git a/data/rooms/casino/6138.yaml b/data/rooms/casino/6138.yaml
deleted file mode 100644
index 2ccb2a8..0000000
--- a/data/rooms/casino/6138.yaml
+++ /dev/null
@@ -1,6 +0,0 @@
-name: First Gallery Future Tournament Hall
-description: Power sockets, table anchor points and service passages are installed, but there is no playable
- game here yet. This hall is reserved for a future casino expansion.
-exits:
- east:
- room: 6137
diff --git a/data/rooms/casino/6139.yaml b/data/rooms/casino/6139.yaml
deleted file mode 100644
index fb45596..0000000
--- a/data/rooms/casino/6139.yaml
+++ /dev/null
@@ -1,5 +0,0 @@
-name: First Gallery Balcony Lounge
-description: A sheltered balcony overlooks the city without requiring a ticket.
-exits:
- west:
- room: 6137
diff --git a/data/rooms/casino/6140.yaml b/data/rooms/casino/6140.yaml
deleted file mode 100644
index 04bf1af..0000000
--- a/data/rooms/casino/6140.yaml
+++ /dev/null
@@ -1,12 +0,0 @@
-name: First Gallery Promenade
-description: The first gallery is freely open for exploration. Completed corridors face unfurnished gaming
- rooms; signs explicitly say that no games operate on this floor yet.
-exits:
- south:
- room: 6137
- west:
- room: 6141
- east:
- room: 6142
- north:
- room: 6143
diff --git a/data/rooms/casino/6141.yaml b/data/rooms/casino/6141.yaml
deleted file mode 100644
index 7195e2c..0000000
--- a/data/rooms/casino/6141.yaml
+++ /dev/null
@@ -1,6 +0,0 @@
-name: First Gallery Future Card Salon
-description: Power sockets, table anchor points and service passages are installed, but there is no playable
- game here yet. This hall is reserved for a future casino expansion.
-exits:
- east:
- room: 6140
diff --git a/data/rooms/casino/6142.yaml b/data/rooms/casino/6142.yaml
deleted file mode 100644
index f49b1b5..0000000
--- a/data/rooms/casino/6142.yaml
+++ /dev/null
@@ -1,5 +0,0 @@
-name: First Gallery Refreshment Room
-description: Empty serving counters stand ready for the next expansion.
-exits:
- west:
- room: 6140
diff --git a/data/rooms/casino/6143.yaml b/data/rooms/casino/6143.yaml
deleted file mode 100644
index 37f3a73..0000000
--- a/data/rooms/casino/6143.yaml
+++ /dev/null
@@ -1,12 +0,0 @@
-name: First Gallery Central Landing
-description: The first gallery is freely open for exploration. Completed corridors face unfurnished gaming
- rooms; signs explicitly say that no games operate on this floor yet.
-exits:
- south:
- room: 6140
- west:
- room: 6144
- east:
- room: 6145
- north:
- room: 6146
diff --git a/data/rooms/casino/6144.yaml b/data/rooms/casino/6144.yaml
deleted file mode 100644
index cc30669..0000000
--- a/data/rooms/casino/6144.yaml
+++ /dev/null
@@ -1,6 +0,0 @@
-name: First Gallery Future Wheel Gallery
-description: Power sockets, table anchor points and service passages are installed, but there is no playable
- game here yet. This hall is reserved for a future casino expansion.
-exits:
- east:
- room: 6143
diff --git a/data/rooms/casino/6145.yaml b/data/rooms/casino/6145.yaml
deleted file mode 100644
index 60f1e0f..0000000
--- a/data/rooms/casino/6145.yaml
+++ /dev/null
@@ -1,5 +0,0 @@
-name: First Gallery Exhibition Room
-description: Building models show the corporation’s increasingly extravagant plans.
-exits:
- west:
- room: 6143
diff --git a/data/rooms/casino/6146.yaml b/data/rooms/casino/6146.yaml
deleted file mode 100644
index 04a9521..0000000
--- a/data/rooms/casino/6146.yaml
+++ /dev/null
@@ -1,12 +0,0 @@
-name: First Gallery North Promenade
-description: The first gallery is freely open for exploration. Completed corridors face unfurnished gaming
- rooms; signs explicitly say that no games operate on this floor yet.
-exits:
- south:
- room: 6143
- west:
- room: 6147
- east:
- room: 6148
- north:
- room: 6149
diff --git a/data/rooms/casino/6147.yaml b/data/rooms/casino/6147.yaml
deleted file mode 100644
index 2b85741..0000000
--- a/data/rooms/casino/6147.yaml
+++ /dev/null
@@ -1,6 +0,0 @@
-name: First Gallery Future Dice Salon
-description: Power sockets, table anchor points and service passages are installed, but there is no playable
- game here yet. This hall is reserved for a future casino expansion.
-exits:
- east:
- room: 6146
diff --git a/data/rooms/casino/6148.yaml b/data/rooms/casino/6148.yaml
deleted file mode 100644
index c9982b5..0000000
--- a/data/rooms/casino/6148.yaml
+++ /dev/null
@@ -1,7 +0,0 @@
-name: First Gallery Staff History Gallery
-description: Photographs remember workers whose names do not appear on the marble dedication.
-exits:
- west:
- room: 6146
-mobs:
-- id: casino_patron
diff --git a/data/rooms/casino/6149.yaml b/data/rooms/casino/6149.yaml
deleted file mode 100644
index e40a521..0000000
--- a/data/rooms/casino/6149.yaml
+++ /dev/null
@@ -1,14 +0,0 @@
-name: First Gallery Grand Stair
-description: The first gallery is freely open for exploration. Completed corridors face unfurnished gaming
- rooms; signs explicitly say that no games operate on this floor yet.
-exits:
- south:
- room: 6146
- down:
- room: 6102
- west:
- room: 6150
- east:
- room: 6151
- up:
- room: 6164
diff --git a/data/rooms/casino/6150.yaml b/data/rooms/casino/6150.yaml
deleted file mode 100644
index 8e627fd..0000000
--- a/data/rooms/casino/6150.yaml
+++ /dev/null
@@ -1,6 +0,0 @@
-name: First Gallery Future Private Table Hall
-description: Power sockets, table anchor points and service passages are installed, but there is no playable
- game here yet. This hall is reserved for a future casino expansion.
-exits:
- east:
- room: 6149
diff --git a/data/rooms/casino/6151.yaml b/data/rooms/casino/6151.yaml
deleted file mode 100644
index f0d2e9f..0000000
--- a/data/rooms/casino/6151.yaml
+++ /dev/null
@@ -1,5 +0,0 @@
-name: First Gallery Viewing Terrace
-description: A long window gives a clear view of the roads that belong to everyone.
-exits:
- west:
- room: 6149
diff --git a/data/rooms/casino/6152.yaml b/data/rooms/casino/6152.yaml
deleted file mode 100644
index 9433ce8..0000000
--- a/data/rooms/casino/6152.yaml
+++ /dev/null
@@ -1,10 +0,0 @@
-name: Second Gallery South Rotunda
-description: The second gallery is freely open for exploration. Completed corridors face unfurnished gaming
- rooms; signs explicitly say that no games operate on this floor yet.
-exits:
- west:
- room: 6153
- east:
- room: 6154
- north:
- room: 6155
diff --git a/data/rooms/casino/6153.yaml b/data/rooms/casino/6153.yaml
deleted file mode 100644
index 34b51b4..0000000
--- a/data/rooms/casino/6153.yaml
+++ /dev/null
@@ -1,6 +0,0 @@
-name: Second Gallery Future Tournament Hall
-description: Power sockets, table anchor points and service passages are installed, but there is no playable
- game here yet. This hall is reserved for a future casino expansion.
-exits:
- east:
- room: 6152
diff --git a/data/rooms/casino/6154.yaml b/data/rooms/casino/6154.yaml
deleted file mode 100644
index a05fd8a..0000000
--- a/data/rooms/casino/6154.yaml
+++ /dev/null
@@ -1,5 +0,0 @@
-name: Second Gallery Balcony Lounge
-description: A sheltered balcony overlooks the city without requiring a ticket.
-exits:
- west:
- room: 6152
diff --git a/data/rooms/casino/6155.yaml b/data/rooms/casino/6155.yaml
deleted file mode 100644
index b0ddb4d..0000000
--- a/data/rooms/casino/6155.yaml
+++ /dev/null
@@ -1,12 +0,0 @@
-name: Second Gallery Promenade
-description: The second gallery is freely open for exploration. Completed corridors face unfurnished gaming
- rooms; signs explicitly say that no games operate on this floor yet.
-exits:
- south:
- room: 6152
- west:
- room: 6156
- east:
- room: 6157
- north:
- room: 6158
diff --git a/data/rooms/casino/6156.yaml b/data/rooms/casino/6156.yaml
deleted file mode 100644
index a879c4b..0000000
--- a/data/rooms/casino/6156.yaml
+++ /dev/null
@@ -1,6 +0,0 @@
-name: Second Gallery Future Card Salon
-description: Power sockets, table anchor points and service passages are installed, but there is no playable
- game here yet. This hall is reserved for a future casino expansion.
-exits:
- east:
- room: 6155
diff --git a/data/rooms/casino/6157.yaml b/data/rooms/casino/6157.yaml
deleted file mode 100644
index 83add4d..0000000
--- a/data/rooms/casino/6157.yaml
+++ /dev/null
@@ -1,5 +0,0 @@
-name: Second Gallery Refreshment Room
-description: Empty serving counters stand ready for the next expansion.
-exits:
- west:
- room: 6155
diff --git a/data/rooms/casino/6158.yaml b/data/rooms/casino/6158.yaml
deleted file mode 100644
index fb598e4..0000000
--- a/data/rooms/casino/6158.yaml
+++ /dev/null
@@ -1,12 +0,0 @@
-name: Second Gallery Central Landing
-description: The second gallery is freely open for exploration. Completed corridors face unfurnished gaming
- rooms; signs explicitly say that no games operate on this floor yet.
-exits:
- south:
- room: 6155
- west:
- room: 6159
- east:
- room: 6160
- north:
- room: 6161
diff --git a/data/rooms/casino/6159.yaml b/data/rooms/casino/6159.yaml
deleted file mode 100644
index fff90ea..0000000
--- a/data/rooms/casino/6159.yaml
+++ /dev/null
@@ -1,6 +0,0 @@
-name: Second Gallery Future Wheel Gallery
-description: Power sockets, table anchor points and service passages are installed, but there is no playable
- game here yet. This hall is reserved for a future casino expansion.
-exits:
- east:
- room: 6158
diff --git a/data/rooms/casino/6160.yaml b/data/rooms/casino/6160.yaml
deleted file mode 100644
index 4c71e15..0000000
--- a/data/rooms/casino/6160.yaml
+++ /dev/null
@@ -1,5 +0,0 @@
-name: Second Gallery Exhibition Room
-description: Building models show the corporation’s increasingly extravagant plans.
-exits:
- west:
- room: 6158
diff --git a/data/rooms/casino/6161.yaml b/data/rooms/casino/6161.yaml
deleted file mode 100644
index 750c8ea..0000000
--- a/data/rooms/casino/6161.yaml
+++ /dev/null
@@ -1,12 +0,0 @@
-name: Second Gallery North Promenade
-description: The second gallery is freely open for exploration. Completed corridors face unfurnished gaming
- rooms; signs explicitly say that no games operate on this floor yet.
-exits:
- south:
- room: 6158
- west:
- room: 6162
- east:
- room: 6163
- north:
- room: 6164
diff --git a/data/rooms/casino/6162.yaml b/data/rooms/casino/6162.yaml
deleted file mode 100644
index c083708..0000000
--- a/data/rooms/casino/6162.yaml
+++ /dev/null
@@ -1,6 +0,0 @@
-name: Second Gallery Future Dice Salon
-description: Power sockets, table anchor points and service passages are installed, but there is no playable
- game here yet. This hall is reserved for a future casino expansion.
-exits:
- east:
- room: 6161
diff --git a/data/rooms/casino/6163.yaml b/data/rooms/casino/6163.yaml
deleted file mode 100644
index 58983a0..0000000
--- a/data/rooms/casino/6163.yaml
+++ /dev/null
@@ -1,5 +0,0 @@
-name: Second Gallery Staff History Gallery
-description: Photographs remember workers whose names do not appear on the marble dedication.
-exits:
- west:
- room: 6161
diff --git a/data/rooms/casino/6164.yaml b/data/rooms/casino/6164.yaml
deleted file mode 100644
index 2cf8ee1..0000000
--- a/data/rooms/casino/6164.yaml
+++ /dev/null
@@ -1,14 +0,0 @@
-name: Second Gallery Grand Stair
-description: The second gallery is freely open for exploration. Completed corridors face unfurnished gaming
- rooms; signs explicitly say that no games operate on this floor yet.
-exits:
- south:
- room: 6161
- down:
- room: 6149
- west:
- room: 6165
- east:
- room: 6166
- up:
- room: 6179
diff --git a/data/rooms/casino/6165.yaml b/data/rooms/casino/6165.yaml
deleted file mode 100644
index f588711..0000000
--- a/data/rooms/casino/6165.yaml
+++ /dev/null
@@ -1,6 +0,0 @@
-name: Second Gallery Future Private Table Hall
-description: Power sockets, table anchor points and service passages are installed, but there is no playable
- game here yet. This hall is reserved for a future casino expansion.
-exits:
- east:
- room: 6164
diff --git a/data/rooms/casino/6166.yaml b/data/rooms/casino/6166.yaml
deleted file mode 100644
index ce4c421..0000000
--- a/data/rooms/casino/6166.yaml
+++ /dev/null
@@ -1,5 +0,0 @@
-name: Second Gallery Viewing Terrace
-description: A long window gives a clear view of the roads that belong to everyone.
-exits:
- west:
- room: 6164
diff --git a/data/rooms/casino/6167.yaml b/data/rooms/casino/6167.yaml
deleted file mode 100644
index 35dc3aa..0000000
--- a/data/rooms/casino/6167.yaml
+++ /dev/null
@@ -1,10 +0,0 @@
-name: Crown Floor South Rotunda
-description: The crown floor is freely open for exploration. Completed corridors face unfurnished gaming
- rooms; signs explicitly say that no games operate on this floor yet.
-exits:
- west:
- room: 6168
- east:
- room: 6169
- north:
- room: 6170
diff --git a/data/rooms/casino/6168.yaml b/data/rooms/casino/6168.yaml
deleted file mode 100644
index 5b5822e..0000000
--- a/data/rooms/casino/6168.yaml
+++ /dev/null
@@ -1,6 +0,0 @@
-name: Crown Floor Future Tournament Hall
-description: Power sockets, table anchor points and service passages are installed, but there is no playable
- game here yet. This hall is reserved for a future casino expansion.
-exits:
- east:
- room: 6167
diff --git a/data/rooms/casino/6169.yaml b/data/rooms/casino/6169.yaml
deleted file mode 100644
index ae54bca..0000000
--- a/data/rooms/casino/6169.yaml
+++ /dev/null
@@ -1,5 +0,0 @@
-name: Crown Floor Balcony Lounge
-description: A sheltered balcony overlooks the city without requiring a ticket.
-exits:
- west:
- room: 6167
diff --git a/data/rooms/casino/6170.yaml b/data/rooms/casino/6170.yaml
deleted file mode 100644
index d275920..0000000
--- a/data/rooms/casino/6170.yaml
+++ /dev/null
@@ -1,12 +0,0 @@
-name: Crown Floor Promenade
-description: The crown floor is freely open for exploration. Completed corridors face unfurnished gaming
- rooms; signs explicitly say that no games operate on this floor yet.
-exits:
- south:
- room: 6167
- west:
- room: 6171
- east:
- room: 6172
- north:
- room: 6173
diff --git a/data/rooms/casino/6171.yaml b/data/rooms/casino/6171.yaml
deleted file mode 100644
index 41fbe05..0000000
--- a/data/rooms/casino/6171.yaml
+++ /dev/null
@@ -1,6 +0,0 @@
-name: Crown Floor Future Card Salon
-description: Power sockets, table anchor points and service passages are installed, but there is no playable
- game here yet. This hall is reserved for a future casino expansion.
-exits:
- east:
- room: 6170
diff --git a/data/rooms/casino/6172.yaml b/data/rooms/casino/6172.yaml
deleted file mode 100644
index 44e9161..0000000
--- a/data/rooms/casino/6172.yaml
+++ /dev/null
@@ -1,5 +0,0 @@
-name: Crown Floor Refreshment Room
-description: Empty serving counters stand ready for the next expansion.
-exits:
- west:
- room: 6170
diff --git a/data/rooms/casino/6173.yaml b/data/rooms/casino/6173.yaml
deleted file mode 100644
index 1fd3e12..0000000
--- a/data/rooms/casino/6173.yaml
+++ /dev/null
@@ -1,12 +0,0 @@
-name: Crown Floor Central Landing
-description: The crown floor is freely open for exploration. Completed corridors face unfurnished gaming
- rooms; signs explicitly say that no games operate on this floor yet.
-exits:
- south:
- room: 6170
- west:
- room: 6174
- east:
- room: 6175
- north:
- room: 6176
diff --git a/data/rooms/casino/6174.yaml b/data/rooms/casino/6174.yaml
deleted file mode 100644
index dd31f28..0000000
--- a/data/rooms/casino/6174.yaml
+++ /dev/null
@@ -1,6 +0,0 @@
-name: Crown Floor Future Wheel Gallery
-description: Power sockets, table anchor points and service passages are installed, but there is no playable
- game here yet. This hall is reserved for a future casino expansion.
-exits:
- east:
- room: 6173
diff --git a/data/rooms/casino/6175.yaml b/data/rooms/casino/6175.yaml
deleted file mode 100644
index ec79025..0000000
--- a/data/rooms/casino/6175.yaml
+++ /dev/null
@@ -1,5 +0,0 @@
-name: Crown Floor Exhibition Room
-description: Building models show the corporation’s increasingly extravagant plans.
-exits:
- west:
- room: 6173
diff --git a/data/rooms/casino/6176.yaml b/data/rooms/casino/6176.yaml
deleted file mode 100644
index d1a0ba6..0000000
--- a/data/rooms/casino/6176.yaml
+++ /dev/null
@@ -1,12 +0,0 @@
-name: Crown Floor North Promenade
-description: The crown floor is freely open for exploration. Completed corridors face unfurnished gaming
- rooms; signs explicitly say that no games operate on this floor yet.
-exits:
- south:
- room: 6173
- west:
- room: 6177
- east:
- room: 6178
- north:
- room: 6179
diff --git a/data/rooms/casino/6177.yaml b/data/rooms/casino/6177.yaml
deleted file mode 100644
index be5d0e9..0000000
--- a/data/rooms/casino/6177.yaml
+++ /dev/null
@@ -1,6 +0,0 @@
-name: Crown Floor Future Dice Salon
-description: Power sockets, table anchor points and service passages are installed, but there is no playable
- game here yet. This hall is reserved for a future casino expansion.
-exits:
- east:
- room: 6176
diff --git a/data/rooms/casino/6178.yaml b/data/rooms/casino/6178.yaml
deleted file mode 100644
index 4929fec..0000000
--- a/data/rooms/casino/6178.yaml
+++ /dev/null
@@ -1,5 +0,0 @@
-name: Crown Floor Staff History Gallery
-description: Photographs remember workers whose names do not appear on the marble dedication.
-exits:
- west:
- room: 6176
diff --git a/data/rooms/casino/6179.yaml b/data/rooms/casino/6179.yaml
deleted file mode 100644
index 7a9e652..0000000
--- a/data/rooms/casino/6179.yaml
+++ /dev/null
@@ -1,12 +0,0 @@
-name: Crown Floor Grand Stair
-description: The crown floor is freely open for exploration. Completed corridors face unfurnished gaming
- rooms; signs explicitly say that no games operate on this floor yet.
-exits:
- south:
- room: 6176
- down:
- room: 6164
- west:
- room: 6180
- east:
- room: 6181
diff --git a/data/rooms/casino/6180.yaml b/data/rooms/casino/6180.yaml
deleted file mode 100644
index 2a83860..0000000
--- a/data/rooms/casino/6180.yaml
+++ /dev/null
@@ -1,6 +0,0 @@
-name: Crown Floor Future Private Table Hall
-description: Power sockets, table anchor points and service passages are installed, but there is no playable
- game here yet. This hall is reserved for a future casino expansion.
-exits:
- east:
- room: 6179
diff --git a/data/rooms/casino/6181.yaml b/data/rooms/casino/6181.yaml
deleted file mode 100644
index 78927b6..0000000
--- a/data/rooms/casino/6181.yaml
+++ /dev/null
@@ -1,5 +0,0 @@
-name: Crown Floor Viewing Terrace
-description: A long window gives a clear view of the roads that belong to everyone.
-exits:
- west:
- room: 6179
diff --git a/data/rooms/casino/6182.yaml b/data/rooms/casino/6182.yaml
index c298be1..9ee541a 100644
--- a/data/rooms/casino/6182.yaml
+++ b/data/rooms/casino/6182.yaml
@@ -1,14 +1,14 @@
name: Single Zero Roulette Salon
-description: A polished European wheel turns beside green felt marked with a single zero. The dealer invites visitors to play roulette.
+description: A polished European wheel turns beside green felt marked with a single zero. The double-zero wheel waits north, the racetrack south, and the craps pit east.
exits:
- south:
- room: 6102
+ west:
+ room: 6127
+ east:
+ room: 6185
north:
room: 6183
- east:
+ south:
room: 6184
- west:
- room: 6185
casino_tables:
- id: roulette_single
game: roulette
diff --git a/data/rooms/casino/6183.yaml b/data/rooms/casino/6183.yaml
index 68f12bc..ac98df9 100644
--- a/data/rooms/casino/6183.yaml
+++ b/data/rooms/casino/6183.yaml
@@ -1,5 +1,5 @@
name: Double Zero Roulette Salon
-description: An American roulette wheel gleams under brass lamps. Its green zero and double-zero pockets face a neatly marked betting layout.
+description: An American roulette wheel gleams under brass lamps; its green zero and double-zero pockets face a neatly marked betting layout.
exits:
south:
room: 6182
diff --git a/data/rooms/casino/6184.yaml b/data/rooms/casino/6184.yaml
index 76c9cf1..745a333 100644
--- a/data/rooms/casino/6184.yaml
+++ b/data/rooms/casino/6184.yaml
@@ -1,7 +1,7 @@
name: Racetrack Roulette Salon
-description: A single-zero wheel turns above a long oval racetrack painted on green felt. Brass plaques name the French call bets for players who prefer the wheel's own neighbours.
+description: A single-zero wheel turns above a long oval racetrack painted on green felt. Brass plaques name the French call bets.
exits:
- west:
+ north:
room: 6182
casino_tables:
- id: roulette_racetrack
diff --git a/data/rooms/casino/6185.yaml b/data/rooms/casino/6185.yaml
index 05e6bcf..11e7805 100644
--- a/data/rooms/casino/6185.yaml
+++ b/data/rooms/casino/6185.yaml
@@ -1,10 +1,14 @@
name: Bank Craps Pit
-description: A sweeping green felt table curves beneath warm brass lamps. Ivory dice rest beside a polished stick, while a bright puck marks the point above the numbered boxes. Inlaid plaques advertise Fire and All Tall Small bets. A northern gallery leads to six house-edition craps tables, each with its own posted rules.
+description: A sweeping green felt table curves beneath warm brass lamps. Ivory dice rest beside a polished stick, and a bright puck marks the point. House editions wait north and south; slots lie east.
exits:
+ west:
+ room: 6182
+ east:
+ room: 6118
north:
room: 6186
- east:
- room: 6182
+ south:
+ room: 6189
casino_tables:
- id: bank_craps
game: craps
diff --git a/data/rooms/casino/6186.yaml b/data/rooms/casino/6186.yaml
index ff21553..efece43 100644
--- a/data/rooms/casino/6186.yaml
+++ b/data/rooms/casino/6186.yaml
@@ -1,5 +1,5 @@
name: Simplified Craps Gallery
-description: A small felt table offers a brisk single-roll game. Its brass plaque reads 'Simplified — house edition; pass only.' The bank pit lies south, with more variations continuing north.
+description: A brisk single-roll game — pass only; the dice roll on their own three seconds after a bet.
exits:
south:
room: 6185
diff --git a/data/rooms/casino/6187.yaml b/data/rooms/casino/6187.yaml
index 84cf6fb..169ad81 100644
--- a/data/rooms/casino/6187.yaml
+++ b/data/rooms/casino/6187.yaml
@@ -1,5 +1,5 @@
name: High Point Craps Gallery
-description: Ascending brass numerals surround a compact felt. A plaque explains that this house edition requires the next total to exceed the point. Simplified craps lies south; crapless craps is north.
+description: Ascending brass numerals surround a compact felt; the next total must exceed the point.
exits:
south:
room: 6186
diff --git a/data/rooms/casino/6188.yaml b/data/rooms/casino/6188.yaml
index bb248f8..92caf26 100644
--- a/data/rooms/casino/6188.yaml
+++ b/data/rooms/casino/6188.yaml
@@ -1,10 +1,8 @@
name: Crapless Craps Gallery
-description: Extra boxes for two, three, eleven and twelve stretch across the felt. A warning makes clear that these totals become points rather than instant losses or wins. High Point lies south; New York craps is north.
+description: Every total can be the point here — only the seven wins on the come-out.
exits:
south:
room: 6187
- north:
- room: 6189
casino_tables:
- id: crapless_craps
game: craps
diff --git a/data/rooms/casino/6189.yaml b/data/rooms/casino/6189.yaml
index 89c78d6..b42eaba 100644
--- a/data/rooms/casino/6189.yaml
+++ b/data/rooms/casino/6189.yaml
@@ -1,9 +1,9 @@
name: New York Craps Gallery
-description: A restrained layout displays pass, don't pass, buy and lay wagers. The house-edition plaque promises win-only commissions and no proposition bets. Crapless lies south; Die Rich is north.
+description: Banker pass, don’t pass, buy and lay bets only, with a 5% win-only commission.
exits:
- south:
- room: 6188
north:
+ room: 6185
+ south:
room: 6190
casino_tables:
- id: new_york_craps
diff --git a/data/rooms/casino/6190.yaml b/data/rooms/casino/6190.yaml
index de23373..3577103 100644
--- a/data/rooms/casino/6190.yaml
+++ b/data/rooms/casino/6190.yaml
@@ -1,9 +1,9 @@
name: Die Rich Craps Gallery
-description: A single ivory die rests in a shallow tray. The house-edition plaque reads 'Six wins coming out; one loses; repeat any other point before one.' New York lies south; the card-driven Diceless table is north.
+description: A single die in play — come-out 6 wins, 1 loses, and 2-5 become the point.
exits:
- south:
- room: 6189
north:
+ room: 6189
+ south:
room: 6191
casino_tables:
- id: die_rich_craps
diff --git a/data/rooms/casino/6191.yaml b/data/rooms/casino/6191.yaml
index 81ebcc0..05313fc 100644
--- a/data/rooms/casino/6191.yaml
+++ b/data/rooms/casino/6191.yaml
@@ -1,7 +1,7 @@
name: Diceless Craps Salon
-description: Two six-card packs labelled A and B replace the dice on a full bank-craps layout. The dealer replaces and reshuffles both packs after every draw. The variations gallery returns south through Die Rich.
+description: Two packs of cards numbered 1-6 replace the dice; every draw is reshuffled.
exits:
- south:
+ north:
room: 6190
casino_tables:
- id: diceless_craps
diff --git a/internal/casino/baccarat.go b/internal/casino/baccarat.go
index 0890d59..50ac111 100644
--- a/internal/casino/baccarat.go
+++ b/internal/casino/baccarat.go
@@ -72,7 +72,11 @@ func (t *baccaratTable) join(name string) []Event {
return []Event{t.private(name, "That baccarat table is full.")}
}
events := []Event{t.addSeatLocked(name, &baccaratPlayer{Participant: Participant{Name: name, Connected: true}})}
- events = append(events, t.private(name, t.Config.BaccaratRulesText()), t.menuEvent(name))
+ // The rules board is a rendered felt (presentation layer); the engine
+ // only ships the configuration.
+ rules := t.private(name, "")
+ rules.Snapshot = &BaccaratRulesView{Config: t.Config}
+ events = append(events, rules, t.menuEvent(name))
return events
}
diff --git a/internal/casino/baccarat_variants_test.go b/internal/casino/baccarat_variants_test.go
index 4f88227..094412e 100644
--- a/internal/casino/baccarat_variants_test.go
+++ b/internal/casino/baccarat_variants_test.go
@@ -89,8 +89,17 @@ func TestBaccaratEZLiveSettlementAndOptionalMenus(t *testing.T) {
cfg := TableConfig{ID: "ez", Game: GameBaccarat, Baccarat: BaccaratConfig{Variant: BaccaratEZ, Dragon7: true}}
table := newBaccaratTestTable(cfg)
events := table.join("alice")
- if !hasMessage(events, "Variant: EZ Baccarat") || !hasMessage(events, "dragon_7") {
- t.Fatal("join missing rules/optional wager")
+ rulesView := false
+ for _, e := range events {
+ if view, ok := e.Snapshot.(*BaccaratRulesView); ok {
+ rulesView = true
+ if !view.Config.Baccarat.Dragon7 {
+ t.Fatal("rules felt missing the enabled Dragon 7 wager")
+ }
+ }
+ }
+ if !rulesView {
+ t.Fatal("join missing rules felt")
}
table.join("bob")
forceShoe(table, baccaratCards("A A A A 2 5")...)
diff --git a/internal/casino/blackjack.go b/internal/casino/blackjack.go
index 53bee6c..bd5b86b 100644
--- a/internal/casino/blackjack.go
+++ b/internal/casino/blackjack.go
@@ -139,7 +139,11 @@ func (t *blackjackTable) join(name string) []Event {
return []Event{t.private(name, "That blackjack table is full.")}
}
events := []Event{t.addSeatLocked(name, &blackjackPlayer{Participant: Participant{Name: name, Connected: true}})}
- events = append(events, t.private(name, t.Config.BlackjackRulesText()), t.menuEvent(name))
+ // The rules board is a rendered felt (presentation layer); the engine
+ // only ships the configuration.
+ rules := t.private(name, "")
+ rules.Snapshot = &BlackjackRulesView{Config: t.Config}
+ events = append(events, rules, t.menuEvent(name))
return events
}
diff --git a/internal/casino/blackjack_rules_test.go b/internal/casino/blackjack_rules_test.go
index 62f4d0a..b0c9459 100644
--- a/internal/casino/blackjack_rules_test.go
+++ b/internal/casino/blackjack_rules_test.go
@@ -27,7 +27,7 @@ func TestBlackjackRuleCardDefaultsAndOverrides(t *testing.T) {
events := table.join("alice")
found := false
for _, e := range events {
- if e.Message == text {
+ if view, ok := e.Snapshot.(*BlackjackRulesView); ok && view.Config.Blackjack == table.Config.Blackjack {
found = true
if e.Public {
t.Error("rules broadcast to bystanders")
@@ -35,7 +35,7 @@ func TestBlackjackRuleCardDefaultsAndOverrides(t *testing.T) {
}
}
if !found {
- t.Fatal("joining did not display effective rules")
+ t.Fatal("joining did not ship the rules view")
}
}
}
diff --git a/internal/casino/types.go b/internal/casino/types.go
index 62a8f34..ecded85 100644
--- a/internal/casino/types.go
+++ b/internal/casino/types.go
@@ -323,6 +323,14 @@ type BlackjackSnapshot struct {
func (*BlackjackSnapshot) snapshot() {}
+// BlackjackRulesView ships a table's configuration so the presentation layer
+// can draw the house-rule felt instead of a raw rules dump.
+type BlackjackRulesView struct {
+ Config TableConfig
+}
+
+func (*BlackjackRulesView) snapshot() {}
+
// BaccaratBetView lists one participant's wager on a baccarat outcome.
type BaccaratBetView struct {
Name string
@@ -340,6 +348,14 @@ type BaccaratSnapshot struct {
func (*BaccaratSnapshot) snapshot() {}
+// BaccaratRulesView ships a table's configuration so the presentation layer
+// can draw the house-rule felt instead of a raw rules dump.
+type BaccaratRulesView struct {
+ Config TableConfig
+}
+
+func (*BaccaratRulesView) snapshot() {}
+
type Participant struct {
Name string
Connected bool
diff --git a/internal/game/baccarat_rules_test.go b/internal/game/baccarat_rules_test.go
index 14328c1..63e2dc2 100644
--- a/internal/game/baccarat_rules_test.go
+++ b/internal/game/baccarat_rules_test.go
@@ -28,9 +28,10 @@ func TestLookBaccaratFixtureWithoutScenery(t *testing.T) {
for _, target := range []string{"baccarat", "baccarat table", "table", "test", "test table"} {
conn.data = nil
g.doLookTarget(sess, target)
- for _, want := range []string{"Variant: EZ Baccarat", "Number of decks: 6", "Dragon 7 (dragon_7): Enabled; 40:1", "Panda 8 (panda_8): Not offered"} {
- if !strings.Contains(string(conn.data), want) {
- t.Fatalf("look %s missing %q: %s", target, want, conn.data)
+ text := plainRoulette(string(conn.data))
+ for _, want := range []string{"EZ BACCARAT — NO COMMISSION", "DECKS", "6", "DRAGON 7", "40:1", "PANDA 8", "NOT OFFERED"} {
+ if !strings.Contains(text, want) {
+ t.Fatalf("look %s missing %q: %s", target, want, text)
}
}
}
@@ -67,12 +68,16 @@ func TestCasinoFloorBaccaratProfiles(t *testing.T) {
events := g.Casino.Join(tc.room, cfg, "alice")
found := false
for _, e := range events {
- if e.Message == cfg.BaccaratRulesText() {
+ if view, ok := e.Snapshot.(*casino.BaccaratRulesView); ok {
found = true
+ art := renderBaccaratRulesFelt(view.Config, "none", true)
+ if !strings.Contains(art, "DRAWING — AUTOMATIC PUNTO BANCO") {
+ t.Fatalf("room %d: rules felt incomplete", tc.room)
+ }
}
}
if !found {
- t.Fatalf("room %d: join missing rule card", tc.room)
+ t.Fatalf("room %d: join missing rules felt", tc.room)
}
if !strings.Contains(cfg.BaccaratRulesText(), tc.spot+"): Enabled") {
t.Fatalf("room %d: missing optional bet", tc.room)
diff --git a/internal/game/blackjack_rules_test.go b/internal/game/blackjack_rules_test.go
index 33598c3..47d057c 100644
--- a/internal/game/blackjack_rules_test.go
+++ b/internal/game/blackjack_rules_test.go
@@ -29,9 +29,10 @@ func TestLookBlackjackFixtureWithoutScenery(t *testing.T) {
if !g.lookBlackjackTable(sess, target) {
t.Fatalf("did not match %q", target)
}
- for _, want := range []string{"Number of decks: 1", "Blackjack pays 6:5", "Double after split: Yes"} {
- if !strings.Contains(string(conn.data), want) {
- t.Fatalf("look %s missing %q: %s", target, want, conn.data)
+ text := plainRoulette(string(conn.data))
+ for _, want := range []string{"DECKS", "1", "BLACKJACK PAYS 6 TO 5", "AFTER SPLIT" + strings.Repeat(" ", 21) + "YES"} {
+ if !strings.Contains(text, want) {
+ t.Fatalf("look %s missing %q: %s", target, want, text)
}
}
}
diff --git a/internal/game/casino.go b/internal/game/casino.go
index 744eac6..f29efd6 100644
--- a/internal/game/casino.go
+++ b/internal/game/casino.go
@@ -719,6 +719,10 @@ func (g *Game) renderCasinoSnapshot(sess *net.Session, snapshot casino.Snapshot)
return renderSlotSnapshot(s, mode, unicode)
case *casino.BlackjackSnapshot:
return renderBlackjackSnapshotFor(s, mode, unicode, size, sess.Player.OptionString("card_style"), g.CardArt, sess.Player.Name)
+ case *casino.BlackjackRulesView:
+ return renderBlackjackRulesFelt(s.Config, mode, unicode)
+ case *casino.BaccaratRulesView:
+ return renderBaccaratRulesFelt(s.Config, mode, unicode)
case *casino.BaccaratSnapshot:
return renderBaccaratSnapshot(s, mode, unicode, size, sess.Player.OptionString("card_style"), g.CardArt)
}
diff --git a/internal/game/casino_ux_test.go b/internal/game/casino_ux_test.go
index 0d5ad63..aa98c87 100644
--- a/internal/game/casino_ux_test.go
+++ b/internal/game/casino_ux_test.go
@@ -96,7 +96,7 @@ func TestTableStatusShowsStateAndRules(t *testing.T) {
}
conn.data = nil
g.executeTableStatus(sess, []string{"rules"}, "table rules")
- if !strings.Contains(stripANSI(string(conn.data)), "payout") {
+ if !strings.Contains(stripANSI(string(conn.data)), "BLACKJACK PAYS") {
t.Fatalf("table rules missing: %q", stripANSI(string(conn.data)))
}
}
diff --git a/internal/game/craps_variants_test.go b/internal/game/craps_variants_test.go
index 76d1424..42f3529 100644
--- a/internal/game/craps_variants_test.go
+++ b/internal/game/craps_variants_test.go
@@ -64,40 +64,57 @@ func TestCrapsVariantRendering(t *testing.T) {
func TestShippedCrapsGallery(t *testing.T) {
w := world.New(filepath.Join("..", "..", "data"))
- variants := []string{"bank", "simplified", "high_point", "crapless", "new_york", "die_rich", "diceless"}
- for i, v := range variants {
- id := 6185 + i
- room, err := w.LoadRoom(id)
- if err != nil {
- t.Fatal(err)
- }
- if len(room.CasinoTables) != 1 {
- t.Fatal(id, "missing table")
+ northVariants := []string{"simplified", "high_point", "crapless"}
+ southVariants := []string{"new_york", "die_rich", "diceless"}
+ for i, v := range northVariants {
+ id := 6186 + i
+ room := loadCrapsRoom(t, w, id, v)
+ if i > 0 && room.Exits["south"].Room != id-1 {
+ t.Fatal(id, "missing return exit")
}
- cfg := room.CasinoTables[0]
- if cfg.Game != casino.GameCraps || cfg.Craps.Variant != v {
- t.Fatal(id, cfg)
+ if i < len(northVariants)-1 && room.Exits["north"].Room != id+1 {
+ t.Fatal(id, "unreachable next table")
}
- if err := cfg.Validate(); err != nil {
- t.Fatal(id, err)
+ }
+ for i, v := range southVariants {
+ id := 6189 + i
+ room := loadCrapsRoom(t, w, id, v)
+ if i == 0 && room.Exits["north"].Room != 6185 {
+ t.Fatal(id, "south gallery unreachable from the bank pit")
}
- if i > 0 && room.Exits["south"].Room != id-1 {
+ if i > 0 && room.Exits["north"].Room != id-1 {
t.Fatal(id, "missing return exit")
}
- if i < len(variants)-1 && room.Exits["north"].Room != id+1 {
+ if i < len(southVariants)-1 && room.Exits["south"].Room != id+1 {
t.Fatal(id, "unreachable next table")
}
- m := casino.NewManager(1)
- m.Join(id, cfg, "a")
- if m.Table(id, cfg.ID) == nil {
- t.Fatal(id, "table registration")
- }
}
- room, err := w.LoadRoom(6182)
+ room := loadCrapsRoom(t, w, 6185, "bank")
+ if room.Exits["west"].Room != 6182 || room.Exits["east"].Room != 6118 {
+ t.Fatal("pit exits missing", room.Exits)
+ }
+}
+
+func loadCrapsRoom(t *testing.T, w *world.World, id int, variant string) *world.Room {
+ t.Helper()
+ room, err := w.LoadRoom(id)
if err != nil {
t.Fatal(err)
}
- if room.Exits["west"].Room != 6185 {
- t.Fatal("gallery unreachable from roulette")
+ if len(room.CasinoTables) != 1 {
+ t.Fatal(id, "missing table")
+ }
+ cfg := room.CasinoTables[0]
+ if cfg.Game != casino.GameCraps || cfg.Craps.Variant != variant {
+ t.Fatal(id, cfg)
+ }
+ if err := cfg.Validate(); err != nil {
+ t.Fatal(id, err)
+ }
+ m := casino.NewManager(1)
+ m.Join(id, cfg, "a")
+ if m.Table(id, cfg.ID) == nil {
+ t.Fatal(id, "table registration")
}
+ return room
}
diff --git a/internal/game/render_baccarat_felt.go b/internal/game/render_baccarat_felt.go
new file mode 100644
index 0000000..84f91e3
--- /dev/null
+++ b/internal/game/render_baccarat_felt.go
@@ -0,0 +1,111 @@
+package game
+
+import (
+ "fmt"
+ "strings"
+
+ "thehouseoficarus/internal/casino"
+)
+
+// A baccarat felt: the three main wagers as betting boxes, the optional side
+// bets and house settings in labelled boxes, and the punto banco drawing
+// table across the bottom. Reuses the shared felt canvas and palette.
+func renderBaccaratRulesFelt(cfg casino.TableConfig, mode string, unicode bool) string {
+ b := cfg.Baccarat.Normalize()
+ c := newCrapsCanvas(22)
+ // Title block: variant, scoring, naturals.
+ c.box(0, 0, 76, 4)
+ c.center(1, 75, 1, baccaratVariantTitle(b), crapsGold)
+ c.center(1, 75, 2, "ACE 1 | 2-9 FACE | 10/J/Q/K 0 — HIGHEST LAST DIGIT WINS", crapsInk)
+ c.center(1, 75, 3, "AN INITIAL 8 OR 9 ON EITHER HAND STOPS ALL DRAWING", crapsMuted)
+ // Three betting boxes: PLAYER | TIE | BANKER.
+ spots := [3][2]int{{0, 25}, {25, 50}, {50, 76}}
+ for _, x := range spots {
+ c.box(x[0], 4, x[1], 8)
+ }
+ c.center(1, 24, 5, "PLAYER", crapsGold)
+ c.center(26, 49, 5, "TIE", crapsGold)
+ c.center(51, 75, 5, "BANKER", crapsGold)
+ c.center(1, 24, 6, "PAYS 1:1", crapsInk)
+ c.center(26, 49, 6, "PAYS "+ratioText(b.TiePayout), crapsInk)
+ c.center(51, 75, 6, bankerPayoutText(b), crapsInk)
+ c.center(1, 24, 7, "", crapsMuted)
+ c.center(26, 49, 7, "PLAYER & BANKER PUSH", crapsMuted)
+ c.center(51, 75, 7, bankerNoteText(b), crapsMuted)
+ // Side bets and house settings.
+ boxY := [2][2]int{{8, 15}, {8, 15}}
+ xs := [2][2]int{{0, 38}, {38, 76}}
+ for i := range boxY {
+ c.box(xs[i][0], boxY[i][0], xs[i][1], boxY[i][1])
+ }
+ c.center(1, 37, 9, "SIDE BETS", crapsGold)
+ c.center(39, 75, 9, "HOUSE", crapsGold)
+ side := [][2]string{
+ {"PLAYER PAIR", optionalText(b.PairBets, "11:1")},
+ {"BANKER PAIR", optionalText(b.PairBets, "11:1")},
+ {"DRAGON 7", optionalText(b.Dragon7, "40:1")},
+ {"PANDA 8", optionalText(b.Panda8, "25:1")},
+ {"SUPER 6", optionalText(b.Super6Bet, "12:1")},
+ }
+ house := [][2]string{
+ {"DECKS", fmt.Sprintf("%d", b.Decks)},
+ {"SHUFFLE AT", fmt.Sprintf("%g%% REMAINING", b.ShuffleAt*100)},
+ {"BET LIMITS", betLimitsText(cfg.MinBet, cfg.MaxBet)},
+ {"SEATS", fmt.Sprintf("%d", cfg.MaxPlayers)},
+ {"BETTING WINDOW", fmt.Sprintf("%d TICKS", cfg.BettingTicks)},
+ }
+ for j, row := range side {
+ c.bjRule(0, 38, 10+j, row[0], row[1])
+ }
+ for j, row := range house {
+ c.bjRule(38, 76, 10+j, row[0], row[1])
+ }
+ // Drawing table across the bottom.
+ c.box(0, 15, 76, 21)
+ c.center(1, 75, 16, "DRAWING — AUTOMATIC PUNTO BANCO", crapsGold)
+ draw := []string{
+ "PLAYER: DRAWS 0-5; STANDS 6-7. BANKER VS A STANDING PLAYER: DRAWS 0-5.",
+ "BANKER VS A DRAWING PLAYER: 0-2 ALWAYS DRAWS; 3 DRAWS UNLESS VS 8;",
+ "4 DRAWS VS 2-7; 5 DRAWS VS 4-7; 6 DRAWS VS 6-7; 7 STANDS.",
+ "ONE WAGER PER PLAYER PER ROUND; SIDE BETS SETTLE INDEPENDENTLY.",
+ }
+ for i, line := range draw {
+ c.text(3, 17+i, line, crapsInk)
+ }
+ return c.render(mode, unicode)
+}
+
+func baccaratVariantTitle(b casino.BaccaratConfig) string {
+ switch b.Variant {
+ case casino.BaccaratSuper6:
+ return "SUPER 6 BACCARAT — NO COMMISSION"
+ case casino.BaccaratEZ:
+ return "EZ BACCARAT — NO COMMISSION"
+ default:
+ return "PUNTO BANCO — COMMISSION"
+ }
+}
+func bankerPayoutText(b casino.BaccaratConfig) string {
+ switch b.Variant {
+ case casino.BaccaratSuper6, casino.BaccaratEZ, casino.BaccaratCommission:
+ return "PAYS 1:1"
+ default:
+ return "PAYS " + ratioText(b.BankerPayout)
+ }
+}
+func bankerNoteText(b casino.BaccaratConfig) string {
+ switch b.Variant {
+ case casino.BaccaratSuper6:
+ return "WINNING 6 PAYS 1:2"
+ case casino.BaccaratEZ:
+ return "3-CARD 7 PUSHES"
+ default:
+ return fmt.Sprintf("%s%% COMMISSION", strings.TrimRight(strings.TrimRight(fmt.Sprintf("%.6g", (1-b.BankerPayout)*100), "0"), "."))
+ }
+}
+func optionalText(enabled bool, payout string) string {
+ if enabled {
+ return payout
+ }
+ return "NOT OFFERED"
+}
diff --git a/internal/game/render_baccarat_felt_test.go b/internal/game/render_baccarat_felt_test.go
new file mode 100644
index 0000000..dabaa36
--- /dev/null
+++ b/internal/game/render_baccarat_felt_test.go
@@ -0,0 +1,92 @@
+package game
+
+import (
+ "strings"
+ "testing"
+
+ "thehouseoficarus/internal/casino"
+ "thehouseoficarus/internal/color"
+)
+
+func TestBaccaratRulesFeltGeometryAndPhrases(t *testing.T) {
+ cfg := casino.TableConfig{ID: "harbor", Game: casino.GameBaccarat, MinBet: 1, MaxBet: 100, MaxPlayers: 8, BettingTicks: 35,
+ Baccarat: casino.BaccaratConfig{Variant: casino.BaccaratCommission, PairBets: true, Decks: 8, ShuffleAt: 0.25, TiePayout: 8, BankerPayout: 0.95}}
+ for _, unicode := range []bool{false, true} {
+ art := renderBaccaratRulesFelt(cfg, "none", unicode)
+ rows := strings.Split(strings.TrimSuffix(art, "\n"), "\n")
+ if len(rows) != 22 {
+ t.Fatalf("felt height %d", len(rows))
+ }
+ for _, row := range rows {
+ if color.VisibleLen(row) != 77 {
+ t.Fatalf("misaligned (%d): %s", color.VisibleLen(row), row)
+ }
+ }
+ for _, want := range []string{
+ "PUNTO BANCO — COMMISSION", "HIGHEST LAST DIGIT WINS", "STOPS ALL DRAWING",
+ "PLAYER", "TIE", "BANKER", "PAYS 1:1", "PAYS 8 TO 1", "5% COMMISSION",
+ "PLAYER & BANKER PUSH",
+ "SIDE BETS", "HOUSE", "PLAYER PAIR", "BANKER PAIR", "11:1",
+ "DRAGON 7", "PANDA 8", "SUPER 6", "NOT OFFERED",
+ "DECKS", "SHUFFLE AT", "BET LIMITS", "1-100 CREDITS", "35 TICKS",
+ "DRAWING — AUTOMATIC PUNTO BANCO", "DRAWS 0-5", "6 DRAWS VS 6-7",
+ "ONE WAGER PER PLAYER PER ROUND",
+ } {
+ if !strings.Contains(art, want) {
+ t.Fatalf("missing %q in unicode=%v felt", want, unicode)
+ }
+ }
+ // The raw dump's phrases must not leak onto the felt.
+ for _, stale := range []string{"Number of decks", "Drawing: Automatic", "Fractional credits:"} {
+ if strings.Contains(art, stale) {
+ t.Fatalf("stale prose %q on felt", stale)
+ }
+ }
+ }
+}
+
+func TestBaccaratRulesFeltFollowsConfig(t *testing.T) {
+ cfg := casino.TableConfig{ID: "crown", Game: casino.GameBaccarat, MinBet: 100, MaxBet: 10000, BettingTicks: 35,
+ Baccarat: casino.BaccaratConfig{Variant: casino.BaccaratEZ, Dragon7: true, Panda8: true, Decks: 6, TiePayout: 9, BankerPayout: 1, ShuffleAt: 0.5}}
+ art := renderBaccaratRulesFelt(cfg, "none", true)
+ for _, want := range []string{"EZ BACCARAT — NO COMMISSION", "PAYS 9 TO 1", "3-CARD 7 PUSHES",
+ "DRAGON 7" + strings.Repeat(" ", 23) + "40:1", "PANDA 8" + strings.Repeat(" ", 24) + "25:1",
+ "100-10000 CREDITS", "50% REMAINING"} {
+ if !strings.Contains(art, want) {
+ t.Fatalf("missing %q: %s", want, art)
+ }
+ }
+ cfg2 := casino.TableConfig{ID: "charter", Game: casino.GameBaccarat,
+ Baccarat: casino.BaccaratConfig{Variant: casino.BaccaratSuper6, Super6Bet: true, TiePayout: 9}}
+ art2 := renderBaccaratRulesFelt(cfg2, "none", true)
+ for _, want := range []string{"SUPER 6 BACCARAT — NO COMMISSION", "WINNING 6 PAYS 1:2", "SUPER 6" + strings.Repeat(" ", 24) + "12:1", "PAYS 9 TO 1"} {
+ if !strings.Contains(art2, want) {
+ t.Fatalf("missing %q: %s", want, art2)
+ }
+ }
+}
+
+func TestBaccaratJoinPrintsFeltNotProse(t *testing.T) {
+ g := &Game{Casino: casino.NewManager(1)}
+ g.Casino.Join(1, casino.TableConfig{ID: "bac", Game: casino.GameBaccarat}, "alice")
+ events := g.Casino.Join(1, casino.TableConfig{ID: "bac", Game: casino.GameBaccarat}, "bob")
+ found := false
+ for _, e := range events {
+ if view, ok := e.Snapshot.(*casino.BaccaratRulesView); ok {
+ found = true
+ art := renderBaccaratRulesFelt(view.Config, "none", true)
+ if !strings.Contains(art, "PUNTO BANCO — COMMISSION") || !strings.Contains(art, "DECKS") {
+ t.Fatal("rules felt missing phrases", art)
+ }
+ if e.Message != "" {
+ t.Fatal("rules event carries prose", e.Message)
+ }
+ }
+ if strings.Contains(e.Message+e.PrivateMessage, "Number of decks") {
+ t.Fatal("raw rules prose leaked onto join", e)
+ }
+ }
+ if !found {
+ t.Fatal("join did not attach the rules felt")
+ }
+}
diff --git a/internal/game/render_blackjack_felt.go b/internal/game/render_blackjack_felt.go
new file mode 100644
index 0000000..41a5382
--- /dev/null
+++ b/internal/game/render_blackjack_felt.go
@@ -0,0 +1,137 @@
+package game
+
+import (
+ "fmt"
+ "strconv"
+
+ "thehouseoficarus/internal/casino"
+)
+
+// A blackjack felt: the classic centre-display phrases a real table prints
+// (blackjack payout, dealer draw rule, insurance), then the remaining
+// configurable rules arranged in labelled boxes. It reuses the craps felt
+// canvas and palette: fixed 77-column art, dark green baize, gold headings.
+// Fixed engine behaviour (peek timing, rounding) is left off the felt.
+func renderBlackjackRulesFelt(cfg casino.TableConfig, mode string, unicode bool) string {
+ b := cfg.Blackjack.Normalize()
+ c := newCrapsCanvas(21)
+ // Centre display: the phrases printed on every real blackjack table.
+ c.box(0, 0, 76, 5)
+ c.center(1, 75, 2, "BLACKJACK PAYS "+ratioText(b.BlackjackPayout), crapsGold)
+ dealer := "DEALER MUST STAND ON 17 AND DRAW TO 16"
+ if b.DealerHitsSoft17() {
+ dealer = "DEALER HITS SOFT 17"
+ }
+ c.center(1, 75, 3, dealer, crapsInk)
+ c.center(1, 75, 4, "INSURANCE PAYS "+ratioText(b.InsurancePayout), crapsGold)
+ // Four labelled rule boxes share edges with the centre display above.
+ boxY := [4][2]int{{5, 12}, {5, 12}, {12, 20}, {12, 20}}
+ xs := [4][2]int{{0, 38}, {38, 76}, {0, 38}, {38, 76}}
+ for i := range boxY {
+ c.box(xs[i][0], boxY[i][0], xs[i][1], boxY[i][1])
+ }
+ titles := [4]string{"THE SHOE", "PAYS", "DOUBLE & SPLIT", "HOUSE"}
+ for i, title := range titles {
+ c.center(xs[i][0]+1, xs[i][1]-1, boxY[i][0]+1, title, crapsGold)
+ }
+ rows := [4][][2]string{
+ {
+ {"DECKS", fmt.Sprintf("%d", b.Decks)},
+ {"SHUFFLE AT", fmt.Sprintf("%g%% REMAINING", b.ShuffleAt*100)},
+ {"SHOE LOW", "RESHUFFLE MID-ROUND"},
+ {"HOLE CARD", "YES (PEEK)"},
+ {"", ""},
+ },
+ {
+ {"ORDINARY WIN", "1:1"},
+ {"TIE", "PUSH"},
+ {"BJ VS BJ", "PUSH"},
+ {"SPLIT-HAND 21", "1:1"},
+ {"SPLIT+DOUBLE 21", splitDouble21Text(b)},
+ {"", ""},
+ },
+ {
+ {"DOUBLE", "ANY TWO, ONE CARD"},
+ {"AFTER SPLIT", yesNoText(b.AllowsDoubleAfterSplit())},
+ {"MAX HANDS", fmt.Sprintf("%d (RESPLIT %s)", b.MaxSplitHands, yesNoText(b.MaxSplitHands > 2))},
+ {"RESPLIT ACES", yesNoText(b.AllowsResplitAces())},
+ {"PAIRS", pairsText(b)},
+ {"SPLIT ACES", "ONE CARD, NO HITS"},
+ },
+ {
+ {"SURRENDER", surrenderText(b)},
+ {"INSURANCE COST", "HALF THE BET"},
+ {"BET LIMITS", betLimitsText(cfg.MinBet, cfg.MaxBet)},
+ {"SEATS", fmt.Sprintf("%d", cfg.MaxPlayers)},
+ {"BETTING WINDOW", fmt.Sprintf("%d TICKS", cfg.BettingTicks)},
+ {"ACTION WINDOW", fmt.Sprintf("%d TICKS", b.ActionTicks)},
+ },
+ }
+ for i, box := range rows {
+ for j, row := range box {
+ if row[0] == "" && row[1] == "" {
+ continue
+ }
+ c.bjRule(xs[i][0], xs[i][1], boxY[i][0]+2+j, row[0], row[1])
+ }
+ }
+ return c.render(mode, unicode)
+}
+
+// bjRule paints one "LABEL ..... VALUE" row inside a box: the label muted at
+// the left margin, the value right-aligned to the box's inner right edge.
+func (c *crapsCanvas) bjRule(x1, x2, y int, label, value string) {
+ c.text(x1+2, y, label, crapsMuted)
+ vx := x2 - 1 - len([]rune(value))
+ if vx < x1+2+len([]rune(label))+1 {
+ vx = x1 + 2 + len([]rune(label)) + 1
+ }
+ c.text(vx, y, value, crapsInk)
+}
+
+// betLimitsText renders the wager range compactly so it fits inside a box.
+func betLimitsText(minBet, maxBet int) string {
+ if maxBet == int(^uint(0)>>1) {
+ return fmt.Sprintf("%d+ CREDITS", minBet)
+ }
+ return fmt.Sprintf("%d-%d CREDITS", minBet, maxBet)
+}
+
+func ratioText(p float64) string {
+ switch p {
+ case 1.5:
+ return "3 TO 2"
+ case 1.2:
+ return "6 TO 5"
+ case 2:
+ return "2 TO 1"
+ case 1:
+ return "1 TO 1"
+ default:
+ return strconv.FormatFloat(p, 'g', -1, 64) + " TO 1"
+ }
+}
+func splitDouble21Text(b casino.BlackjackConfig) string {
+ if b.CountsDoubleSplitBlackjack() {
+ return "BLACKJACK " + ratioText(b.BlackjackPayout)
+ }
+ return "1:1"
+}
+func yesNoText(v bool) string {
+ if v {
+ return "YES"
+ }
+ return "NO"
+}
+func pairsText(b casino.BlackjackConfig) string {
+ if b.AllowsUnlikeTenSplit() {
+ return "RANKS OR TENS"
+ }
+ return "IDENTICAL RANKS"
+}
+func surrenderText(b casino.BlackjackConfig) string {
+ if b.AllowsSurrender() {
+ return "LATE, TWO CARDS"
+ }
+ return "NOT OFFERED"
+}
diff --git a/internal/game/render_blackjack_felt_test.go b/internal/game/render_blackjack_felt_test.go
new file mode 100644
index 0000000..a67251a
--- /dev/null
+++ b/internal/game/render_blackjack_felt_test.go
@@ -0,0 +1,84 @@
+package game
+
+import (
+ "strings"
+ "testing"
+
+ "thehouseoficarus/internal/casino"
+ "thehouseoficarus/internal/color"
+)
+
+func TestBlackjackRulesFeltGeometryAndPhrases(t *testing.T) {
+ cfg := casino.TableConfig{ID: "midnight", Game: casino.GameBlackjack, MinBet: 100, MaxBet: 10000, MaxPlayers: 6, BettingTicks: 18,
+ Blackjack: casino.BlackjackConfig{ActionTicks: 15, DoubleSplitBlackjack: &[]bool{false}[0]}}
+ for _, unicode := range []bool{false, true} {
+ art := renderBlackjackRulesFelt(cfg, "none", unicode)
+ rows := strings.Split(strings.TrimSuffix(art, "\n"), "\n")
+ if len(rows) != 21 {
+ t.Fatalf("felt height %d", len(rows))
+ }
+ for _, row := range rows {
+ if color.VisibleLen(row) != 77 {
+ t.Fatalf("misaligned (%d): %s", color.VisibleLen(row), row)
+ }
+ }
+ for _, want := range []string{
+ "BLACKJACK PAYS 3 TO 2", "DEALER MUST STAND ON 17 AND DRAW TO 16", "INSURANCE PAYS 2 TO 1",
+ "THE SHOE", "PAYS", "DOUBLE & SPLIT", "HOUSE",
+ "DECKS", "SHUFFLE AT", "25% REMAINING", "RESHUFFLE MID-ROUND", "HOLE CARD",
+ "ORDINARY WIN", "SPLIT+DOUBLE 21", "DOUBLE", "ANY TWO, ONE CARD", "MAX HANDS", "ONE CARD, NO HITS",
+ "SURRENDER", "NOT OFFERED", "100-10000 CREDITS", "18 TICKS", "15 TICKS",
+ } {
+ if !strings.Contains(art, want) {
+ t.Fatalf("missing %q in unicode=%v felt", want, unicode)
+ }
+ }
+ // The raw dump's phrases and fixed engine rules must not leak onto the felt.
+ for _, stale := range []string{"Number of decks", "Dealer hole card / peek:", "FRACTIONS", "BJ CHECK", "HOUSE BLACKJACK"} {
+ if strings.Contains(art, stale) {
+ t.Fatalf("stale prose %q on felt", stale)
+ }
+ }
+ }
+}
+
+func TestBlackjackRulesFeltFollowsConfig(t *testing.T) {
+ yes, no := true, false
+ cfg := casino.TableConfig{ID: "high", Game: casino.GameBlackjack, MinBet: 5, MaxBet: 500,
+ Blackjack: casino.BlackjackConfig{Decks: 1, BlackjackPayout: 1.2, DealerHitSoft17: &yes, Surrender: &yes,
+ ShuffleAt: 0.5, MaxSplitHands: 2, DoubleAfterSplit: &no, ResplitAces: &yes, SplitUnlikeTenValues: &no,
+ DoubleSplitBlackjack: &no, InsurancePayout: 2}}
+ art := renderBlackjackRulesFelt(cfg, "none", true)
+ for _, want := range []string{"BLACKJACK PAYS 6 TO 5", "DEALER HITS SOFT 17", "LATE, TWO CARDS", "50% REMAINING",
+ "AFTER SPLIT NO", "RESPLIT ACES YES", "PAIRS IDENTICAL RANKS",
+ "SPLIT+DOUBLE 21 1:1", "5-500 CREDITS"} {
+ if !strings.Contains(art, want) {
+ t.Fatalf("missing %q: %s", want, art)
+ }
+ }
+}
+
+func TestBlackjackJoinPrintsFeltNotProse(t *testing.T) {
+ g := &Game{Casino: casino.NewManager(1)}
+ g.Casino.Join(1, casino.TableConfig{ID: "bj", Game: casino.GameBlackjack, BettingTicks: 2}, "alice")
+ events := g.Casino.Join(1, casino.TableConfig{ID: "bj", Game: casino.GameBlackjack, BettingTicks: 2}, "bob")
+ found := false
+ for _, e := range events {
+ if view, ok := e.Snapshot.(*casino.BlackjackRulesView); ok {
+ found = true
+ art := renderBlackjackRulesFelt(view.Config, "none", true)
+ if !strings.Contains(art, "BLACKJACK PAYS 3 TO 2") || !strings.Contains(art, "DECKS") {
+ t.Fatal("rules felt missing phrases", art)
+ }
+ if e.Message != "" {
+ t.Fatal("rules event carries prose", e.Message)
+ }
+ }
+ if strings.Contains(e.Message+e.PrivateMessage, "Number of decks") {
+ t.Fatal("raw rules prose leaked onto join", e)
+ }
+ }
+ if !found {
+ t.Fatal("join did not attach the rules felt")
+ }
+}
diff --git a/internal/game/render_roulette.go b/internal/game/render_roulette.go
index 1e7f971..0e4b1a3 100644
--- a/internal/game/render_roulette.go
+++ b/internal/game/render_roulette.go
@@ -597,6 +597,13 @@ func renderRouletteResult(pockets [5]string, mode string, unicode bool, size int
}
func (g *Game) renderCasinoRules(sess *net.Session, cfg casino.TableConfig) string {
+ if cfg.Game == casino.GameBlackjack {
+ // The whole rule card is a rendered felt.
+ return g.renderCasinoSnapshot(sess, &casino.BlackjackRulesView{Config: cfg})
+ }
+ if cfg.Game == casino.GameBaccarat {
+ return g.renderCasinoSnapshot(sess, &casino.BaccaratRulesView{Config: cfg})
+ }
rules := g.colorize(sess, "casino_action", cfg.RulesText())
if cfg.Game == casino.GameCraps {
snap := &casino.CrapsSnapshot{Phase: "waiting"}
diff --git a/internal/game/vesta_world_test.go b/internal/game/vesta_world_test.go
index 50e799c..1a26bf6 100644
--- a/internal/game/vesta_world_test.go
+++ b/internal/game/vesta_world_test.go
@@ -269,12 +269,11 @@ func TestVestaEarlySkillsAndSeparateServices(t *testing.T) {
func TestVestaCasinoFloorsAndVariants(t *testing.T) {
_, rooms, zones := loadVesta(t)
- grid := world.BuildGrid(6102, func(id int) (*world.Room, bool) {
+ grid := world.BuildGrid(6101, func(id int) (*world.Room, bool) {
r, ok := rooms[id]
return r, ok
}, nil, nil, nil)
games := map[string]int{}
- floors := map[int]int{}
future := 0
soft17, surrender, singleDeck, huff, classic := false, false, false, false, false
for id, r := range rooms {
@@ -284,14 +283,17 @@ func TestVestaCasinoFloorsAndVariants(t *testing.T) {
}
continue
}
- floor := grid.Coord[id][2]
- floors[floor]++
+ // The vestibule stays at street level (below the gaming floor);
+ // every gaming room shares 6101's floor.
+ if id != 6100 && grid.Coord[id][2] != grid.Coord[6101][2] {
+ t.Errorf("casino room %d drifted off the gaming floor", id)
+ }
if strings.Contains(r.Name, "Future ") {
future++
}
count := len(r.CasinoTables) + len(r.CasinoMachines)
- if count > 1 || count > 0 && floor != 0 {
- t.Errorf("room %d: games must have individual ground-floor salons", id)
+ if count > 1 {
+ t.Errorf("room %d: games must have individual salons", id)
}
for _, table := range r.CasinoTables {
games[string(table.Game)]++
@@ -307,11 +309,57 @@ func TestVestaCasinoFloorsAndVariants(t *testing.T) {
classic = classic || machine.Variant == ""
}
}
- if games["blackjack"] < 5 || games["baccarat"] < 3 || games["slots"] < 5 || !soft17 || !surrender || !singleDeck || !huff || !classic {
+ if games["blackjack"] != 5 || games["baccarat"] != 3 || games["roulette"] != 3 || games["craps"] != 7 || games["slots"] != 5 || !soft17 || !surrender || !singleDeck || !huff || !classic {
t.Errorf("casino variant coverage lost: %v", games)
}
- if future < 15 || floors[0] < 30 || floors[1] < 15 || floors[2] < 15 || floors[3] < 15 {
- t.Errorf("casino needs a large ground floor and substantial expansion floors: floors=%v future=%d", floors, future)
+ if future != 0 {
+ t.Errorf("placeholder galleries remain: %d", future)
+ }
+ // One salon per game sits on the gaming street; each keeps its editions
+ // within a couple of steps north or south, so no variant is ever far away.
+ street := map[int]string{6101: "blackjack", 6127: "baccarat", 6182: "roulette", 6185: "craps", 6118: "slots"}
+ for id, game := range street {
+ r := rooms[id]
+ found := false
+ for _, table := range r.CasinoTables {
+ found = found || string(table.Game) == game
+ }
+ for _, machine := range r.CasinoMachines {
+ found = found || string(machine.Game) == game
+ }
+ if !found {
+ t.Errorf("street room %d lost its %s salon", id, game)
+ }
+ }
+ // Street links: the blackjack salon joins the vestibule by stairs and
+ // the street eastward; the middle salons connect west and east; the
+ // slots salon ends the street.
+ for id, want := range map[int][]world.ExitDir{
+ 6101: {world.Down, world.East},
+ 6127: {world.West, world.East},
+ 6182: {world.West, world.East},
+ 6185: {world.West, world.East},
+ 6118: {world.West},
+ } {
+ r := rooms[id]
+ for _, dir := range want {
+ exit, ok := r.Exits[dir]
+ if !ok || rooms[exit.Room] == nil {
+ t.Errorf("street room %d lost its %s link", id, dir)
+ }
+ }
+ }
+ if rooms[6100].Exits[world.Up].Room != 6101 || rooms[5111].Exits[world.North].Room != 6100 {
+ t.Fatal("vestibule disconnected")
+ }
+ foundBank := false
+ for _, ref := range rooms[6100].Objects {
+ if ref.ID == "bank_booth" {
+ foundBank = true
+ }
+ }
+ if !foundBank {
+ t.Fatal("casino lost its bank booth")
}
}