diff options
Diffstat (limited to 'internal/casino/baccarat.go')
| -rw-r--r-- | internal/casino/baccarat.go | 64 |
1 files changed, 46 insertions, 18 deletions
diff --git a/internal/casino/baccarat.go b/internal/casino/baccarat.go index d415901..fa54ac8 100644 --- a/internal/casino/baccarat.go +++ b/internal/casino/baccarat.go @@ -179,6 +179,8 @@ func (t *baccaratTable) rebet(name string) (string, int) { // deal plays out the hand by the punto banco tableau, settles every wager, // and resets the table for the next round. func (t *baccaratTable) deal() []Event { + // The cut-card reshuffle happens between rounds, never mid-hand. + t.shoe.shuffleIfDue() player, banker := playTableau(t.shoe) playerTotal, bankerTotal := baccaratTotal(player), baccaratTotal(banker) round := t.event("New baccarat hand!", true) @@ -189,19 +191,38 @@ func (t *baccaratTable) deal() []Event { continue } result, payout := settleBaccaratBet(p.lastSpot, p.Wager.Total, playerTotal, bankerTotal, t.Rules) + payoutFailed := false if payout > 0 && p.Wallet != nil { - p.Wallet.PayCredits(payout) + if err := p.Wallet.PayCredits(payout); err != nil { + // The payout could not be made durable; recovery will + // re-apply the owed amount from the open journal record. + // Do not announce the win as paid and leave the stakes + // open for recovery to settle. + payoutFailed = true + publicResult, _ := baccaratResultMessages(p.Name, result, payout) + settlement := t.playerAction(p.Name, publicResult, payoutFailedMessage()) + if result == "win" { + settlement.Color = "casino_big_win" + } + events = append(events, settlement) + } } - publicResult, privateResult := baccaratResultMessages(p.Name, result, payout) - settlement := t.playerAction(p.Name, publicResult, privateResult) - if result == "win" { - if p.lastSpot == baccaratSpotTie { - settlement.Color = "casino_jackpot_win" - } else { - settlement.Color = "casino_big_win" + if !payoutFailed { + publicResult, privateResult := baccaratResultMessages(p.Name, result, payout) + settlement := t.playerAction(p.Name, publicResult, privateResult) + if result == "win" { + if p.lastSpot == baccaratSpotTie { + settlement.Color = "casino_jackpot_win" + } else { + settlement.Color = "casino_big_win" + } } + events = append(events, settlement) + } + if p.Wallet != nil && !payoutFailed { + // The round resolved; the stakes are no longer outstanding. + p.Wallet.ResolveStakes() } - events = append(events, settlement) } t.resetRound() for _, p := range t.players.ordered() { @@ -247,6 +268,11 @@ func (t *baccaratTable) markDisconnected(name string) []Event { if !t.players.has(name) { return nil } + // A disconnected player forfeits the round in flight: resolve the + // stakes now so startup recovery does not refund them later. + if p := t.players.get(name); p != nil && p.HasBet && p.Wallet != nil { + p.Wallet.ResolveStakes() + } t.players.remove(name) events := []Event{t.event(fmt.Sprintf("%s stands up.", name), true)} if t.players.count() == 0 { @@ -364,7 +390,8 @@ func baccaratResultMessages(name, result string, payout int) (string, string) { } // refundAll resolves every committed wager at this table (graceful -// shutdown): stakes return to their players through the wallet. +// shutdown/eviction): the wallet returns every outstanding stake exactly +// once. func (t *baccaratTable) refundAll() []Event { t.mu.Lock() defer t.mu.Unlock() @@ -373,17 +400,18 @@ func (t *baccaratTable) refundAll() []Event { if !p.HasBet || p.Wallet == nil { continue } - name, refund := p.Name, p.Wager.Total - if refund <= 0 { - continue - } - if err := p.Wallet.PayCredits(refund); err != nil { - events = append(events, t.private(name, "Shutdown refund could not be persisted; the stake is recorded for recovery.")) + name := p.Name + chips, credits, err := p.Wallet.RefundOutstanding() + refund := chips + credits + if err != nil || refund <= 0 { + if err != nil { + events = append(events, t.private(name, "Shutdown refund could not be persisted; the stake is recorded for recovery.")) + } continue } events = append(events, t.playerAction(name, - fmt.Sprintf("%s is refunded %d credits as the casino closes.", name, refund), - fmt.Sprintf("The house refunds your %d credit stake for the shutdown.", refund))) + fmt.Sprintf("%s is refunded %d as the casino closes.", name, refund), + fmt.Sprintf("The house refunds your %d stake for the shutdown.", refund))) } return events } |
