diff options
Diffstat (limited to 'internal/game/casino.go')
| -rw-r--r-- | internal/game/casino.go | 84 |
1 files changed, 81 insertions, 3 deletions
diff --git a/internal/game/casino.go b/internal/game/casino.go index 9282653..08695e7 100644 --- a/internal/game/casino.go +++ b/internal/game/casino.go @@ -563,7 +563,7 @@ func (g *Game) emitCasinoEvents(events []casino.Event) { // Style prose before composing it with independently styled art. // Wrapping the finished message leaks casino_action into borders // and plain spaces between a snapshot's own color resets. - message := g.colorize(sess, category, g.casinoTimeText(event.Message)) + message := g.casinoProse(sess, category, g.casinoTimeText(event.Message)) if details { if rendered := g.renderCasinoSnapshot(sess, event.Snapshot); rendered != "" { message += "\n" + rendered @@ -592,7 +592,10 @@ func (g *Game) emitCasinoEvents(events []casino.Event) { } privateMessage := g.casinoPrivateMessage(sess, category, event.Menu, g.casinoTimeText(text)) privateMessage = g.renderCasinoMessage(sess, category, event.Menu, privateMessage) - if rendered := g.renderCasinoSnapshot(sess, event.Snapshot); rendered != "" { + rendered := g.renderCasinoSnapshot(sess, event.Snapshot) + if header, reminder, ok := g.casinoBoardReminder(sess, category, event.Menu, event.Snapshot, g.casinoTimeText(text)); ok { + privateMessage = composeTableThenReminder(header, rendered, reminder) + } else if rendered != "" { privateMessage += "\n" + rendered } if event.ShowBalance { @@ -605,13 +608,66 @@ func (g *Game) emitCasinoEvents(events []casino.Event) { } } +// casinoBoardReminder splits a roulette menu so its wager instructions can be +// printed under the betting board, which is the table the reminder refers to. +// Every other game, and compact displays, keep their menu in one block. +func (g *Game) casinoBoardReminder(sess *net.Session, category string, menu *casino.MenuView, snapshot casino.Snapshot, message string) (header, reminder string, ok bool) { + if category != "casino_menu" || menu == nil || sess.Player == nil { + return "", "", false + } + s, isRoulette := snapshot.(*casino.RouletteSnapshot) + if !isRoulette || s.Phase == "playing" || s.Phase == "result" { + return "", "", false + } + if sess.Player.OptionString("casino_display") == "compact" && (menu.Repeat || !menu.ShowBalance) { + // The compact reminder is already a short block; leave it intact. + return "", "", false + } + balance := "" + if menu.ShowBalance { + balance = g.casinoBalance(sess) + } + heading := g.casinoTimeText(menu.Heading) + if heading == "" { + heading = message + } + commands := append([]casino.MenuCommand(nil), menu.Commands...) + for i := range commands { + if commands[i].Cost > casinoAffordable(sess.Player) { + commands[i].Desc += " [unavailable: insufficient funds]" + } + } + return g.casinoMenuHeader(sess, balance, heading), g.casinoMenuCommands(sess, commands), true +} + +// composeTableThenReminder joins a menu header, the board graphic, and the +// wager reminder. The board snapshot already leads with a blank line, so only +// the reminder needs an extra gap before it. +func composeTableThenReminder(header, table, reminder string) string { + out := header + if table != "" { + out += "\n" + table + } + if reminder != "" { + out += "\n\n" + reminder + } + return out +} + // renderCasinoMessage colorizes a casino message unless it is an // already-composed structured menu. func (g *Game) renderCasinoMessage(sess *net.Session, category string, menu *casino.MenuView, message string) string { if category == "casino_menu" && menu != nil { return message } - return g.colorize(sess, category, message) + return g.casinoProse(sess, category, message) +} + +// casinoProse colorizes casino event text, expanding inline color tags such as +// {casino_result_win}WIN{/} against the session's palette. The event's color +// category is the default for untagged text. +func (g *Game) casinoProse(sess *net.Session, category, text string) string { + return g.expandTagsDefault(sess, g.resolveColor(sess, category), text) } func (g *Game) casinoPrivateMessage(sess *net.Session, category string, menu *casino.MenuView, message string) string { @@ -701,6 +757,22 @@ func (g *Game) renderCasinoSnapshot(sess *net.Session, snapshot casino.Snapshot) // optional balance line, a heading, and an aligned command list with each // command in the "command" color and its description in plain text. func (g *Game) renderCasinoCommands(sess *net.Session, balance, heading string, commands []casino.MenuCommand) string { + head := g.casinoMenuHeader(sess, balance, heading) + body := g.casinoMenuCommands(sess, commands) + switch { + case head == "": + return body + case body == "": + return head + default: + return head + "\n\n" + body + } +} + +// casinoMenuHeader formats the balance and heading that introduce a casino +// menu. It is split from the command list so the roulette board can be drawn +// between them. +func (g *Game) casinoMenuHeader(sess *net.Session, balance, heading string) string { var sb strings.Builder if balance != "" { sb.WriteString(balance) @@ -710,12 +782,18 @@ func (g *Game) renderCasinoCommands(sess *net.Session, balance, heading string, sb.WriteString(g.colorize(sess, "casino_action", heading)) sb.WriteString("\n\n") } + return strings.TrimRight(sb.String(), "\n") +} + +// casinoMenuCommands formats an aligned, colored command list. +func (g *Game) casinoMenuCommands(sess *net.Session, commands []casino.MenuCommand) string { width := 0 for _, c := range commands { if n := utf8.RuneCountInString(c.Command); n > width { width = n } } + var sb strings.Builder for _, c := range commands { padded := c.Command + strings.Repeat(" ", width-utf8.RuneCountInString(c.Command)) category := "command" |
