diff options
Diffstat (limited to 'internal/casino/machine.go')
| -rw-r--r-- | internal/casino/machine.go | 48 |
1 files changed, 43 insertions, 5 deletions
diff --git a/internal/casino/machine.go b/internal/casino/machine.go index 64dcfd4..6a7b7aa 100644 --- a/internal/casino/machine.go +++ b/internal/casino/machine.go @@ -2,6 +2,7 @@ package casino import ( "fmt" + "math" "math/rand" "sync" ) @@ -100,8 +101,8 @@ func (m *Machine) start(name string, wallet Wallet) []Event { } func (m *Machine) beginSpinLocked(name string, amount int, wallet Wallet, publicMessage, privateMessage string) []Event { - if _, ok := wallet.Wager(amount); !ok { - return m.private("You don't have enough chips and credits for that bet.") + if _, err := wallet.Wager(amount); err != nil { + return m.private(walletErrorMessage(err, "You don't have enough chips and credits for that bet.")) } m.bet = amount m.spinBet = amount @@ -203,9 +204,9 @@ func (m *Machine) autoBetTick() []Event { m.autoBet = false return m.private("Autobet stopped: no wager is set.") } - if _, ok := m.player.Wallet.Wager(m.bet); !ok { + if _, err := m.player.Wallet.Wager(m.bet); err != nil { m.autoBet = false - return m.private("Autobet stopped: you don't have enough chips and credits for that bet.") + return m.private(walletErrorMessage(err, "Autobet stopped: you don't have enough chips and credits for that bet.")) } m.spinBet = m.bet m.spin = generateSlotSpinWithProfile(m.rng, m.spinBet, m.profile) @@ -220,7 +221,12 @@ func (m *Machine) autoBetTick() []Event { func (m *Machine) finishSpin() []Event { events := []Event{} result := m.spin.Payout + m.payoutRemainder - credits := int(result) + // Checked conversion: a runaway profile or overflow must clamp at the + // economy cap instead of wrapping through a machine-sized int. + if result > MaxCredits || result != result || result < 0 { + result = MaxCredits + } + credits := int(math.Floor(result)) m.payoutRemainder = result - float64(credits) if credits > 0 && m.player.Wallet != nil { m.player.Wallet.PayCredits(credits) @@ -380,3 +386,35 @@ func (m *Machine) privateMenu(prefix string) []Event { func (m *Machine) menuEvents(prefix string) []Event { return m.privateMenu(prefix) } + +// refundAll refunds any committed stake at graceful shutdown. Locked. +func (m *Machine) refundAll() []Event { + m.mu.Lock() + defer m.mu.Unlock() + if m.player == nil || m.player.Wallet == nil || m.spinBet <= 0 { + return nil + } + name, refund := m.player.Name, m.spinBet + wallet := m.player.Wallet + m.spinBet = 0 + m.bet = 0 + if err := wallet.PayCredits(refund); err != nil { + return []Event{{RoomID: m.RoomID, TableID: m.Config.ID, Type: EventResult, + PrivateTo: name, Player: name, + PrivateMessage: "Shutdown refund could not be persisted; the stake is recorded for recovery.", + Color: "casino_action", Public: false}} + } + return []Event{{RoomID: m.RoomID, TableID: m.Config.ID, Type: EventResult, + PrivateTo: name, Player: name, + Message: fmt.Sprintf("%s is refunded %d credits as the casino closes.", name, refund), + PrivateMessage: fmt.Sprintf("The house refunds your %d credit stake for the shutdown.", refund), + Color: "casino_action", Public: true}} +} + +// idle reports whether no player is seated: only an idle machine may have +// its live rules replaced by new room configuration. +func (m *Machine) idle() bool { + m.mu.Lock() + defer m.mu.Unlock() + return m.player == nil +} |
