aboutsummaryrefslogtreecommitdiff
path: root/internal
diff options
context:
space:
mode:
Diffstat (limited to 'internal')
-rw-r--r--internal/casino/command_hints_test.go10
-rw-r--r--internal/casino/roulette.go21
-rw-r--r--internal/game/look_casino.go9
-rw-r--r--internal/game/look_casino_test.go13
-rw-r--r--internal/game/roulette_animation_test.go10
5 files changed, 42 insertions, 21 deletions
diff --git a/internal/casino/command_hints_test.go b/internal/casino/command_hints_test.go
index bd15883..b391526 100644
--- a/internal/casino/command_hints_test.go
+++ b/internal/casino/command_hints_test.go
@@ -119,23 +119,23 @@ func TestOtherCasinoHintsRespectCommitment(t *testing.T) {
r := newRouletteTable(1, TableConfig{ID: "r", Game: GameRoulette, BettingTicks: 10}, newTestRand())
r.join("a")
r.join("b")
- if hintHas(r.menuEvent("a").Menu.Commands, "bet / rebet") {
+ if hintHas(r.menuEvent("a", true).Menu.Commands, "bet / rebet") {
t.Fatal("roulette no wager to repeat")
}
r.bet("a", "red", 10, &testWallet{chips: 1000})
- if hintBet(r.menuEvent("a").Menu.Commands) {
+ if hintBet(r.menuEvent("a", true).Menu.Commands) {
t.Fatal("roulette duplicate bet advertised")
}
- if !hintBet(r.menuEvent("b").Menu.Commands) {
+ if !hintBet(r.menuEvent("b", true).Menu.Commands) {
t.Fatal("roulette missing eligible bet")
}
r.phase = phasePlaying
- if hintBet(r.menuEvent("b").Menu.Commands) {
+ if hintBet(r.menuEvent("b", true).Menu.Commands) {
t.Fatal("roulette spinning bets advertised")
}
r.phase = phaseWaiting
r.players.get("a").HasBet = false
- if !hintHas(r.menuEvent("a").Menu.Commands, "bet / rebet") {
+ if !hintHas(r.menuEvent("a", true).Menu.Commands, "bet / rebet") {
t.Fatal("roulette repeat missing")
}
}
diff --git a/internal/casino/roulette.go b/internal/casino/roulette.go
index 783d22f..8f45d49 100644
--- a/internal/casino/roulette.go
+++ b/internal/casino/roulette.go
@@ -510,7 +510,11 @@ func (t *rouletteTable) board(_ string) Snapshot {
defer t.mu.Unlock()
return t.view()
}
-func (t *rouletteTable) menuEvent(name string) Event {
+
+// menuEvent builds the next-spin menu. board controls whether it also carries
+// the table graphic: sitting down and the live status keep it, settlement does
+// not, so a resolved spin does not redraw the wheel, racetrack, and history.
+func (t *rouletteTable) menuEvent(name string, board bool) Event {
commands := []MenuCommand{}
p := t.players.get(name)
if t.phase != phasePlaying && p != nil && !p.HasBet {
@@ -527,20 +531,22 @@ func (t *rouletteTable) menuEvent(name string) Event {
commands = withTableHints(commands, t.wagerRows())
}
e := t.betMenuEvent(name, commands)
- e.Snapshot = t.view()
+ if board {
+ e.Snapshot = t.view()
+ }
return e
}
func (t *rouletteTable) join(name string) []Event {
t.mu.Lock()
defer t.mu.Unlock()
if t.players.has(name) {
- return []Event{t.menuEvent(name)}
+ return []Event{t.menuEvent(name, true)}
}
if t.players.count() >= t.Config.MaxPlayers {
return []Event{t.private(name, "That roulette table is full.")}
}
e := t.addSeatLocked(name, &roulettePlayer{Participant: Participant{Name: name, Connected: true}})
- return []Event{e, t.menuEvent(name)}
+ return []Event{e, t.menuEvent(name, true)}
}
func (t *rouletteTable) leave(name string) []Event {
t.mu.Lock()
@@ -715,7 +721,8 @@ func (t *rouletteTable) settle() []Event {
wheel[(index+1)%n], wheel[(index+2)%n],
}
t.lastResult = s
- e := t.event("The ball lands in {casino_emphasis}"+pocket+"{/}!", true)
+ // A leading blank line separates the reveal from the spinning strips above.
+ e := t.event("\nThe ball lands in {casino_emphasis}"+pocket+"{/}!", true)
e.Snapshot = s
events := []Event{e}
for _, p := range t.players.ordered() {
@@ -762,7 +769,7 @@ func (t *rouletteTable) settle() []Event {
events = append(events, t.finishControlsLocked()...)
for _, p := range t.players.ordered() {
if p.Connected && !p.SittingOut {
- events = append(events, t.menuEvent(p.Name))
+ events = append(events, t.menuEvent(p.Name, false))
}
}
return events
@@ -799,7 +806,7 @@ func (t *rouletteTable) rebet(name string) (string, int) {
func (t *rouletteTable) status(name string) []Event {
t.mu.Lock()
defer t.mu.Unlock()
- e := t.menuEvent(name)
+ e := t.menuEvent(name, true)
events := []Event{e}
if t.phase == phaseWaiting && t.lastResult != nil {
last := t.private(name, "Last spin:")
diff --git a/internal/game/look_casino.go b/internal/game/look_casino.go
index 1e91ced..8d2b5c3 100644
--- a/internal/game/look_casino.go
+++ b/internal/game/look_casino.go
@@ -66,7 +66,8 @@ func (g *Game) resolveRoomTable(sess *net.Session, target string, game casino.Ga
}
// renderCasinoTableDetails formats the details block and, for the felt games,
-// the per-table rule card. It never prints the craps or roulette board.
+// the per-table rule card. Roulette adds its live wheel; craps never prints a
+// board here.
func (g *Game) renderCasinoTableDetails(sess *net.Session, cfg casino.TableConfig, table *casino.Table) string {
seats := table.Seats()
seated := "no one"
@@ -82,6 +83,12 @@ func (g *Game) renderCasinoTableDetails(sess *net.Session, cfg casino.TableConfi
fmt.Sprintf(" Seated %s", seated),
}
switch cfg.Game {
+ case casino.GameRoulette:
+ // Look, like sitting down and the table command, shows the live
+ // wheel: tracker, racetrack, and betting layout.
+ if rendered := strings.TrimRight(g.renderCasinoSnapshot(sess, table.Board(sess.Player.Name)), "\n"); rendered != "" {
+ lines = append(lines, "", rendered)
+ }
case casino.GameBlackjack:
lines = append(lines, "", g.renderCasinoSnapshot(sess, &casino.BlackjackRulesView{Config: cfg}))
case casino.GameBaccarat:
diff --git a/internal/game/look_casino_test.go b/internal/game/look_casino_test.go
index 37205cd..651c74f 100644
--- a/internal/game/look_casino_test.go
+++ b/internal/game/look_casino_test.go
@@ -28,7 +28,7 @@ casino_tables:
envy: true
`
-func TestLookCasinoTableShowsDetailsWithoutInvariantBoards(t *testing.T) {
+func TestLookCasinoTableDetailsAndBoards(t *testing.T) {
g, sess, conn := casinoCrapsGame(t, lookCasinoRoom)
// Craps: details only. The graphical felt and the full rule dump are gone.
@@ -48,7 +48,7 @@ func TestLookCasinoTableShowsDetailsWithoutInvariantBoards(t *testing.T) {
}
}
- // Roulette: details only, no betting board or payout reference.
+ // Roulette: details plus the live wheel (tracker, layout, and history).
conn.data = nil
if !g.lookRouletteTable(sess, "roulette") {
t.Fatal("look roulette did not match")
@@ -57,10 +57,11 @@ func TestLookCasinoTableShowsDetailsWithoutInvariantBoards(t *testing.T) {
if !strings.Contains(text, "Roulette / Single zero") || !strings.Contains(text, "single-zero wheel") {
t.Fatalf("look roulette missing variant summary: %s", text)
}
- for _, gone := range []string{"Roulette payouts", "History: last"} {
- if strings.Contains(text, gone) {
- t.Fatalf("look roulette showed invariant board %q: %s", gone, text)
- }
+ if !strings.Contains(text, "History: last") {
+ t.Fatalf("look roulette missing the live board: %s", text)
+ }
+ if strings.Contains(text, "Roulette payouts") {
+ t.Fatalf("look roulette still dumps the payout reference: %s", text)
}
}
diff --git a/internal/game/roulette_animation_test.go b/internal/game/roulette_animation_test.go
index 96f14bb..d7f5626 100644
--- a/internal/game/roulette_animation_test.go
+++ b/internal/game/roulette_animation_test.go
@@ -46,8 +46,14 @@ func TestRouletteSpinOutputHasNoPromptOrBlankLines(t *testing.T) {
}
}
g.CasinoTick()
- if !strings.Contains(string(conn.data), "The ball lands in") || g.rouletteSpinning(sess) {
- t.Fatal("result did not end animation")
+ result := strings.ReplaceAll(string(conn.data), "\r\n", "\n")
+ if !strings.HasPrefix(result, "\nThe ball lands in") || g.rouletteSpinning(sess) {
+ t.Fatalf("result did not start on a blank line or end animation: %q", result)
+ }
+ // A resolved spin shows the result and the next-spin menu, but not the
+ // tracker, racetrack, and history again.
+ if strings.Contains(result, "History: last") {
+ t.Fatalf("settlement redrew the table board: %q", result)
}
sess.ClearPrompt()
conn.data = nil