aboutsummaryrefslogtreecommitdiff
path: root/internal/casino/craps_ux.go
blob: 10f7fcbd29920df9df86798c8ab11c853c8eadab (plain)
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
133
134
135
136
137
package casino

import (
	"fmt"
	"strings"
)

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 seconds after a bet"})
				} else {
					commands = append(commands, MenuCommand{Command: "bet <amount>", Desc: "Wager on pass; the dice roll 3 seconds 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: table rules"})
			}
		}
		if p.HasBet {
			if !simplified {
				if t.shooter() == p && (!t.multiplayer || t.roundElapsed >= crapsBetDelay) {
					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 <target> / unbet all", Desc: "Return optional bets or 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})
	}
	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 each open wager, then the dice pairs that beat them. The
// roll table shares payout math with settlement (crapsNet), so the numbers
// never promise a win a real roll cannot pay.
func (t *crapsTable) betSummary(p *crapsPlayer) Event {
	text := "{casino_emphasis}YOUR BETS{/}"
	if len(p.bets) == 0 {
		text += "  No active bets."
	} else {
		for _, b := range p.bets {
			label := crapsBetLabel(b)
			if strings.HasPrefix(b.kind, "dont") || b.kind == "lay" {
				label += " (against)"
			}
			if t.point == 0 && (b.kind == "place" || b.kind == "buy" || b.kind == "hard") {
				label += " (off)"
			}
			text += fmt.Sprintf("\n  {casino_action}%-24s{/} {casino_chips}%d{/}", label, b.stake.Total)
			if b.odds.Total > 0 {
				text += fmt.Sprintf(" + %d odds", b.odds.Total)
				if t.point == 0 && b.kind == "come" {
					text += " (odds off)"
				}
			}
		}
	}
	e := t.private(p.Name, text+"\n"+t.rollTable(p))
	e.ShowBalance = true
	return e
}
func (t *crapsTable) reminder(name string) Event {
	e := t.private(name, "Your move")
	e.Color = "casino_menu"
	e.Menu = &MenuView{Heading: "Your move", 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)
	if len(lines) > 0 {
		e.Color = settlementTag(lines)
	}
	return e
}