187 lines
6.9 KiB
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()
|
|
}
|
|
|