diff options
Diffstat (limited to 'internal/game/render_video_poker.go')
| -rw-r--r-- | internal/game/render_video_poker.go | 216 |
1 files changed, 216 insertions, 0 deletions
diff --git a/internal/game/render_video_poker.go b/internal/game/render_video_poker.go new file mode 100644 index 0000000..d7b1a57 --- /dev/null +++ b/internal/game/render_video_poker.go @@ -0,0 +1,216 @@ +package game + +import ( + "fmt" + "strings" + + "thehouseoficarus/internal/cardart" + "thehouseoficarus/internal/casino" + "thehouseoficarus/internal/color" +) + +var ( + pokerBlue = color.NearestXterm256(0, 2, 105) + pokerRed = color.NearestXterm256(254, 0, 0) + pokerYellow = color.NearestXterm256(247, 231, 14) +) + +func pokerInk(mode, text string, active bool) string { + bg := pokerBlue + if active { + bg = pokerRed + } + return color.Render(mode, color.ColorSpec{Fg: pokerYellow, Bg: bg, Bold: true}, text) +} +func renderPokerPayTable(s *casino.VideoPokerSnapshot, mode string) string { + rows := s.Config.PayRows(s.Hands) + if len(rows) == 0 { + return "" + } + nameWidth, coinWidth := len("COINS PER HAND")+3, 6 + for _, row := range rows { + nameWidth = max(nameWidth, color.VisibleLen(row.Name)+3) + for _, n := range row.Coins { + coinWidth = max(coinWidth, len(fmt.Sprint(n))+2) + } + } + hasNext := s.Config.Feature == casino.PokerUltimateX + widths := []int{nameWidth, coinWidth, coinWidth, coinWidth, coinWidth, coinWidth} + if hasNext { + widths = append(widths, 8) + } + border := func(left, middle, right string) string { + parts := make([]string, len(widths)) + for i, w := range widths { + parts[i] = strings.Repeat("─", w) + } + return pokerInk(mode, left+strings.Join(parts, middle)+right, false) + } + row := func(cells []string, leaders bool) string { + var b strings.Builder + b.WriteString(pokerInk(mode, "│", false)) + for i, text := range cells { + if i == 0 && leaders { + text = " " + text + " " + strings.Repeat(".", widths[0]-color.VisibleLen(text)-2) + } else { + pad := max(0, widths[i]-color.VisibleLen(text)) + text = strings.Repeat(" ", pad/2) + text + strings.Repeat(" ", pad-pad/2) + } + b.WriteString(pokerInk(mode, text, i > 0 && i <= 5 && s.Coins == i)) + b.WriteString(pokerInk(mode, "│", false)) + } + return b.String() + } + headers := []string{"COINS PER HAND", "1", "2", "3", "4", "5"} + if hasNext { + headers = append(headers, "NEXT X") + } + lines := []string{s.Config.GameLabel() + " — " + s.Config.Payout + " pay", border("┌", "┬", "┐"), row(headers, false), border("├", "┼", "┤")} + for _, pay := range rows { + cells := []string{pay.Name} + for _, n := range pay.Coins { + cells = append(cells, fmt.Sprint(n)) + } + if hasNext { + cells = append(cells, fmt.Sprintf("%dx", pay.NextAward)) + } + lines = append(lines, row(cells, true)) + } + lines = append(lines, border("└", "┴", "┘"), fmt.Sprintf("1 coin = %d credits | %d hand(s) | selected coin column in red", s.Denomination, s.Hands)) + if s.Config.Feature != "" { + lines = append(lines, fmt.Sprintf("Feature ON adds %d coin(s) per hand; requires five base coins.", s.Config.FeatureFeeCoins())) + } + return strings.Join(lines, "\n") +} + +func renderPokerCards(cards []casino.CardView, held *[5]bool, mode string, unicode bool, size int, style string, store *cardart.Store) []string { + if len(cards) == 0 { + return nil + } + art := make([][]string, len(cards)) + widths := make([]int, len(cards)) + height := 0 + for i, c := range cards { + art[i] = cardsLine(mode, []casino.CardView{c}, size, unicode, style, store) + height = max(height, len(art[i])) + for _, line := range art[i] { + widths[i] = max(widths[i], color.VisibleLen(line)) + } + if held != nil { + widths[i] = max(widths[i], 6) + } + } + var lines []string + for r := 0; r < height; r++ { + parts := make([]string, len(cards)) + for i := range cards { + text := "" + if r < len(art[i]) { + text = art[i][r] + } + parts[i] = rouletteCenter(text, widths[i]) + } + lines = append(lines, strings.Join(parts, " ")) + } + if held != nil { + labels := make([]string, len(cards)) + for i := range cards { + text := fmt.Sprint(i + 1) + if held[i] { + text = color.Render(mode, color.ColorSpec{Fg: pokerYellow, Bg: -1, Bold: true}, text+" HELD") + } + labels[i] = rouletteCenter(text, widths[i]) + } + lines = append(lines, strings.Join(labels, " ")) + } + return lines +} + +func renderVideoPoker(s *casino.VideoPokerSnapshot, mode string, unicode bool, size int, style string, store *cardart.Store) string { + if s.Hands > 1 { + size = min(size, 5) + } + if s.Hands >= 25 { + size = 1 + } + if s.Hands >= 100 { + unicode = true + } + var lines []string + if s.ShowPayTable { + lines = append(lines, renderPokerPayTable(s, mode), "") + } + feature := "CLASSIC" + if s.Config.Feature != "" { + feature = s.Config.FeatureLabel() + " OFF" + if s.FeatureOn { + feature = s.Config.FeatureLabel() + " ON" + } + } + badge := pokerInk(mode, " ["+feature+"] ", false) + bonus := "" + mult := s.Multiplier + if s.Config.Feature == casino.PokerUltimateX { + for _, n := range s.Current { + mult = max(mult, n) + } + } + if mult > 1 { + text := fmt.Sprintf(" %dX! ", mult) + if s.Config.Feature == casino.PokerUltimateX && s.Hands > 1 { + text = fmt.Sprintf(" Up to %dX! ", mult) + } + bonus = pokerInk(mode, text, true) + } + if s.Dice[0] > 0 { + bonus += fmt.Sprintf(" Dice [%d] + [%d]", s.Dice[0], s.Dice[1]) + } + if len(s.Cards) > 0 && len(s.Lines) == 0 { + art := renderPokerCards(s.Cards, &s.Held, mode, unicode, size, style, store) + if len(art) > 0 { + art[0] += " " + badge + bonus + } + lines = append(lines, art...) + } else if len(s.Lines) == 0 { + lines = append(lines, badge+bonus) + } + for i, line := range s.Lines { + art := renderPokerCards(line.Cards, nil, mode, unicode, size, style, store) + if i == 0 && len(art) > 0 { + art[0] += " " + badge + bonus + } + if len(s.Lines) > 1 && len(art) > 0 { + art[0] = fmt.Sprintf("%3d: ", i+1) + art[0] + } + lines = append(lines, art...) + text := "No win" + fg := 15 + if line.Coins > 0 { + text = fmt.Sprintf("WIN: %s — %d coins", line.Category, line.Coins) + fg = pokerYellow + } + if line.Multiplier > 1 { + text += fmt.Sprintf(" (%dx)", line.Multiplier) + } + if len(s.Next) > i && s.Next[i] > 1 { + text += fmt.Sprintf(" | earns %dx", s.Next[i]) + } + // Results always follow the cards, including compact hundred-play. + lines = append(lines, color.Render(mode, color.ColorSpec{Fg: fg, Bg: -1, Bold: true}, text)) + } + if s.Result != "" { + fg := pokerYellow + if s.NoWin { + fg = 15 + } + lines = append(lines, color.Render(mode, color.ColorSpec{Fg: fg, Bg: -1, Bold: true}, s.Result)) + } + if !s.Drawing && len(s.Lines) == 0 && s.Config.Feature == casino.PokerUltimateX && len(s.Next) > 0 { + var parts []string + for i, n := range s.Next { + parts = append(parts, fmt.Sprintf("%d:%dx", i+1, n)) + } + lines = append(lines, "Multipliers on the machine: "+strings.Join(parts, " ")) + } + return "\n" + strings.Join(lines, "\n") + "\n" +} |
