CasinoGames/Blackjack/Views/RulesHelpView.swift

187 lines
6.9 KiB
Swift

//
// RulesHelpView.swift
// Blackjack
//
// Game rules and how to play guide.
//
import SwiftUI
import CasinoKit
struct RulesHelpView: View {
@Environment(\.dismiss) private var dismiss
@State private var currentPage = 0
private let pages: [RulePage] = [
RulePage(
title: String(localized: "Objective"),
icon: "target",
content: [
String(localized: "Beat the dealer by getting a hand value closer to 21 without going over."),
String(localized: "If you go over 21, you 'bust' and lose immediately."),
String(localized: "If the dealer busts and you haven't, you win.")
]
),
RulePage(
title: String(localized: "Card Values"),
icon: "suit.spade.fill",
content: [
String(localized: "2-10: Face value"),
String(localized: "Jack, Queen, King: 10"),
String(localized: "Ace: 1 or 11 (whichever helps your hand)"),
String(localized: "A 'soft' hand has an Ace counting as 11.")
]
),
RulePage(
title: String(localized: "Blackjack"),
icon: "star.fill",
content: [
String(localized: "An Ace + 10-value card dealt initially is 'Blackjack'."),
String(localized: "Blackjack pays 3:2 (1.5x your bet)."),
String(localized: "If both you and dealer have Blackjack, it's a push (tie).")
]
),
RulePage(
title: String(localized: "Actions"),
icon: "hand.tap.fill",
content: [
String(localized: "Hit: Take another card"),
String(localized: "Stand: Keep your current hand"),
String(localized: "Double Down: Double your bet, take one card, then stand"),
String(localized: "Split: If you have two cards of the same value, split into two hands"),
String(localized: "Surrender: Give up half your bet and end the hand")
]
),
RulePage(
title: String(localized: "Insurance"),
icon: "shield.fill",
content: [
String(localized: "Offered when dealer shows an Ace."),
String(localized: "Costs half your original bet."),
String(localized: "Pays 2:1 if dealer has Blackjack."),
String(localized: "Generally not recommended by basic strategy.")
]
),
RulePage(
title: String(localized: "Dealer Rules"),
icon: "person.fill",
content: [
String(localized: "Dealer must hit on 16 or less."),
String(localized: "Dealer must stand on 17 or more (varies by rules)."),
String(localized: "Some games: Dealer hits on 'soft 17' (Ace + 6).")
]
),
RulePage(
title: String(localized: "Payouts"),
icon: "dollarsign.circle.fill",
content: [
String(localized: "Win: 1:1 (even money)"),
String(localized: "Blackjack: 3:2"),
String(localized: "Insurance: 2:1"),
String(localized: "Push: Bet returned"),
String(localized: "Surrender: Half bet returned")
]
)
]
var body: some View {
NavigationStack {
ZStack {
Color.Settings.background
.ignoresSafeArea()
VStack(spacing: 0) {
// Page content
TabView(selection: $currentPage) {
ForEach(pages.indices, id: \.self) { index in
RulePageView(page: pages[index])
.tag(index)
}
}
.tabViewStyle(.page(indexDisplayMode: .never))
// Page indicator
HStack(spacing: Design.Spacing.small) {
ForEach(pages.indices, id: \.self) { index in
Circle()
.fill(index == currentPage ? Color.Settings.accent : Color.white.opacity(Design.Opacity.light))
.frame(width: 8, height: 8)
}
}
.padding(.vertical, Design.Spacing.medium)
}
}
.navigationTitle(String(localized: "How to Play"))
.navigationBarTitleDisplayMode(.inline)
.toolbar {
ToolbarItem(placement: .topBarTrailing) {
Button(String(localized: "Done")) {
dismiss()
}
.foregroundStyle(Color.Settings.accent)
}
}
.toolbarBackground(Color.Settings.background, for: .navigationBar)
.toolbarColorScheme(.dark, for: .navigationBar)
}
}
}
// MARK: - Rule Page Model
struct RulePage: Identifiable {
let id = UUID()
let title: String
let icon: String
let content: [String]
}
// MARK: - Rule Page View
struct RulePageView: View {
let page: RulePage
@ScaledMetric(relativeTo: .title) private var iconSize: CGFloat = Design.BaseFontSize.display
@ScaledMetric(relativeTo: .title) private var titleSize: CGFloat = Design.BaseFontSize.title
@ScaledMetric(relativeTo: .body) private var bodySize: CGFloat = Design.BaseFontSize.body
var body: some View {
ScrollView {
VStack(spacing: Design.Spacing.xLarge) {
// Icon
Image(systemName: page.icon)
.font(.system(size: iconSize))
.foregroundStyle(Color.Settings.accent)
.padding(.top, Design.Spacing.xxLarge)
// Title
Text(page.title)
.font(.system(size: titleSize, weight: .bold))
.foregroundStyle(.white)
// Content
VStack(alignment: .leading, spacing: Design.Spacing.medium) {
ForEach(page.content.indices, id: \.self) { index in
HStack(alignment: .top, spacing: Design.Spacing.medium) {
Text("")
.foregroundStyle(Color.Settings.accent)
Text(page.content[index])
.font(.system(size: bodySize))
.foregroundStyle(.white.opacity(Design.Opacity.heavy))
}
}
}
.padding(.horizontal, Design.Spacing.xxLarge)
Spacer()
}
}
}
}
#Preview {
RulesHelpView()
}