Signed-off-by: Matt Bruce <mbrucedogs@gmail.com>
This commit is contained in:
parent
abf4ba9b97
commit
1fe7bbb274
@ -994,13 +994,10 @@ final class GameState: CasinoGameState {
|
|||||||
// MARK: - Game Reset
|
// MARK: - Game Reset
|
||||||
|
|
||||||
/// Resets the entire game (keeps statistics).
|
/// Resets the entire game (keeps statistics).
|
||||||
|
/// Uses CasinoKit's performResetGame() which properly handles session ending.
|
||||||
func resetGame() {
|
func resetGame() {
|
||||||
balance = settings.startingBalance
|
performResetGame()
|
||||||
roundHistory = []
|
// Note: newRoundInternal() is called by resetForNewSession()
|
||||||
engine = BaccaratEngine(deckCount: settings.deckCount.rawValue)
|
|
||||||
startNewSession()
|
|
||||||
newRoundInternal()
|
|
||||||
saveGameData()
|
|
||||||
|
|
||||||
// Play new game sound
|
// Play new game sound
|
||||||
sound.playNewRound()
|
sound.playNewRound()
|
||||||
|
|||||||
@ -518,7 +518,12 @@ final class GameState: CasinoGameState {
|
|||||||
playerHandsVisibleCardCount = [0]
|
playerHandsVisibleCardCount = [0]
|
||||||
dealerVisibleCardCount = 0
|
dealerVisibleCardCount = 0
|
||||||
|
|
||||||
let delay = settings.showAnimations ? 0.3 * settings.dealingSpeed : 0
|
// Animation timing
|
||||||
|
let animationDuration = Design.Animation.springDuration * settings.dealingSpeed
|
||||||
|
// Small delay for card to appear on screen before updating badge (~15% of animation)
|
||||||
|
let cardAppearDelay = settings.showAnimations ? animationDuration * 0.15 : 0
|
||||||
|
// Remaining delay after badge update to complete the animation
|
||||||
|
let remainingDelay = settings.showAnimations ? animationDuration * 0.85 : 0
|
||||||
|
|
||||||
// European no-hole-card: deal 3 cards (player, dealer, player)
|
// European no-hole-card: deal 3 cards (player, dealer, player)
|
||||||
// American style: deal 4 cards (player, dealer, player, dealer)
|
// American style: deal 4 cards (player, dealer, player, dealer)
|
||||||
@ -532,16 +537,23 @@ final class GameState: CasinoGameState {
|
|||||||
dealerHand.cards.append(card)
|
dealerHand.cards.append(card)
|
||||||
}
|
}
|
||||||
sound.play(.cardDeal)
|
sound.play(.cardDeal)
|
||||||
if delay > 0 {
|
|
||||||
try? await Task.sleep(for: .seconds(delay))
|
// Wait for card to appear on screen
|
||||||
|
if cardAppearDelay > 0 {
|
||||||
|
try? await Task.sleep(for: .seconds(cardAppearDelay))
|
||||||
}
|
}
|
||||||
|
|
||||||
// Mark card as visible after animation delay
|
// Now mark card as visible (badge updates)
|
||||||
if i % 2 == 0 {
|
if i % 2 == 0 {
|
||||||
playerHandsVisibleCardCount[0] += 1
|
playerHandsVisibleCardCount[0] += 1
|
||||||
} else {
|
} else {
|
||||||
dealerVisibleCardCount += 1
|
dealerVisibleCardCount += 1
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Wait for remaining animation before dealing next card
|
||||||
|
if remainingDelay > 0 {
|
||||||
|
try? await Task.sleep(for: .seconds(remainingDelay))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -660,15 +672,24 @@ final class GameState: CasinoGameState {
|
|||||||
playerHands[activeHandIndex].cards.append(card)
|
playerHands[activeHandIndex].cards.append(card)
|
||||||
sound.play(.cardDeal)
|
sound.play(.cardDeal)
|
||||||
|
|
||||||
// Wait for animation if enabled
|
// Animation timing
|
||||||
let delay = settings.showAnimations ? 0.3 * settings.dealingSpeed : 0
|
let animationDuration = Design.Animation.springDuration * settings.dealingSpeed
|
||||||
if delay > 0 {
|
let cardAppearDelay = settings.showAnimations ? animationDuration * 0.15 : 0
|
||||||
try? await Task.sleep(for: .seconds(delay))
|
let remainingDelay = settings.showAnimations ? animationDuration * 0.85 : 0
|
||||||
|
|
||||||
|
// Wait for card to appear on screen
|
||||||
|
if cardAppearDelay > 0 {
|
||||||
|
try? await Task.sleep(for: .seconds(cardAppearDelay))
|
||||||
}
|
}
|
||||||
|
|
||||||
// Mark card as visible after animation
|
// Mark card as visible (badge updates)
|
||||||
playerHandsVisibleCardCount[activeHandIndex] += 1
|
playerHandsVisibleCardCount[activeHandIndex] += 1
|
||||||
|
|
||||||
|
// Wait for remaining animation before processing result
|
||||||
|
if remainingDelay > 0 {
|
||||||
|
try? await Task.sleep(for: .seconds(remainingDelay))
|
||||||
|
}
|
||||||
|
|
||||||
// Check for bust or 21
|
// Check for bust or 21
|
||||||
if playerHands[activeHandIndex].isBusted {
|
if playerHands[activeHandIndex].isBusted {
|
||||||
playerHands[activeHandIndex].result = .bust
|
playerHands[activeHandIndex].result = .bust
|
||||||
@ -717,14 +738,23 @@ final class GameState: CasinoGameState {
|
|||||||
playerHands[activeHandIndex].cards.append(card)
|
playerHands[activeHandIndex].cards.append(card)
|
||||||
sound.play(.cardDeal)
|
sound.play(.cardDeal)
|
||||||
|
|
||||||
// Wait for animation if enabled
|
// Animation timing
|
||||||
let delay = settings.showAnimations ? 0.3 * settings.dealingSpeed : 0
|
let animationDuration = Design.Animation.springDuration * settings.dealingSpeed
|
||||||
if delay > 0 {
|
let cardAppearDelay = settings.showAnimations ? animationDuration * 0.15 : 0
|
||||||
try? await Task.sleep(for: .seconds(delay))
|
let remainingDelay = settings.showAnimations ? animationDuration * 0.85 : 0
|
||||||
|
|
||||||
|
// Wait for card to appear on screen
|
||||||
|
if cardAppearDelay > 0 {
|
||||||
|
try? await Task.sleep(for: .seconds(cardAppearDelay))
|
||||||
}
|
}
|
||||||
|
|
||||||
// Mark card as visible after animation
|
// Mark card as visible (badge updates)
|
||||||
playerHandsVisibleCardCount[activeHandIndex] += 1
|
playerHandsVisibleCardCount[activeHandIndex] += 1
|
||||||
|
|
||||||
|
// Wait for remaining animation
|
||||||
|
if remainingDelay > 0 {
|
||||||
|
try? await Task.sleep(for: .seconds(remainingDelay))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if playerHands[activeHandIndex].isBusted {
|
if playerHands[activeHandIndex].isBusted {
|
||||||
@ -762,34 +792,47 @@ final class GameState: CasinoGameState {
|
|||||||
balance -= originalHand.bet
|
balance -= originalHand.bet
|
||||||
sound.play(.chipPlace)
|
sound.play(.chipPlace)
|
||||||
|
|
||||||
// Deal one card to each hand
|
// Replace original with split hands first (so visible counts are tracked correctly)
|
||||||
let delay = settings.showAnimations ? 0.3 * settings.dealingSpeed : 0
|
|
||||||
|
|
||||||
if let card1 = engine.dealCard() {
|
|
||||||
hand1.cards.append(card1)
|
|
||||||
sound.play(.cardDeal)
|
|
||||||
if delay > 0 {
|
|
||||||
try? await Task.sleep(for: .seconds(delay))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if let card2 = engine.dealCard() {
|
|
||||||
hand2.cards.append(card2)
|
|
||||||
sound.play(.cardDeal)
|
|
||||||
if delay > 0 {
|
|
||||||
try? await Task.sleep(for: .seconds(delay))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Replace original with split hands
|
|
||||||
playerHands.remove(at: activeHandIndex)
|
playerHands.remove(at: activeHandIndex)
|
||||||
playerHands.insert(hand1, at: activeHandIndex)
|
playerHands.insert(hand1, at: activeHandIndex)
|
||||||
playerHands.insert(hand2, at: activeHandIndex + 1)
|
playerHands.insert(hand2, at: activeHandIndex + 1)
|
||||||
|
|
||||||
// Update visible card counts - each split hand starts with 2 visible cards
|
// Each split hand starts with 1 visible card (the original cards)
|
||||||
playerHandsVisibleCardCount.remove(at: activeHandIndex)
|
playerHandsVisibleCardCount.remove(at: activeHandIndex)
|
||||||
playerHandsVisibleCardCount.insert(2, at: activeHandIndex)
|
playerHandsVisibleCardCount.insert(1, at: activeHandIndex)
|
||||||
playerHandsVisibleCardCount.insert(2, at: activeHandIndex + 1)
|
playerHandsVisibleCardCount.insert(1, at: activeHandIndex + 1)
|
||||||
|
|
||||||
|
// Animation timing
|
||||||
|
let animationDuration = Design.Animation.springDuration * settings.dealingSpeed
|
||||||
|
let cardAppearDelay = settings.showAnimations ? animationDuration * 0.15 : 0
|
||||||
|
let remainingDelay = settings.showAnimations ? animationDuration * 0.85 : 0
|
||||||
|
|
||||||
|
// Deal one card to each hand
|
||||||
|
if let card1 = engine.dealCard() {
|
||||||
|
playerHands[activeHandIndex].cards.append(card1)
|
||||||
|
sound.play(.cardDeal)
|
||||||
|
|
||||||
|
if cardAppearDelay > 0 {
|
||||||
|
try? await Task.sleep(for: .seconds(cardAppearDelay))
|
||||||
|
}
|
||||||
|
playerHandsVisibleCardCount[activeHandIndex] += 1
|
||||||
|
if remainingDelay > 0 {
|
||||||
|
try? await Task.sleep(for: .seconds(remainingDelay))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if let card2 = engine.dealCard() {
|
||||||
|
playerHands[activeHandIndex + 1].cards.append(card2)
|
||||||
|
sound.play(.cardDeal)
|
||||||
|
|
||||||
|
if cardAppearDelay > 0 {
|
||||||
|
try? await Task.sleep(for: .seconds(cardAppearDelay))
|
||||||
|
}
|
||||||
|
playerHandsVisibleCardCount[activeHandIndex + 1] += 1
|
||||||
|
if remainingDelay > 0 {
|
||||||
|
try? await Task.sleep(for: .seconds(remainingDelay))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// If split aces, typically only one card each and stand
|
// If split aces, typically only one card each and stand
|
||||||
if originalHand.cards[0].rank == .ace && !settings.resplitAces {
|
if originalHand.cards[0].rank == .ace && !settings.resplitAces {
|
||||||
@ -848,20 +891,24 @@ final class GameState: CasinoGameState {
|
|||||||
private func dealerTurn() async {
|
private func dealerTurn() async {
|
||||||
currentPhase = .dealerTurn
|
currentPhase = .dealerTurn
|
||||||
|
|
||||||
let delay = settings.showAnimations ? 0.5 * settings.dealingSpeed : 0
|
// Animation timing
|
||||||
|
let animationDuration = Design.Animation.springDuration * settings.dealingSpeed
|
||||||
|
let delay = settings.showAnimations ? animationDuration : 0
|
||||||
|
// For flip animation, card face becomes visible halfway through (at 90° rotation)
|
||||||
|
let flipMidpointDelay = settings.showAnimations ? animationDuration / 2.0 : 0
|
||||||
|
|
||||||
// European no-hole-card: deal the second card now
|
// European no-hole-card: deal the second card now
|
||||||
if settings.noHoleCard && dealerHand.cards.count == 1 {
|
if settings.noHoleCard && dealerHand.cards.count == 1 {
|
||||||
if let card = engine.dealCard() {
|
if let card = engine.dealCard() {
|
||||||
dealerHand.cards.append(card)
|
dealerHand.cards.append(card)
|
||||||
|
// Mark card as visible immediately - face is visible as soon as card appears
|
||||||
|
dealerVisibleCardCount += 1
|
||||||
sound.play(.cardDeal)
|
sound.play(.cardDeal)
|
||||||
|
|
||||||
|
// Wait for animation to complete before checking blackjack
|
||||||
if delay > 0 {
|
if delay > 0 {
|
||||||
try? await Task.sleep(for: .seconds(delay))
|
try? await Task.sleep(for: .seconds(delay))
|
||||||
}
|
}
|
||||||
|
|
||||||
// Mark card as visible after animation
|
|
||||||
dealerVisibleCardCount += 1
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check for dealer blackjack in European mode
|
// Check for dealer blackjack in European mode
|
||||||
@ -881,31 +928,38 @@ final class GameState: CasinoGameState {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// American style: reveal hole card (card is already in hand, just mark as visible)
|
// American style: reveal hole card (card is already in hand)
|
||||||
|
// The flip animation shows the card face at the midpoint (90° rotation)
|
||||||
sound.play(.cardFlip)
|
sound.play(.cardFlip)
|
||||||
|
|
||||||
if delay > 0 {
|
// Wait until card face becomes visible (halfway through flip)
|
||||||
try? await Task.sleep(for: .seconds(delay))
|
if flipMidpointDelay > 0 {
|
||||||
|
try? await Task.sleep(for: .seconds(flipMidpointDelay))
|
||||||
}
|
}
|
||||||
|
|
||||||
// Mark hole card as visible (if not already)
|
// Mark hole card as visible now that card face is showing
|
||||||
if dealerVisibleCardCount < dealerHand.cards.count {
|
if dealerVisibleCardCount < dealerHand.cards.count {
|
||||||
dealerVisibleCardCount = dealerHand.cards.count
|
dealerVisibleCardCount = dealerHand.cards.count
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Wait for remaining flip animation to complete before drawing more cards
|
||||||
|
if flipMidpointDelay > 0 {
|
||||||
|
try? await Task.sleep(for: .seconds(flipMidpointDelay))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Dealer draws
|
// Dealer draws
|
||||||
while engine.dealerShouldHit(hand: dealerHand) {
|
while engine.dealerShouldHit(hand: dealerHand) {
|
||||||
if let card = engine.dealCard() {
|
if let card = engine.dealCard() {
|
||||||
dealerHand.cards.append(card)
|
dealerHand.cards.append(card)
|
||||||
|
// Mark card as visible immediately - face is visible as soon as card appears
|
||||||
|
dealerVisibleCardCount += 1
|
||||||
sound.play(.cardDeal)
|
sound.play(.cardDeal)
|
||||||
|
|
||||||
|
// Wait for animation to complete before drawing next card
|
||||||
if delay > 0 {
|
if delay > 0 {
|
||||||
try? await Task.sleep(for: .seconds(delay))
|
try? await Task.sleep(for: .seconds(delay))
|
||||||
}
|
}
|
||||||
|
|
||||||
// Mark card as visible after animation
|
|
||||||
dealerVisibleCardCount += 1
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1209,13 +1263,10 @@ final class GameState: CasinoGameState {
|
|||||||
// MARK: - Game Reset
|
// MARK: - Game Reset
|
||||||
|
|
||||||
/// Resets the entire game (keeps statistics).
|
/// Resets the entire game (keeps statistics).
|
||||||
|
/// Uses CasinoKit's performResetGame() which properly handles session ending.
|
||||||
func resetGame() {
|
func resetGame() {
|
||||||
balance = settings.startingBalance
|
performResetGame()
|
||||||
roundHistory = []
|
// Note: newRound() is called by resetForNewSession()
|
||||||
engine.reshuffle()
|
|
||||||
startNewSession()
|
|
||||||
newRound()
|
|
||||||
saveGameData()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -126,3 +126,38 @@ public extension CasinoGameState {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// MARK: - Game Reset Extensions
|
||||||
|
|
||||||
|
public extension CasinoGameState {
|
||||||
|
/// Default implementation of resetGame that properly handles session ending.
|
||||||
|
/// Games can override this but should call the helper methods in the correct order.
|
||||||
|
///
|
||||||
|
/// The correct order is:
|
||||||
|
/// 1. End current session (captures actual ending balance)
|
||||||
|
/// 2. Reset balance to starting balance
|
||||||
|
/// 3. Reset game-specific state (via resetForNewSession)
|
||||||
|
/// 4. Start new session with fresh balance
|
||||||
|
/// 5. Save data
|
||||||
|
func performResetGame() {
|
||||||
|
// 1. End current session FIRST while balance still shows actual state
|
||||||
|
if currentSession != nil {
|
||||||
|
endCurrentSession(reason: .manualEnd)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2. Reset balance to starting balance
|
||||||
|
balance = startingBalance
|
||||||
|
|
||||||
|
// 3. Let game reset its specific state (reshuffle deck, clear history, etc.)
|
||||||
|
resetForNewSession()
|
||||||
|
|
||||||
|
// 4. Create new session with fresh balance
|
||||||
|
currentSession = GameSession<Stats>(
|
||||||
|
gameStyle: currentGameStyle,
|
||||||
|
startingBalance: balance
|
||||||
|
)
|
||||||
|
|
||||||
|
// 5. Save
|
||||||
|
saveGameData()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user