1.0.0 for iOS
This commit is contained in:
@@ -425,7 +425,7 @@
|
|||||||
"$(inherited)",
|
"$(inherited)",
|
||||||
"@executable_path/Frameworks",
|
"@executable_path/Frameworks",
|
||||||
);
|
);
|
||||||
MARKETING_VERSION = 1.0;
|
MARKETING_VERSION = 1.0.0;
|
||||||
PRODUCT_BUNDLE_IDENTIFIER = com.atri.dad.MagicCounter.MagicCounter;
|
PRODUCT_BUNDLE_IDENTIFIER = com.atri.dad.MagicCounter.MagicCounter;
|
||||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||||
STRING_CATALOG_GENERATE_SYMBOLS = YES;
|
STRING_CATALOG_GENERATE_SYMBOLS = YES;
|
||||||
@@ -459,7 +459,7 @@
|
|||||||
"$(inherited)",
|
"$(inherited)",
|
||||||
"@executable_path/Frameworks",
|
"@executable_path/Frameworks",
|
||||||
);
|
);
|
||||||
MARKETING_VERSION = 1.0;
|
MARKETING_VERSION = 1.0.0;
|
||||||
PRODUCT_BUNDLE_IDENTIFIER = com.atri.dad.MagicCounter.MagicCounter;
|
PRODUCT_BUNDLE_IDENTIFIER = com.atri.dad.MagicCounter.MagicCounter;
|
||||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||||
STRING_CATALOG_GENERATE_SYMBOLS = YES;
|
STRING_CATALOG_GENERATE_SYMBOLS = YES;
|
||||||
|
|||||||
Binary file not shown.
@@ -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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -63,7 +63,7 @@ struct ContentView: View {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
.listStyle(.insetGrouped)
|
.listStyle(.insetGrouped)
|
||||||
.navigationTitle("History")
|
.navigationTitle("Games")
|
||||||
.toolbar {
|
.toolbar {
|
||||||
ToolbarItem(placement: .primaryAction) {
|
ToolbarItem(placement: .primaryAction) {
|
||||||
Button(action: { showSetup = true }) {
|
Button(action: { showSetup = true }) {
|
||||||
@@ -73,7 +73,7 @@ struct ContentView: View {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
.tabItem {
|
.tabItem {
|
||||||
Label("History", systemImage: "clock.fill")
|
Label("Games", systemImage: "clock.fill")
|
||||||
}
|
}
|
||||||
|
|
||||||
SettingsView()
|
SettingsView()
|
||||||
|
|||||||
@@ -76,7 +76,6 @@ struct GameView: View {
|
|||||||
.clipShape(Circle())
|
.clipShape(Circle())
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// Placeholder for balance
|
|
||||||
Color.clear.frame(width: 40, height: 40)
|
Color.clear.frame(width: 40, height: 40)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -85,9 +84,6 @@ struct GameView: View {
|
|||||||
|
|
||||||
// Players Grid
|
// Players Grid
|
||||||
GeometryReader { geometry in
|
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)]
|
let columns = [GridItem(.adaptive(minimum: 320), spacing: 24)]
|
||||||
ScrollView {
|
ScrollView {
|
||||||
LazyVGrid(columns: columns, spacing: 24) {
|
LazyVGrid(columns: columns, spacing: 24) {
|
||||||
@@ -128,6 +124,9 @@ struct GameView: View {
|
|||||||
.presentationDetents([.medium])
|
.presentationDetents([.medium])
|
||||||
}
|
}
|
||||||
.onChange(of: gameState) { newState in
|
.onChange(of: gameState) { newState in
|
||||||
|
if newState.winner != nil {
|
||||||
|
Haptics.notification(.success)
|
||||||
|
}
|
||||||
gameManager.updateActiveGame(state: newState)
|
gameManager.updateActiveGame(state: newState)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -172,6 +171,8 @@ struct PlayerCell: View {
|
|||||||
let onCommanderTap: () -> Void
|
let onCommanderTap: () -> Void
|
||||||
let onScoop: () -> Void
|
let onScoop: () -> Void
|
||||||
|
|
||||||
|
@State private var showScoopConfirmation = false
|
||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
ZStack {
|
ZStack {
|
||||||
RoundedRectangle(cornerRadius: 24)
|
RoundedRectangle(cornerRadius: 24)
|
||||||
@@ -192,12 +193,8 @@ struct PlayerCell: View {
|
|||||||
.foregroundStyle(.primary)
|
.foregroundStyle(.primary)
|
||||||
Spacer()
|
Spacer()
|
||||||
|
|
||||||
Menu {
|
Button(action: { showScoopConfirmation = true }) {
|
||||||
Button(role: .destructive, action: onScoop) {
|
Image(systemName: "flag.fill")
|
||||||
Label("Scoop", systemImage: "flag.fill")
|
|
||||||
}
|
|
||||||
} label: {
|
|
||||||
Image(systemName: "ellipsis.circle")
|
|
||||||
.foregroundStyle(.secondary)
|
.foregroundStyle(.secondary)
|
||||||
.padding(4)
|
.padding(4)
|
||||||
}
|
}
|
||||||
@@ -257,20 +254,39 @@ struct PlayerCell: View {
|
|||||||
.opacity(player.isEliminated && !isWinner ? 0.8 : 1)
|
.opacity(player.isEliminated && !isWinner ? 0.8 : 1)
|
||||||
.scaleEffect(isWinner ? 1.05 : 1)
|
.scaleEffect(isWinner ? 1.05 : 1)
|
||||||
.animation(.spring, value: isWinner)
|
.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) {
|
private func adjustLife(by amount: Int) {
|
||||||
if player.isEliminated { return }
|
if player.isEliminated { return }
|
||||||
|
Haptics.play(.light)
|
||||||
var newPlayer = player
|
var newPlayer = player
|
||||||
newPlayer.life += amount
|
newPlayer.life += amount
|
||||||
onUpdate(newPlayer)
|
onUpdate(newPlayer)
|
||||||
|
|
||||||
|
if newPlayer.isEliminated && !player.isEliminated {
|
||||||
|
Haptics.notification(.error)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private func adjustPoison(by amount: Int) {
|
private func adjustPoison(by amount: Int) {
|
||||||
if player.isEliminated { return }
|
if player.isEliminated { return }
|
||||||
|
Haptics.play(.light)
|
||||||
var newPlayer = player
|
var newPlayer = player
|
||||||
newPlayer.poison = max(0, newPlayer.poison + amount)
|
newPlayer.poison = max(0, newPlayer.poison + amount)
|
||||||
onUpdate(newPlayer)
|
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) {
|
private func adjustCommanderDamage(attackerId: Int, by amount: Int) {
|
||||||
|
Haptics.play(.light)
|
||||||
var newPlayer = targetPlayer
|
var newPlayer = targetPlayer
|
||||||
var damages = newPlayer.commanderDamages
|
var damages = newPlayer.commanderDamages
|
||||||
let current = damages[attackerId] ?? 0
|
let current = damages[attackerId] ?? 0
|
||||||
damages[attackerId] = max(0, current + amount)
|
damages[attackerId] = max(0, current + amount)
|
||||||
newPlayer.commanderDamages = damages
|
newPlayer.commanderDamages = damages
|
||||||
onUpdate(newPlayer)
|
onUpdate(newPlayer)
|
||||||
|
|
||||||
|
if newPlayer.isEliminated && !targetPlayer.isEliminated {
|
||||||
|
Haptics.notification(.error)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Binary file not shown.
|
Before Width: | Height: | Size: 100 KiB |
BIN
ios/MagicCounter/Logo.icon/Assets/logo 2.png
Normal file
BIN
ios/MagicCounter/Logo.icon/Assets/logo 2.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.1 MiB |
@@ -7,10 +7,10 @@
|
|||||||
{
|
{
|
||||||
"layers" : [
|
"layers" : [
|
||||||
{
|
{
|
||||||
"image-name" : "MagicCounter 2.png",
|
"image-name" : "logo 2.png",
|
||||||
"name" : "MagicCounter 2",
|
"name" : "logo 2",
|
||||||
"position" : {
|
"position" : {
|
||||||
"scale" : 0.8,
|
"scale" : 0.85,
|
||||||
"translation-in-points" : [
|
"translation-in-points" : [
|
||||||
0,
|
0,
|
||||||
0
|
0
|
||||||
|
|||||||
@@ -10,6 +10,12 @@ import SwiftUI
|
|||||||
@main
|
@main
|
||||||
struct MagicCounterApp: App {
|
struct MagicCounterApp: App {
|
||||||
@StateObject private var gameManager = GameManager()
|
@StateObject private var gameManager = GameManager()
|
||||||
|
|
||||||
|
init() {
|
||||||
|
UserDefaults.standard.register(defaults: [
|
||||||
|
"hapticFeedbackEnabled": true
|
||||||
|
])
|
||||||
|
}
|
||||||
|
|
||||||
var body: some Scene {
|
var body: some Scene {
|
||||||
WindowGroup {
|
WindowGroup {
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ import SwiftUI
|
|||||||
|
|
||||||
struct SettingsView: View {
|
struct SettingsView: View {
|
||||||
@AppStorage("accentColorName") private var accentColorName = "Blue"
|
@AppStorage("accentColorName") private var accentColorName = "Blue"
|
||||||
|
@AppStorage("hapticFeedbackEnabled") private var hapticFeedbackEnabled = true
|
||||||
|
|
||||||
private let colors: [(name: String, color: Color)] = [
|
private let colors: [(name: String, color: Color)] = [
|
||||||
("Blue", .blue),
|
("Blue", .blue),
|
||||||
@@ -44,6 +45,10 @@ struct SettingsView: View {
|
|||||||
var body: some View {
|
var body: some View {
|
||||||
NavigationStack {
|
NavigationStack {
|
||||||
Form {
|
Form {
|
||||||
|
Section("General") {
|
||||||
|
Toggle("Haptic Feedback", isOn: $hapticFeedbackEnabled)
|
||||||
|
}
|
||||||
|
|
||||||
Section("Appearance") {
|
Section("Appearance") {
|
||||||
VStack(alignment: .leading, spacing: 16) {
|
VStack(alignment: .leading, spacing: 16) {
|
||||||
Text("ACCENT COLOR")
|
Text("ACCENT COLOR")
|
||||||
|
|||||||
Reference in New Issue
Block a user