aboutsummaryrefslogtreecommitdiff
path: root/internal/casino/craps_ux.go
blob: 948ab9971041e32989261e1c77e7d637208a9584 (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
package casino

import (
	"fmt"
	"strings"
)

func (t *crapsTable) commands(name string) []MenuCommand {
	commands := []MenuCommand{}
	p := t.players.get(name)
	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) {
			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 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
}