From fa016047c2e89414f9c040442eb901bb4692ea5a Mon Sep 17 00:00:00 2001 From: Matt Bruce Date: Sun, 28 Dec 2025 18:25:33 -0600 Subject: [PATCH] 1st attempt of roation Signed-off-by: Matt Bruce --- Baccarat/Baccarat/Engine/GameState.swift | 7 ++ .../Baccarat/Views/Game/GameTableView.swift | 40 +++----- .../Views/Table/CardsDisplayArea.swift | 97 +++++++++++++++---- .../Blackjack/Theme/DesignConstants.swift | 2 +- 4 files changed, 99 insertions(+), 47 deletions(-) diff --git a/Baccarat/Baccarat/Engine/GameState.swift b/Baccarat/Baccarat/Engine/GameState.swift index a168b6a..567a6cf 100644 --- a/Baccarat/Baccarat/Engine/GameState.swift +++ b/Baccarat/Baccarat/Engine/GameState.swift @@ -518,6 +518,13 @@ final class GameState { return bet.amount < minBet } + /// Which main bet is placed - nil if no main bet, true if Player, false if Banker. + /// Used to determine card layout ordering (betted hand appears on bottom). + var bettedOnPlayer: Bool? { + guard let bet = mainBet else { return nil } + return bet.type == .player + } + /// Amount needed to reach the minimum bet. var amountNeededForMinimum: Int { guard let bet = mainBet else { return minBet } diff --git a/Baccarat/Baccarat/Views/Game/GameTableView.swift b/Baccarat/Baccarat/Views/Game/GameTableView.swift index 0443371..6368141 100644 --- a/Baccarat/Baccarat/Views/Game/GameTableView.swift +++ b/Baccarat/Baccarat/Views/Game/GameTableView.swift @@ -37,21 +37,11 @@ struct GameTableView: View { isLandscape ? Design.Spacing.medium : Design.Spacing.xSmall } - /// Minimum spacer height - smaller in landscape to fit content - private var minSpacerHeight: CGFloat { - isLandscape ? 0 : Design.Spacing.xSmall - } - /// Smaller spacer height - reduced in landscape private var smallSpacerHeight: CGFloat { isLandscape ? Design.Spacing.xxSmall : Design.Spacing.small } - /// Medium spacer height - reduced in landscape - private var mediumSpacerHeight: CGFloat { - isLandscape ? Design.Spacing.xSmall : Design.Spacing.medium - } - /// Maximum width for game content on large screens private var maxContentWidth: CGFloat { isLandscape ? CasinoDesign.Size.maxContentWidthLandscape : CasinoDesign.Size.maxContentWidthPortrait @@ -73,6 +63,11 @@ struct GameTableView: View { state.lastResult == .tie } + /// Whether we're in a dealing/result phase (vertical layout) vs betting phase (horizontal) + private var isDealing: Bool { + state.currentPhase != .betting + } + // Use global debug flag from Design constants private var showDebugBorders: Bool { Design.showDebugBorders } @@ -195,7 +190,9 @@ struct GameTableView: View { bankerIsWinner: bankerIsWinner, isTie: isTie, showAnimations: settings.showAnimations, - dealingSpeed: settings.dealingSpeed + dealingSpeed: settings.dealingSpeed, + bettedOnPlayer: state.bettedOnPlayer, + isDealing: isDealing ) .frame(maxWidth: maxContentWidth) .padding(.horizontal, Design.Spacing.medium) @@ -264,7 +261,7 @@ struct GameTableView: View { .safeAreaPadding(.bottom, Design.Spacing.small) } - /// Portrait layout with RoadMap inline + /// Portrait layout - vertical card layout, no RoadMap (shown only in landscape) private var portraitLayout: some View { VStack(spacing: 0) { // Top bar with balance and info (from CasinoKit) @@ -278,7 +275,7 @@ struct GameTableView: View { ) .debugBorder(showDebugBorders, color: .cyan, label: "TopBar") - // Cards display area + // Cards display area - animates from horizontal to vertical when dealing CardsDisplayArea( playerCards: state.visiblePlayerCards, bankerCards: state.visibleBankerCards, @@ -290,25 +287,16 @@ struct GameTableView: View { bankerIsWinner: bankerIsWinner, isTie: isTie, showAnimations: settings.showAnimations, - dealingSpeed: settings.dealingSpeed + dealingSpeed: settings.dealingSpeed, + bettedOnPlayer: state.bettedOnPlayer, + isDealing: isDealing ) .frame(maxWidth: isLargeScreen ? maxContentWidth : .infinity) .padding(.horizontal, Design.Spacing.medium) .debugBorder(showDebugBorders, color: .red, label: "CardsArea") - Spacer(minLength: minSpacerHeight) - .debugBorder(showDebugBorders, color: .yellow, label: "Spacer2") - - // Road map history - if settings.showHistory { - RoadMapView(results: state.recentResults) - .frame(maxWidth: isLargeScreen ? maxContentWidth : .infinity) - .padding(.horizontal, Design.Spacing.medium) - .debugBorder(showDebugBorders, color: .orange, label: "RoadMap") - } - Spacer(minLength: smallSpacerHeight) - .debugBorder(showDebugBorders, color: .yellow, label: "Spacer3") + .debugBorder(showDebugBorders, color: .yellow, label: "Spacer2") // Betting table BettingTableView( diff --git a/Baccarat/Baccarat/Views/Table/CardsDisplayArea.swift b/Baccarat/Baccarat/Views/Table/CardsDisplayArea.swift index 67348b6..06ddfbe 100644 --- a/Baccarat/Baccarat/Views/Table/CardsDisplayArea.swift +++ b/Baccarat/Baccarat/Views/Table/CardsDisplayArea.swift @@ -3,12 +3,16 @@ // Baccarat // // The cards display area showing both Player and Banker hands. +// Defaults to side-by-side during betting, animates to vertical during dealing +// with the betted hand on bottom. // import SwiftUI import CasinoKit /// The cards display area showing both hands. +/// - Betting phase: Horizontal side-by-side layout (Player | Banker) +/// - Dealing/Result phases: Vertical layout with betted hand on bottom struct CardsDisplayArea: View { let playerCards: [Card] let bankerCards: [Card] @@ -21,6 +25,11 @@ struct CardsDisplayArea: View { let isTie: Bool let showAnimations: Bool let dealingSpeed: Double + /// Which main bet is placed - nil if no main bet, true if Player, false if Banker. + /// Determines layout ordering in vertical mode: betted hand appears on bottom. + let bettedOnPlayer: Bool? + /// Whether the game is in dealing/result phase (vertical layout) or betting phase (horizontal). + let isDealing: Bool // MARK: - State @@ -50,9 +59,22 @@ struct CardsDisplayArea: View { isLargeScreen ? 40 : Design.Size.labelRowHeight } - /// Spacing between PLAYER and BANKER hands - reduced on smaller screens + /// Spacing between hands private var handsSpacing: CGFloat { - isLargeScreen ? Design.Spacing.xxxLarge : Design.Spacing.large + if isDealing { + return isLargeScreen ? Design.Spacing.large : Design.Spacing.medium + } else { + return isLargeScreen ? Design.Spacing.xxxLarge : Design.Spacing.large + } + } + + /// Card section width - larger in vertical mode (more horizontal space) + private var handSectionWidth: CGFloat { + if isDealing { + return containerWidth * 0.7 + } else { + return (containerWidth - handsSpacing) / 2 + } } // MARK: - Accessibility @@ -89,22 +111,50 @@ struct CardsDisplayArea: View { return visibleCards.joined(separator: ", ") + ". " + String(format: format, bankerValue) } - /// Calculate hand section width from total container width - private var handSectionWidth: CGFloat { - (containerWidth - handsSpacing) / 2 + /// Whether Player hand should be on bottom (true) or top (false) in vertical mode. + /// Defaults to Player on bottom if no bet placed. + private var playerOnBottom: Bool { + bettedOnPlayer ?? true + } + + /// The layout to use - HStack for horizontal, VStack for vertical + private var layout: AnyLayout { + isDealing ? AnyLayout(VStackLayout(spacing: handsSpacing)) : AnyLayout(HStackLayout(spacing: handsSpacing)) } // MARK: - Body var body: some View { - HStack(spacing: handsSpacing) { - // Player side - playerHandSection(width: handSectionWidth) - .debugBorder(showDebugBorders, color: .blue, label: "Player") + layout { + // First position: Player in horizontal, or top hand in vertical + if isDealing && !playerOnBottom { + // Vertical mode, player on top + playerHandSection(width: handSectionWidth) + .debugBorder(showDebugBorders, color: .blue, label: "Player") + } else if isDealing && playerOnBottom { + // Vertical mode, banker on top + bankerHandSection(width: handSectionWidth) + .debugBorder(showDebugBorders, color: .red, label: "Banker") + } else { + // Horizontal mode - player always on left + playerHandSection(width: handSectionWidth) + .debugBorder(showDebugBorders, color: .blue, label: "Player") + } - // Banker side - bankerHandSection(width: handSectionWidth) - .debugBorder(showDebugBorders, color: .red, label: "Banker") + // Second position: Banker in horizontal, or bottom hand in vertical + if isDealing && !playerOnBottom { + // Vertical mode, banker on bottom + bankerHandSection(width: handSectionWidth) + .debugBorder(showDebugBorders, color: .red, label: "Banker") + } else if isDealing && playerOnBottom { + // Vertical mode, player on bottom + playerHandSection(width: handSectionWidth) + .debugBorder(showDebugBorders, color: .blue, label: "Player") + } else { + // Horizontal mode - banker always on right + bankerHandSection(width: handSectionWidth) + .debugBorder(showDebugBorders, color: .red, label: "Banker") + } } .frame(maxWidth: .infinity) .padding(.top, Design.Spacing.medium) @@ -126,6 +176,8 @@ struct CardsDisplayArea: View { .accessibilityHidden(true) ) .debugBorder(showDebugBorders, color: .mint, label: "HandsContainer") + .animation(.spring(duration: 0.5, bounce: 0.2), value: isDealing) + .animation(.spring(duration: 0.4, bounce: 0.15), value: playerOnBottom) } // MARK: - Private Views @@ -193,7 +245,7 @@ struct CardsDisplayArea: View { // MARK: - Previews -#Preview("Empty Hands") { +#Preview("Horizontal - Betting Phase") { ZStack { TableBackgroundView() CardsDisplayArea( @@ -207,12 +259,14 @@ struct CardsDisplayArea: View { bankerIsWinner: false, isTie: false, showAnimations: true, - dealingSpeed: 1.0 + dealingSpeed: 1.0, + bettedOnPlayer: nil, + isDealing: false ) } } -#Preview("Player Wins") { +#Preview("Vertical - Dealing (Bet on Player)") { ZStack { TableBackgroundView() CardsDisplayArea( @@ -232,12 +286,14 @@ struct CardsDisplayArea: View { bankerIsWinner: false, isTie: false, showAnimations: true, - dealingSpeed: 1.0 + dealingSpeed: 1.0, + bettedOnPlayer: true, + isDealing: true ) } } -#Preview("Banker Wins with 3 Cards") { +#Preview("Vertical - Dealing (Bet on Banker)") { ZStack { TableBackgroundView() CardsDisplayArea( @@ -254,13 +310,14 @@ struct CardsDisplayArea: View { playerCardsFaceUp: [true, true, true], bankerCardsFaceUp: [true, true, true], playerValue: 9, - bankerValue: 8, + bankerValue: 9, playerIsWinner: false, bankerIsWinner: true, isTie: false, showAnimations: true, - dealingSpeed: 1.0 + dealingSpeed: 1.0, + bettedOnPlayer: false, + isDealing: true ) } } - diff --git a/Blackjack/Blackjack/Theme/DesignConstants.swift b/Blackjack/Blackjack/Theme/DesignConstants.swift index f655476..c87e9ed 100644 --- a/Blackjack/Blackjack/Theme/DesignConstants.swift +++ b/Blackjack/Blackjack/Theme/DesignConstants.swift @@ -17,7 +17,7 @@ enum Design { // MARK: - Debug /// Set to true to show layout debug borders on views - static let showDebugBorders = true + static let showDebugBorders = false /// Set to true to show debug log statements static let showDebugLogs = false