113 lines
4.2 KiB
Swift
113 lines
4.2 KiB
Swift
//
|
|
// BlackjackTableView.swift
|
|
// Blackjack
|
|
//
|
|
// The main table layout showing dealer and player hands.
|
|
//
|
|
|
|
import SwiftUI
|
|
import CasinoKit
|
|
|
|
struct BlackjackTableView: View {
|
|
@Bindable var state: GameState
|
|
let onPlaceBet: () -> Void
|
|
|
|
/// Whether to show Hi-Lo card count values on cards.
|
|
var showCardCount: Bool { state.settings.showCardCount }
|
|
|
|
// MARK: - Scaled Metrics
|
|
|
|
@ScaledMetric(relativeTo: .headline) private var labelFontSize: CGFloat = Design.BaseFontSize.medium
|
|
@ScaledMetric(relativeTo: .title) private var valueFontSize: CGFloat = Design.BaseFontSize.xLarge
|
|
@ScaledMetric(relativeTo: .caption) private var hintFontSize: CGFloat = Design.BaseFontSize.small
|
|
|
|
// MARK: - Layout
|
|
|
|
private let cardWidth: CGFloat = Design.Size.cardWidth
|
|
private let cardSpacing: CGFloat = Design.Size.cardOverlap
|
|
|
|
/// Fixed height for the hint area to prevent layout shifts
|
|
private let hintAreaHeight: CGFloat = 44
|
|
|
|
// Use global debug flag from Design constants
|
|
private var showDebugBorders: Bool { Design.showDebugBorders }
|
|
|
|
var body: some View {
|
|
VStack(spacing: Design.Spacing.small) {
|
|
// Dealer area
|
|
DealerHandView(
|
|
hand: state.dealerHand,
|
|
showHoleCard: state.shouldShowDealerHoleCard,
|
|
showCardCount: showCardCount,
|
|
cardWidth: cardWidth,
|
|
cardSpacing: cardSpacing
|
|
)
|
|
.debugBorder(showDebugBorders, color: .red, label: "Dealer")
|
|
|
|
// Flexible space between dealer and player (minimum 60pt)
|
|
Spacer(minLength: 60)
|
|
.debugBorder(showDebugBorders, color: .yellow, label: "Spacer")
|
|
|
|
// Player hands area - only show when there are cards dealt
|
|
if state.playerHands.first?.cards.isEmpty == false {
|
|
PlayerHandsView(
|
|
hands: state.playerHands,
|
|
activeHandIndex: state.activeHandIndex,
|
|
isPlayerTurn: state.isPlayerTurn,
|
|
showCardCount: showCardCount,
|
|
cardWidth: cardWidth,
|
|
cardSpacing: cardSpacing
|
|
)
|
|
.transition(.opacity)
|
|
.debugBorder(showDebugBorders, color: .green, label: "Player")
|
|
}
|
|
|
|
// Betting zone (when betting)
|
|
if state.currentPhase == .betting {
|
|
Spacer()
|
|
.debugBorder(showDebugBorders, color: .yellow, label: "Spacer2")
|
|
|
|
BettingZoneView(
|
|
betAmount: state.currentBet,
|
|
minBet: state.settings.minBet,
|
|
maxBet: state.settings.maxBet,
|
|
onTap: onPlaceBet
|
|
)
|
|
.transition(.scale.combined(with: .opacity))
|
|
.debugBorder(showDebugBorders, color: .blue, label: "BetZone")
|
|
|
|
// Betting hint based on count (only when card counting enabled)
|
|
if let hint = state.bettingHint {
|
|
BettingHintView(hint: hint, trueCount: state.engine.trueCount)
|
|
.transition(.opacity)
|
|
.debugBorder(showDebugBorders, color: .purple, label: "BetHint")
|
|
}
|
|
} else {
|
|
// Fixed-height hint area to prevent layout shifts during player turn
|
|
ZStack {
|
|
if let hint = state.currentHint {
|
|
HintView(hint: hint)
|
|
.transition(.opacity)
|
|
}
|
|
}
|
|
.frame(height: hintAreaHeight)
|
|
.debugBorder(showDebugBorders, color: .orange, label: "HintArea")
|
|
}
|
|
}
|
|
.padding(.horizontal, Design.Spacing.large)
|
|
.padding(.vertical, Design.Spacing.medium)
|
|
.debugBorder(showDebugBorders, color: .white, label: "TableView")
|
|
.animation(.spring(duration: Design.Animation.springDuration), value: state.currentPhase)
|
|
}
|
|
}
|
|
|
|
// MARK: - Previews
|
|
|
|
#Preview {
|
|
ZStack {
|
|
Color.Table.felt.ignoresSafeArea()
|
|
Text("Use GameTableView for full preview")
|
|
.foregroundStyle(.white)
|
|
}
|
|
}
|