Signed-off-by: Matt Bruce <mbrucedogs@gmail.com>

This commit is contained in:
Matt Bruce 2025-12-17 12:38:15 -06:00
parent 3132d760ad
commit 4bd99d88ef
5 changed files with 168 additions and 60 deletions

View File

@ -159,16 +159,52 @@ final class GameSettings {
static let soundVolume = "settings.soundVolume" 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 // MARK: - Initialization
init() { init() {
load() 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 // MARK: - Persistence
/// Loads settings from UserDefaults. /// Loads settings from UserDefaults, then checks iCloud for newer settings.
func load() { 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 let defaults = UserDefaults.standard
if let rawDeckCount = defaults.object(forKey: Keys.deckCount) as? Int, 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() { func save() {
// Save to local UserDefaults
let defaults = UserDefaults.standard let defaults = UserDefaults.standard
defaults.set(deckCount.rawValue, forKey: Keys.deckCount) defaults.set(deckCount.rawValue, forKey: Keys.deckCount)
defaults.set(tableLimits.rawValue, forKey: Keys.tableLimits) defaults.set(tableLimits.rawValue, forKey: Keys.tableLimits)
@ -227,6 +313,22 @@ final class GameSettings {
defaults.set(soundEnabled, forKey: Keys.soundEnabled) defaults.set(soundEnabled, forKey: Keys.soundEnabled)
defaults.set(hapticsEnabled, forKey: Keys.hapticsEnabled) defaults.set(hapticsEnabled, forKey: Keys.hapticsEnabled)
defaults.set(soundVolume, forKey: Keys.soundVolume) 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. /// Resets all settings to defaults.

View File

@ -67,13 +67,17 @@ ChipSelectorView(
ChipStackView(amount: 500, chipColor: .red) ChipStackView(amount: 500, chipColor: .red)
``` ```
**Chip Denominations** **Chip Denominations** (with standard casino colors)
- `.one` (1) - `.one` ($1) - White/Light Blue
- `.five` (5) - `.five` ($5) - Red
- `.twentyFive` (25) - `.twentyFive` ($25) - Green
- `.hundred` (100) - `.hundred` ($100) - Black
- `.fiveHundred` (500) - `.fiveHundred` ($500) - Purple
- `.thousand` (1000) - `.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 ### 📋 Sheets & Popups

View File

@ -8,34 +8,33 @@
import SwiftUI import SwiftUI
/// The available chip denominations for casino games. /// The available chip denominations for casino games.
/// Colors follow standard US casino chip conventions.
public enum ChipDenomination: Int, CaseIterable, Identifiable, Sendable { public enum ChipDenomination: Int, CaseIterable, Identifiable, Sendable {
case ten = 10 case one = 1 // White/Blue/Gray
case twentyFive = 25 case five = 5 // Red
case fifty = 50 case twentyFive = 25 // Green
case hundred = 100 case hundred = 100 // Black
case fiveHundred = 500 case fiveHundred = 500 // Purple
case thousand = 1_000 case thousand = 1_000 // Yellow
case fiveThousand = 5_000 case fiveThousand = 5_000 // Brown/Orange
case tenThousand = 10_000 case tenThousand = 10_000 // Custom (often plaques)
case twentyFiveThousand = 25_000 case twentyFiveThousand = 25_000 // Plaque style
case fiftyThousand = 50_000 case hundredThousand = 100_000 // Plaque style
case hundredThousand = 100_000
public var id: Int { rawValue } public var id: Int { rawValue }
/// The display text for this denomination. /// The display text for this denomination.
public var displayText: String { public var displayText: String {
switch self { switch self {
case .ten: return "10" case .one: return "1"
case .five: return "5"
case .twentyFive: return "25" case .twentyFive: return "25"
case .fifty: return "50"
case .hundred: return "100" case .hundred: return "100"
case .fiveHundred: return "500" case .fiveHundred: return "500"
case .thousand: return "1K" case .thousand: return "1K"
case .fiveThousand: return "5K" case .fiveThousand: return "5K"
case .tenThousand: return "10K" case .tenThousand: return "10K"
case .twentyFiveThousand: return "25K" case .twentyFiveThousand: return "25K"
case .fiftyThousand: return "50K"
case .hundredThousand: return "100K" case .hundredThousand: return "100K"
} }
} }
@ -44,13 +43,12 @@ public enum ChipDenomination: Int, CaseIterable, Identifiable, Sendable {
/// Higher chips unlock as you win more! /// Higher chips unlock as you win more!
public var unlockBalance: Int { public var unlockBalance: Int {
switch self { 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 .fiveHundred: return 500
case .thousand: return 1_000 case .thousand: return 1_000
case .fiveThousand: return 5_000 case .fiveThousand: return 5_000
case .tenThousand: return 10_000 case .tenThousand: return 10_000
case .twentyFiveThousand: return 25_000 case .twentyFiveThousand: return 25_000
case .fiftyThousand: return 50_000
case .hundredThousand: return 100_000 case .hundredThousand: return 100_000
} }
} }

View File

@ -121,71 +121,75 @@ public struct DefaultCasinoTheme: CasinoTheme {
public func chipColors(for denomination: ChipDenomination) -> ChipColorSet { public func chipColors(for denomination: ChipDenomination) -> ChipColorSet {
switch denomination { switch denomination {
case .ten: // $1 - White/Light Blue (classic casino white chip)
case .one:
return ChipColorSet( return ChipColorSet(
primary: Color(red: 0.15, green: 0.35, blue: 0.65), primary: Color(red: 0.92, green: 0.94, blue: 0.96),
secondary: Color(red: 0.25, green: 0.5, blue: 0.85), 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 stripe: .white
) )
// $25 - Green (standard casino green)
case .twentyFive: case .twentyFive:
return ChipColorSet( return ChipColorSet(
primary: Color(red: 0.1, green: 0.55, blue: 0.25), primary: Color(red: 0.1, green: 0.55, blue: 0.25),
secondary: Color(red: 0.15, green: 0.75, blue: 0.35), secondary: Color(red: 0.15, green: 0.7, 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),
stripe: .white stripe: .white
) )
// $100 - Black (standard casino black)
case .hundred: case .hundred:
return ChipColorSet( return ChipColorSet(
primary: Color(red: 0.1, green: 0.1, blue: 0.12), primary: Color(red: 0.1, green: 0.1, blue: 0.12),
secondary: Color(red: 0.25, green: 0.25, blue: 0.28), secondary: Color(red: 0.2, green: 0.2, blue: 0.22),
stripe: Color(red: 0.85, green: 0.65, blue: 0.2) // Gold stripe: Color(red: 0.85, green: 0.65, blue: 0.2) // Gold accent
) )
// $500 - Purple (standard casino purple)
case .fiveHundred: case .fiveHundred:
return ChipColorSet( return ChipColorSet(
primary: Color(red: 0.5, green: 0.15, blue: 0.55), primary: Color(red: 0.45, green: 0.15, blue: 0.55),
secondary: Color(red: 0.7, green: 0.25, blue: 0.75), secondary: Color(red: 0.6, green: 0.25, blue: 0.7),
stripe: .white stripe: .white
) )
// $1,000 - Yellow/Gold (standard casino yellow)
case .thousand: case .thousand:
return ChipColorSet( return ChipColorSet(
primary: Color(red: 0.85, green: 0.75, blue: 0.5), primary: Color(red: 0.95, green: 0.8, blue: 0.2),
secondary: Color(red: 1.0, green: 0.95, blue: 0.7), secondary: Color(red: 1.0, green: 0.9, blue: 0.4),
stripe: .black stripe: .black
) )
// $5,000 - Brown/Orange (standard casino brown)
case .fiveThousand: case .fiveThousand:
return ChipColorSet( return ChipColorSet(
primary: Color(red: 0.65, green: 0.35, blue: 0.2), primary: Color(red: 0.55, green: 0.35, blue: 0.2),
secondary: Color(red: 0.85, green: 0.5, blue: 0.3), secondary: Color(red: 0.7, green: 0.45, blue: 0.25),
stripe: Color(red: 0.85, green: 0.65, blue: 0.2) // Gold stripe: Color(red: 0.9, green: 0.7, blue: 0.3) // Gold accent
) )
// $10,000+ - Custom/Gray (high roller, often plaque style)
case .tenThousand: case .tenThousand:
return ChipColorSet( return ChipColorSet(
primary: Color(red: 0.7, green: 0.75, blue: 0.8), primary: Color(red: 0.6, green: 0.65, blue: 0.7),
secondary: Color(red: 0.85, green: 0.9, blue: 0.95), secondary: Color(red: 0.75, green: 0.8, blue: 0.85),
stripe: .white 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: case .twentyFiveThousand:
return ChipColorSet( return ChipColorSet(
primary: Color(red: 0.15, green: 0.4, blue: 0.45), primary: Color(red: 0.15, green: 0.45, blue: 0.5),
secondary: Color(red: 0.2, green: 0.55, blue: 0.6), secondary: Color(red: 0.2, green: 0.6, blue: 0.65),
stripe: Color(red: 0.85, green: 0.65, blue: 0.2) // Gold stripe: Color(red: 0.9, green: 0.75, blue: 0.3) // Gold accent
)
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)
) )
// $100,000 - Burgundy/Custom (VIP plaques)
case .hundredThousand: case .hundredThousand:
return ChipColorSet( return ChipColorSet(
primary: Color(red: 0.6, green: 0.1, blue: 0.15), primary: Color(red: 0.5, green: 0.1, blue: 0.15),
secondary: Color(red: 0.75, green: 0.15, blue: 0.2), secondary: Color(red: 0.65, green: 0.15, blue: 0.2),
stripe: Color(red: 0.95, green: 0.75, blue: 0.3) // Bright gold stripe: Color(red: 0.95, green: 0.8, blue: 0.35) // Bright gold
) )
} }
} }

View File

@ -87,7 +87,7 @@ public struct ChipOnTableView: View {
private var chipColor: Color { private var chipColor: Color {
// Find the closest denomination for coloring // 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 return theme.chipColors(for: denomination).primary
} }