aboutsummaryrefslogtreecommitdiff
path: root/internal/casino/machine.go
diff options
context:
space:
mode:
Diffstat (limited to 'internal/casino/machine.go')
-rw-r--r--internal/casino/machine.go49
1 files changed, 30 insertions, 19 deletions
diff --git a/internal/casino/machine.go b/internal/casino/machine.go
index aad6352..610e745 100644
--- a/internal/casino/machine.go
+++ b/internal/casino/machine.go
@@ -246,6 +246,11 @@ func (m *Machine) autoBetTick() []Event {
// intact until the feature completes.
func (m *Machine) finishSpin() []Event {
events := []Event{}
+ if len(m.spin.Wins) > 0 {
+ matches := m.private(formatWins(m.spin.Wins, m.profile.Ways))
+ matches[0].Color = "casino_win"
+ events = append(events, matches...)
+ }
result := m.spin.Payout + m.payoutRemainder
// Checked conversion: a runaway profile or overflow must clamp at the
// economy cap instead of wrapping through a machine-sized int. A NaN or
@@ -275,9 +280,9 @@ func (m *Machine) finishSpin() []Event {
}
payFailed := false
- if payNow > 0 && m.player.Wallet != nil {
- // Credit the payout and consume the triggering stake in ONE save: a
- // crash can then never pay the win AND refund the same stake.
+ if (!inFeature || featureEnds) && m.player.Wallet != nil {
+ // Credit the return (including zero) and consume the triggering stake
+ // in ONE durable save before announcing any completed settlement.
if err := m.player.Wallet.PayCredits(payNow, m.stakeID); err != nil {
// The payout could not be made durable. Do not announce the win as
// paid, stop any autobet chain, and keep the stake PENDING
@@ -306,25 +311,33 @@ func (m *Machine) finishSpin() []Event {
}
if payNow > 0 && !payFailed {
message := fmt.Sprintf("\nYou win %d credits.", payNow)
- switch {
- case inFeature && !featureEnds:
- // Banked, not spent: say so, or the player would read the win
- // as already in their balance.
- message = fmt.Sprintf("\nYou win %d credits, paid when the free spins end.", payNow)
- case featureEnds:
+ if featureEnds {
message = fmt.Sprintf("\nThe free spins pay %d credits.", payNow)
}
winEvents := m.private(message)
+ label := "Spin"
+ if featureEnds {
+ label = "Spin + free spins"
+ }
+ winEvents[0].Settlement = []SettlementLine{{Label: label, Odds: "Varies", Stake: m.spinBet, Returned: payNow, Result: "win"}}
winEvents[0].Color = "casino_result_win"
if payNow > m.spinBet {
winEvents[0].Color = "casino_big_win"
}
events = append(events, winEvents...)
}
- if len(m.spin.Wins) > 0 {
- winEvents := m.private(formatWins(m.spin.Wins, m.profile.Ways))
- winEvents[0].Color = "casino_win"
- events = append(events, winEvents...)
+ if !payFailed && payNow == 0 {
+ line := SettlementLine{Label: "Spin", Odds: "Varies", Stake: m.spinBet, Result: "loss"}
+ if inFeature {
+ line.Label = "Spin + free spins"
+ }
+ line.Unresolved = inFeature && !featureEnds
+ receipt := m.private(settlementText([]settlementLine{line}))
+ receipt[0].Settlement = []SettlementLine{line}
+ if line.Unresolved {
+ receipt[0].SettlementNote = fmt.Sprintf("%d credits banked; paid when free spins end.", int(m.featureBank))
+ }
+ events = append(events, receipt...)
}
// A paid feature in progress never retriggers: scatters landing during a
// free spin are ignored, matching the CalculateRTP model (the feature is
@@ -348,10 +361,8 @@ func (m *Machine) finishSpin() []Event {
} else {
m.inFreeSpins = false
if !payFailed && m.player.Wallet != nil {
- // A win already consumed this round's stake inside PayCredits; a
- // loss (payNow == 0) has not, so consume it here. Either way the
- // round is over and the stake must not be refunded.
- m.player.Wallet.ResolveStake(m.stakeID)
+ // PayCredits already consumed this round's stake, even on a loss.
+ // Never resolve older stakes retained after failed settlements.
m.stakeID = ""
m.stakeFailed = false
}
@@ -504,9 +515,9 @@ func stoppedScatters(grid [slotGridRows][slotGridReels]string, reels int, trigge
}
func formatWins(wins []SlotWin, ways int) string {
- message := fmt.Sprintf("Winning lines/ways (%d-way evaluation):\n", ways)
+ message := fmt.Sprintf("Matches (%d-way evaluation):\n", ways)
for _, win := range wins {
- message += fmt.Sprintf(" %s x%d: %d ways x %.4gx bet / %d = %.4f credits\n", win.Symbol, win.Length, win.Ways, win.Multiplier, ways, win.Payout)
+ message += fmt.Sprintf(" %s x%d: %d ways\n", win.Symbol, win.Length, win.Ways)
}
return message[:len(message)-1]
}