diff options
Diffstat (limited to 'internal/game/casino.go')
| -rw-r--r-- | internal/game/casino.go | 135 |
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 |
