Signed-off-by: Matt Bruce <mbrucedogs@gmail.com>
This commit is contained in:
parent
09770ec625
commit
7234cd718a
@ -2832,6 +2832,52 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"Reset Game" : {
|
||||||
|
"comment" : "A button label that resets the game balance and reshuffles the deck.",
|
||||||
|
"localizations" : {
|
||||||
|
"en" : {
|
||||||
|
"stringUnit" : {
|
||||||
|
"state" : "translated",
|
||||||
|
"value" : "Reset Game"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"es-MX" : {
|
||||||
|
"stringUnit" : {
|
||||||
|
"state" : "translated",
|
||||||
|
"value" : "Reiniciar juego"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"fr-CA" : {
|
||||||
|
"stringUnit" : {
|
||||||
|
"state" : "translated",
|
||||||
|
"value" : "Réinitialiser la partie"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"Restore starting balance and reshuffle" : {
|
||||||
|
"comment" : "Description for the reset game button explaining what it does.",
|
||||||
|
"localizations" : {
|
||||||
|
"en" : {
|
||||||
|
"stringUnit" : {
|
||||||
|
"state" : "translated",
|
||||||
|
"value" : "Restore starting balance and reshuffle"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"es-MX" : {
|
||||||
|
"stringUnit" : {
|
||||||
|
"state" : "translated",
|
||||||
|
"value" : "Restaurar saldo inicial y barajar"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"fr-CA" : {
|
||||||
|
"stringUnit" : {
|
||||||
|
"state" : "translated",
|
||||||
|
"value" : "Restaurer le solde initial et mélanger"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"Reset to Defaults" : {
|
"Reset to Defaults" : {
|
||||||
"comment" : "A button label that resets game settings to their default values.",
|
"comment" : "A button label that resets game settings to their default values.",
|
||||||
"localizations" : {
|
"localizations" : {
|
||||||
|
|||||||
@ -135,7 +135,7 @@ struct GameTableView: View {
|
|||||||
balance: state.balance,
|
balance: state.balance,
|
||||||
secondaryInfo: settings.showCardsRemaining ? "\(state.engine.shoe.cardsRemaining)" : nil,
|
secondaryInfo: settings.showCardsRemaining ? "\(state.engine.shoe.cardsRemaining)" : nil,
|
||||||
secondaryIcon: settings.showCardsRemaining ? "rectangle.portrait.on.rectangle.portrait.fill" : nil,
|
secondaryIcon: settings.showCardsRemaining ? "rectangle.portrait.on.rectangle.portrait.fill" : nil,
|
||||||
onReset: { state.resetGame() },
|
showReset: false,
|
||||||
onSettings: { showSettings = true },
|
onSettings: { showSettings = true },
|
||||||
onHelp: { showRules = true },
|
onHelp: { showRules = true },
|
||||||
onStats: { showStats = true }
|
onStats: { showStats = true }
|
||||||
@ -256,7 +256,7 @@ struct GameTableView: View {
|
|||||||
balance: state.balance,
|
balance: state.balance,
|
||||||
secondaryInfo: settings.showCardsRemaining ? "\(state.engine.shoe.cardsRemaining)" : nil,
|
secondaryInfo: settings.showCardsRemaining ? "\(state.engine.shoe.cardsRemaining)" : nil,
|
||||||
secondaryIcon: settings.showCardsRemaining ? "rectangle.portrait.on.rectangle.portrait.fill" : nil,
|
secondaryIcon: settings.showCardsRemaining ? "rectangle.portrait.on.rectangle.portrait.fill" : nil,
|
||||||
onReset: { state.resetGame() },
|
showReset: false,
|
||||||
onSettings: { showSettings = true },
|
onSettings: { showSettings = true },
|
||||||
onHelp: { showRules = true },
|
onHelp: { showRules = true },
|
||||||
onStats: { showStats = true }
|
onStats: { showStats = true }
|
||||||
|
|||||||
@ -150,7 +150,7 @@ struct SettingsView: View {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
.tint(accent)
|
.tint(accent)
|
||||||
.padding(.vertical, Design.Spacing.xSmall)
|
.frame(minHeight: CasinoDesign.Size.actionRowMinHeight)
|
||||||
|
|
||||||
if gameState.iCloudEnabled {
|
if gameState.iCloudEnabled {
|
||||||
Divider()
|
Divider()
|
||||||
@ -173,6 +173,7 @@ struct SettingsView: View {
|
|||||||
.foregroundStyle(.white.opacity(Design.Opacity.medium))
|
.foregroundStyle(.white.opacity(Design.Opacity.medium))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
.frame(minHeight: CasinoDesign.Size.actionRowMinHeight)
|
||||||
|
|
||||||
Divider()
|
Divider()
|
||||||
.background(Color.white.opacity(Design.Opacity.subtle))
|
.background(Color.white.opacity(Design.Opacity.subtle))
|
||||||
@ -181,11 +182,13 @@ struct SettingsView: View {
|
|||||||
gameState.syncWithCloud()
|
gameState.syncWithCloud()
|
||||||
} label: {
|
} label: {
|
||||||
HStack {
|
HStack {
|
||||||
Image(systemName: "arrow.triangle.2.circlepath")
|
|
||||||
Text(String(localized: "Sync Now"))
|
Text(String(localized: "Sync Now"))
|
||||||
|
Spacer()
|
||||||
|
Image(systemName: "arrow.triangle.2.circlepath")
|
||||||
}
|
}
|
||||||
.font(.system(size: Design.BaseFontSize.body, weight: .medium))
|
.font(.system(size: Design.BaseFontSize.body, weight: .medium))
|
||||||
.foregroundStyle(accent)
|
.foregroundStyle(accent)
|
||||||
|
.frame(minHeight: CasinoDesign.Size.actionRowMinHeight)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@ -203,7 +206,7 @@ struct SettingsView: View {
|
|||||||
.foregroundStyle(.white.opacity(Design.Opacity.medium))
|
.foregroundStyle(.white.opacity(Design.Opacity.medium))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.padding(.vertical, Design.Spacing.xSmall)
|
.frame(minHeight: CasinoDesign.Size.actionRowMinHeight)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -237,15 +240,43 @@ struct SettingsView: View {
|
|||||||
Divider()
|
Divider()
|
||||||
.background(Color.white.opacity(Design.Opacity.subtle))
|
.background(Color.white.opacity(Design.Opacity.subtle))
|
||||||
|
|
||||||
|
// Reset Game - resets balance, keeps stats
|
||||||
|
Button {
|
||||||
|
gameState.resetGame()
|
||||||
|
dismiss()
|
||||||
|
} label: {
|
||||||
|
HStack {
|
||||||
|
VStack(alignment: .leading, spacing: Design.Spacing.xxSmall) {
|
||||||
|
Text(String(localized: "Reset Game"))
|
||||||
|
.font(.system(size: Design.BaseFontSize.body, weight: .medium))
|
||||||
|
.foregroundStyle(.white)
|
||||||
|
Text(String(localized: "Restore starting balance and reshuffle"))
|
||||||
|
.font(.system(size: Design.BaseFontSize.small))
|
||||||
|
.foregroundStyle(.white.opacity(Design.Opacity.medium))
|
||||||
|
}
|
||||||
|
Spacer()
|
||||||
|
Image(systemName: "arrow.counterclockwise")
|
||||||
|
.font(.system(size: Design.BaseFontSize.large))
|
||||||
|
.foregroundStyle(Color.Sheet.accent)
|
||||||
|
}
|
||||||
|
.frame(minHeight: CasinoDesign.Size.actionRowMinHeight)
|
||||||
|
}
|
||||||
|
|
||||||
|
Divider()
|
||||||
|
.background(Color.white.opacity(Design.Opacity.subtle))
|
||||||
|
|
||||||
|
// Clear All Data - nuclear option
|
||||||
Button(role: .destructive) {
|
Button(role: .destructive) {
|
||||||
showClearDataAlert = true
|
showClearDataAlert = true
|
||||||
} label: {
|
} label: {
|
||||||
HStack {
|
HStack {
|
||||||
Image(systemName: "trash")
|
|
||||||
Text(String(localized: "Clear All Data"))
|
Text(String(localized: "Clear All Data"))
|
||||||
|
Spacer()
|
||||||
|
Image(systemName: "trash")
|
||||||
}
|
}
|
||||||
.font(.system(size: Design.BaseFontSize.body, weight: .medium))
|
.font(.system(size: Design.BaseFontSize.body, weight: .medium))
|
||||||
.foregroundStyle(.red)
|
.foregroundStyle(.red)
|
||||||
|
.frame(minHeight: CasinoDesign.Size.actionRowMinHeight)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -263,6 +294,7 @@ struct SettingsView: View {
|
|||||||
.font(.system(size: Design.BaseFontSize.body))
|
.font(.system(size: Design.BaseFontSize.body))
|
||||||
.foregroundStyle(.white.opacity(Design.Opacity.medium))
|
.foregroundStyle(.white.opacity(Design.Opacity.medium))
|
||||||
}
|
}
|
||||||
|
.frame(minHeight: CasinoDesign.Size.actionRowMinHeight)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -5434,6 +5434,52 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"Reset Game": {
|
||||||
|
"comment": "Button to reset game balance and reshuffle cards.",
|
||||||
|
"localizations": {
|
||||||
|
"en": {
|
||||||
|
"stringUnit": {
|
||||||
|
"state": "translated",
|
||||||
|
"value": "Reset Game"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"es-MX": {
|
||||||
|
"stringUnit": {
|
||||||
|
"state": "translated",
|
||||||
|
"value": "Reiniciar Juego"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"fr-CA": {
|
||||||
|
"stringUnit": {
|
||||||
|
"state": "translated",
|
||||||
|
"value": "Réinitialiser le Jeu"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"Restore starting balance and reshuffle": {
|
||||||
|
"comment": "Subtitle for Reset Game button explaining what it does.",
|
||||||
|
"localizations": {
|
||||||
|
"en": {
|
||||||
|
"stringUnit": {
|
||||||
|
"state": "translated",
|
||||||
|
"value": "Restore starting balance and reshuffle"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"es-MX": {
|
||||||
|
"stringUnit": {
|
||||||
|
"state": "translated",
|
||||||
|
"value": "Restaurar saldo inicial y barajar"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"fr-CA": {
|
||||||
|
"stringUnit": {
|
||||||
|
"state": "translated",
|
||||||
|
"value": "Restaurer le solde initial et remélanger"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"Running Count: Sum of all card values seen.": {
|
"Running Count: Sum of all card values seen.": {
|
||||||
"localizations": {
|
"localizations": {
|
||||||
"en": {
|
"en": {
|
||||||
|
|||||||
@ -97,7 +97,7 @@ struct GameTableView: View {
|
|||||||
balance: state.balance,
|
balance: state.balance,
|
||||||
secondaryInfo: settings.showCardsRemaining ? "\(state.engine.cardsRemaining)" : nil,
|
secondaryInfo: settings.showCardsRemaining ? "\(state.engine.cardsRemaining)" : nil,
|
||||||
secondaryIcon: settings.showCardsRemaining ? "rectangle.portrait.on.rectangle.portrait.fill" : nil,
|
secondaryIcon: settings.showCardsRemaining ? "rectangle.portrait.on.rectangle.portrait.fill" : nil,
|
||||||
onReset: { state.resetGame() },
|
showReset: false,
|
||||||
onSettings: { showSettings = true },
|
onSettings: { showSettings = true },
|
||||||
onHelp: { showRules = true },
|
onHelp: { showRules = true },
|
||||||
onStats: { showStats = true }
|
onStats: { showStats = true }
|
||||||
@ -105,16 +105,6 @@ struct GameTableView: View {
|
|||||||
.frame(maxWidth: maxContentWidth)
|
.frame(maxWidth: maxContentWidth)
|
||||||
.debugBorder(showDebugBorders, color: .cyan, label: "TopBar")
|
.debugBorder(showDebugBorders, color: .cyan, label: "TopBar")
|
||||||
|
|
||||||
// Card count display (when enabled)
|
|
||||||
if settings.showCardCount {
|
|
||||||
CardCountView(
|
|
||||||
runningCount: state.engine.runningCount,
|
|
||||||
trueCount: state.engine.trueCount
|
|
||||||
)
|
|
||||||
.frame(maxWidth: maxContentWidth)
|
|
||||||
.debugBorder(showDebugBorders, color: .mint, label: "CardCount")
|
|
||||||
}
|
|
||||||
|
|
||||||
// Reshuffle notification
|
// Reshuffle notification
|
||||||
if state.showReshuffleNotification {
|
if state.showReshuffleNotification {
|
||||||
ReshuffleNotificationView(showCardCount: settings.showCardCount)
|
ReshuffleNotificationView(showCardCount: settings.showCardCount)
|
||||||
|
|||||||
@ -204,7 +204,7 @@ struct SettingsView: View {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
.tint(accent)
|
.tint(accent)
|
||||||
.padding(.vertical, Design.Spacing.xSmall)
|
.frame(minHeight: CasinoDesign.Size.actionRowMinHeight)
|
||||||
|
|
||||||
if state.persistence.iCloudEnabled {
|
if state.persistence.iCloudEnabled {
|
||||||
Divider()
|
Divider()
|
||||||
@ -227,6 +227,7 @@ struct SettingsView: View {
|
|||||||
.foregroundStyle(.white.opacity(Design.Opacity.medium))
|
.foregroundStyle(.white.opacity(Design.Opacity.medium))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
.frame(minHeight: CasinoDesign.Size.actionRowMinHeight)
|
||||||
|
|
||||||
Divider()
|
Divider()
|
||||||
.background(Color.white.opacity(Design.Opacity.subtle))
|
.background(Color.white.opacity(Design.Opacity.subtle))
|
||||||
@ -235,11 +236,13 @@ struct SettingsView: View {
|
|||||||
state.persistence.sync()
|
state.persistence.sync()
|
||||||
} label: {
|
} label: {
|
||||||
HStack {
|
HStack {
|
||||||
Image(systemName: "arrow.triangle.2.circlepath")
|
|
||||||
Text(String(localized: "Sync Now"))
|
Text(String(localized: "Sync Now"))
|
||||||
|
Spacer()
|
||||||
|
Image(systemName: "arrow.triangle.2.circlepath")
|
||||||
}
|
}
|
||||||
.font(.system(size: Design.BaseFontSize.body, weight: .medium))
|
.font(.system(size: Design.BaseFontSize.body, weight: .medium))
|
||||||
.foregroundStyle(accent)
|
.foregroundStyle(accent)
|
||||||
|
.frame(minHeight: CasinoDesign.Size.actionRowMinHeight)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@ -257,7 +260,7 @@ struct SettingsView: View {
|
|||||||
.foregroundStyle(.white.opacity(Design.Opacity.medium))
|
.foregroundStyle(.white.opacity(Design.Opacity.medium))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.padding(.vertical, Design.Spacing.xSmall)
|
.frame(minHeight: CasinoDesign.Size.actionRowMinHeight)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -293,15 +296,44 @@ struct SettingsView: View {
|
|||||||
Divider()
|
Divider()
|
||||||
.background(Color.white.opacity(Design.Opacity.subtle))
|
.background(Color.white.opacity(Design.Opacity.subtle))
|
||||||
|
|
||||||
|
// Reset Game - resets balance, keeps stats
|
||||||
|
Button {
|
||||||
|
state.resetGame()
|
||||||
|
dismiss()
|
||||||
|
} label: {
|
||||||
|
HStack {
|
||||||
|
VStack(alignment: .leading, spacing: Design.Spacing.xxSmall) {
|
||||||
|
Text(String(localized: "Reset Game"))
|
||||||
|
.font(.system(size: Design.BaseFontSize.body, weight: .medium))
|
||||||
|
.foregroundStyle(.white)
|
||||||
|
Text(String(localized: "Restore starting balance and reshuffle"))
|
||||||
|
.font(.system(size: Design.BaseFontSize.small))
|
||||||
|
.foregroundStyle(.white.opacity(Design.Opacity.medium))
|
||||||
|
}
|
||||||
|
Spacer()
|
||||||
|
Image(systemName: "arrow.counterclockwise")
|
||||||
|
.font(.system(size: Design.BaseFontSize.large))
|
||||||
|
.foregroundStyle(Color.Sheet.accent)
|
||||||
|
}
|
||||||
|
.frame(minHeight: CasinoDesign.Size.actionRowMinHeight)
|
||||||
|
}
|
||||||
|
|
||||||
|
Divider()
|
||||||
|
.background(Color.white.opacity(Design.Opacity.subtle))
|
||||||
|
|
||||||
|
// Clear All Data - nuclear option
|
||||||
Button(role: .destructive) {
|
Button(role: .destructive) {
|
||||||
showClearDataAlert = true
|
showClearDataAlert = true
|
||||||
} label: {
|
} label: {
|
||||||
HStack {
|
HStack {
|
||||||
Image(systemName: "trash")
|
|
||||||
Text(String(localized: "Clear All Data"))
|
Text(String(localized: "Clear All Data"))
|
||||||
}
|
|
||||||
.font(.system(size: Design.BaseFontSize.body, weight: .medium))
|
.font(.system(size: Design.BaseFontSize.body, weight: .medium))
|
||||||
|
Spacer()
|
||||||
|
Image(systemName: "trash")
|
||||||
|
.font(.system(size: Design.BaseFontSize.large))
|
||||||
|
}
|
||||||
.foregroundStyle(.red)
|
.foregroundStyle(.red)
|
||||||
|
.frame(minHeight: CasinoDesign.Size.actionRowMinHeight)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -320,6 +352,7 @@ struct SettingsView: View {
|
|||||||
.font(.system(size: Design.BaseFontSize.body))
|
.font(.system(size: Design.BaseFontSize.body))
|
||||||
.foregroundStyle(.white.opacity(Design.Opacity.medium))
|
.foregroundStyle(.white.opacity(Design.Opacity.medium))
|
||||||
}
|
}
|
||||||
|
.frame(minHeight: CasinoDesign.Size.actionRowMinHeight)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -48,7 +48,7 @@ struct BlackjackTableView: View {
|
|||||||
/// Card width based on full screen height (stable - doesn't change with content)
|
/// Card width based on full screen height (stable - doesn't change with content)
|
||||||
private var cardWidth: CGFloat {
|
private var cardWidth: CGFloat {
|
||||||
let maxDimension = screenHeight
|
let maxDimension = screenHeight
|
||||||
let percentage: CGFloat = 0.13 // ~10% of screen
|
let percentage: CGFloat = 0.15 // ~10% of screen
|
||||||
return maxDimension * percentage
|
return maxDimension * percentage
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -93,9 +93,32 @@ struct BlackjackTableView: View {
|
|||||||
)
|
)
|
||||||
.debugBorder(showDebugBorders, color: .red, label: "Dealer")
|
.debugBorder(showDebugBorders, color: .red, label: "Dealer")
|
||||||
|
|
||||||
// Flexible space between dealer and player - scales with screen size
|
// Space between dealer and player - contains card count if enabled
|
||||||
|
if showCardCount {
|
||||||
|
// Card count view fills the space between hands
|
||||||
|
VStack {
|
||||||
|
// Top spacer for larger screens
|
||||||
|
if screenHeight > 700 {
|
||||||
|
Spacer(minLength: Design.Spacing.small)
|
||||||
|
}
|
||||||
|
|
||||||
|
CardCountView(
|
||||||
|
runningCount: state.engine.runningCount,
|
||||||
|
trueCount: state.engine.trueCount
|
||||||
|
)
|
||||||
|
|
||||||
|
// Bottom spacer for larger screens
|
||||||
|
if screenHeight > 700 {
|
||||||
|
Spacer(minLength: Design.Spacing.small)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.frame(minHeight: dealerPlayerSpacing)
|
||||||
|
.debugBorder(showDebugBorders, color: .mint, label: "CardCount")
|
||||||
|
} else {
|
||||||
|
// No card count - just use flexible spacer
|
||||||
Spacer(minLength: dealerPlayerSpacing)
|
Spacer(minLength: dealerPlayerSpacing)
|
||||||
.debugBorder(showDebugBorders, color: .yellow, label: "Spacer \(Int(dealerPlayerSpacing))")
|
.debugBorder(showDebugBorders, color: .yellow, label: "Spacer \(Int(dealerPlayerSpacing))")
|
||||||
|
}
|
||||||
|
|
||||||
// Player hands area - only show when there are cards dealt
|
// Player hands area - only show when there are cards dealt
|
||||||
if state.playerHands.first?.cards.isEmpty == false {
|
if state.playerHands.first?.cards.isEmpty == false {
|
||||||
|
|||||||
@ -147,6 +147,9 @@ public enum CasinoDesign {
|
|||||||
/// Checkmark size.
|
/// Checkmark size.
|
||||||
public static let checkmark: CGFloat = 22
|
public static let checkmark: CGFloat = 22
|
||||||
|
|
||||||
|
/// Minimum height for tappable action rows (Apple HIG: 44pt minimum touch target).
|
||||||
|
public static let actionRowMinHeight: CGFloat = 44
|
||||||
|
|
||||||
/// Common button dimensions.
|
/// Common button dimensions.
|
||||||
public static let actionButtonHeight: CGFloat = 50
|
public static let actionButtonHeight: CGFloat = 50
|
||||||
public static var actionButtonMinWidth: CGFloat {
|
public static var actionButtonMinWidth: CGFloat {
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user