diff options
Diffstat (limited to 'internal/casino/craps.go')
| -rw-r--r-- | internal/casino/craps.go | 53 |
1 files changed, 40 insertions, 13 deletions
diff --git a/internal/casino/craps.go b/internal/casino/craps.go index 286fe0a..90c95c9 100644 --- a/internal/casino/craps.go +++ b/internal/casino/craps.go @@ -57,6 +57,7 @@ type CrapsSnapshot struct { AutoRoll bool AutoIn int Field12 int + Odds string // free-odds cap label, e.g. "3x-4x-5x" or "100x" } func (*CrapsSnapshot) snapshot() {} @@ -73,7 +74,7 @@ func crapsTarget(b *crapsBet) string { return fmt.Sprintf("%s %d", b.kind, b.number) } func (t *crapsTable) view() *CrapsSnapshot { - s := &CrapsSnapshot{Variant: t.Config.Craps.variant(), Phase: string(t.phase), Point: t.point, Dice: t.dice, Queue: append([]string(nil), t.queue...), Field12: t.Config.Craps.Field12} + s := &CrapsSnapshot{Variant: t.Config.Craps.variant(), Phase: string(t.phase), Point: t.point, Dice: t.dice, Queue: append([]string(nil), t.queue...), Field12: t.Config.Craps.Field12, Odds: t.Config.Craps.oddsDisplay()} if t.hasLiveBets() { if t.Config.Craps.variant() == "simplified" { // One automatic roll: the snapshot counts down the shared window. @@ -246,9 +247,11 @@ func (t *crapsTable) bet(name, spot string, amount int, w Wallet) []Event { if b.odds.Total > 0 { continue } + requested := amount + amount = roundUpTo(amount, payoutDen) // Compare without multiplying a potentially very large stake by Nx. - if (amount-1)/limit >= b.stake.Total || amount%payoutDen != 0 { - return reject("Odds already placed, above the free-odds limit, or not in exact payout units.") + if (amount-1)/limit >= b.stake.Total { + return reject("Odds already placed or above the free-odds limit.") } stake, err := w.Wager(amount) if err != nil { @@ -257,7 +260,11 @@ func (t *crapsTable) bet(name, spot string, amount int, w Wallet) []Event { b.odds = stake p.Wallet = w p.lastSpot, p.lastBet = spot, amount - return []Event{t.private(name, fmt.Sprintf("You add {casino_chips}%d{/} free odds on %s.", amount, target)), t.betSummary(p), t.reminder(name)} + msg := fmt.Sprintf("You add {casino_chips}%d{/} free odds on %s.", amount, target) + if amount != requested { + msg += fmt.Sprintf(" {casino_emphasis}Rounded up from %d for exact odds units.{/}", requested) + } + return []Event{t.private(name, msg), t.betSummary(p), t.reminder(name)} } return reject("Place an established line/come bet before taking odds.") } @@ -283,15 +290,19 @@ func (t *crapsTable) bet(name, spot string, amount int, w Wallet) []Event { switch kind { case "place": _, unit = crapsPlaceOdds(n) - case "buy": - _, unit = crapsTrueOdds(n) - case "lay": - unit, _ = crapsTrueOdds(n) + case "buy", "lay": + // Weird odds must land on whole-credit payouts and a whole-credit vig. + unit = crapsVigUnit(kind, n) case "horn": unit = 4 } - if amount%unit != 0 { - return reject(fmt.Sprintf("Use multiples of %d for exact payouts.", unit)) + requested := amount + amount = roundUpTo(amount, unit) + if amount > t.Config.MaxBet { + return reject(fmt.Sprintf("That wager rounds up to %d, above the table maximum.", amount)) + } + if _, err := MulPayout(amount, 1000); err != nil { + return reject("That potential payout is too large.") } stake, err := w.Wager(amount) if err != nil { @@ -302,7 +313,11 @@ func (t *crapsTable) bet(name, spot string, amount int, w Wallet) []Event { p.HasBet = true p.SittingOut = false p.lastSpot, p.lastBet = spot, amount - events := []Event{t.playerAction(name, fmt.Sprintf("%s bets %d on %s.", name, amount, spot), fmt.Sprintf("You bet {casino_chips}%d{/} on %s.", amount, spot))} + private := fmt.Sprintf("You bet {casino_chips}%d{/} on %s.", amount, spot) + if amount != requested { + private += fmt.Sprintf(" {casino_emphasis}Rounded up from %d for exact payout units.{/}", requested) + } + events := []Event{t.playerAction(name, fmt.Sprintf("%s bets %d on %s.", name, amount, spot), private)} events = append(events, t.syncRoundClock()...) return append(events, t.betSummary(p), t.reminder(name)) } @@ -348,7 +363,9 @@ func (t *crapsTable) resolveRoll(a, b int) []Event { if len(t.queue) > 0 { shooter = t.queue[0] } - events := []Event{t.event(fmt.Sprintf("{casino_emphasis}%s shoots{/} {casino_payout}%d{/}", shooter, s), true)} + // The dice tile already shows the total, so the roll leads with the felt + // itself. Diceless has no dice tile to carry the cards, so it keeps its line. + events := []Event{t.event("", true)} if r.variant() == "diceless" { events[0].Message = fmt.Sprintf("{casino_emphasis}%s draws cards{/} [%d] [%d] = %d", shooter, a, b, s) } @@ -393,7 +410,17 @@ func (t *crapsTable) resolveRoll(a, b int) []Event { } if oldPoint == 0 && r.pointNumber(s) { t.point = s - events = append(events, t.event(fmt.Sprintf("{casino_emphasis}POINT %d{/} / Puck ON. Make your point!", s), true)) + events = append(events, t.event(fmt.Sprintf("{casino_emphasis}POINT %d{/} / Puck ON.", s), true)) + // "Make your point!" only speaks to the shooter and players with a + // pass-line contract; everyone else just sees the puck move. + for _, p := range t.players.ordered() { + if !p.Connected || p.SittingOut { + continue + } + if p.Name == shooter || crapsHasPassLine(p) { + events = append(events, t.private(p.Name, "{casino_emphasis}Make your point!{/}")) + } + } } else if pointMade || sevenOut { if pointMade { events = append(events, t.event(fmt.Sprintf("{casino_result_win}POINT %d MADE!{/} / Puck OFF. Same shooter, coming out.", oldPoint), true)) |
