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.
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.
var activeHand: BlackjackHand? {
guard activeHandIndex < playerHands.count else { return nil }
@ -184,18 +187,21 @@ final class GameState {
/// Whether the current hand can hit.
var canHit: Bool {
guard !isProcessingAction else { return false }
guard case .playerTurn = currentPhase else { return false }
return activeHand?.canHit ?? false
}
/// Whether the current hand can stand.
var canStand: Bool {
guard !isProcessingAction else { return false }
guard case .playerTurn = currentPhase else { return false }
return !(activeHand?.isBusted ?? true)
}
/// Whether the current hand can double.
var canDouble: Bool {
guard !isProcessingAction else { return false }
guard case .playerTurn = currentPhase else { return false }
guard let hand = activeHand else { return false }
return engine.canDoubleDown(hand: hand, balance: balance)
@ -203,6 +209,7 @@ final class GameState {
/// Whether the current hand can split.
var canSplit: Bool {
guard !isProcessingAction else { return false }
guard case .playerTurn = currentPhase else { return false }
guard let hand = activeHand else { return false }
let splitCount = playerHands.count - 1
@ -211,6 +218,7 @@ final class GameState {
/// Whether the player can surrender.
var canSurrender: Bool {
guard !isProcessingAction else { return false }
guard case .playerTurn = currentPhase else { return false }
guard let hand = activeHand else { return false }
return engine.canSurrender(hand: hand)
@ -445,11 +453,18 @@ final class GameState {
// Ensure enough cards for a full hand - reshuffle if needed
if !engine.canDealNewHand {
engine.reshuffle()
showReshuffleNotification = true
// Show notification with animation
withAnimation(.spring(duration: Design.Animation.springDuration)) {
showReshuffleNotification = true
}
// Auto-dismiss after 2 seconds
Task {
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).
func hit() async {
guard canHit else { return }
isProcessingAction = true
defer { isProcessingAction = false }
guard let card = engine.dealCard() else { return }
playerHands[activeHandIndex].cards.append(card)
@ -600,6 +618,8 @@ final class GameState {
/// Player stands.
func stand() async {
guard canStand else { return }
isProcessingAction = true
defer { isProcessingAction = false }
playerHands[activeHandIndex].isStanding = true
await moveToNextHand()
@ -608,6 +628,8 @@ final class GameState {
/// Player doubles down.
func doubleDown() async {
guard canDouble else { return }
isProcessingAction = true
defer { isProcessingAction = false }
let additionalBet = playerHands[activeHandIndex].bet
balance -= additionalBet
@ -632,6 +654,8 @@ final class GameState {
/// Player splits the hand.
func split() async {
guard canSplit else { return }
isProcessingAction = true
defer { isProcessingAction = false }
let originalHand = playerHands[activeHandIndex]
let splitCard = originalHand.cards[1]
@ -676,6 +700,8 @@ final class GameState {
/// Player surrenders.
func surrender() async {
guard canSurrender else { return }
isProcessingAction = true
defer { isProcessingAction = false }
playerHands[activeHandIndex].result = .surrender
await completeRound()
@ -964,12 +990,19 @@ final class GameState {
// Check if shoe needs reshuffling
if engine.needsReshuffle {
engine.reshuffle()
showReshuffleNotification = true
// Auto-dismiss after a delay
// Show notification after delay so it appears after result banner is dismissed
Task {
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)
.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
BlackjackTableView(
state: state,
@ -178,6 +171,13 @@ struct GameTableView: View {
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)
if state.currentPhase == .insurance {
Color.clear