diff options
Diffstat (limited to 'internal/casino/blackjack.go')
| -rw-r--r-- | internal/casino/blackjack.go | 95 |
1 files changed, 27 insertions, 68 deletions
diff --git a/internal/casino/blackjack.go b/internal/casino/blackjack.go index fe212f7..55ee702 100644 --- a/internal/casino/blackjack.go +++ b/internal/casino/blackjack.go @@ -102,7 +102,7 @@ func newBlackjackTable(roomID int, cfg TableConfig, rng *rand.Rand) *blackjackTa } func (t *blackjackTable) menuEvent(name string) Event { - return t.betMenuEvent(name, MenuBlackjackBet, []MenuCommand{ + return t.betMenuEvent(name, []MenuCommand{ {Command: "bet", Desc: "Place a wager (min/max accepted)"}, {Command: "bet <amount>", Desc: "Place a specific wager"}, {Command: "stop", Desc: "Leave the table"}, @@ -258,7 +258,7 @@ func (t *blackjackTable) action(name, action string) []Event { hand.bust = true } e := t.playerAction(name, fmt.Sprintf("%s hits and receives %s.", name, hand.cards[len(hand.cards)-1]), fmt.Sprintf("You hit and receive %s.", hand.cards[len(hand.cards)-1])) - e.Blackjack = t.snapshot(false) + e.Snapshot = t.snapshot(false) events := []Event{e} if total >= 21 { events = append(events, t.finishHand()...) @@ -286,7 +286,7 @@ func (t *blackjackTable) action(name, action string) []Event { hand.bust = true } e := t.playerAction(name, fmt.Sprintf("%s doubles down.", name), "You double down.") - e.Blackjack = t.snapshot(false) + e.Snapshot = t.snapshot(false) return append([]Event{e}, t.finishHand()...) case "split": return t.splitHand(name, p, hand) @@ -376,7 +376,7 @@ func (t *blackjackTable) splitHand(name string, p *blackjackPlayer, hand *blackj index := t.activeHand p.hands = append(p.hands[:index], append([]*blackjackHand{left, right}, p.hands[index+1:]...)...) e := t.playerAction(name, fmt.Sprintf("%s splits the hand.", name), "You split the hand.") - e.Blackjack = t.snapshot(false) + e.Snapshot = t.snapshot(false) events := []Event{e} if left.cards[0].rank == "A" { // Split aces receive one card each; they only stay live when a @@ -418,7 +418,7 @@ func (t *blackjackTable) startRound() []Event { } } roundEvent := t.event("", true) - roundEvent.Blackjack = t.snapshot(false) + roundEvent.Snapshot = t.snapshot(false) events := []Event{roundEvent} if t.dealer.cards[1].rank == "A" { t.phase = blackjackInsurance @@ -490,7 +490,7 @@ func (t *blackjackTable) prompt() []Event { actions := t.availableActions(p, h) e := t.private(name, fmt.Sprintf("Your turn (%s).\nActions: %s", strings.Join(ranks, ","), strings.Join(actions, ", "))) e.Color = "casino_menu" - e.Menu = &MenuView{Kind: MenuBlackjackTurn, Title: fmt.Sprintf("Your turn (%s)", strings.Join(ranks, ",")), Actions: actions, Commands: blackjackActionCommands(actions)} + e.Menu = &MenuView{Heading: fmt.Sprintf("Your turn (%s)", strings.Join(ranks, ",")), Commands: blackjackActionCommands(actions)} return []Event{e} } @@ -585,7 +585,7 @@ func (t *blackjackTable) settle() []Event { } dealerTotal, _ := t.dealer.total() dealerEvent := t.event("", true) - dealerEvent.Blackjack = t.snapshot(true) + dealerEvent.Snapshot = t.snapshot(true) events := []Event{dealerEvent} dealerBlackjack := t.dealerBlackjack() for _, p := range t.players.ordered() { @@ -595,50 +595,20 @@ func (t *blackjackTable) settle() []Event { payoutFailed := false for _, hand := range p.hands { result, payout, settleErr := settleHand(hand, dealerTotal, dealerBlackjack, t.Rules) - if settleErr != nil { - // A payout multiplier that overflows the credit cap is a - // configuration defect, not a payable win. Treat it like a - // failed settlement so the stake stays open for recovery to - // refund rather than paying a clamped/absurd amount. + // One shared settlement path for every game: pay before + // announcing, leave the stakes open on any failure, and resolve + // them only once the payout is durable. + if settleStakeOutcome(p.Wallet, payout, hand.stakes, settleErr) { payoutFailed = true - events = append(events, t.playerAction(p.Name, "Settlement unavailable.", payoutFailedMessage())) + publicResult, _ := blackjackResultMessages(p.Name, result, payout) + settlement := t.playerAction(p.Name, publicResult, payoutFailedMessage()) + colorizeBlackjackSettlement(&settlement, result) + events = append(events, settlement) continue } - if payout > 0 && p.Wallet != nil { - if err := p.Wallet.PayStakeCover(payout, hand.stakes); err != nil { - // The payout could not be made durable. The journal - // record stays open listing the hand's stakes as - // covered: recovery re-applies the owed payout (which - // returns those stakes) and refunds must not pay them - // again. The win is not announced as paid. - payoutFailed = true - publicResult, _ := blackjackResultMessages(p.Name, result, payout) - settlement := t.playerAction(p.Name, publicResult, payoutFailedMessage()) - if result == "blackjack" { - settlement.Color = "casino_jackpot_win" - } else if result == "win" { - settlement.Color = "casino_big_win" - } - events = append(events, settlement) - continue - } - // The hand was paid: its stakes are resolved immediately - // so a later sibling-hand payout failure cannot cause a - // refund of a stake this settlement already returned. - t.resolveHandStakes(p.Wallet, hand) - } else if payout == 0 && p.Wallet != nil { - // A lost hand resolves its stakes even when a sibling - // hand's payout is still in flight: the stake was - // consumed by the rules of the game. - t.resolveHandStakes(p.Wallet, hand) - } publicResult, privateResult := blackjackResultMessages(p.Name, result, payout) settlement := t.playerAction(p.Name, publicResult, privateResult) - if result == "blackjack" { - settlement.Color = "casino_jackpot_win" - } else if result == "win" { - settlement.Color = "casino_big_win" - } + colorizeBlackjackSettlement(&settlement, result) events = append(events, settlement) } if p.insurance > 0 { @@ -647,23 +617,12 @@ func (t *blackjackTable) settle() []Event { if dealerBlackjack { insurancePayout, insuranceErr = MulPayout(p.insurance, 1+t.Rules.InsurancePayout) } - switch { - case insuranceErr != nil: - // Overflow is a config defect: leave the insurance stake open - // for recovery to refund instead of paying a clamped amount. + if settleStakeOutcome(p.Wallet, insurancePayout, []string{p.insuranceStakeID}, insuranceErr) { payoutFailed = true events = append(events, t.private(p.Name, payoutFailedMessage())) - case insurancePayout > 0: - if err := p.Wallet.PayStakeCover(insurancePayout, []string{p.insuranceStakeID}); err != nil { - payoutFailed = true - events = append(events, t.private(p.Name, payoutFailedMessage())) - } else { - p.Wallet.ResolveStake(p.insuranceStakeID) - events = append(events, t.private(p.Name, fmt.Sprintf("Insurance pays %d credits.", insurancePayout))) - } - default: - // Insurance lost: its stake was consumed by the rules. - p.Wallet.ResolveStake(p.insuranceStakeID) + } else if insurancePayout > 0 { + events = append(events, t.private(p.Name, fmt.Sprintf("Insurance pays %d credits.", insurancePayout))) + } else { events = append(events, t.private(p.Name, "Insurance pays 0 credits.")) } } @@ -725,13 +684,13 @@ func (t *blackjackTable) resetRound() { } } -// resolveHandStakes settles every stake wager a hand committed. Nil-safe. -func (t *blackjackTable) resolveHandStakes(wallet Wallet, hand *blackjackHand) { - if wallet == nil { - return - } - for _, stakeID := range hand.stakes { - wallet.ResolveStake(stakeID) +// colorizeBlackjackSettlement paints a winning settlement with the big-win +// (or jackpot, for a natural) color. +func colorizeBlackjackSettlement(e *Event, result string) { + if result == "blackjack" { + e.Color = "casino_jackpot_win" + } else if result == "win" { + e.Color = "casino_big_win" } } |
