diff options
Diffstat (limited to 'internal/game/casino_ux.go')
| -rw-r--r-- | internal/game/casino_ux.go | 141 |
1 files changed, 138 insertions, 3 deletions
diff --git a/internal/game/casino_ux.go b/internal/game/casino_ux.go index a2e7330..2881a49 100644 --- a/internal/game/casino_ux.go +++ b/internal/game/casino_ux.go @@ -8,6 +8,8 @@ import ( "thehouseoficarus/internal/casino" "thehouseoficarus/internal/net" + "thehouseoficarus/internal/player" + "thehouseoficarus/internal/world" ) type casinoChoice struct { @@ -104,6 +106,8 @@ func (g *Game) executeTableStatus(sess *net.Session, args []string, _ string) { t, playing := g.Casino.Participation(sess.Player.Name) if !playing || t == nil { if len(args) > 0 && args[0] == "help" { + // Machine players get their cabinet's guide; everyone else the + // shared casino one. topic := "casino" if playing { topic = "slots" @@ -115,17 +119,19 @@ func (g *Game) executeTableStatus(sess *net.Session, args []string, _ string) { return } if len(args) > 0 && args[0] == "rules" { + // The felt and the exact, per-table wording stay under table rules; + // bare rules opens the game's own help page. sess.WriteLine(g.renderCasinoRules(sess, t.Config)) return } if len(args) > 0 && args[0] == "help" { - g.doHelp(sess, string(t.Config.Game)) + g.doHelp(sess, t.HelpTopic()) } else { g.emitCasinoEvents(t.Status(sess.Player.Name)) if t.Config.Game == casino.GameRoulette { sess.WriteLine(g.rouletteReminder(sess, t.Config)) } else { - sess.WriteLine(g.colorize(sess, "command", "Learn to play: table help / help casino. Exact payouts: table rules.")) + sess.WriteLine(g.colorize(sess, "command", "Learn to play: rules. Exact payouts: table rules.")) } } if len(args) > 0 { @@ -133,8 +139,10 @@ func (g *Game) executeTableStatus(sess *net.Session, args []string, _ string) { // (insurance, split, rebet, etc.) that may not be legal right now. g.emitCasinoEvents(t.Status(sess.Player.Name)) commands := []casino.MenuCommand{ + {Command: "rules", Desc: "Help page for this game variant"}, + {Command: "list", Desc: "Every bet available right now, with payouts"}, + {Command: "table rules", Desc: "This table’s exact rules and payouts"}, {Command: "option casino_display compact", Desc: "Compact action hints (detailed for full command menus)"}, - {Command: "table rules", Desc: "Review this table’s rules"}, {Command: "help casino", Desc: "Quick guides to all casino games"}, } sess.WriteLine(g.renderCasinoCommands(sess, "", "Table controls", commands)) @@ -236,6 +244,14 @@ func (g *Game) casinoTimeText(text string) string { }) } +// betRangeWords renders the wager range in words for one-line hints. +func betRangeWords(minBet, maxBet int) string { + if maxBet <= 0 || maxBet >= casino.DefaultMaxBet { + return fmt.Sprintf("%d or more", minBet) + } + return fmt.Sprintf("%d-%d", minBet, maxBet) +} + func (g *Game) casinoBalance(sess *net.Session) string { if sess.Player == nil { return "" @@ -243,3 +259,122 @@ func (g *Game) casinoBalance(sess *net.Session) string { p := sess.Player return fmt.Sprintf("Available %s (%s chips + %s credits)", g.colorize(sess, "casino_chips", strconv.Itoa(casinoAffordable(p))), g.colorize(sess, "casino_chips", strconv.Itoa(countChips(p))), g.colorize(sess, "casino_credits", strconv.Itoa(p.Credits))) } + +// executeRules is the seated table's own help entry point: a variant's page +// (Simplified Craps gets its own short page, not a section of the craps +// manual), slots help at a machine, and the room's own game while merely +// standing in a casino room. Explicit topics pass straight through to help. +func (g *Game) executeRules(sess *net.Session, args []string, rawInput string) { + if sess.Player == nil { + return + } + if len(args) > 0 { + g.doHelp(sess, strings.Join(args, " ")) + return + } + if g.Casino == nil { + g.doHelp(sess, "casino") + return + } + if topic := g.casinoRulesTopic(sess.Player.RoomID, sess.Player.Name); topic != "" { + g.doHelp(sess, topic) + return + } + // Not in a casino game or room: the shared guide still answers. + g.doHelp(sess, "casino") +} + +// casinoRulesTopic picks the help page "rules" should open: the seated +// table's variant page first, then a machine's game, then the room's own +// casino games (so standing in the craps pit answers about craps). Mixed +// games, or no casino content at all, fall back to the shared guide. +func (g *Game) casinoRulesTopic(roomID int, name string) string { + if table, playing := g.Casino.Participation(name); playing && table != nil { + return table.HelpTopic() + } + if machine, ok := g.Casino.MachineFor(name); ok { + return string(machine.Config.Game) + } + room, err := g.World.LoadRoom(roomID) + if err != nil { + return "" + } + topic := "" + for _, cfg := range room.CasinoTables { + page := cfg.HelpTopic() + if topic == "" { + topic = page + } else if topic != page { + return "casino" + } + } + for _, m := range room.CasinoMachines { + page := string(m.Game) + if topic == "" { + topic = page + } else if topic != page { + return "casino" + } + } + return topic +} + +// executeList shows the bets a casino game accepts right now — the game's +// own state-driven list, from the come-out pass line to the travelling come +// bets and one-roll props. Everywhere else (shops, plain rooms) "list" +// keeps its shop meaning. +func (g *Game) executeList(sess *net.Session, args []string, rawInput string) { + p := sess.Player + if p == nil { + return + } + if len(args) > 0 || g.Casino == nil { + g.executeShopList(sess, args, rawInput) + return + } + if machine, ok := g.Casino.MachineFor(p.Name); ok { + sess.WriteLine(g.colorize(sess, "casino_action", "A "+casinoGameLabel(machine.Config.Game)+" takes one wager: bet <amount> (or bet max), then spin or autospin.")) + return + } + if table, playing := g.Casino.Participation(p.Name); playing && table != nil { + g.showCasinoWagerList(sess, table) + return + } + g.executeShopList(sess, args, rawInput) +} + +// showCasinoWagerList renders the game's live wager list. Two or more rows +// become a brief table; a single row is one line; an empty list means the +// only bet is the ordinary amount-only wager. +func (g *Game) showCasinoWagerList(sess *net.Session, t *casino.Table) { + rows := t.Wagers(sess.Player.Name) + switch { + case len(rows) == 1: + r := rows[0] + sess.WriteLine(g.colorize(sess, "casino_action", "Your only wager: bet <amount> on "+r.Bet+" — "+r.Payout+" — "+r.Win+".")) + case len(rows) == 0: + sess.WriteLine(g.colorize(sess, "casino_action", "The only wager here is bet <amount>") + " (" + betRangeWords(t.Config.MinBet, t.Config.MaxBet) + ").") + default: + commands := make([]casino.MenuCommand, 0, len(rows)) + for _, r := range rows { + commands = append(commands, casino.MenuCommand{Command: "bet <amount> on " + r.Bet, Desc: r.Payout + " | " + r.Win}) + } + sess.WriteLine(g.renderCasinoCommands(sess, g.casinoBalance(sess), t.Config.Label()+" — available bets", commands)) + } +} + +// showCasinoGameLook appends the live game state and hint menu for the +// table the player is seated at, so look reads room first, then the table, +// then the commands that matter now. Machines are skipped: their menus +// arrive with every spin. +func (g *Game) showCasinoGameLook(sess *net.Session, p *player.Player, room *world.Room) { + if g.Casino == nil || p == nil { + return + } + table, playing := g.Casino.Participation(p.Name) + if !playing || table == nil || table.RoomID != room.ID { + return + } + sess.WriteLine("") + g.emitCasinoEvents(table.Status(p.Name)) +} |
