aboutsummaryrefslogtreecommitdiff
path: root/internal/casino/roulette.go
diff options
context:
space:
mode:
authorhistoria <[not public]>2026-10-01 19:30:38 -0400
committerhistoria <[not public]>2026-10-01 19:30:38 -0400
commit6d9908b29c8187196d23b126b1165c7e0bd0fc76 (patch)
treeddbc02357ed92646b801687b90b1a06c71a15d71 /internal/casino/roulette.go
parent8a94f21bd47c0a00bc415395232dba22fa958837 (diff)
downloadthehouseoficarus-6d9908b29c8187196d23b126b1165c7e0bd0fc76.tar.gz
fix: casino pai gow sorting, universal unbet, table/rules split, remove solo timers
Diffstat (limited to 'internal/casino/roulette.go')
-rw-r--r--internal/casino/roulette.go45
1 files changed, 37 insertions, 8 deletions
diff --git a/internal/casino/roulette.go b/internal/casino/roulette.go
index 761af47..163ed20 100644
--- a/internal/casino/roulette.go
+++ b/internal/casino/roulette.go
@@ -482,6 +482,19 @@ func newRouletteTable(room int, cfg TableConfig, rng *rand.Rand) *rouletteTable
cfg = cfg.Normalize()
return &rouletteTable{tableCore: newTableCore[*roulettePlayer](&tableBase{RoomID: room, Config: cfg}), rng: rng}
}
+
+// activePlayersLocked counts connected, non-sitting-out seats. A lone player
+// never has a betting deadline; the wheel spins as soon as they wager.
+func (t *rouletteTable) activePlayersLocked() int {
+ n := 0
+ for _, p := range t.players.ordered() {
+ if p.Connected && !p.SittingOut {
+ n++
+ }
+ }
+ return n
+}
+
func (t *rouletteTable) view() *RouletteSnapshot {
s := &RouletteSnapshot{DoubleZero: t.Config.Roulette.DoubleZero, Racetrack: t.Config.Roulette.Racetrack, Phase: string(t.phase), Wheel: rouletteWheel(t.Config.Roulette.DoubleZero), History: append([]string(nil), t.history...)}
for _, p := range t.players.ordered() {
@@ -569,15 +582,24 @@ func (t *rouletteTable) bet(name, spot string, amount int, w Wallet) []Event {
}
p.HasBet, p.Wager, p.Wallet, p.lastSpot, p.lastBet, p.SittingOut = true, wager, w, spot, amount, false
events := []Event{t.playerAction(name, fmt.Sprintf("%s bets %d on %s.", name, amount, spot), fmt.Sprintf("You bet %d on %s.", amount, spot))}
- // Always offer a real betting phase, even with only one player. The
- // board is not attached here: it is sent once per round by the menu event
- // (join, settlement, or an explicit "table"), so placing a wager never
- // redraws it.
+ // With two or more players a timed betting window lets the table bet
+ // together. A lone player has no time limit: once their wager lands,
+ // the wheel spins. The board is not attached here: it is sent once per
+ // round by the menu event (join, settlement, or an explicit "table"), so
+ // placing a wager never redraws it.
if t.phase == phaseWaiting {
- t.phase = phaseBetting
- t.window.open(t.Config.BettingTicks)
- e := t.event(fmt.Sprintf("Roulette betting is open for %d ticks.", t.Config.BettingTicks), true)
- events = append(events, e)
+ if t.activePlayersLocked() > 1 {
+ t.phase = phaseBetting
+ t.window.open(t.Config.BettingTicks)
+ e := t.event(fmt.Sprintf("Roulette betting is open for %d ticks.", t.Config.BettingTicks), true)
+ events = append(events, e)
+ } else {
+ t.phase = phasePlaying
+ t.spinRemaining = t.Config.Roulette.SpinTicks
+ e := t.event("No more bets! The roulette wheel spins...", true)
+ e.Snapshot = t.view()
+ events = append(events, e)
+ }
}
return events
}
@@ -692,6 +714,13 @@ func (t *rouletteTable) resetRound() {
func (t *rouletteTable) action(name, action string) []Event {
t.mu.Lock()
defer t.mu.Unlock()
+ if action == "unbet" {
+ events, _ := t.unbetLocked(name)
+ if !anyBet(t.players.participants()) {
+ t.resetRound()
+ }
+ return events
+ }
if events, ok := t.controlLocked(name, action); ok {
return events
}