diff options
| author | historia <[not public]> | 2026-08-03 14:08:09 -0400 |
|---|---|---|
| committer | historia <[not public]> | 2026-08-03 14:08:09 -0400 |
| commit | 612e429de438821ae8e099d6b54f87c11ff6c1b5 (patch) | |
| tree | ad6f9edf74339b9b50f9c7c2cd314ee7b6e04b2c /internal/game | |
| parent | f51424c90dbce7191a31b6e675818c985f0f4539 (diff) | |
| download | thehouseoficarus-612e429de438821ae8e099d6b54f87c11ff6c1b5.tar.gz | |
refactor: casino game slop refactor
Diffstat (limited to 'internal/game')
| -rw-r--r-- | internal/game/casino.go | 360 | ||||
| -rw-r--r-- | internal/game/casino_test.go | 80 | ||||
| -rw-r--r-- | internal/game/cmd_option.go | 20 | ||||
| -rw-r--r-- | internal/game/cmd_registry.go | 6 | ||||
| -rw-r--r-- | internal/game/game.go | 4 | ||||
| -rw-r--r-- | internal/game/render_help.go | 2 |
6 files changed, 419 insertions, 53 deletions
diff --git a/internal/game/casino.go b/internal/game/casino.go index c6fb63b..801c887 100644 --- a/internal/game/casino.go +++ b/internal/game/casino.go @@ -4,7 +4,9 @@ import ( "fmt" "strconv" "strings" + "unicode/utf8" + "thehouseoficarus/internal/cardart" "thehouseoficarus/internal/casino" "thehouseoficarus/internal/color" "thehouseoficarus/internal/net" @@ -17,6 +19,9 @@ type casinoWallet struct { } func (w casinoWallet) Wager(amount int) (casino.Wager, bool) { + if amount <= 0 { + return casino.Wager{}, false + } chips := countChips(w.p) if chips > amount { chips = amount @@ -119,23 +124,46 @@ func (g *Game) executeBet(sess *net.Session, args []string, rawInput string) { g.emitCasinoEvents(g.Casino.StartMachine(machine.RoomID, machine.Config.ID, p.Name, casinoWallet{g: g, p: p})) return } - if len(args) == 1 && args[0] == "max" { - g.emitCasinoEvents(g.Casino.SetMachineBet(machine.RoomID, machine.Config.ID, p.Name, machine.Config.MaxBet)) - return - } if len(args) == 1 { + if args[0] == "max" { + g.emitCasinoEvents(g.Casino.SetMachineBet(machine.RoomID, machine.Config.ID, p.Name, machine.Config.MaxBet)) + return + } if amount, err := strconv.Atoi(args[0]); err == nil && amount > 0 { g.emitCasinoEvents(g.Casino.SetMachineBet(machine.RoomID, machine.Config.ID, p.Name, amount)) return } } - g.emitCasinoEvents(g.Casino.StartMachine(machine.RoomID, machine.Config.ID, p.Name, casinoWallet{g: g, p: p})) + sess.WriteLine("Use bet, bet <amount>, or bet max.") return } if len(args) == 0 { + if table, playing := g.Casino.Participation(p.Name); playing && table != nil && table.Config.Game == casino.GameBlackjack { + amount := g.Casino.RebetAmount(table.RoomID, table.Config.ID, p.Name) + g.emitCasinoEvents(g.Casino.Bet(table.RoomID, table.Config.ID, p.Name, amount, casinoWallet{g: g, p: p})) + return + } sess.WriteLine("Bet how much?") return } + if table, playing := g.Casino.Participation(p.Name); playing && table != nil && table.Config.Game == casino.GameBlackjack { + amount := 0 + switch args[0] { + case "min": + amount = table.Config.MinBet + case "max": + amount = table.Config.MaxBet + default: + var err error + amount, err = strconv.Atoi(args[0]) + if err != nil { + sess.WriteLine("Use bet, bet <amount>, bet min, or bet max.") + return + } + } + g.emitCasinoEvents(g.Casino.Bet(table.RoomID, table.Config.ID, p.Name, amount, casinoWallet{g: g, p: p})) + return + } amount, err := strconv.Atoi(args[0]) if err != nil || amount <= 0 { sess.WriteLine("Your bet must be a positive whole number.") @@ -161,6 +189,19 @@ func (g *Game) executeAutoBet(sess *net.Session, args []string, rawInput string) sess.WriteLine("Autobet is only available at games without player choices.") } +func (g *Game) executeBlackjackAction(sess *net.Session, action string) { + p := sess.Player + if p == nil { + return + } + table, playing := g.Casino.Participation(p.Name) + if !playing || table == nil { + sess.WriteLine("You're not playing a multiplayer casino game.") + return + } + g.emitCasinoEvents(g.Casino.Action(table.RoomID, table.Config.ID, p.Name, action)) +} + func (g *Game) CasinoTick() { if g.Casino != nil { g.emitCasinoEvents(g.Casino.Tick()) @@ -169,7 +210,7 @@ func (g *Game) CasinoTick() { func (g *Game) emitCasinoEvents(events []casino.Event) { for _, event := range events { - if event.Message == "" { + if event.Message == "" && event.Menu == nil { continue } category := event.Color @@ -182,48 +223,51 @@ func (g *Game) emitCasinoEvents(events []casino.Event) { continue } message := event.Message - if category == "casino_menu" && sess.Player != nil && sess.Player.Name == event.PrivateTo { - message = g.casinoMenuBalance(sess, 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) + } sess.WriteLine(g.colorize(sess, category, message)) } } - if event.PrivateMessage != "" && event.PrivateTo != "" { - g.charsMu.Lock() - sess := g.loggedInChars[event.PrivateTo] - g.charsMu.Unlock() - if sess != nil { - privateMessage := event.PrivateMessage - if category == "casino_menu" { - privateMessage = g.casinoMenuBalance(sess, privateMessage) - } - sess.WriteLine(g.colorize(sess, category, privateMessage)) - if category == "casino_menu" { - g.writePrompt(sess) - } - } + if event.PrivateTo == "" { + continue } - if event.PrivateTo != "" { - g.charsMu.Lock() - sess := g.loggedInChars[event.PrivateTo] - g.charsMu.Unlock() - if sess != nil && event.PrivateMessage == "" { - privateMessage := event.Message - if category == "casino_menu" { - privateMessage = g.casinoMenuBalance(sess, privateMessage) - } - sess.WriteLine(g.colorize(sess, category, privateMessage)) - if category == "casino_menu" { - g.writePrompt(sess) - } - } + text := event.PrivateMessage + if text == "" { + text = event.Message + } + if text == "" && event.Menu == nil { + continue + } + g.charsMu.Lock() + sess := g.loggedInChars[event.PrivateTo] + g.charsMu.Unlock() + if sess == nil { + 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) + } + sess.WriteLine(g.renderCasinoMessage(sess, category, event.Menu, privateMessage)) + if category == "casino_menu" { + g.writePrompt(sess) } } } +// 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 && menu.Kind != casino.MenuSlotCommands { + return message + } + return g.colorize(sess, category, message) +} + func casinoEventColor(eventType casino.EventType) string { if eventType == casino.EventPayout { return "casino_win" @@ -231,14 +275,230 @@ func casinoEventColor(eventType casino.EventType) string { return "casino_action" } -func (g *Game) casinoMenuBalance(sess *net.Session, message string) string { - if sess == nil || sess.Player == nil { +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)) - line := fmt.Sprintf("You have %s chips and %s credits.", chips, credits) - return strings.Replace(message, "Slot machine commands:", line+"\n\nSlot machine commands:", 1) + balance := fmt.Sprintf("You have %s chips and %s credits.", chips, credits) + switch menu.Kind { + case casino.MenuBlackjackBet: + command := func(value string) string { return g.colorize(sess, "casino_command", value) } + return fmt.Sprintf("%s\n\nNew hand! You can %s, %s (inc. min/max), or %s.", + balance, command("bet"), command("bet <amount>"), command("stop")) + case casino.MenuBlackjackTurn: + commands := make([]string, len(menu.Actions)) + for i, action := range menu.Actions { + commands[i] = g.colorize(sess, "casino_command", action) + } + return menu.Title + "\nActions: " + strings.Join(commands, ", ") + case casino.MenuSlotCommands: + if message == "" { + return balance + "\n\n" + menu.Body + } + return message + "\n\n" + balance + "\n\n" + menu.Body + } + return message +} + +func renderBlackjackSnapshot(snapshot *casino.BlackjackSnapshot, mode string, unicode bool, size int, style string, art *cardart.Store) string { + if snapshot == nil { + return "" + } + size = blackjackCardSize(unicode, size) + var lines []string + dealerTitle := "Dealer:" + if snapshot.DealerRevealed { + dealerTitle = "Dealer (" + snapshot.DealerScore + "):" + } else if snapshot.DealerShownScore != "" { + dealerTitle = "Dealer (" + snapshot.DealerShownScore + "):" + } + lines = append(lines, blackjackCardLine(mode, dealerTitle)) + lines = append(lines, blackjackCardsLine(mode, snapshot.Dealer, size, unicode, style, art)...) + for _, player := range snapshot.Players { + lines = append(lines, blackjackCardLine(mode, fmt.Sprintf("%s (%s):", player.Name, player.Score))) + lines = append(lines, blackjackCardsLine(mode, player.Cards, size, unicode, style, art)...) + } + return strings.Join(lines, "\n") +} + +func blackjackCardLine(mode, text string) string { + spec := color.NoColor() + spec.Fg = 39 + return color.Render(mode, spec, text) +} + +func blackjackCardsLine(mode string, cards []casino.BlackjackCardView, size int, unicode bool, style string, art *cardart.Store) []string { + if len(cards) == 0 { + return nil + } + if size == 1 { + parts := make([]string, len(cards)) + for i, card := range cards { + parts[i] = colorizeCardSymbol(mode, card) + } + return []string{strings.Join(parts, " ")} + } + artLines := make([][]string, len(cards)) + for i, card := range cards { + artLines[i] = blackjackCardArt(card, size, unicode, style, art) + } + lines := make([]string, size) + for row := 0; row < size; row++ { + var lineParts []string + for i, card := range artLines { + lineParts = append(lineParts, colorizeCardLine(mode, card[row], cards[i], row, size, style)) + } + lines[row] = strings.Join(lineParts, " ") + } + return lines +} + +func blackjackCardSize(unicode bool, size int) int { + if !unicode { + return 5 + } + if size == 1 || size == 5 || size == 7 || size == 9 { + return size + } + return 7 +} + +var blackjackSuitSymbols = map[string]string{"hearts": "♥", "spades": "♠", "diamonds": "♦", "clubs": "♣"} + +func blackjackSymbol(card casino.BlackjackCardView) string { + if card.Hidden { + return "?" + } + return card.Rank + blackjackSuitSymbols[card.Suit] +} + +func blackjackCardArt(card casino.BlackjackCardView, size int, unicode bool, style string, art *cardart.Store) []string { + if card.Hidden { + return generatedCardArt("?", "", size, unicode, style) + } + key := card.Rank + strings.ToUpper(card.Suit[:1]) + if configured := art.Art(key, size); len(configured) == size { + return configured + } + return generatedCardArt(card.Rank, card.Suit, size, unicode, style) +} + +func generatedCardArt(rank, suit string, size int, unicode bool, style string) []string { + if size < 5 { + size = 5 + } + border := "-" + left, right := "+", "+" + if unicode { + border, left, right = "─", "┌", "┐" + } + bottomLeft, bottomRight := left, right + if unicode { + bottomLeft, bottomRight = "└", "┘" + } + vertical := "|" + if unicode { + vertical = "│" + } + if unicode && style == "light" { + lines := []string{strings.Repeat("▀", size)} + lines = append(lines, generatedCardBody(rank, suit, size, unicode, vertical)...) + return append(lines, strings.Repeat("▄", size)) + } + lines := []string{left + strings.Repeat(border, size-2) + right} + lines = append(lines, generatedCardBody(rank, suit, size, unicode, vertical)...) + lines = append(lines, bottomLeft+strings.Repeat(border, size-2)+bottomRight) + return lines +} + +func generatedCardBody(rank, suit string, size int, unicode bool, vertical string) []string { + inner := size - 2 + lines := make([]string, 0, size-2) + for row := 1; row < size-1; row++ { + content := strings.Repeat(" ", inner) + if row == 1 { + content = rank + strings.Repeat(" ", inner-len(rank)) + } + if row == (size-1)/2 { + suitText := "" + if suit != "" { + suitText = suitLetter(suit, unicode) + } + suitWidth := utf8.RuneCountInString(suitText) + leftPadding := (inner - suitWidth) / 2 + content = strings.Repeat(" ", leftPadding) + suitText + strings.Repeat(" ", inner-leftPadding-suitWidth) + } + if row == size-2 { + content = strings.Repeat(" ", inner-len(rank)) + rank + } + lines = append(lines, vertical+content+vertical) + } + return lines +} + +func suitLetter(suit string, unicode bool) string { + if !unicode { + return strings.ToUpper(suit[:1]) + } + return blackjackSuitSymbols[suit] +} + +func colorizeCardLine(mode, line string, card casino.BlackjackCardView, row, size int, style string) string { + red := card.Suit == "hearts" || card.Suit == "diamonds" + rankColor := 27 + if red { + rankColor = 196 + } + suitColor := 27 + if red { + suitColor = 196 + } + rankLine := row == 1 || row == size-2 + light := style == "light" + bodyBackground := light && row > 0 && row < size-1 + var builder strings.Builder + for _, r := range line { + if strings.ContainsRune("┌─┐└┘│┬┴├┤┼╔╗╚╝═║╦╩╠╣╬▀▄+|-", r) { + spec := color.ColorSpec{Fg: 15} + if bodyBackground { + spec.Bg = 15 + } + builder.WriteString(color.Render(mode, spec, string(r))) + } else if r == ' ' { + if bodyBackground { + builder.WriteString(color.Render(mode, color.ColorSpec{Fg: 15, Bg: 15}, " ")) + } else { + builder.WriteRune(r) + } + } else { + value := suitColor + if rankLine { + value = rankColor + } + spec := color.ColorSpec{Fg: value} + if bodyBackground { + spec.Bg = 15 + } + builder.WriteString(color.Render(mode, spec, string(r))) + } + } + return builder.String() +} + +func colorizeCardSymbol(mode string, card casino.BlackjackCardView) string { + if card.Hidden { + return color.Render(mode, color.ColorSpec{Fg: 15}, "?") + } + rankColor, suitColor := 27, 27 + if card.Suit == "hearts" || card.Suit == "diamonds" { + rankColor, suitColor = 196, 196 + } + runes := []rune(blackjackSymbol(card)) + rankLength := len([]rune(card.Rank)) + return color.Render(mode, color.ColorSpec{Fg: rankColor}, string(runes[:rankLength])) + + color.Render(mode, color.ColorSpec{Fg: suitColor}, string(runes[rankLength:])) } func renderSlotSnapshot(snapshot *casino.SlotSnapshot, mode string, unicode bool) string { @@ -295,23 +555,17 @@ func colorSlotSymbol(mode, symbol string) string { spec.Fg = 250 switch symbol { case "straw": - spec = color.NoColor() - spec.Gradient = []int{220, 226} + spec.Fg = 220 case "stick": - spec = color.NoColor() - spec.Gradient = []int{34, 46} + spec.Fg = 46 case "brick": - spec = color.NoColor() - spec.Gradient = []int{160, 196} + spec.Fg = 196 case "hat": - spec = color.NoColor() - spec.Gradient = []int{129, 201} + spec.Fg = 201 case "wolf": - spec = color.NoColor() - spec.Gradient = []int{27, 39} + spec.Fg = 39 case "scatter": - spec = color.NoColor() - spec.Gradient = []int{196, 220, 46, 51, 129, 201} + spec.Fg = 226 default: spec.Dim = true } diff --git a/internal/game/casino_test.go b/internal/game/casino_test.go index b2b37c4..b74baab 100644 --- a/internal/game/casino_test.go +++ b/internal/game/casino_test.go @@ -6,8 +6,45 @@ import ( "thehouseoficarus/internal/casino" "thehouseoficarus/internal/color" + "thehouseoficarus/internal/net" + "thehouseoficarus/internal/player" ) +type casinoTestConn struct { + data []byte +} + +func (c *casinoTestConn) ReadMessage() (string, error) { return "", nil } +func (c *casinoTestConn) Write(b []byte) (int, error) { + c.data = append(c.data, b...) + return len(b), nil +} +func (c *casinoTestConn) Close() error { return nil } +func (c *casinoTestConn) SetEcho(bool) error { return nil } + +func TestExecuteBetRejectsInvalidMachineArgs(t *testing.T) { + g := &Game{Casino: casino.NewManager(1)} + cfg := casino.MachineConfig{ID: "slots", Game: casino.GameSlots} + g.Casino.JoinMachine(1, cfg, "alice") + conn := &casinoTestConn{} + sess := &net.Session{Player: player.New("alice"), Conn: conn} + g.loggedInChars = map[string]*net.Session{"alice": sess} + for _, args := range [][]string{{"banana"}, {"-5"}, {"10", "extra"}} { + conn.data = nil + g.executeBet(sess, args, "bet "+strings.Join(args, " ")) + if !strings.Contains(string(conn.data), "Use bet, bet <amount>, or bet max.") { + t.Fatalf("bet %v: expected usage error, got %q", args, string(conn.data)) + } + } + // A bare bet at a machine reaches the spin path instead of erroring. + g.Casino.SetMachineBet(1, "slots", "alice", 10) + conn.data = nil + g.executeBet(sess, nil, "bet") + if !strings.Contains(string(conn.data), "You don't have enough chips and credits") { + t.Fatalf("bare bet should attempt a spin, got %q", string(conn.data)) + } +} + func TestRenderSlotSnapshotColorsWordSymbols(t *testing.T) { snapshot := &casino.SlotSnapshot{StoppedReels: 1} snapshot.Grid[0][0] = "scatter" @@ -27,3 +64,46 @@ func TestRenderSlotSnapshotColorsWordSymbols(t *testing.T) { t.Fatalf("slot output lacks the Unicode frame: %q", output) } } + +func TestRenderBlackjackSnapshotUsesCompactASCIIArt(t *testing.T) { + snapshot := &casino.BlackjackSnapshot{ + Dealer: []casino.BlackjackCardView{{Rank: "?", Hidden: true}, {Rank: "K", Suit: "hearts"}}, + Players: []casino.BlackjackPlayerView{{Name: "alice", Score: "20", Cards: []casino.BlackjackCardView{{Rank: "5", Suit: "clubs"}, {Rank: "Q", Suit: "spades"}}}}, + } + plain := renderBlackjackSnapshot(snapshot, "none", false, 5, "dark", nil) + if !strings.Contains(plain, "+---+") || !strings.Contains(plain, "Dealer:") || !strings.Contains(plain, "alice (20):") { + t.Fatalf("unexpected blackjack art: %q", plain) + } + output := renderBlackjackSnapshot(snapshot, "xterm256", false, 5, "dark", nil) + if !strings.Contains(output, "\033[38;5;") { + t.Fatalf("expected colored blackjack art: %q", output) + } + if strings.ContainsAny(output, "│─┌┐└┘") { + t.Fatalf("blackjack art contains Unicode borders: %q", output) + } + for _, line := range strings.Split(output, "\n") { + if color.VisibleLen(line) > 80 { + t.Fatalf("blackjack line exceeds console width: %d", color.VisibleLen(line)) + } + } + unicodeOutput := renderBlackjackSnapshot(snapshot, "none", true, 7, "dark", nil) + if !strings.Contains(unicodeOutput, "│5 │") || !strings.Contains(unicodeOutput, "│ 5│") || !strings.Contains(unicodeOutput, "│ ♣ │") { + t.Fatalf("unexpected Unicode card alignment: %q", unicodeOutput) + } + coloredUnicode := renderBlackjackSnapshot(snapshot, "xterm256", true, 7, "dark", nil) + if !strings.Contains(coloredUnicode, "\033[38;5;15m") || !strings.Contains(coloredUnicode, "\033[38;5;27m") { + t.Fatalf("expected white borders and blue card art: %q", coloredUnicode) + } + light := renderBlackjackSnapshot(snapshot, "xterm256", true, 7, "light", nil) + if !strings.Contains(light, "\033[48;5;15m") { + t.Fatalf("expected white card background in light style: %q", light) + } + lightLines := strings.Split(light, "\n") + if strings.Contains(lightLines[1], "\033[48;5;15m") || strings.Contains(lightLines[7], "\033[48;5;15m") { + t.Fatalf("card edges should not have a background: %q", light) + } + lightPlain := renderBlackjackSnapshot(snapshot, "none", true, 7, "light", nil) + if !strings.Contains(lightPlain, "▀▀▀▀▀▀▀") || !strings.Contains(lightPlain, "▄▄▄▄▄▄▄") { + t.Fatalf("expected half-height light card edges: %q", lightPlain) + } +} diff --git a/internal/game/cmd_option.go b/internal/game/cmd_option.go index 55a04de..d35ca12 100644 --- a/internal/game/cmd_option.go +++ b/internal/game/cmd_option.go @@ -62,6 +62,14 @@ func (g *Game) doOption(sess *net.Session, input string) { } value := strings.ToLower(parts[1]) + if def.Name == "card_size" { + switch value { + case "1", "5", "7", "9": + default: + sess.WriteLine("Invalid value for card_size: use 1, 5, 7, or 9.") + return + } + } parsed, ok := parseOptionValue(def, value) if !ok { valid := formatValidValues(def) @@ -73,11 +81,17 @@ func (g *Game) doOption(sess *net.Session, input string) { p.Options = make(map[string]any) } p.Options[def.Name] = parsed + if def.Name == "unicode" && parsed == false { + p.Options["card_size"] = 5 + } if sess.Account.Options == nil { sess.Account.Options = make(map[string]any) } sess.Account.Options[def.Name] = parsed + if def.Name == "unicode" && parsed == false { + sess.Account.Options["card_size"] = 5 + } acc, err := g.AccountStore.LoadAccount(sess.Account.Name) if err == nil { @@ -85,6 +99,9 @@ func (g *Game) doOption(sess *net.Session, input string) { acc.Options = make(map[string]any) } acc.Options[def.Name] = parsed + if def.Name == "unicode" && parsed == false { + acc.Options["card_size"] = 5 + } g.AccountStore.SaveAccount(acc) } sess.WriteLine(fmt.Sprintf("%s set to %s.", def.Name, formatOptionValue(p, def))) @@ -119,6 +136,9 @@ func formatValidValues(def *player.OptionDef) string { } return "string" case player.OptInt: + if def.Name == "card_size" { + return "1/5/7/9" + } return "num" } return "" diff --git a/internal/game/cmd_registry.go b/internal/game/cmd_registry.go index 4c41216..db6cd96 100644 --- a/internal/game/cmd_registry.go +++ b/internal/game/cmd_registry.go @@ -28,6 +28,12 @@ var commandRegistry = map[string]commandDef{ "spin": {(*Game).executeBet, ClassActive}, "autobet": {(*Game).executeAutoBet, ClassActive}, "autospin": {(*Game).executeAutoBet, ClassActive}, + "hit": {func(g *Game, s *net.Session, _ []string, _ string) { g.executeBlackjackAction(s, "hit") }, ClassActive}, + "stand": {func(g *Game, s *net.Session, _ []string, _ string) { g.executeBlackjackAction(s, "stand") }, ClassActive}, + "double": {func(g *Game, s *net.Session, _ []string, _ string) { g.executeBlackjackAction(s, "double") }, ClassActive}, + "split": {func(g *Game, s *net.Session, _ []string, _ string) { g.executeBlackjackAction(s, "split") }, ClassActive}, + "insurance": {func(g *Game, s *net.Session, _ []string, _ string) { g.executeBlackjackAction(s, "insurance") }, ClassActive}, + "surrender": {func(g *Game, s *net.Session, _ []string, _ string) { g.executeBlackjackAction(s, "surrender") }, ClassActive}, "style": {(*Game).executeStyle, ClassInstant}, "look": {(*Game).executeLook, ClassInstant}, "l": {(*Game).executeLook, ClassInstant}, diff --git a/internal/game/game.go b/internal/game/game.go index cac0ec9..23e0635 100644 --- a/internal/game/game.go +++ b/internal/game/game.go @@ -2,11 +2,13 @@ package game import ( "fmt" + "path/filepath" "sort" "strings" "sync" "time" + "thehouseoficarus/internal/cardart" "thehouseoficarus/internal/casino" "thehouseoficarus/internal/combat" "thehouseoficarus/internal/config" @@ -42,6 +44,7 @@ type Deps struct { Ticks *engine.Engine ColorConfig *config.ColorsConfig ConstantColorConfig *config.ColorsConfig + CardArt *cardart.Store DataDir string } @@ -91,6 +94,7 @@ func New(dataDir string, colorConfig *config.ColorsConfig, constantColorConfig * Ticks: engine.New(), ColorConfig: colorConfig, ConstantColorConfig: constantColorConfig, + CardArt: cardart.Load(filepath.Join(dataDir, "cards.yaml")), DataDir: dataDir, }, GlobalFlags: NewGlobalFlagStore(), diff --git a/internal/game/render_help.go b/internal/game/render_help.go index 8af3096..afd5b86 100644 --- a/internal/game/render_help.go +++ b/internal/game/render_help.go @@ -57,6 +57,8 @@ var commandList = []cmdEntry{ {"bet", "Active", "Place a casino wager"}, {"autobet / autospin", "Active", "Automatically repeat a casino wager"}, {"spin", "Active", "Spin the current casino wager"}, + {"hit / stand / double / split", "Active", "Play a blackjack hand"}, + {"insurance / surrender", "Active", "Use blackjack table options"}, {"mine", "Active", "Mine rocks (Mining)"}, {"mix", "Active", "Mix potions (Pharmacy)"}, {"mods / modlist", "Instant", "List available science modules"}, |
