diff options
Diffstat (limited to 'internal/game/casino.go')
| -rw-r--r-- | internal/game/casino.go | 192 |
1 files changed, 163 insertions, 29 deletions
diff --git a/internal/game/casino.go b/internal/game/casino.go index 698cb78..579371c 100644 --- a/internal/game/casino.go +++ b/internal/game/casino.go @@ -340,15 +340,36 @@ func (g *Game) casinoMachine(roomID int, gameName string) (*casino.Machine, bool func (g *Game) executePlay(sess *net.Session, args []string, rawInput string) { p := sess.Player - if p == nil || len(args) == 0 { - sess.WriteLine("Play what?") + if p == nil { return } if _, playing := g.Casino.Participation(p.Name); playing { sess.WriteLine("You're already playing a game. Type \"stop\" between rounds to leave it.") return } - g.startCasinoGame(sess, p, args[0]) + choices := g.casinoChoices(p.RoomID) + input := "" + if len(args) > 0 { + input = args[0] + } + choice, ok := resolveCasinoChoice(choices, input) + if !ok { + g.showCasinoChoices(sess, choices) + return + } + if len(args) > 1 { + amount, err := strconv.Atoi(args[1]) + if !choice.table || len(args) > 3 || (args[1] != "min" && args[1] != "max" && (err != nil || amount <= 0)) { + sess.WriteLine("Use play <game> [amount|min|max] [target]. No wager was placed.") + return + } + } + g.startCasinoGame(sess, p, choice.address) + if len(args) > 1 { + if t, joined := g.Casino.Participation(p.Name); joined && t != nil { + g.executeBet(sess, args[1:], rawInput) + } + } } func (g *Game) startCasinoGame(sess *net.Session, p *player.Player, gameName string) { @@ -411,8 +432,12 @@ func (g *Game) executeBet(sess *net.Session, args []string, rawInput string) { // 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 { + for i, arg := range args { if strings.EqualFold(arg, "on") { + if sawOn || spot != "" || i == len(args)-1 { + sess.WriteLine("Use bet <amount> on <target>. No wager was placed.") + return + } sawOn = true continue } @@ -453,6 +478,16 @@ func (g *Game) executeBet(sess *net.Session, args []string, rawInput string) { } spot = strings.ToLower(arg) } + if table.Config.Game == casino.GameBaccarat { + switch spot { + case "b": + spot = "banker" + case "p": + spot = "player" + case "t": + spot = "tie" + } + } if !hasAmount { var lastSpot string lastSpot, amount = g.Casino.Rebet(table.RoomID, table.Config.ID, p.Name) @@ -512,11 +547,19 @@ func (g *Game) emitCasinoEvents(events []casino.Event) { if event.PrivateTo != "" && sess.Player != nil && sess.Player.Name == event.PrivateTo { continue } - message := event.Message - if rendered := g.renderCasinoSnapshot(sess, event.Snapshot); rendered != "" { - message += "\n" + rendered + details := g.seesCasinoDetails(sess, event) + if event.Detail && !details { + continue + } + message := g.casinoTimeText(event.Message) + if details { + if rendered := g.renderCasinoSnapshot(sess, event.Snapshot); rendered != "" { + message += "\n" + rendered + } + } + if message != "" { + sess.WriteLine(g.colorize(sess, category, message)) } - sess.WriteLine(g.colorize(sess, category, message)) } } if event.PrivateTo == "" { @@ -526,7 +569,7 @@ func (g *Game) emitCasinoEvents(events []casino.Event) { if text == "" { text = event.Message } - if text == "" && event.Menu == nil { + if text == "" && event.Menu == nil && event.Snapshot == nil { continue } g.charsMu.Lock() @@ -535,10 +578,13 @@ func (g *Game) emitCasinoEvents(events []casino.Event) { if sess == nil { continue } - privateMessage := g.casinoPrivateMessage(sess, category, event.Menu, text) + privateMessage := g.casinoPrivateMessage(sess, category, event.Menu, g.casinoTimeText(text)) if rendered := g.renderCasinoSnapshot(sess, event.Snapshot); rendered != "" { privateMessage += "\n" + rendered } + if event.ShowBalance { + privateMessage += "\n" + g.casinoBalance(sess) + } sess.WriteLine(g.renderCasinoMessage(sess, category, event.Menu, privateMessage)) if category == "casino_menu" { g.writePrompt(sess) @@ -561,15 +607,55 @@ func (g *Game) casinoPrivateMessage(sess *net.Session, category string, menu *ca } 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) + balance = g.casinoBalance(sess) } - heading := menu.Heading + heading := g.casinoTimeText(menu.Heading) if heading == "" { heading = message } - return g.renderCasinoCommands(sess, balance, heading, menu.Commands) + commands := append([]casino.MenuCommand(nil), menu.Commands...) + for i := range commands { + if commands[i].Cost > casinoAffordable(sess.Player) { + commands[i].Desc += " [unavailable: insufficient funds]" + } + } + if sess.Player.OptionString("casino_display") == "compact" && menu.Repeat { + var repeat string + for _, c := range commands { + if c.Command == "bet" { + repeat = g.colorize(sess, "command", "bet / rebet") + " " + c.Desc + break + } + } + return g.colorize(sess, "casino_action", heading) + "\n" + balance + "\n " + repeat + "\n " + g.colorize(sess, "command", "bet <amount> [target] | bet min/max | table | sitout | stop") + } + if sess.Player.OptionString("casino_display") == "compact" && !menu.ShowBalance { + var hints []string + for _, c := range commands { + label := c.Command + switch c.Command { + case "hit": + label = "h:hit" + case "stand": + label = "st:stand" + case "double": + label = "dd:double" + case "split": + label = "sp:split" + } + if c.Cost > 0 { + label += fmt.Sprintf(" (+%d)", c.Cost) + } + category := "command" + if c.Cost > casinoAffordable(sess.Player) { + label += " [unavailable]" + category = "casino_warning" + } + hints = append(hints, g.colorize(sess, category, label)) + } + return g.colorize(sess, "casino_action", heading) + "\n " + strings.Join(hints, " ") + } + return g.renderCasinoCommands(sess, balance, heading, commands) } // renderCasinoSnapshot renders a game's structured view for one session. It is @@ -582,13 +668,17 @@ func (g *Game) renderCasinoSnapshot(sess *net.Session, snapshot casino.Snapshot) } mode := g.colorMode(sess) unicode := sess.Player.OptionBool("unicode") + size := sess.Player.OptionInt("card_size") + if sess.Player.OptionString("casino_display") == "compact" { + size = 1 + } 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) + return renderBlackjackSnapshotFor(s, mode, unicode, size, sess.Player.OptionString("card_style"), g.CardArt, sess.Player.Name) case *casino.BaccaratSnapshot: - return renderBaccaratSnapshot(s, mode, unicode, sess.Player.OptionInt("card_size"), sess.Player.OptionString("card_style"), g.CardArt) + return renderBaccaratSnapshot(s, mode, unicode, size, sess.Player.OptionString("card_style"), g.CardArt) } return "" } @@ -603,7 +693,7 @@ func (g *Game) renderCasinoCommands(sess *net.Session, balance, heading string, sb.WriteString("\n\n") } if heading != "" { - sb.WriteString(heading) + sb.WriteString(g.colorize(sess, "casino_action", heading)) sb.WriteString("\n\n") } width := 0 @@ -614,7 +704,11 @@ func (g *Game) renderCasinoCommands(sess *net.Session, balance, heading string, } for _, c := range commands { padded := c.Command + strings.Repeat(" ", width-utf8.RuneCountInString(c.Command)) - cmd := g.colorize(sess, "command", padded) + category := "command" + if sess.Player != nil && c.Cost > casinoAffordable(sess.Player) { + category = "casino_warning" + } + cmd := g.colorize(sess, category, padded) if c.Desc != "" { fmt.Fprintf(&sb, " %s %s\n", cmd, c.Desc) } else { @@ -624,7 +718,7 @@ func (g *Game) renderCasinoCommands(sess *net.Session, balance, heading string, return strings.TrimRight(sb.String(), "\n") } -func renderBlackjackSnapshot(snapshot *casino.BlackjackSnapshot, mode string, unicode bool, size int, style string, art *cardart.Store) string { +func renderBlackjackSnapshotFor(snapshot *casino.BlackjackSnapshot, mode string, unicode bool, size int, style string, art *cardart.Store, viewer string) string { if snapshot == nil { return "" } @@ -636,11 +730,35 @@ func renderBlackjackSnapshot(snapshot *casino.BlackjackSnapshot, mode string, un } else if snapshot.DealerShownScore != "" { dealerTitle = "Dealer (" + snapshot.DealerShownScore + "):" } - lines = append(lines, cardLine(mode, dealerTitle)) - lines = append(lines, cardsLine(mode, snapshot.Dealer, size, unicode, style, art)...) + if size == 1 { + lines = append(lines, cardLine(mode, dealerTitle)+" "+strings.Join(cardsLine(mode, snapshot.Dealer, size, unicode, style, art), " ")) + } else { + lines = append(lines, cardLine(mode, dealerTitle)) + lines = append(lines, cardsLine(mode, snapshot.Dealer, size, unicode, style, art)...) + } for _, player := range snapshot.Players { - lines = append(lines, cardLine(mode, fmt.Sprintf("%s (%s):", player.Name, player.Score))) - lines = append(lines, cardsLine(mode, player.Cards, size, unicode, style, art)...) + name := player.Name + if name == viewer { + name = "You" + } + marker := " " + if player.Active { + marker = "> " + } + detail := "" + if player.Hand > 0 { + detail = fmt.Sprintf(" | Hand %d/%d | Wager %d | %s", player.Hand, player.Hands, player.Bet, player.Status) + } + if player.Active && player.Name != viewer { + detail = strings.ReplaceAll(detail, "Your turn", "Playing") + } + title := fmt.Sprintf("%s%s (%s)%s:", marker, name, player.Score, detail) + if size == 1 { + lines = append(lines, cardLine(mode, title)+" "+strings.Join(cardsLine(mode, player.Cards, size, unicode, style, art), " ")) + } else { + lines = append(lines, cardLine(mode, title)) + lines = append(lines, cardsLine(mode, player.Cards, size, unicode, style, art)...) + } } return strings.Join(lines, "\n") } @@ -653,10 +771,14 @@ func renderBaccaratSnapshot(snapshot *casino.BaccaratSnapshot, mode string, unic } size = cardRenderSize(unicode, size) var lines []string - lines = append(lines, cardLine(mode, "Player ("+snapshot.PlayerScore+"):")) - lines = append(lines, cardsLine(mode, snapshot.Player, size, unicode, style, art)...) - lines = append(lines, cardLine(mode, "Banker ("+snapshot.BankerScore+"):")) - lines = append(lines, cardsLine(mode, snapshot.Banker, size, unicode, style, art)...) + if size == 1 { + lines = append(lines, cardLine(mode, "Player ("+snapshot.PlayerScore+"):")+" "+strings.Join(cardsLine(mode, snapshot.Player, size, unicode, style, art), " "), cardLine(mode, "Banker ("+snapshot.BankerScore+"):")+" "+strings.Join(cardsLine(mode, snapshot.Banker, size, unicode, style, art), " ")) + } else { + lines = append(lines, cardLine(mode, "Player ("+snapshot.PlayerScore+"):")) + lines = append(lines, cardsLine(mode, snapshot.Player, size, unicode, style, art)...) + lines = append(lines, cardLine(mode, "Banker ("+snapshot.BankerScore+"):")) + lines = append(lines, cardsLine(mode, snapshot.Banker, size, unicode, style, art)...) + } for _, bet := range snapshot.Bets { lines = append(lines, cardLine(mode, fmt.Sprintf("%s bets %d on %s.", bet.Name, bet.Amount, bet.Spot))) } @@ -676,7 +798,16 @@ func cardsLine(mode string, cards []casino.CardView, size int, unicode bool, sty if size == 1 { parts := make([]string, len(cards)) for i, card := range cards { - parts[i] = colorizeCardSymbol(mode, card) + if !unicode { + if card.Hidden { + parts[i] = "[??]" + } else { + suit := map[string]string{"hearts": "H", "diamonds": "D", "spades": "S", "clubs": "C"}[card.Suit] + parts[i] = card.Rank + suit + } + } else { + parts[i] = colorizeCardSymbol(mode, card) + } } return []string{strings.Join(parts, " ")} } @@ -696,6 +827,9 @@ func cardsLine(mode string, cards []casino.CardView, size int, unicode bool, sty } func cardRenderSize(unicode bool, size int) int { + if size == 1 { + return 1 + } if !unicode { return 5 } |
