1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
|
package game
import (
"fmt"
"strings"
"thehouseoficarus/internal/casino"
"thehouseoficarus/internal/net"
)
// lookCasinoTable renders a table fixture's details for a look target: the
// wager limits, the live state and seated players, and this table's rule
// variants. The invariant roulette and craps graphics and the generic rule
// dumps are deliberately omitted; blackjack, baccarat and Pai Gow keep their
// felt because it carries per-table rules and pay tables. It reports whether
// the target named this room's game.
func (g *Game) lookCasinoTable(sess *net.Session, target string, game casino.GameName) bool {
table, ok := g.resolveRoomTable(sess, target, game)
if !ok {
return false
}
// Use the live table's normalized config: the room fixture may omit
// defaults (seat count, bet limits) that EnsureTable fills in.
sess.WriteLine(g.renderCasinoTableDetails(sess, table.Config, table))
return true
}
// resolveRoomTable matches a look target against the room's table for game
// using the same names and prefix rules as the play command, plus the fixture
// id, an optional " table" suffix, and the bare "table" target when the room
// holds exactly one table.
func (g *Game) resolveRoomTable(sess *net.Session, target string, game casino.GameName) (*casino.Table, bool) {
if g.Casino == nil || sess.Player == nil {
return nil, false
}
room, err := g.World.LoadRoom(sess.Player.RoomID)
if err != nil {
return nil, false
}
target = strings.TrimSpace(strings.ToLower(target))
target = strings.TrimSuffix(target, " table")
address := string(game)
if target == "table" {
if len(room.CasinoTables) != 1 || room.CasinoTables[0].Game != game {
return nil, false
}
} else {
var choices []casinoChoice
for _, cfg := range room.CasinoTables {
if cfg.Game != game {
continue
}
choices = append(choices, casinoChoice{address: string(cfg.Game), names: append(casinoTableAliases(cfg), cfg.ID), table: true})
}
choice, ok := resolveCasinoChoice(choices, target)
if !ok {
return nil, false
}
address = choice.address
}
table, ok := g.casinoTable(room.ID, address)
if !ok {
return nil, false
}
return table, true
}
// renderCasinoTableDetails formats the details block and, for the felt games,
// the per-table rule card. It never prints the craps or roulette board.
func (g *Game) renderCasinoTableDetails(sess *net.Session, cfg casino.TableConfig, table *casino.Table) string {
seats := table.Seats()
seated := "no one"
if len(seats) > 0 {
seated = strings.Join(seats, ", ")
}
lines := []string{
g.colorize(sess, "casino_action", cfg.Label()),
fmt.Sprintf(" Limits %s", betLimitsText(cfg.MinBet, cfg.MaxBet)),
fmt.Sprintf(" Seats %d/%d", len(seats), cfg.MaxPlayers),
fmt.Sprintf(" State %s", table.State()),
fmt.Sprintf(" Rules %s", casinoTableSummary(cfg)),
fmt.Sprintf(" Seated %s", seated),
}
switch cfg.Game {
case casino.GameBlackjack:
lines = append(lines, "", g.renderCasinoSnapshot(sess, &casino.BlackjackRulesView{Config: cfg}))
case casino.GameBaccarat:
lines = append(lines, "", g.renderCasinoSnapshot(sess, &casino.BaccaratRulesView{Config: cfg}))
case casino.GamePaiGow:
lines = append(lines, "", renderPaiGowFelt(cfg, g.colorMode(sess), sess.Player.OptionBool("unicode")))
}
return strings.Join(lines, "\n")
}
// casinoTableSummary is the compact per-table rule description used by the
// look details block. The full wording stays in RulesText.
func casinoTableSummary(cfg casino.TableConfig) string {
switch cfg.Game {
case casino.GameBlackjack:
b := cfg.Blackjack.Normalize()
dealer := "dealer stands on soft 17"
if b.DealerHitsSoft17() {
dealer = "dealer hits soft 17"
}
return fmt.Sprintf("%d decks; %s; blackjack pays %s", b.Decks, dealer, ratioText(b.BlackjackPayout))
case casino.GameBaccarat:
return fmt.Sprintf("%d decks", cfg.Baccarat.Normalize().Decks)
case casino.GameRoulette:
summary := "single-zero wheel"
if cfg.Roulette.DoubleZero {
summary = "double-zero wheel"
}
if cfg.Roulette.Racetrack {
summary += "; racetrack call bets"
}
return summary
case casino.GameCraps:
return cfg.Craps.Normalize().Title()
case casino.GamePaiGow:
p := cfg.PaiGow.Normalize()
envy := "Envy off"
if p.Envy {
envy = "Envy on"
}
return p.Label() + "; " + envy
}
return cfg.Label()
}
|