158 lines
5.3 KiB
Swift
158 lines
5.3 KiB
Swift
//
|
|
// DealerHandView.swift
|
|
// Blackjack
|
|
//
|
|
// Displays the dealer's hand with cards and value.
|
|
//
|
|
|
|
import SwiftUI
|
|
import CasinoKit
|
|
|
|
struct DealerHandView: View {
|
|
let hand: BlackjackHand
|
|
let showHoleCard: Bool
|
|
let showCardCount: Bool
|
|
let cardWidth: CGFloat
|
|
let cardSpacing: CGFloat
|
|
|
|
@ScaledMetric(relativeTo: .headline) private var labelFontSize: CGFloat = Design.Size.handLabelFontSize
|
|
|
|
var body: some View {
|
|
VStack(spacing: Design.Spacing.small) {
|
|
// Label and value
|
|
HStack(spacing: Design.Spacing.small) {
|
|
Text(String(localized: "DEALER"))
|
|
.font(.system(size: labelFontSize, weight: .bold, design: .rounded))
|
|
.foregroundStyle(.white)
|
|
|
|
// Show value: always show if hole card visible, or show single card value in European mode
|
|
if !hand.cards.isEmpty {
|
|
if showHoleCard {
|
|
ValueBadge(value: hand.value, color: Color.Hand.dealer)
|
|
} else if hand.cards.count == 1 {
|
|
// European mode: show single visible card value
|
|
ValueBadge(value: hand.cards[0].blackjackValue, color: Color.Hand.dealer)
|
|
}
|
|
}
|
|
}
|
|
|
|
// Cards
|
|
HStack(spacing: hand.cards.isEmpty ? Design.Spacing.small : cardSpacing) {
|
|
if hand.cards.isEmpty {
|
|
CardPlaceholderView(width: cardWidth)
|
|
CardPlaceholderView(width: cardWidth)
|
|
} else {
|
|
ForEach(hand.cards.indices, id: \.self) { index in
|
|
let isFaceUp = index == 0 || showHoleCard
|
|
CardView(
|
|
card: hand.cards[index],
|
|
isFaceUp: isFaceUp,
|
|
cardWidth: cardWidth
|
|
)
|
|
.overlay(alignment: .bottomLeading) {
|
|
if showCardCount && isFaceUp {
|
|
HiLoCountBadge(card: hand.cards[index])
|
|
}
|
|
}
|
|
.zIndex(Double(index))
|
|
}
|
|
|
|
// Show placeholder for second card in European mode (no hole card)
|
|
if hand.cards.count == 1 && !showHoleCard {
|
|
CardPlaceholderView(width: cardWidth)
|
|
.opacity(Design.Opacity.medium)
|
|
}
|
|
}
|
|
}
|
|
|
|
// Result badge
|
|
if let result = hand.cards.count >= 2 && showHoleCard ? handResultText : nil {
|
|
Text(result)
|
|
.font(.system(size: labelFontSize, weight: .black))
|
|
.foregroundStyle(handResultColor)
|
|
.padding(.horizontal, Design.Spacing.medium)
|
|
.padding(.vertical, Design.Spacing.xSmall)
|
|
.background(
|
|
Capsule()
|
|
.fill(handResultColor.opacity(Design.Opacity.hint))
|
|
)
|
|
}
|
|
}
|
|
.accessibilityElement(children: .ignore)
|
|
.accessibilityLabel(dealerAccessibilityLabel)
|
|
}
|
|
|
|
// MARK: - Computed Properties
|
|
|
|
private var handResultText: String? {
|
|
if hand.isBlackjack {
|
|
return String(localized: "BLACKJACK")
|
|
}
|
|
if hand.isBusted {
|
|
return String(localized: "BUST")
|
|
}
|
|
return nil
|
|
}
|
|
|
|
private var handResultColor: Color {
|
|
if hand.isBlackjack { return .yellow }
|
|
if hand.isBusted { return .green } // Good for player
|
|
return .white
|
|
}
|
|
|
|
private var dealerAccessibilityLabel: String {
|
|
if hand.cards.isEmpty {
|
|
return String(localized: "Dealer: No cards")
|
|
}
|
|
let visibleCards = showHoleCard ? hand.cards : [hand.cards[0]]
|
|
let cardsDescription = visibleCards.map { $0.accessibilityDescription }.joined(separator: ", ")
|
|
return String(localized: "Dealer: \(cardsDescription). Value: \(showHoleCard ? String(hand.value) : "hidden")")
|
|
}
|
|
}
|
|
|
|
#Preview("Empty Hand") {
|
|
ZStack {
|
|
Color.Table.felt.ignoresSafeArea()
|
|
DealerHandView(
|
|
hand: BlackjackHand(),
|
|
showHoleCard: false,
|
|
showCardCount: false,
|
|
cardWidth: 60,
|
|
cardSpacing: -20
|
|
)
|
|
}
|
|
}
|
|
|
|
#Preview("Two Cards - Hole Hidden") {
|
|
ZStack {
|
|
Color.Table.felt.ignoresSafeArea()
|
|
DealerHandView(
|
|
hand: BlackjackHand(cards: [
|
|
Card(suit: .spades, rank: .ace),
|
|
Card(suit: .hearts, rank: .king)
|
|
]),
|
|
showHoleCard: false,
|
|
showCardCount: false,
|
|
cardWidth: 60,
|
|
cardSpacing: -20
|
|
)
|
|
}
|
|
}
|
|
|
|
#Preview("Blackjack - Revealed") {
|
|
ZStack {
|
|
Color.Table.felt.ignoresSafeArea()
|
|
DealerHandView(
|
|
hand: BlackjackHand(cards: [
|
|
Card(suit: .spades, rank: .ace),
|
|
Card(suit: .hearts, rank: .king)
|
|
]),
|
|
showHoleCard: true,
|
|
showCardCount: true,
|
|
cardWidth: 60,
|
|
cardSpacing: -20
|
|
)
|
|
}
|
|
}
|
|
|