Files
MagicCounter/ios/MagicCounter/GameManager.swift

138 lines
3.8 KiB
Swift

//
// GameManager.swift
// MagicCounter
//
// Created by Atridad Lahiji on 2025-12-06.
//
import Foundation
import SwiftUI
import Combine
/**
* Manages the global game state and match history.
*
* - matchHistory: List of all past and current matches.
* - activeMatch: The currently active match, if any.
*/
@MainActor
final class GameManager: ObservableObject {
@Published var matchHistory: [MatchRecord] = []
@Published var activeMatch: MatchRecord?
private let historyKey = "match_history_json"
private let saveSubject = PassthroughSubject<Void, Never>()
private var cancellables = Set<AnyCancellable>()
nonisolated init() {
Task { @MainActor in
self.loadHistory()
self.setupAutoSave()
}
}
private func setupAutoSave() {
saveSubject
.debounce(for: .seconds(1), scheduler: RunLoop.main)
.sink { [weak self] _ in
self?.saveHistory()
}
.store(in: &cancellables)
}
func startNewGame(players: [String], startingLife: Int, trackPoison: Bool, trackCommander: Bool, matchName: String) {
let playerStates = players.enumerated().map { (index, name) in
PlayerState(
id: index,
name: name,
life: startingLife,
poison: 0,
commanderDamages: [:],
scooped: false
)
}
let gameState = GameState(
players: playerStates,
startingLife: startingLife,
trackPoison: trackPoison,
trackCommanderDamage: trackCommander,
stopped: false
)
let newMatch = MatchRecord(
id: UUID().uuidString,
name: matchName.isEmpty ? "Match \(Date().formatted())" : matchName,
startedAt: Date(),
lastUpdated: Date(),
ongoing: true,
winnerPlayerId: nil,
state: gameState
)
activeMatch = newMatch
matchHistory.insert(newMatch, at: 0)
saveHistory()
}
func updateActiveGame(state: GameState) {
guard var match = activeMatch else { return }
match.state = state
match.lastUpdated = Date()
if state.stopped {
match.ongoing = false
} else if let winner = state.winner {
match.ongoing = false
match.winnerPlayerId = winner.id
} else {
match.ongoing = true
match.winnerPlayerId = nil
}
activeMatch = match
if let index = matchHistory.firstIndex(where: { $0.id == match.id }) {
matchHistory[index] = match
}
saveSubject.send()
}
func stopGame() {
guard var match = activeMatch else { return }
match.ongoing = false
match.state.stopped = true
activeMatch = nil
if let index = matchHistory.firstIndex(where: { $0.id == match.id }) {
matchHistory[index] = match
}
saveHistory()
}
func deleteMatch(id: String) {
if activeMatch?.id == id {
activeMatch = nil
}
matchHistory.removeAll { $0.id == id }
saveHistory()
}
func resumeMatch(_ match: MatchRecord) {
activeMatch = match
}
private func loadHistory() {
if let data = UserDefaults.standard.data(forKey: historyKey) {
if let decoded = try? JSONDecoder().decode([MatchRecord].self, from: data) {
matchHistory = decoded
}
}
}
private func saveHistory() {
if let encoded = try? JSONEncoder().encode(matchHistory) {
UserDefaults.standard.set(encoded, forKey: historyKey)
}
}
}