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
129
130
131
132
|
package casino
import (
"fmt"
)
func (t *crapsTable) commands(name string) []MenuCommand {
commands := []MenuCommand{}
p := t.players.get(name)
simplified := t.Config.Craps.variant() == "simplified"
if p != nil {
// Pass-only editions cannot accept another contract while a point or
// the player's existing pass bet is live.
if t.Config.Craps.allows("come") || t.Config.Craps.allows("buy") || (t.point == 0 && !p.HasBet) {
if simplified {
// Pass is the only target, so the bare bet is a rebet.
if p.lastBet > 0 {
commands = append(commands,
MenuCommand{Command: "bet", Desc: fmt.Sprintf("Rebet %d on pass (also: rebet)", p.lastBet)},
MenuCommand{Command: "bet <amount>", Desc: "New amount on pass; the dice roll 3 ticks after a bet"})
} else {
commands = append(commands, MenuCommand{Command: "bet <amount>", Desc: "Wager on pass; the dice roll 3 ticks after a bet"})
}
} else {
target := "<target>"
if !t.Config.Craps.allows("buy") {
target = "pass"
}
commands = append(commands, MenuCommand{Command: "bet <amount> on " + target, Desc: "Place a wager; targets: list"})
}
}
if p.HasBet {
if !simplified {
if t.shooter() == p && (!t.multiplayer || t.roundTicks >= crapsBetTicks) && t.pendingRollTicks == 0 {
commands = append(commands, MenuCommand{Command: "roll", Desc: "Shoot now"})
}
if !p.autoRoll {
commands = append(commands, MenuCommand{Command: "autoroll", Desc: "Roll automatically on your turn"})
}
}
for _, b := range p.bets {
if !crapsContract(b.kind) || b.odds.Total > 0 {
commands = append(commands, MenuCommand{Command: "unbet", Desc: "Return every optional bet and free odds"})
break
}
}
}
stop := "Leave the table"
if p.HasBet {
stop = "Leave after your live bets settle"
}
if p.autoRoll {
stop = "Cancel autoroll; keep your bets"
}
commands = append(commands, MenuCommand{Command: "stop", Desc: stop})
// "rules" stays available to a seated player at every phase of every
// variant; "list" rides along whenever the wager table names more
// than one bet.
commands = withTableHints(commands, t.wagerRows(name))
}
return append(commands, MenuCommand{Command: "table", Desc: "View the felt"})
}
func crapsContract(kind string) bool {
switch kind {
case "pass", "come", "fire", "small", "tall", "all":
return true
default:
return false
}
}
func crapsBetLabel(b *crapsBet) string {
names := map[string]string{"pass": "Pass", "dontpass": "Don't pass", "come": "Come", "dontcome": "Don't come", "ace-deuce": "Ace-deuce", "small": "All Small", "tall": "All Tall", "all": "All", "fire": "Fire"}
label := names[b.kind]
if label == "" {
label = gameTitle(GameName(b.kind))
}
if b.kind == "hop" {
return fmt.Sprintf("Hop [%d + %d]", b.number/10, b.number%10)
}
if b.number != 0 {
label += fmt.Sprintf(" [%d]", b.number)
}
return label
}
// Numbers describe covered targets, not a promise that every listed roll wins
// (don't/lay bets oppose their number; bonus bets need complete sequences).
// betSummary lists the dice pairs that settle the player's open wagers. The
// roll table shares payout math with settlement (crapsNet), so the numbers
// never promise a win a real roll cannot pay. The open-bet list itself is no
// longer shown: the settlement wager table already names every stake.
func (t *crapsTable) betSummary(p *crapsPlayer) Event {
return t.private(p.Name, t.rollTable(p))
}
// shooterHeading names the current shooter for the join, status and reminder
// menus. The board no longer carries its own Shooter line.
func (t *crapsTable) shooterHeading() string {
if shooter := t.shooter(); shooter != nil {
return "Shooter " + shooter.Name
}
return "Shooter"
}
func (t *crapsTable) reminder(name string) Event {
heading := t.shooterHeading()
e := t.private(name, heading)
e.Color = "casino_menu"
e.Menu = &MenuView{ShowBalance: true, Heading: heading, Commands: t.commands(name)}
return e
}
func (t *crapsTable) settlementEvent(p *crapsPlayer, lines []settlementLine, failed bool) Event {
text := ""
if len(lines) > 0 {
text = settlementText(lines)
}
if failed {
if text != "" {
text += "\n"
}
text += payoutFailedMessage()
}
e := t.private(p.Name, text)
e.Settlement = lines
if len(lines) > 0 {
if failed {
e.SettlementNote = payoutFailedMessage()
}
e.Color = settlementTag(lines)
}
return e
}
|