From 4bd99d88ef7eda1e65ca42276decfd03fced0d29 Mon Sep 17 00:00:00 2001 From: Matt Bruce Date: Wed, 17 Dec 2025 12:38:15 -0600 Subject: [PATCH] Signed-off-by: Matt Bruce --- Baccarat/Models/GameSettings.swift | 106 +++++++++++++++++- CasinoKit/README.md | 18 +-- .../CasinoKit/Models/ChipDenomination.swift | 30 +++-- .../Sources/CasinoKit/Theme/CasinoTheme.swift | 72 ++++++------ .../CasinoKit/Views/Chips/ChipStackView.swift | 2 +- 5 files changed, 168 insertions(+), 60 deletions(-) diff --git a/Baccarat/Models/GameSettings.swift b/Baccarat/Models/GameSettings.swift index 5974f36..f162f2d 100644 --- a/Baccarat/Models/GameSettings.swift +++ b/Baccarat/Models/GameSettings.swift @@ -159,16 +159,52 @@ final class GameSettings { static let soundVolume = "settings.soundVolume" } + // MARK: - iCloud + + private let iCloudStore = NSUbiquitousKeyValueStore.default + + /// Whether iCloud is available. + var iCloudAvailable: Bool { + FileManager.default.ubiquityIdentityToken != nil + } + // MARK: - Initialization init() { load() + + // Register for iCloud changes + NotificationCenter.default.addObserver( + forName: NSUbiquitousKeyValueStore.didChangeExternallyNotification, + object: iCloudStore, + queue: .main + ) { [weak self] _ in + Task { @MainActor in + self?.loadFromiCloud() + } + } + + // Trigger iCloud sync + if iCloudAvailable { + iCloudStore.synchronize() + } } // MARK: - Persistence - /// Loads settings from UserDefaults. + /// Loads settings from UserDefaults, then checks iCloud for newer settings. func load() { + // First load from local UserDefaults + loadFromLocal() + + // Then check iCloud for potentially newer settings + if iCloudAvailable { + loadFromiCloud() + } + } + + /// Loads settings from local UserDefaults. + private func loadFromLocal() { let defaults = UserDefaults.standard if let rawDeckCount = defaults.object(forKey: Keys.deckCount) as? Int, @@ -214,8 +250,58 @@ final class GameSettings { } } - /// Saves settings to UserDefaults. + /// Loads settings from iCloud. + private func loadFromiCloud() { + guard iCloudAvailable else { return } + + if let rawDeckCount = iCloudStore.object(forKey: Keys.deckCount) as? Int, + let deckCount = DeckCount(rawValue: rawDeckCount) { + self.deckCount = deckCount + } + + if let rawTableLimits = iCloudStore.string(forKey: Keys.tableLimits), + let tableLimits = TableLimits(rawValue: rawTableLimits) { + self.tableLimits = tableLimits + } + + if let balance = iCloudStore.object(forKey: Keys.startingBalance) as? Int { + self.startingBalance = balance + } + + if iCloudStore.object(forKey: Keys.showAnimations) != nil { + self.showAnimations = iCloudStore.bool(forKey: Keys.showAnimations) + } + + if let speed = iCloudStore.object(forKey: Keys.dealingSpeed) as? Double { + self.dealingSpeed = speed + } + + if iCloudStore.object(forKey: Keys.showCardsRemaining) != nil { + self.showCardsRemaining = iCloudStore.bool(forKey: Keys.showCardsRemaining) + } + + if iCloudStore.object(forKey: Keys.showHistory) != nil { + self.showHistory = iCloudStore.bool(forKey: Keys.showHistory) + } + + if iCloudStore.object(forKey: Keys.soundEnabled) != nil { + self.soundEnabled = iCloudStore.bool(forKey: Keys.soundEnabled) + } + + if iCloudStore.object(forKey: Keys.hapticsEnabled) != nil { + self.hapticsEnabled = iCloudStore.bool(forKey: Keys.hapticsEnabled) + } + + if let volume = iCloudStore.object(forKey: Keys.soundVolume) as? Double { + self.soundVolume = Float(volume) + } + + print("GameSettings: Loaded from iCloud") + } + + /// Saves settings to UserDefaults and iCloud. func save() { + // Save to local UserDefaults let defaults = UserDefaults.standard defaults.set(deckCount.rawValue, forKey: Keys.deckCount) defaults.set(tableLimits.rawValue, forKey: Keys.tableLimits) @@ -227,6 +313,22 @@ final class GameSettings { defaults.set(soundEnabled, forKey: Keys.soundEnabled) defaults.set(hapticsEnabled, forKey: Keys.hapticsEnabled) defaults.set(soundVolume, forKey: Keys.soundVolume) + + // Also save to iCloud + if iCloudAvailable { + iCloudStore.set(deckCount.rawValue, forKey: Keys.deckCount) + iCloudStore.set(tableLimits.rawValue, forKey: Keys.tableLimits) + iCloudStore.set(startingBalance, forKey: Keys.startingBalance) + iCloudStore.set(showAnimations, forKey: Keys.showAnimations) + iCloudStore.set(dealingSpeed, forKey: Keys.dealingSpeed) + iCloudStore.set(showCardsRemaining, forKey: Keys.showCardsRemaining) + iCloudStore.set(showHistory, forKey: Keys.showHistory) + iCloudStore.set(soundEnabled, forKey: Keys.soundEnabled) + iCloudStore.set(hapticsEnabled, forKey: Keys.hapticsEnabled) + iCloudStore.set(Double(soundVolume), forKey: Keys.soundVolume) + iCloudStore.synchronize() + print("GameSettings: Saved to iCloud") + } } /// Resets all settings to defaults. diff --git a/CasinoKit/README.md b/CasinoKit/README.md index ccbedfe..1907b87 100644 --- a/CasinoKit/README.md +++ b/CasinoKit/README.md @@ -67,13 +67,17 @@ ChipSelectorView( ChipStackView(amount: 500, chipColor: .red) ``` -**Chip Denominations** -- `.one` (1) -- `.five` (5) -- `.twentyFive` (25) -- `.hundred` (100) -- `.fiveHundred` (500) -- `.thousand` (1000) +**Chip Denominations** (with standard casino colors) +- `.one` ($1) - White/Light Blue +- `.five` ($5) - Red +- `.twentyFive` ($25) - Green +- `.hundred` ($100) - Black +- `.fiveHundred` ($500) - Purple +- `.thousand` ($1,000) - Yellow +- `.fiveThousand` ($5,000) - Brown +- `.tenThousand` ($10,000) - Gray (custom) +- `.twentyFiveThousand` ($25,000) - Teal (plaque style) +- `.hundredThousand` ($100,000) - Burgundy (VIP) ### 📋 Sheets & Popups diff --git a/CasinoKit/Sources/CasinoKit/Models/ChipDenomination.swift b/CasinoKit/Sources/CasinoKit/Models/ChipDenomination.swift index ecb2357..f375bba 100644 --- a/CasinoKit/Sources/CasinoKit/Models/ChipDenomination.swift +++ b/CasinoKit/Sources/CasinoKit/Models/ChipDenomination.swift @@ -8,34 +8,33 @@ import SwiftUI /// The available chip denominations for casino games. +/// Colors follow standard US casino chip conventions. public enum ChipDenomination: Int, CaseIterable, Identifiable, Sendable { - case ten = 10 - case twentyFive = 25 - case fifty = 50 - case hundred = 100 - case fiveHundred = 500 - case thousand = 1_000 - case fiveThousand = 5_000 - case tenThousand = 10_000 - case twentyFiveThousand = 25_000 - case fiftyThousand = 50_000 - case hundredThousand = 100_000 + case one = 1 // White/Blue/Gray + case five = 5 // Red + case twentyFive = 25 // Green + case hundred = 100 // Black + case fiveHundred = 500 // Purple + case thousand = 1_000 // Yellow + case fiveThousand = 5_000 // Brown/Orange + case tenThousand = 10_000 // Custom (often plaques) + case twentyFiveThousand = 25_000 // Plaque style + case hundredThousand = 100_000 // Plaque style public var id: Int { rawValue } /// The display text for this denomination. public var displayText: String { switch self { - case .ten: return "10" + case .one: return "1" + case .five: return "5" case .twentyFive: return "25" - case .fifty: return "50" case .hundred: return "100" case .fiveHundred: return "500" case .thousand: return "1K" case .fiveThousand: return "5K" case .tenThousand: return "10K" case .twentyFiveThousand: return "25K" - case .fiftyThousand: return "50K" case .hundredThousand: return "100K" } } @@ -44,13 +43,12 @@ public enum ChipDenomination: Int, CaseIterable, Identifiable, Sendable { /// Higher chips unlock as you win more! public var unlockBalance: Int { switch self { - case .ten, .twentyFive, .fifty, .hundred: return 0 // Always available + case .one, .five, .twentyFive, .hundred: return 0 // Always available case .fiveHundred: return 500 case .thousand: return 1_000 case .fiveThousand: return 5_000 case .tenThousand: return 10_000 case .twentyFiveThousand: return 25_000 - case .fiftyThousand: return 50_000 case .hundredThousand: return 100_000 } } diff --git a/CasinoKit/Sources/CasinoKit/Theme/CasinoTheme.swift b/CasinoKit/Sources/CasinoKit/Theme/CasinoTheme.swift index f7529c7..37b16b9 100644 --- a/CasinoKit/Sources/CasinoKit/Theme/CasinoTheme.swift +++ b/CasinoKit/Sources/CasinoKit/Theme/CasinoTheme.swift @@ -121,71 +121,75 @@ public struct DefaultCasinoTheme: CasinoTheme { public func chipColors(for denomination: ChipDenomination) -> ChipColorSet { switch denomination { - case .ten: + // $1 - White/Light Blue (classic casino white chip) + case .one: return ChipColorSet( - primary: Color(red: 0.15, green: 0.35, blue: 0.65), - secondary: Color(red: 0.25, green: 0.5, blue: 0.85), + primary: Color(red: 0.92, green: 0.94, blue: 0.96), + secondary: Color(red: 0.98, green: 0.99, blue: 1.0), + stripe: Color(red: 0.3, green: 0.5, blue: 0.7) // Blue accent + ) + // $5 - Red (standard casino red) + case .five: + return ChipColorSet( + primary: Color(red: 0.8, green: 0.15, blue: 0.15), + secondary: Color(red: 0.95, green: 0.25, blue: 0.25), stripe: .white ) + // $25 - Green (standard casino green) case .twentyFive: return ChipColorSet( primary: Color(red: 0.1, green: 0.55, blue: 0.25), - secondary: Color(red: 0.15, green: 0.75, blue: 0.35), - stripe: .white - ) - case .fifty: - return ChipColorSet( - primary: Color(red: 0.9, green: 0.5, blue: 0.1), - secondary: Color(red: 1.0, green: 0.65, blue: 0.2), + secondary: Color(red: 0.15, green: 0.7, blue: 0.35), stripe: .white ) + // $100 - Black (standard casino black) case .hundred: return ChipColorSet( primary: Color(red: 0.1, green: 0.1, blue: 0.12), - secondary: Color(red: 0.25, green: 0.25, blue: 0.28), - stripe: Color(red: 0.85, green: 0.65, blue: 0.2) // Gold + secondary: Color(red: 0.2, green: 0.2, blue: 0.22), + stripe: Color(red: 0.85, green: 0.65, blue: 0.2) // Gold accent ) + // $500 - Purple (standard casino purple) case .fiveHundred: return ChipColorSet( - primary: Color(red: 0.5, green: 0.15, blue: 0.55), - secondary: Color(red: 0.7, green: 0.25, blue: 0.75), + primary: Color(red: 0.45, green: 0.15, blue: 0.55), + secondary: Color(red: 0.6, green: 0.25, blue: 0.7), stripe: .white ) + // $1,000 - Yellow/Gold (standard casino yellow) case .thousand: return ChipColorSet( - primary: Color(red: 0.85, green: 0.75, blue: 0.5), - secondary: Color(red: 1.0, green: 0.95, blue: 0.7), + primary: Color(red: 0.95, green: 0.8, blue: 0.2), + secondary: Color(red: 1.0, green: 0.9, blue: 0.4), stripe: .black ) + // $5,000 - Brown/Orange (standard casino brown) case .fiveThousand: return ChipColorSet( - primary: Color(red: 0.65, green: 0.35, blue: 0.2), - secondary: Color(red: 0.85, green: 0.5, blue: 0.3), - stripe: Color(red: 0.85, green: 0.65, blue: 0.2) // Gold + primary: Color(red: 0.55, green: 0.35, blue: 0.2), + secondary: Color(red: 0.7, green: 0.45, blue: 0.25), + stripe: Color(red: 0.9, green: 0.7, blue: 0.3) // Gold accent ) + // $10,000+ - Custom/Gray (high roller, often plaque style) case .tenThousand: return ChipColorSet( - primary: Color(red: 0.7, green: 0.75, blue: 0.8), - secondary: Color(red: 0.85, green: 0.9, blue: 0.95), - stripe: .white + primary: Color(red: 0.6, green: 0.65, blue: 0.7), + secondary: Color(red: 0.75, green: 0.8, blue: 0.85), + stripe: Color(red: 0.2, green: 0.25, blue: 0.4) // Dark blue accent ) + // $25,000 - Teal/Custom (often plaques at this level) case .twentyFiveThousand: return ChipColorSet( - primary: Color(red: 0.15, green: 0.4, blue: 0.45), - secondary: Color(red: 0.2, green: 0.55, blue: 0.6), - stripe: Color(red: 0.85, green: 0.65, blue: 0.2) // Gold - ) - case .fiftyThousand: - return ChipColorSet( - primary: Color(red: 0.85, green: 0.4, blue: 0.5), - secondary: Color(red: 1.0, green: 0.55, blue: 0.65), - stripe: Color(red: 0.3, green: 0.15, blue: 0.2) + primary: Color(red: 0.15, green: 0.45, blue: 0.5), + secondary: Color(red: 0.2, green: 0.6, blue: 0.65), + stripe: Color(red: 0.9, green: 0.75, blue: 0.3) // Gold accent ) + // $100,000 - Burgundy/Custom (VIP plaques) case .hundredThousand: return ChipColorSet( - primary: Color(red: 0.6, green: 0.1, blue: 0.15), - secondary: Color(red: 0.75, green: 0.15, blue: 0.2), - stripe: Color(red: 0.95, green: 0.75, blue: 0.3) // Bright gold + primary: Color(red: 0.5, green: 0.1, blue: 0.15), + secondary: Color(red: 0.65, green: 0.15, blue: 0.2), + stripe: Color(red: 0.95, green: 0.8, blue: 0.35) // Bright gold ) } } diff --git a/CasinoKit/Sources/CasinoKit/Views/Chips/ChipStackView.swift b/CasinoKit/Sources/CasinoKit/Views/Chips/ChipStackView.swift index 4cc46a0..9c67ec6 100644 --- a/CasinoKit/Sources/CasinoKit/Views/Chips/ChipStackView.swift +++ b/CasinoKit/Sources/CasinoKit/Views/Chips/ChipStackView.swift @@ -87,7 +87,7 @@ public struct ChipOnTableView: View { private var chipColor: Color { // Find the closest denomination for coloring - let denomination = ChipDenomination.allCases.last { $0.rawValue <= amount } ?? .ten + let denomination = ChipDenomination.allCases.last { $0.rawValue <= amount } ?? .one return theme.chipColors(for: denomination).primary }