Signed-off-by: Matt Bruce <mbrucedogs@gmail.com>
This commit is contained in:
parent
98d72d0db8
commit
45ad602d9a
@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -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
|
||||
|
||||
Loading…
Reference in New Issue
Block a user