aboutsummaryrefslogtreecommitdiff
path: root/internal/casino/machine.go
diff options
context:
space:
mode:
authorhistoria <[not public]>2026-09-18 18:38:35 -0400
committerhistoria <[not public]>2026-09-18 18:38:35 -0400
commit1f118d79e319da4e765502e9ca5b661772de94f1 (patch)
treebca8a3fd1e538f1c302480511e6ba872c92e10b8 /internal/casino/machine.go
parent5ad42a51df4dcc5ccc3aa6a6d902085ce2ee4239 (diff)
downloadthehouseoficarus-1f118d79e319da4e765502e9ca5b661772de94f1.tar.gz
refactor: casino table core; fix: stake/migration/validation gaps
Diffstat (limited to 'internal/casino/machine.go')
-rw-r--r--internal/casino/machine.go30
1 files changed, 19 insertions, 11 deletions
diff --git a/internal/casino/machine.go b/internal/casino/machine.go
index 83e018f..f285518 100644
--- a/internal/casino/machine.go
+++ b/internal/casino/machine.go
@@ -30,6 +30,11 @@ type Machine struct {
inFreeSpins bool
spinBet int
payoutRemainder float64
+ // stakeID is the wallet handle of the wager committed to the current
+ // round (the triggering wager for a free-spin feature). Resolving by
+ // handle — not the player-wide ResolveStakes — keeps an older
+ // failed-payout stake open as recovery evidence when a new round starts.
+ stakeID string
// featureBank holds the winnings of a paid round whose free-spin feature
// is still running. They are paid as ONE settlement when the feature
// ends: see finishSpin for why no payout may be journaled while the
@@ -105,11 +110,13 @@ func (m *Machine) start(name string, wallet Wallet) []Event {
}
func (m *Machine) beginSpinLocked(amount int, wallet Wallet, publicMessage, privateMessage string) []Event {
- if _, err := wallet.Wager(amount); err != nil {
+ wager, err := wallet.Wager(amount)
+ if err != nil {
return m.private(walletErrorMessage(err, "You don't have enough chips and credits for that bet."))
}
m.bet = amount
m.spinBet = amount
+ m.stakeID = wager.ID
m.player.Wallet = wallet
m.stakeFailed = false
m.spin = generateSlotSpinWithProfile(m.rng, m.spinBet, m.profile)
@@ -209,9 +216,11 @@ func (m *Machine) autoBetTick() []Event {
m.autoBet = false
return m.private("Autobet stopped: no wager is set.")
}
- if _, err := m.player.Wallet.Wager(m.bet); err != nil {
+ if wager, err := m.player.Wallet.Wager(m.bet); err != nil {
m.autoBet = false
return m.private(walletErrorMessage(err, "Autobet stopped: you don't have enough chips and credits for that bet."))
+ } else {
+ m.stakeID = wager.ID
}
m.spinBet = m.bet
m.stakeFailed = false
@@ -328,13 +337,15 @@ func (m *Machine) finishSpin() []Event {
m.inFreeSpins = false
if !payFailed && m.player.Wallet != nil {
// The paid round fully resolved (the feature either never
- // started or just completed; free spins wager nothing): the
- // stake is no longer outstanding.
- m.player.Wallet.ResolveStakes()
+ // started or just completed; free spins wager nothing): resolve
+ // ONLY this round's stake, so an older failed-payout stake stays
+ // open as recovery evidence.
+ m.player.Wallet.ResolveStake(m.stakeID)
+ m.stakeID = ""
m.stakeFailed = false
}
if !m.autoBet {
- events = append(events, m.menuEvents("")...)
+ events = append(events, m.privateMenu("")...)
}
}
return events
@@ -362,12 +373,13 @@ func (m *Machine) leave(name string) []Event {
// unresolved for recovery (or the shutdown refund) to complete.
func (m *Machine) resetSeat() {
if m.player != nil && m.player.Wallet != nil && !m.stakeFailed {
- m.player.Wallet.ResolveStakes()
+ m.player.Wallet.ResolveStake(m.stakeID)
}
m.player = nil
m.bet = 0
m.spin = nil
m.spinBet = 0
+ m.stakeID = ""
m.phase = machineReady
m.reel, m.clock, m.suspense = 0, 0, false
m.freeSpins = 0
@@ -496,10 +508,6 @@ func (m *Machine) privateMenu(prefix string) []Event {
return events
}
-func (m *Machine) menuEvents(prefix string) []Event {
- return m.privateMenu(prefix)
-}
-
// refundAll refunds any outstanding stake at graceful shutdown/eviction and
// then DISCARDS the round (the machine closes with the process). The wallet
// resolves every outstanding stake exactly once; a completed spin has no