diff GeoQuiz/Models/Controllers/GameProtocol+Extension.swift @ 26:425078c01194

refactor code
author Dennis C. M. <dennis@denniscm.com>
date Wed, 09 Nov 2022 10:30:01 +0100
parents GeoQuiz/Logic/GameProtocol+Extension.swift@e281791e0494
children 3f4b366d476d
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/GeoQuiz/Models/Controllers/GameProtocol+Extension.swift	Wed Nov 09 10:30:01 2022 +0100
@@ -0,0 +1,157 @@
+//
+//  GameProtocol+Extension.swift
+//  GeoQuiz
+//
+//  Created by Dennis Concepción Martín on 18/9/22.
+//
+
+import Foundation
+import SwiftUI
+import AVFAudio
+import CoreData
+
+@objc
+public enum GameType: Int16, CaseIterable {
+    case guessTheFlag
+    case guessTheCapital
+    case guessTheCountry
+    case guessThePopulation
+}
+
+protocol Game: ObservableObject {
+    
+    // Define generic type
+    associatedtype T: Equatable
+    
+    // Game
+    var data: [String: T] { get set}
+    var dataAsked: [String: T] { get set }
+    var correctAnswer: (key: String, value: T) { get set }
+    
+    // User
+    var userChoices: [String: T] { get set }
+    var userScore: Int { get set }
+    var userLives: Int { get set }
+    var correctAnswers: [String: T] { get set }
+    var wrongAnswers: [String: T] { get set }
+    
+    // Alerts
+    var alertTitle: String { get set }
+    var alertMessage: String { get set }
+    var showingEndGameAlert: Bool { get set }
+    var showingWrongAnswerAlert: Bool { get set }
+    var showingExitGameAlert: Bool { get set }
+    
+    // Animations
+    var scoreScaleAmount: Double { get set }
+    var livesScaleAmount: Double { get set }
+    
+    // Sound effects
+    var player: AVAudioPlayer? { get set }
+    
+    func selector()
+}
+
+extension Game {
+    var questionCounter: Int {
+       dataAsked.count
+    }
+    
+    func askQuestion(selector: () -> Void) {
+        guard questionCounter < data.count else {
+            alertTitle = "⭐️ Congratulations ⭐️"
+            alertMessage = "You completed the game."
+            showingEndGameAlert = true
+            
+            return
+        }
+        
+        selector()
+    }
+    
+    func answer(choice: (key: String, value: T), wrongMessage: String, selector: () -> Void) {
+        let haptics = HapticsController()
+        
+        if correctAnswer == choice {
+            haptics.success()
+            playSound("correctAnswer")
+            
+            withAnimation(.easeIn(duration: 0.5)) {
+                scoreScaleAmount += 1
+                userScore += 1
+            }
+            
+            correctAnswers[correctAnswer.key] = correctAnswer.value
+            askQuestion {
+                selector()
+            }
+        } else {
+            haptics.error()
+            playSound("wrongAnswer")
+
+            withAnimation(.easeIn(duration: 0.5)) {
+                livesScaleAmount += 1
+                userLives -= 1
+            }
+            
+            wrongAnswers[choice.key] = choice.value
+            
+            if userLives == 0 {
+                alertTitle = "🤕 Game over 🤕"
+                alertMessage = "Get up and try again."
+                showingEndGameAlert = true
+            } else {
+                alertTitle = "🔴 Wrong 🔴"
+                alertMessage = "\(wrongMessage). You have \(userLives) lives left."
+                showingWrongAnswerAlert = true
+            }
+        }
+        
+        DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) { [self] in
+            withAnimation(.easeIn(duration: 0.5)) {
+                scoreScaleAmount = 1
+                livesScaleAmount = 1
+            }
+        }
+    }
+    
+    func save(_ gameType: GameType, with moc: NSManagedObjectContext) {
+        let playedGame = PlayedGame(context: moc)
+
+        playedGame.type = gameType
+        playedGame.date = Date()
+        playedGame.score = Int32(userScore)
+        playedGame.correctAnswers = Array(correctAnswers.keys)
+        playedGame.wrongAnswers = Array(wrongAnswers.keys)
+        
+        do {
+            try moc.save()
+        } catch {
+            print("Couldn't save object to CoreData: \(error)")
+        }
+    }
+    
+    private func playSound(_ filename: String) {
+        let user = UserController()
+        
+        if user.data.sound {
+            guard let soundFileURL = Bundle.main.url(forResource: filename, withExtension: "wav") else {
+                fatalError("Sound file \(filename) couldn't be found")
+            }
+            
+            do {
+                try AVAudioSession.sharedInstance().setCategory(AVAudioSession.Category.ambient)
+                try AVAudioSession.sharedInstance().setActive(true)
+            } catch {
+                fatalError("Couldn't activate session")
+            }
+            
+            do {
+                player = try AVAudioPlayer(contentsOf: soundFileURL)
+                player?.play()
+            } catch {
+                fatalError("Couldn't play sound effect")
+            }
+        }
+    }
+}