aboutsummaryrefslogtreecommitdiff
path: root/internal/casino/blackjack.go
diff options
context:
space:
mode:
Diffstat (limited to 'internal/casino/blackjack.go')
-rw-r--r--internal/casino/blackjack.go95
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"
}
}