aboutsummaryrefslogtreecommitdiff
path: root/internal/game/casino.go
diff options
context:
space:
mode:
Diffstat (limited to 'internal/game/casino.go')
-rw-r--r--internal/game/casino.go135
1 files changed, 72 insertions, 63 deletions
diff --git a/internal/game/casino.go b/internal/game/casino.go
index 971952a..736add2 100644
--- a/internal/game/casino.go
+++ b/internal/game/casino.go
@@ -489,42 +489,47 @@ func (g *Game) executeBet(sess *net.Session, args []string, rawInput string) {
sess.WriteLine("You're not playing a game.")
return
}
- // Table bet syntax: bet | bet <amount> [on] [spot] | bet min|max [on] [spot].
- // Tokens that parse as an amount (or min/max) set the wager; any other
- // token is the bet target and the table game validates it.
- amount, spot, hasAmount := 0, "", false
+ // Table bet syntax: bet | bet <amount> [on] <target> | bet min|max [on] <target>.
+ // BEFORE an "on" keyword, a token that parses as an amount (or min/max)
+ // sets the wager and any other token is the bet target; AFTER "on", the
+ // next token is always the target. That makes numeric targets reachable
+ // ("bet 5 on 17"), which roulette, craps, and keno need.
+ amount, spot, hasAmount, sawOn := 0, "", false, false
for _, arg := range args {
if strings.EqualFold(arg, "on") {
+ sawOn = true
continue
}
- if strings.EqualFold(arg, "min") {
- if hasAmount {
- sess.WriteLine("Use bet, bet <amount> [on <target>], bet min, or bet max.")
- return
- }
- amount, hasAmount = table.Config.MinBet, true
- continue
- }
- if strings.EqualFold(arg, "max") {
- if hasAmount {
- sess.WriteLine("Use bet, bet <amount> [on <target>], bet min, or bet max.")
- return
+ if !sawOn {
+ if strings.EqualFold(arg, "min") {
+ if hasAmount {
+ sess.WriteLine("Use bet, bet <amount> [on <target>], bet min, or bet max.")
+ return
+ }
+ amount, hasAmount = table.Config.MinBet, true
+ continue
}
- amount = min(table.Config.MaxBet, casinoAffordable(p))
- if amount < 1 {
- sess.WriteLine("You don't have any chips or credits to bet.")
- return
+ if strings.EqualFold(arg, "max") {
+ if hasAmount {
+ sess.WriteLine("Use bet, bet <amount> [on <target>], bet min, or bet max.")
+ return
+ }
+ amount = min(table.Config.MaxBet, casinoAffordable(p))
+ if amount < 1 {
+ sess.WriteLine("You don't have any chips or credits to bet.")
+ return
+ }
+ hasAmount = true
+ continue
}
- hasAmount = true
- continue
- }
- if value, err := strconv.Atoi(arg); err == nil && value > 0 {
- if hasAmount {
- sess.WriteLine("Use bet, bet <amount> [on <target>], bet min, or bet max.")
- return
+ if value, err := strconv.Atoi(arg); err == nil && value > 0 {
+ if hasAmount {
+ sess.WriteLine("Use bet, bet <amount> [on <target>], bet min, or bet max.")
+ return
+ }
+ amount, hasAmount = value, true
+ continue
}
- amount, hasAmount = value, true
- continue
}
if spot != "" {
sess.WriteLine("Use bet, bet <amount> [on <target>], bet min, or bet max.")
@@ -579,12 +584,12 @@ func (g *Game) CasinoTick() {
func (g *Game) emitCasinoEvents(events []casino.Event) {
for _, event := range events {
- if event.Message == "" && event.PrivateMessage == "" && event.Menu == nil && event.Blackjack == nil && event.Baccarat == nil && event.Slot == nil {
+ if event.Message == "" && event.PrivateMessage == "" && event.Menu == nil && event.Snapshot == nil {
continue
}
category := event.Color
if category == "" {
- category = casinoEventColor(event.Type)
+ category = "casino_action"
}
if event.Public && g.Hub != nil {
for _, sess := range g.Hub.PlayersInRoom(event.RoomID) {
@@ -592,14 +597,8 @@ func (g *Game) emitCasinoEvents(events []casino.Event) {
continue
}
message := event.Message
- if event.Slot != nil && sess.Player != nil {
- message += "\n" + renderSlotSnapshot(event.Slot, g.colorMode(sess), sess.Player.OptionBool("unicode"))
- }
- if event.Blackjack != nil && sess.Player != nil {
- message += "\n" + renderBlackjackSnapshot(event.Blackjack, g.colorMode(sess), sess.Player.OptionBool("unicode"), sess.Player.OptionInt("card_size"), sess.Player.OptionString("card_style"), g.CardArt)
- }
- if event.Baccarat != nil && sess.Player != nil {
- message += "\n" + renderBaccaratSnapshot(event.Baccarat, g.colorMode(sess), sess.Player.OptionBool("unicode"), sess.Player.OptionInt("card_size"), sess.Player.OptionString("card_style"), g.CardArt)
+ if rendered := g.renderCasinoSnapshot(sess, event.Snapshot); rendered != "" {
+ message += "\n" + rendered
}
sess.WriteLine(g.colorize(sess, category, message))
}
@@ -621,11 +620,8 @@ func (g *Game) emitCasinoEvents(events []casino.Event) {
continue
}
privateMessage := g.casinoPrivateMessage(sess, category, event.Menu, text)
- if event.Blackjack != nil && sess.Player != nil {
- privateMessage += "\n" + renderBlackjackSnapshot(event.Blackjack, g.colorMode(sess), sess.Player.OptionBool("unicode"), sess.Player.OptionInt("card_size"), sess.Player.OptionString("card_style"), g.CardArt)
- }
- if event.Baccarat != nil && sess.Player != nil {
- privateMessage += "\n" + renderBaccaratSnapshot(event.Baccarat, g.colorMode(sess), sess.Player.OptionBool("unicode"), sess.Player.OptionInt("card_size"), sess.Player.OptionString("card_style"), g.CardArt)
+ if rendered := g.renderCasinoSnapshot(sess, event.Snapshot); rendered != "" {
+ privateMessage += "\n" + rendered
}
sess.WriteLine(g.renderCasinoMessage(sess, category, event.Menu, privateMessage))
if category == "casino_menu" {
@@ -643,29 +639,42 @@ func (g *Game) renderCasinoMessage(sess *net.Session, category string, menu *cas
return g.colorize(sess, category, message)
}
-func casinoEventColor(eventType casino.EventType) string {
- if eventType == casino.EventPayout {
- return "casino_win"
- }
- return "casino_action"
-}
-
func (g *Game) casinoPrivateMessage(sess *net.Session, category string, menu *casino.MenuView, message string) string {
if category != "casino_menu" || menu == nil {
return message
}
- chips := g.colorize(sess, "casino_chips", fmt.Sprintf("%d", countChips(sess.Player)))
- credits := g.colorize(sess, "casino_credits", fmt.Sprintf("%d", sess.Player.Credits))
- balance := fmt.Sprintf("You have %s chips and %s credits.", chips, credits)
- switch menu.Kind {
- case casino.MenuBlackjackBet, casino.MenuBaccaratBet:
- return g.renderCasinoCommands(sess, balance, message, menu.Commands)
- case casino.MenuBlackjackTurn:
- return g.renderCasinoCommands(sess, "", menu.Title, menu.Commands)
- case casino.MenuSlotCommands:
- return g.renderCasinoCommands(sess, balance, message, menu.Commands)
- }
- return message
+ balance := ""
+ if menu.ShowBalance {
+ chips := g.colorize(sess, "casino_chips", fmt.Sprintf("%d", countChips(sess.Player)))
+ credits := g.colorize(sess, "casino_credits", fmt.Sprintf("%d", sess.Player.Credits))
+ balance = fmt.Sprintf("You have %s chips and %s credits.", chips, credits)
+ }
+ heading := menu.Heading
+ if heading == "" {
+ heading = message
+ }
+ return g.renderCasinoCommands(sess, balance, heading, menu.Commands)
+}
+
+// renderCasinoSnapshot renders a game's structured view for one session. It is
+// the only per-game switch in the presentation layer: adding a game means
+// adding its snapshot type here, and the Snapshot interface guarantees the
+// event carries it rather than being silently dropped by a field filter.
+func (g *Game) renderCasinoSnapshot(sess *net.Session, snapshot casino.Snapshot) string {
+ if snapshot == nil || sess.Player == nil {
+ return ""
+ }
+ mode := g.colorMode(sess)
+ unicode := sess.Player.OptionBool("unicode")
+ switch s := snapshot.(type) {
+ case *casino.SlotSnapshot:
+ return renderSlotSnapshot(s, mode, unicode)
+ case *casino.BlackjackSnapshot:
+ return renderBlackjackSnapshot(s, mode, unicode, sess.Player.OptionInt("card_size"), sess.Player.OptionString("card_style"), g.CardArt)
+ case *casino.BaccaratSnapshot:
+ return renderBaccaratSnapshot(s, mode, unicode, sess.Player.OptionInt("card_size"), sess.Player.OptionString("card_style"), g.CardArt)
+ }
+ return ""
}
// renderCasinoCommands lays every casino menu out in the same style: an