164 lines
4.7 KiB
Swift
164 lines
4.7 KiB
Swift
//
|
|
// CasinoGameState.swift
|
|
// CasinoKit
|
|
//
|
|
// Protocol defining common state management patterns for casino games.
|
|
// Extends SessionManagedGame with additional shared functionality.
|
|
//
|
|
|
|
import Foundation
|
|
|
|
/// Protocol for casino game state classes that manage game flow,
|
|
/// session tracking, and common behaviors.
|
|
///
|
|
/// This extends `SessionManagedGame` to add additional requirements
|
|
/// that are common across all casino games in this app.
|
|
@MainActor
|
|
public protocol CasinoGameState: SessionManagedGame {
|
|
/// The settings object conforming to GameSettingsProtocol.
|
|
associatedtype GameSettingsType: GameSettingsProtocol
|
|
|
|
/// Game-specific settings.
|
|
var settings: GameSettingsType { get }
|
|
|
|
/// The shared sound manager.
|
|
var soundManager: SoundManager { get }
|
|
|
|
/// Onboarding state for first-time users.
|
|
var onboarding: OnboardingState { get }
|
|
|
|
/// Resets the game to starting conditions (balance, cards, etc.)
|
|
/// without clearing session history.
|
|
func resetGame()
|
|
|
|
/// Clears all data including session history.
|
|
func clearAllData()
|
|
|
|
/// Aggregated statistics from all sessions.
|
|
var aggregatedStats: AggregatedSessionStats { get }
|
|
}
|
|
|
|
// MARK: - Default Implementations
|
|
|
|
public extension CasinoGameState {
|
|
/// Default implementation uses shared SoundManager.
|
|
var soundManager: SoundManager { SoundManager.shared }
|
|
|
|
/// Default aggregated stats computed from session history.
|
|
var aggregatedStats: AggregatedSessionStats {
|
|
sessionHistory.aggregatedStats()
|
|
}
|
|
|
|
/// Play a sound effect if enabled.
|
|
func playSound(_ sound: GameSound) {
|
|
soundManager.play(sound)
|
|
}
|
|
|
|
/// Play light haptic feedback if enabled.
|
|
func hapticLight() {
|
|
soundManager.hapticLight()
|
|
}
|
|
|
|
/// Play medium haptic feedback if enabled.
|
|
func hapticMedium() {
|
|
soundManager.hapticMedium()
|
|
}
|
|
|
|
/// Play success haptic feedback if enabled.
|
|
func hapticSuccess() {
|
|
soundManager.hapticSuccess()
|
|
}
|
|
|
|
/// Play error haptic feedback if enabled.
|
|
func hapticError() {
|
|
soundManager.hapticError()
|
|
}
|
|
|
|
/// Convenience: Check if we should play sounds.
|
|
var isSoundEnabled: Bool {
|
|
settings.soundEnabled && soundManager.soundEnabled
|
|
}
|
|
|
|
/// Convenience: Check if we should play haptics.
|
|
var isHapticsEnabled: Bool {
|
|
settings.hapticsEnabled && soundManager.hapticsEnabled
|
|
}
|
|
}
|
|
|
|
// MARK: - Balance Management Extensions
|
|
|
|
public extension CasinoGameState {
|
|
/// Whether the player is out of chips (balance is 0 or below minimum bet).
|
|
var isBroke: Bool {
|
|
balance < settings.minBet
|
|
}
|
|
|
|
/// Whether the player can afford a bet of the given amount.
|
|
func canAffordBet(_ amount: Int) -> Bool {
|
|
balance >= amount
|
|
}
|
|
|
|
/// The maximum bet the player can currently place.
|
|
var maxAffordableBet: Int {
|
|
min(balance, settings.maxBet)
|
|
}
|
|
}
|
|
|
|
// MARK: - Game Flow Extensions
|
|
|
|
public extension CasinoGameState {
|
|
/// Ends the current session due to running out of chips.
|
|
func endSessionDueToBroke() {
|
|
if currentSession != nil {
|
|
endCurrentSession(reason: .brokeOut)
|
|
}
|
|
}
|
|
|
|
/// Checks if the player is broke and handles session ending if needed.
|
|
/// Returns true if the player is broke.
|
|
@discardableResult
|
|
func checkBrokeStatus() -> Bool {
|
|
if isBroke {
|
|
endSessionDueToBroke()
|
|
return true
|
|
}
|
|
return false
|
|
}
|
|
}
|
|
|
|
// MARK: - Game Reset Extensions
|
|
|
|
public extension CasinoGameState {
|
|
/// Default implementation of resetGame that properly handles session ending.
|
|
/// Games can override this but should call the helper methods in the correct order.
|
|
///
|
|
/// The correct order is:
|
|
/// 1. End current session (captures actual ending balance)
|
|
/// 2. Reset balance to starting balance
|
|
/// 3. Reset game-specific state (via resetForNewSession)
|
|
/// 4. Start new session with fresh balance
|
|
/// 5. Save data
|
|
func performResetGame() {
|
|
// 1. End current session FIRST while balance still shows actual state
|
|
if currentSession != nil {
|
|
endCurrentSession(reason: .manualEnd)
|
|
}
|
|
|
|
// 2. Reset balance to starting balance
|
|
balance = startingBalance
|
|
|
|
// 3. Let game reset its specific state (reshuffle deck, clear history, etc.)
|
|
resetForNewSession()
|
|
|
|
// 4. Create new session with fresh balance
|
|
currentSession = GameSession<Stats>(
|
|
gameStyle: currentGameStyle,
|
|
startingBalance: balance
|
|
)
|
|
|
|
// 5. Save
|
|
saveGameData()
|
|
}
|
|
}
|
|
|