aboutsummaryrefslogtreecommitdiff
path: root/internal/casino/table.go
diff options
context:
space:
mode:
Diffstat (limited to 'internal/casino/table.go')
-rw-r--r--internal/casino/table.go62
1 files changed, 30 insertions, 32 deletions
diff --git a/internal/casino/table.go b/internal/casino/table.go
index 09c19de..4be60a0 100644
--- a/internal/casino/table.go
+++ b/internal/casino/table.go
@@ -1,6 +1,7 @@
package casino
import (
+ "fmt"
"math/rand"
)
@@ -17,55 +18,71 @@ type tableGame interface {
markDisconnected(name string) []Event
hasParticipant(name string) bool
refundAll() []Event
- setRoom(roomID int)
idle() bool
+ // base returns the table identity shared with the Table handle.
+ base() *tableBase
}
-// tableBase carries the identity every table game needs to build events.
+// tableBase is the identity every table game shares with its Table handle.
+// The Table and the embedded tableGame hold the SAME *tableBase, so the room
+// ID has a single source of truth: a rekey updates it once and every event the
+// game builds follows, with no second field to keep in sync.
type tableBase struct {
RoomID int
Config TableConfig
}
-func (b tableBase) event(message string, public bool) Event {
+func (b *tableBase) event(message string, public bool) Event {
return Event{RoomID: b.RoomID, TableID: b.Config.ID, Type: EventResult, Message: message, Public: public, Color: "casino_action"}
}
-func (b tableBase) private(name, message string) Event {
+func (b *tableBase) private(name, message string) Event {
e := b.event(message, false)
e.PrivateTo = name
return e
}
-func (b tableBase) playerAction(name, publicMessage, privateMessage string) Event {
+func (b *tableBase) playerAction(name, publicMessage, privateMessage string) Event {
e := b.event(publicMessage, true)
e.PrivateTo = name
e.PrivateMessage = privateMessage
return e
}
+// betMenuEvent builds the shared "place your bet" menu heading for a table
+// game; only the menu kind and command list differ per game.
+func (b *tableBase) betMenuEvent(name string, kind MenuKind, commands []MenuCommand) Event {
+ e := b.private(name, fmt.Sprintf("New %s hand! Place your bet.", b.Config.Game))
+ e.Color = "casino_menu"
+ e.Menu = &MenuView{Kind: kind, Commands: commands}
+ return e
+}
+
// Table is the manager-facing handle for a multiplayer table. It only holds
-// routing state; all game logic lives in the tableGame implementation.
+// routing state; all game logic lives in the tableGame implementation. It
+// shares one *tableBase with the game, so RoomID/Config are a single source of
+// truth for both.
type Table struct {
- RoomID int
- Config TableConfig
- game tableGame
+ *tableBase
+ game tableGame
}
// newTable builds the table game for cfg.Game, or nil when the game has no
// multiplayer table implementation.
func newTable(roomID int, cfg TableConfig, rng *rand.Rand) *Table {
cfg = cfg.Normalize()
- t := &Table{RoomID: roomID, Config: cfg}
+ var game tableGame
switch cfg.Game {
case GameBlackjack:
- t.game = newBlackjackTable(roomID, cfg, rng)
+ game = newBlackjackTable(roomID, cfg, rng)
case GameBaccarat:
- t.game = newBaccaratTable(roomID, cfg, rng)
+ game = newBaccaratTable(roomID, cfg, rng)
default:
return nil
}
- return t
+ // The Table and the game share the game's base, so RoomID/Config are a
+ // single source of truth for both.
+ return &Table{tableBase: game.base(), game: game}
}
func (t *Table) join(name string) []Event { return t.game.join(name) }
@@ -86,24 +103,5 @@ func (t *Table) hasParticipant(name string) bool { return t.game.hasPartici
// shutdown): stakes return to their players through the wallet.
func (t *Table) refundAll() []Event { return t.game.refundAll() }
-// setRoom updates the table game's room for event routing after a room-ID
-// rekey. Games keep their state; only their location moves.
-func (b *tableBase) setRoom(roomID int) { b.RoomID = roomID }
-
-// idle reports whether no player is seated (or mid-round): only an idle
-// table may have its live rules replaced by new room configuration.
-func (t *blackjackTable) idle() bool {
- t.mu.Lock()
- defer t.mu.Unlock()
- return t.players.count() == 0
-}
-
-// idle reports whether no player is seated (or mid-round).
-func (t *baccaratTable) idle() bool {
- t.mu.Lock()
- defer t.mu.Unlock()
- return t.players.count() == 0
-}
-
// idle on the Table wrapper delegates to the game.
func (t *Table) idle() bool { return t.game.idle() }