diff --git a/ios/MagicCounter.xcodeproj/project.pbxproj b/ios/MagicCounter.xcodeproj/project.pbxproj index 54dccbe..7cd344c 100644 --- a/ios/MagicCounter.xcodeproj/project.pbxproj +++ b/ios/MagicCounter.xcodeproj/project.pbxproj @@ -425,7 +425,7 @@ "$(inherited)", "@executable_path/Frameworks", ); - MARKETING_VERSION = 1.0; + MARKETING_VERSION = 1.0.0; PRODUCT_BUNDLE_IDENTIFIER = com.atri.dad.MagicCounter.MagicCounter; PRODUCT_NAME = "$(TARGET_NAME)"; STRING_CATALOG_GENERATE_SYMBOLS = YES; @@ -459,7 +459,7 @@ "$(inherited)", "@executable_path/Frameworks", ); - MARKETING_VERSION = 1.0; + MARKETING_VERSION = 1.0.0; PRODUCT_BUNDLE_IDENTIFIER = com.atri.dad.MagicCounter.MagicCounter; PRODUCT_NAME = "$(TARGET_NAME)"; STRING_CATALOG_GENERATE_SYMBOLS = YES; diff --git a/ios/MagicCounter.xcodeproj/project.xcworkspace/xcuserdata/atridad.xcuserdatad/UserInterfaceState.xcuserstate b/ios/MagicCounter.xcodeproj/project.xcworkspace/xcuserdata/atridad.xcuserdatad/UserInterfaceState.xcuserstate index 086476a..80b153e 100644 Binary files a/ios/MagicCounter.xcodeproj/project.xcworkspace/xcuserdata/atridad.xcuserdatad/UserInterfaceState.xcuserstate and b/ios/MagicCounter.xcodeproj/project.xcworkspace/xcuserdata/atridad.xcuserdatad/UserInterfaceState.xcuserstate differ diff --git a/ios/MagicCounter/Components.swift b/ios/MagicCounter/Components.swift index 6f1110e..c476835 100644 --- a/ios/MagicCounter/Components.swift +++ b/ios/MagicCounter/Components.swift @@ -112,3 +112,20 @@ struct SettingSlider: View { } } } + +/** + * Helper for haptic feedback. + */ +enum Haptics { + static func play(_ style: UIImpactFeedbackGenerator.FeedbackStyle) { + guard UserDefaults.standard.bool(forKey: "hapticFeedbackEnabled") else { return } + let generator = UIImpactFeedbackGenerator(style: style) + generator.impactOccurred() + } + + static func notification(_ type: UINotificationFeedbackGenerator.FeedbackType) { + guard UserDefaults.standard.bool(forKey: "hapticFeedbackEnabled") else { return } + let generator = UINotificationFeedbackGenerator() + generator.notificationOccurred(type) + } +} diff --git a/ios/MagicCounter/ContentView.swift b/ios/MagicCounter/ContentView.swift index 61d998f..b1c832a 100644 --- a/ios/MagicCounter/ContentView.swift +++ b/ios/MagicCounter/ContentView.swift @@ -63,7 +63,7 @@ struct ContentView: View { } } .listStyle(.insetGrouped) - .navigationTitle("History") + .navigationTitle("Games") .toolbar { ToolbarItem(placement: .primaryAction) { Button(action: { showSetup = true }) { @@ -73,7 +73,7 @@ struct ContentView: View { } } .tabItem { - Label("History", systemImage: "clock.fill") + Label("Games", systemImage: "clock.fill") } SettingsView() diff --git a/ios/MagicCounter/GameView.swift b/ios/MagicCounter/GameView.swift index 320302c..f64441f 100644 --- a/ios/MagicCounter/GameView.swift +++ b/ios/MagicCounter/GameView.swift @@ -76,7 +76,6 @@ struct GameView: View { .clipShape(Circle()) } } else { - // Placeholder for balance Color.clear.frame(width: 40, height: 40) } } @@ -85,9 +84,6 @@ struct GameView: View { // Players Grid GeometryReader { geometry in - // Use adaptive grid with minimum width to handle responsiveness - // 320pt minimum ensures 1 column on iPhone Portrait (width ~390) - // and 2 columns on iPhone Landscape (width ~844) or iPad let columns = [GridItem(.adaptive(minimum: 320), spacing: 24)] ScrollView { LazyVGrid(columns: columns, spacing: 24) { @@ -128,6 +124,9 @@ struct GameView: View { .presentationDetents([.medium]) } .onChange(of: gameState) { newState in + if newState.winner != nil { + Haptics.notification(.success) + } gameManager.updateActiveGame(state: newState) } } @@ -172,6 +171,8 @@ struct PlayerCell: View { let onCommanderTap: () -> Void let onScoop: () -> Void + @State private var showScoopConfirmation = false + var body: some View { ZStack { RoundedRectangle(cornerRadius: 24) @@ -192,12 +193,8 @@ struct PlayerCell: View { .foregroundStyle(.primary) Spacer() - Menu { - Button(role: .destructive, action: onScoop) { - Label("Scoop", systemImage: "flag.fill") - } - } label: { - Image(systemName: "ellipsis.circle") + Button(action: { showScoopConfirmation = true }) { + Image(systemName: "flag.fill") .foregroundStyle(.secondary) .padding(4) } @@ -257,20 +254,39 @@ struct PlayerCell: View { .opacity(player.isEliminated && !isWinner ? 0.8 : 1) .scaleEffect(isWinner ? 1.05 : 1) .animation(.spring, value: isWinner) + .alert("Scoop?", isPresented: $showScoopConfirmation) { + Button("Cancel", role: .cancel) { } + Button("Scoop", role: .destructive) { + Haptics.notification(.warning) + onScoop() + } + } message: { + Text("Are you sure you want to scoop?") + } } private func adjustLife(by amount: Int) { if player.isEliminated { return } + Haptics.play(.light) var newPlayer = player newPlayer.life += amount onUpdate(newPlayer) + + if newPlayer.isEliminated && !player.isEliminated { + Haptics.notification(.error) + } } private func adjustPoison(by amount: Int) { if player.isEliminated { return } + Haptics.play(.light) var newPlayer = player newPlayer.poison = max(0, newPlayer.poison + amount) onUpdate(newPlayer) + + if newPlayer.isEliminated && !player.isEliminated { + Haptics.notification(.error) + } } } @@ -328,11 +344,16 @@ struct CommanderDamageView: View { } private func adjustCommanderDamage(attackerId: Int, by amount: Int) { + Haptics.play(.light) var newPlayer = targetPlayer var damages = newPlayer.commanderDamages let current = damages[attackerId] ?? 0 damages[attackerId] = max(0, current + amount) newPlayer.commanderDamages = damages onUpdate(newPlayer) + + if newPlayer.isEliminated && !targetPlayer.isEliminated { + Haptics.notification(.error) + } } } diff --git a/ios/MagicCounter/Logo.icon/Assets/MagicCounter 2.png b/ios/MagicCounter/Logo.icon/Assets/MagicCounter 2.png deleted file mode 100644 index 412d3de..0000000 Binary files a/ios/MagicCounter/Logo.icon/Assets/MagicCounter 2.png and /dev/null differ diff --git a/ios/MagicCounter/Logo.icon/Assets/logo 2.png b/ios/MagicCounter/Logo.icon/Assets/logo 2.png new file mode 100644 index 0000000..3bb832d Binary files /dev/null and b/ios/MagicCounter/Logo.icon/Assets/logo 2.png differ diff --git a/ios/MagicCounter/Logo.icon/icon.json b/ios/MagicCounter/Logo.icon/icon.json index 7be127c..02e6c94 100644 --- a/ios/MagicCounter/Logo.icon/icon.json +++ b/ios/MagicCounter/Logo.icon/icon.json @@ -7,10 +7,10 @@ { "layers" : [ { - "image-name" : "MagicCounter 2.png", - "name" : "MagicCounter 2", + "image-name" : "logo 2.png", + "name" : "logo 2", "position" : { - "scale" : 0.8, + "scale" : 0.85, "translation-in-points" : [ 0, 0 diff --git a/ios/MagicCounter/MagicCounterApp.swift b/ios/MagicCounter/MagicCounterApp.swift index ffcfff7..1aa5bea 100644 --- a/ios/MagicCounter/MagicCounterApp.swift +++ b/ios/MagicCounter/MagicCounterApp.swift @@ -10,6 +10,12 @@ import SwiftUI @main struct MagicCounterApp: App { @StateObject private var gameManager = GameManager() + + init() { + UserDefaults.standard.register(defaults: [ + "hapticFeedbackEnabled": true + ]) + } var body: some Scene { WindowGroup { diff --git a/ios/MagicCounter/SettingsView.swift b/ios/MagicCounter/SettingsView.swift index 9737af9..ca697f8 100644 --- a/ios/MagicCounter/SettingsView.swift +++ b/ios/MagicCounter/SettingsView.swift @@ -9,6 +9,7 @@ import SwiftUI struct SettingsView: View { @AppStorage("accentColorName") private var accentColorName = "Blue" + @AppStorage("hapticFeedbackEnabled") private var hapticFeedbackEnabled = true private let colors: [(name: String, color: Color)] = [ ("Blue", .blue), @@ -44,6 +45,10 @@ struct SettingsView: View { var body: some View { NavigationStack { Form { + Section("General") { + Toggle("Haptic Feedback", isOn: $hapticFeedbackEnabled) + } + Section("Appearance") { VStack(alignment: .leading, spacing: 16) { Text("ACCENT COLOR")