aboutsummaryrefslogtreecommitdiff
path: root/internal/game/casino.go
diff options
context:
space:
mode:
Diffstat (limited to 'internal/game/casino.go')
-rw-r--r--internal/game/casino.go60
1 files changed, 55 insertions, 5 deletions
diff --git a/internal/game/casino.go b/internal/game/casino.go
index 3edb26b..8ba9097 100644
--- a/internal/game/casino.go
+++ b/internal/game/casino.go
@@ -20,10 +20,15 @@ type casinoWallet struct {
// Wager debits the stake durably. Order of operations: durable journal
// record FIRST (with balance checkpoints), then the in-memory mutation,
-// then the character save, then the settlement marker. A failed save
-// reverts the in-memory mutation and voids the record: no charge is kept
-// in memory, and the error distinguishes persistence failure from
-// insufficient funds.
+// then the character save, then the record is registered as an OUTSTANDING
+// STAKE. It is NOT settled here: it stays open until the round resolves
+// (casino.Wallet.ResolveStakes) or is refunded (RefundOutstanding). An open
+// record at startup recovery means the round state was lost in a crash and
+// the stake is refunded — the documented refund-on-crash policy.
+//
+// A failed save reverts the in-memory mutation and voids the record: no
+// charge is kept in memory, and the error distinguishes persistence failure
+// from insufficient funds.
func (w casinoWallet) Wager(amount int) (casino.Wager, error) {
if amount <= 0 {
return casino.Wager{}, casino.ErrInsufficientFunds
@@ -57,7 +62,9 @@ func (w casinoWallet) Wager(amount int) (casino.Wager, error) {
w.g.Economy.void(rec)
return casino.Wager{}, casino.ErrWalletPersistence
}
- w.g.Economy.settle(rec)
+ // The record stays open as an outstanding stake until the round
+ // resolves; recovery refunds open stakes after a crash.
+ w.g.Economy.registerStake(rec)
return casino.Wager{Total: amount, Chips: chips, Credits: credits}, nil
}
@@ -90,6 +97,49 @@ func (w casinoWallet) PayCredits(amount int) error {
return nil
}
+// ResolveStakes marks every outstanding stake for the player settled: the
+// round resolved (won, lost, pushed, or forfeited), so startup recovery
+// must no longer refund it.
+func (w casinoWallet) ResolveStakes() {
+ w.g.Economy.ResolveStakes(w.p.Name)
+}
+
+// RefundOutstanding returns every outstanding stake to the player: chips
+// come back as chips, credits through the durable payout path. The stakes
+// are resolved only once the return is durable; on failure the chip return
+// is rolled back out of memory and the open records drive recovery instead.
+func (w casinoWallet) RefundOutstanding() (chips, credits int, err error) {
+ for _, rec := range w.g.Economy.openStakes(w.p.Name) {
+ chips += rec.Chips
+ credits += rec.Credit
+ }
+ if chips <= 0 && credits <= 0 {
+ w.ResolveStakes()
+ return 0, 0, nil
+ }
+ if chips > 0 {
+ w.g.giveChips(w.p, chips)
+ }
+ if credits > 0 {
+ if err := w.PayCredits(credits); err != nil {
+ if chips > 0 {
+ // Roll the chip return back out of memory: the durable
+ // refund will re-apply both amounts from the open records.
+ w.p.RemoveItem("chips", chips)
+ }
+ return chips, credits, err
+ }
+ } else if chips > 0 {
+ // A chips-only refund needs its own durable save.
+ if err := w.g.AccountStore.SaveCharacter(w.p); err != nil {
+ w.p.RemoveItem("chips", chips)
+ return chips, 0, casino.ErrWalletPersistence
+ }
+ }
+ w.ResolveStakes()
+ return chips, credits, nil
+}
+
// casinoAffordable is the most a player can wager: chips are spent first,
// then credits.
func casinoAffordable(p *player.Player) int { return p.Credits + countChips(p) }