Signed-off-by: Matt Bruce <mbrucedogs@gmail.com>

This commit is contained in:
Matt Bruce 2025-12-25 09:39:48 -06:00
parent 98d72d0db8
commit 45ad602d9a
2 changed files with 45 additions and 12 deletions

View File

@ -74,6 +74,9 @@ final class GameState {
/// Index of the hand currently being played. /// Index of the hand currently being played.
private(set) var activeHandIndex: Int = 0 private(set) var activeHandIndex: Int = 0
/// Whether an action is currently being processed (prevents double-tap issues).
private(set) var isProcessingAction: Bool = false
/// The active player hand. /// The active player hand.
var activeHand: BlackjackHand? { var activeHand: BlackjackHand? {
guard activeHandIndex < playerHands.count else { return nil } guard activeHandIndex < playerHands.count else { return nil }
@ -184,18 +187,21 @@ final class GameState {
/// Whether the current hand can hit. /// Whether the current hand can hit.
var canHit: Bool { var canHit: Bool {
guard !isProcessingAction else { return false }
guard case .playerTurn = currentPhase else { return false } guard case .playerTurn = currentPhase else { return false }
return activeHand?.canHit ?? false return activeHand?.canHit ?? false
} }
/// Whether the current hand can stand. /// Whether the current hand can stand.
var canStand: Bool { var canStand: Bool {
guard !isProcessingAction else { return false }
guard case .playerTurn = currentPhase else { return false } guard case .playerTurn = currentPhase else { return false }
return !(activeHand?.isBusted ?? true) return !(activeHand?.isBusted ?? true)
} }
/// Whether the current hand can double. /// Whether the current hand can double.
var canDouble: Bool { var canDouble: Bool {
guard !isProcessingAction else { return false }
guard case .playerTurn = currentPhase else { return false } guard case .playerTurn = currentPhase else { return false }
guard let hand = activeHand else { return false } guard let hand = activeHand else { return false }
return engine.canDoubleDown(hand: hand, balance: balance) return engine.canDoubleDown(hand: hand, balance: balance)
@ -203,6 +209,7 @@ final class GameState {
/// Whether the current hand can split. /// Whether the current hand can split.
var canSplit: Bool { var canSplit: Bool {
guard !isProcessingAction else { return false }
guard case .playerTurn = currentPhase else { return false } guard case .playerTurn = currentPhase else { return false }
guard let hand = activeHand else { return false } guard let hand = activeHand else { return false }
let splitCount = playerHands.count - 1 let splitCount = playerHands.count - 1
@ -211,6 +218,7 @@ final class GameState {
/// Whether the player can surrender. /// Whether the player can surrender.
var canSurrender: Bool { var canSurrender: Bool {
guard !isProcessingAction else { return false }
guard case .playerTurn = currentPhase else { return false } guard case .playerTurn = currentPhase else { return false }
guard let hand = activeHand else { return false } guard let hand = activeHand else { return false }
return engine.canSurrender(hand: hand) return engine.canSurrender(hand: hand)
@ -445,11 +453,18 @@ final class GameState {
// Ensure enough cards for a full hand - reshuffle if needed // Ensure enough cards for a full hand - reshuffle if needed
if !engine.canDealNewHand { if !engine.canDealNewHand {
engine.reshuffle() engine.reshuffle()
showReshuffleNotification = true
// Show notification with animation
withAnimation(.spring(duration: Design.Animation.springDuration)) {
showReshuffleNotification = true
}
// Auto-dismiss after 2 seconds
Task { Task {
try? await Task.sleep(for: .seconds(2)) try? await Task.sleep(for: .seconds(2))
showReshuffleNotification = false withAnimation(.spring(duration: Design.Animation.springDuration)) {
showReshuffleNotification = false
}
} }
} }
@ -582,6 +597,9 @@ final class GameState {
/// Player hits (takes another card). /// Player hits (takes another card).
func hit() async { func hit() async {
guard canHit else { return } guard canHit else { return }
isProcessingAction = true
defer { isProcessingAction = false }
guard let card = engine.dealCard() else { return } guard let card = engine.dealCard() else { return }
playerHands[activeHandIndex].cards.append(card) playerHands[activeHandIndex].cards.append(card)
@ -600,6 +618,8 @@ final class GameState {
/// Player stands. /// Player stands.
func stand() async { func stand() async {
guard canStand else { return } guard canStand else { return }
isProcessingAction = true
defer { isProcessingAction = false }
playerHands[activeHandIndex].isStanding = true playerHands[activeHandIndex].isStanding = true
await moveToNextHand() await moveToNextHand()
@ -608,6 +628,8 @@ final class GameState {
/// Player doubles down. /// Player doubles down.
func doubleDown() async { func doubleDown() async {
guard canDouble else { return } guard canDouble else { return }
isProcessingAction = true
defer { isProcessingAction = false }
let additionalBet = playerHands[activeHandIndex].bet let additionalBet = playerHands[activeHandIndex].bet
balance -= additionalBet balance -= additionalBet
@ -632,6 +654,8 @@ final class GameState {
/// Player splits the hand. /// Player splits the hand.
func split() async { func split() async {
guard canSplit else { return } guard canSplit else { return }
isProcessingAction = true
defer { isProcessingAction = false }
let originalHand = playerHands[activeHandIndex] let originalHand = playerHands[activeHandIndex]
let splitCard = originalHand.cards[1] let splitCard = originalHand.cards[1]
@ -676,6 +700,8 @@ final class GameState {
/// Player surrenders. /// Player surrenders.
func surrender() async { func surrender() async {
guard canSurrender else { return } guard canSurrender else { return }
isProcessingAction = true
defer { isProcessingAction = false }
playerHands[activeHandIndex].result = .surrender playerHands[activeHandIndex].result = .surrender
await completeRound() await completeRound()
@ -964,12 +990,19 @@ final class GameState {
// Check if shoe needs reshuffling // Check if shoe needs reshuffling
if engine.needsReshuffle { if engine.needsReshuffle {
engine.reshuffle() engine.reshuffle()
showReshuffleNotification = true
// Auto-dismiss after a delay // Show notification after delay so it appears after result banner is dismissed
Task { Task {
try? await Task.sleep(for: .seconds(2)) try? await Task.sleep(for: .seconds(2))
showReshuffleNotification = false withAnimation(.spring(duration: Design.Animation.springDuration)) {
showReshuffleNotification = true
}
// Auto-dismiss after showing for 2 seconds
try? await Task.sleep(for: .seconds(2))
withAnimation(.spring(duration: Design.Animation.springDuration)) {
showReshuffleNotification = false
}
} }
} }
} }

View File

@ -136,13 +136,6 @@ struct GameTableView: View {
.frame(maxWidth: maxContentWidth) .frame(maxWidth: maxContentWidth)
.debugBorder(showDebugBorders, color: .cyan, label: "TopBar") .debugBorder(showDebugBorders, color: .cyan, label: "TopBar")
// Reshuffle notification
if state.showReshuffleNotification {
ReshuffleNotificationView(showCardCount: settings.showCardCount)
.frame(maxWidth: maxContentWidth)
.transition(.move(edge: .top).combined(with: .opacity))
}
// Table layout // Table layout
BlackjackTableView( BlackjackTableView(
state: state, state: state,
@ -178,6 +171,13 @@ struct GameTableView: View {
Design.debugLog("🔄 Phase changed: \(oldPhase)\(newPhase)") Design.debugLog("🔄 Phase changed: \(oldPhase)\(newPhase)")
} }
// Reshuffle notification overlay (centered, floating)
if state.showReshuffleNotification {
ReshuffleNotificationView(showCardCount: settings.showCardCount)
.transition(.scale.combined(with: .opacity))
.zIndex(50)
}
// Insurance popup overlay (covers entire screen) // Insurance popup overlay (covers entire screen)
if state.currentPhase == .insurance { if state.currentPhase == .insurance {
Color.clear Color.clear