OSDN Git Service

staticプロパティをインスタンスプロパティに変更
[kcd/KCD.git] / KCD / DamageCalculator.swift
index c000699..d4a6f15 100644 (file)
@@ -11,9 +11,42 @@ import SwiftyJSON
 
 enum BattleFleet {
     
-    case first
-    case second
-    case each
+    case normal
+    
+    case secondOnly
+}
+
+private struct PositionedDamage {
+    
+    let position: Int
+    let damage: Int
+    
+    static let zero = PositionedDamage(position: 0, damage: 0)
+}
+
+extension PositionedDamage: Equatable {
+    
+    static func == (lhs: PositionedDamage, rhs: PositionedDamage) -> Bool {
+        
+        return lhs.position == rhs.position && lhs.damage == rhs.damage
+    }
+}
+
+private struct HogekiBattleData {
+    
+    let targetPositionList: [Int]
+    let damageList: [Int]
+    let enemyFlags: Bool
+}
+
+private func friendDamage(_ data: HogekiBattleData) -> PositionedDamage {
+    
+    guard !data.enemyFlags else { return .zero }
+    
+    guard let pos = data.targetPositionList.first else { return .zero }
+    
+    return PositionedDamage(position: pos,
+                            damage: data.damageList.filter({ $0 > 0 }).reduce(0, +))
 }
 
 final class DamageCalculator {
@@ -35,35 +68,60 @@ extension DamageCalculator {
     
     func calculateBattle() {
         
-        calcKouku()
-        calcOpeningTaisen()
-        calcOpeningAttack()
-        calcHougeki1()
-        calcHougeki2()
-        calcHougeki3()
-        calcRaigeki()
+        store.sync {
+            
+            self.calcKouku()
+            self.calcOpeningTaisen()
+            self.calcOpeningAttack()
+            self.calcHougeki1()
+            self.calcHougeki2()
+            self.calcHougeki3()
+            self.calcRaigeki()
+        }
     }
     
     func calcCombinedBattleAir() {
         
-        calcKouku()
-        calcOpeningTaisen()
-        calcOpeningAttack()
-        calcHougeki1()
-        calcRaigeki()
-        calcHougeki2()
-        calcHougeki3()
+        store.sync {
+            
+            self.calcKouku()
+            self.calcOpeningTaisen()
+            self.calcOpeningAttack()
+            self.calcHougeki1()
+            self.calcRaigeki()
+            self.calcHougeki2()
+            self.calcHougeki3()
+        }
     }
     
     func calcEachBattleAir() {
         
-        calcKouku()
-        calcOpeningTaisen()
-        calcOpeningAttack()
-        calcHougeki1()
-        calcHougeki2()
-        calcRaigeki()
-        calcHougeki3()
+        store.sync {
+            
+            self.calcKouku()
+            self.calcOpeningTaisen()
+            self.calcOpeningAttack()
+            self.calcHougeki1()
+            self.calcHougeki2()
+            self.calcRaigeki()
+            self.calcHougeki3()
+        }
+    }
+    
+    func calcEachNightToDay() {
+        
+        store.sync {
+            
+            self.calcNightHogeki1()
+            self.calcNightHogeki2()
+            self.calcKouku()
+            self.calcOpeningTaisen()
+            self.calcOpeningAttack()
+            self.calcHougeki1()
+            self.calcHougeki2()
+            self.calcRaigeki()
+            self.calcHougeki3()
+        }
     }
     
     func calcEnemyCombinedBattle() {
@@ -74,7 +132,10 @@ extension DamageCalculator {
     
     func calcMidnight() {
         
-        calculateMidnightBattle()
+        store.sync {
+            
+            self.calculateMidnightBattle()
+        }
     }
 }
 
@@ -86,19 +147,21 @@ extension DamageCalculator {
         calculateFDam(baseKeyPath: "api_data.api_kouku.api_stage3")
         calculateFDam(baseKeyPath: "api_data.api_kouku2.api_stage3")
         
-        // 艦隊 戦闘艦隊
-        // 連合vs通常(水上) 第2
-        // 連合vs通常(機動) 第2
-        // 連合vs連合(水上) 第2 全体 use kouku nor kouku2
-        // 連合vs連合(機動) 第1 全体 use kouku nor kouku2
         let bf: () -> BattleFleet = {
             
             switch self.battleType {
-            case .combinedWater, .combinedAir,
-                 .eachCombinedWater, .eachCombinedAir:
-                return .second
                 
-            default: return .first
+            case .combinedWater,
+                 .combinedAir,
+                 .eachCombinedWater,
+                 .eachCombinedAir:
+                
+                return .secondOnly
+                
+            default:
+                
+                return .normal
+                
             }
         }
         calculateFDam(baseKeyPath: "api_data.api_kouku.api_stage3_combined", bf)
@@ -108,117 +171,47 @@ extension DamageCalculator {
     
     private func calcOpeningAttack() {
         
-        // 艦隊 戦闘艦隊
-        // 連合vs通常(水上) 第2
-        // 連合vs通常(機動) 第2
-        // 連合vs連合(水上) 第2 全体
-        // 連合vs連合(機動) 第2 全体
-        calculateFDam(baseKeyPath: "api_data.api_opening_atack") {
-            
-            switch battleType {
-            case .combinedWater, .combinedAir: return .second
-                
-            case .eachCombinedWater, .eachCombinedAir: return .each
-                
-            default: return .first
-            }
-        }
+        calculateFDam(baseKeyPath: "api_data.api_opening_atack")
     }
     
     private func calcOpeningTaisen() {
         
-        calculateHogeki(baseKeyPath: "api_data.api_opening_taisen") {
-            
-            isCombinedBattle ? .second : .first
-        }
+        calculateHogeki(baseKeyPath: "api_data.api_opening_taisen")
     }
     
     private func calcHougeki1() {
         
-        // 艦隊 戦闘艦隊
-        // 連合vs通常(水上) 第1
-        // 連合vs通常(機動) 第2
-        // 連合vs連合(水上) 第1
-        // 連合vs連合(機動) 第1
-        calculateHogeki(baseKeyPath: "api_data.api_hougeki1") {
-            
-            switch battleType {
-            case .combinedAir: return .second
-                
-            default: return .first
-            }
-        }
+        calculateHogeki(baseKeyPath: "api_data.api_hougeki1")
     }
     
     private func calcHougeki2() {
         
-        // 艦隊 戦闘艦隊
-        // 連合vs通常(水上) 第1
-        // 連合vs通常(機動) 第1
-        // 連合vs連合(水上) 第1 全体
-        // 連合vs連合(機動) 第2
-        calculateHogeki(baseKeyPath: "api_data.api_hougeki2") {
-            
-            switch battleType {
-            case .eachCombinedWater: return .each
-                
-            case .eachCombinedAir: return .each
-                
-            default: return .first
-            }
-        }
+        calculateHogeki(baseKeyPath: "api_data.api_hougeki2")
     }
     
     private func calcHougeki3() {
         
-        // 艦隊 戦闘艦隊
-        // 連合vs通常(水上) 第2
-        // 連合vs通常(機動) 第1
-        // 連合vs連合(水上) 第2
-        // 連合vs連合(機動) 第1 全体
-        calculateHogeki(baseKeyPath: "api_data.api_hougeki3") {
-            
-            switch battleType {
-            case .combinedWater: return .second
-                
-//            case .eachCombinedWater: return .second  // 1~12
-            case .eachCombinedAir: return .each
-                
-            default: return .first
-            }
-        }
+        calculateHogeki(baseKeyPath: "api_data.api_hougeki3")
+    }
+    
+    private func calcNightHogeki1() {
+        
+        calculateHogeki(baseKeyPath: "api_data.api_n_hougeki1")
+    }
+    
+    private func calcNightHogeki2() {
+        
+        calculateHogeki(baseKeyPath: "api_data.api_n_hougeki2")
     }
     
     private func calcRaigeki() {
         
-        // 艦隊 戦闘艦隊
-        // 連合vs通常(水上) 第2
-        // 連合vs通常(機動) 第2
-        // 連合vs連合(水上) 第2 全体
-        // 連合vs連合(機動) 第2 全体
-        calculateFDam(baseKeyPath: "api_data.api_raigeki") {
-            
-            switch battleType {
-            case .combinedWater, .combinedAir: return .second
-                
-            case .eachCombinedWater, .eachCombinedAir: return .each
-                
-            default: return .first
-            }
-        }
+        calculateFDam(baseKeyPath: "api_data.api_raigeki")
     }
     
     private func calculateMidnightBattle() {
         
-        // 艦隊 戦闘艦隊
-        // 連合vs通常(水上) 第2
-        // 連合vs通常(機動) 第2
-        // 連合vs連合(水上) 第2
-        // 連合vs連合(機動) 第2
-        calculateHogeki(baseKeyPath: "api_data.api_hougeki") {
-            
-            isCombinedBattle ? .second : .first
-        }
+        calculateHogeki(baseKeyPath: "api_data.api_hougeki")
     }
 }
 
@@ -229,19 +222,11 @@ extension DamageCalculator {
         
         let array = store.sortedDamagesById()
         
-        if array.count != 12 {
+        if array.isEmpty {
             
             buildDamagedEntity()
             
-            let newDamages = store.sortedDamagesById()
-            
-            guard newDamages.count == 12 else {
-                
-                print("ERROR!!!! CAN NOT CREATE DAMAGE OBJECT")
-                return []
-            }
-            
-            return newDamages
+            return store.sortedDamagesById()
         }
         
         return array
@@ -250,44 +235,102 @@ extension DamageCalculator {
     private var isCombinedBattle: Bool {
         
         switch battleType {
+            
         case .combinedAir, .combinedWater, .eachCombinedAir, .eachCombinedWater:
+            
             return true
             
         default:
+            
             return false
+            
         }
     }
     
-    private func buildDamagesOfFleet(fleet: Int, ships: [Ship]) {
+    private func makeDamage(num: Int) -> [Damage] {
         
-        guard case 0...1 = fleet else { return print("fleet must 0 or 1.") }
-        guard let battle = store.battle() else { return print("Battle is invalid.") }
+        guard let battle = store.battle() else {
+            
+            Logger.shared.log("Battle is invalid.")
+            
+            return []
+        }
         
-        (0..<6).forEach {
+        return (0..<num).compactMap {
             
-            guard let damage = store.createDamage() else { return print("Can not create Damage") }
+            guard let damage = store.createDamage() else {
+                
+                Logger.shared.log("Can not create Damage")
+                
+                return nil
+            }
             
             damage.battle = battle
-            damage.id = $0 + fleet * 6
+            damage.id = $0
+            
+            return damage
+        }
+    }
+    
+    private func buildDamages(first: [Ship], second: [Ship]?) {
+        
+        guard let battle = store.battle() else {
             
-            guard case 0..<ships.count = $0 else { return }
+            Logger.shared.log("Battle is invalid.")
             
-            damage.hp = ships[$0].nowhp
-            damage.shipID = ships[$0].id
+            return
         }
+        
+        let damages = makeDamage(num: 12)
+        
+        func setShip(_ ship: Ship, into damage: Damage) {
+            
+            let sStore = ServerDataStore.default
+            
+            damage.shipID = sStore.sync { ship.id }
+            damage.hp = sStore.sync { ship.nowhp }
+            
+            Debug.excute(level: .debug) {
+                
+                let name = sStore.sync { ship.name }
+                print("add Damage entity of \(name) at \(damage.id)")
+            }
+        }
+        
+        zip(first, damages).forEach(setShip)
+
+        if let second = second {
+
+            let secondsDamage = damages[6...]
+            zip(second, secondsDamage).forEach(setShip)
+        }
+        
     }
     
     private func buildDamagedEntity() {
         
-        guard let battle = store.battle() else { return print("Battle is invalid.") }
+        guard let battle = store.battle() else {
+            
+            Logger.shared.log("Battle is invalid.")
+            
+            return
+        }
         
+        let sStore = ServerDataStore.default
         // 第一艦隊
-        let firstFleetShips = ServerDataStore.default.ships(byDeckId: battle.deckId)
-        buildDamagesOfFleet(fleet: 0, ships: firstFleetShips)
+        let deckId = battle.deckId
+        let firstFleetShips = sStore.sync { sStore.ships(byDeckId: deckId) }
         
         // 第二艦隊
-        let secondFleetShips = ServerDataStore.default.ships(byDeckId: 2)
-        buildDamagesOfFleet(fleet: 1, ships: secondFleetShips)
+        if isCombinedBattle {
+            
+            let secondFleetShips = sStore.sync { sStore.ships(byDeckId: 2) }
+            buildDamages(first: firstFleetShips, second: secondFleetShips)
+            
+        } else {
+            
+            buildDamages(first: firstFleetShips, second: nil)
+        }
     }
 }
 
@@ -298,14 +341,15 @@ extension DamageCalculator {
         
         guard let targetArraysArray = list
             .array?
-            .flatMap({ $0.array?.flatMap { $0.int } }) else {
+            .compactMap({ $0.array?.compactMap { $0.int } }) else {
                 
                 return nil
         }
         
-        guard list.count - 1 == targetArraysArray.count else {
+        guard list.count == targetArraysArray.count else {
+            
+            Logger.shared.log("api_df_list is wrong")
             
-            print("api_df_list is wrong")
             return nil
         }
         
@@ -314,55 +358,58 @@ extension DamageCalculator {
     
     private func hogekiDamages(_ list: JSON) -> [[Int]]? {
         
-        return list.array?.flatMap { $0.array?.flatMap { $0.int } }
+        return list.array?.compactMap { $0.array?.compactMap { $0.int } }
     }
     
     private func enemyFlags(_ list: JSON) -> [Int]? {
         
-        return list.array?.flatMap { $0.int }.filter { $0 != -1 }
-    }
-    
-    private func isTargetFriend(eFlags: [Int]?, index: Int) -> Bool {
-        
-        if let eFlags = eFlags, 0..<eFlags.count ~= index {
-            
-            return eFlags[index] == 1
-        }
-        
-        return true
+        return list.array?.compactMap { $0.int }
     }
     
     private func validTargetPos(_ targetPos: Int, in battleFleet: BattleFleet) -> Bool {
         
-        let upper = (battleFleet == .each ? 12 : 6)
-        
-        return 1...upper ~= targetPos
+        return 0..<damages.count ~= targetPos
     }
     
     private func position(_ pos: Int, in fleet: BattleFleet) -> Int? {
         
-        let shipOffset = (fleet == .second) ? 6 : 0
-        
-        let damagePos = pos - 1 + shipOffset
+        let damagePos = (fleet == .secondOnly ? pos + 6 : pos)
         
-        guard case 0..<damages.count = damagePos else { return nil }
+        guard case 0..<damages.count = damagePos else {
+            
+            return nil
+        }
         
         return damagePos
     }
     
     private func calcHP(damage: Damage, receive: Int) {
         
-        damage.hp -= receive
+        Debug.excute(level: .debug) {
+            
+            let store = ServerDataStore.default
+            if receive != 0, let shipName = store.sync(execute: { store.ship(by: damage.shipID)?.name }) {
+                
+                print("\(shipName) recieve Damage \(receive)")
+            }
+        }
         
-        if damage.hp > 0 { return }
+        damage.hp -= receive
         
-        guard let ship = ServerDataStore.default.ship(by: damage.shipID) else { return }
+        if damage.hp > 0 {
+            
+            return
+        }
         
-        damage.hp = damageControlIfPossible(ship: ship)
-        if damage.hp != 0 {
+        let sStore = ServerDataStore.default
+        guard let ship = sStore.sync(execute: { sStore.ship(by: damage.shipID) }) else {
             
-            damage.useDamageControl = true
+            return
         }
+        
+        damage.hp = damageControlIfPossible(ship: ship)
+        damage.useDamageControl = (damage.hp != 0)
+        
     }
     
     private func calculateHogeki(baseKeyPath: String, _ bf: () -> BattleFleet) {
@@ -370,7 +417,7 @@ extension DamageCalculator {
         calculateHogeki(baseKeyPath: baseKeyPath, battleFleet: bf())
     }
     
-    private func calculateHogeki(baseKeyPath: String, battleFleet: BattleFleet = .first) {
+    private func buildBattleData(baseKeyPath: String) -> [HogekiBattleData] {
         
         let baseValue = json[baseKeyPath.components(separatedBy: ".")]
         
@@ -378,45 +425,57 @@ extension DamageCalculator {
             let damageLists = hogekiDamages(baseValue["api_damage"]) else {
                 
                 Debug.print("Cound not find api_df_list or api_damage for \(baseKeyPath)", level: .full)
-                return
+                
+                return []
         }
         
-        guard targetPosLists.count == damageLists.count else { return print("api_damage is wrong.") }
+        guard targetPosLists.count == damageLists.count else {
+            
+            Logger.shared.log("api_damage is wrong.")
+            
+            return []
+        }
         
-        let eFlags = enemyFlags(baseValue["api_at_eflag"])
+        guard let eFlags = enemyFlags(baseValue["api_at_eflag"]) else {
+            
+            return zip(targetPosLists, damageLists).map { HogekiBattleData(targetPositionList: $0.0, damageList: $0.1, enemyFlags: false) }
+        }
+        
+        return zip(zip(targetPosLists, damageLists), eFlags)
+            .map { arg -> HogekiBattleData in
+                
+                let ((targetPosList, damageList), eflag) = arg
+                
+                return HogekiBattleData(targetPositionList: targetPosList, damageList: damageList, enemyFlags: eflag != 1)
+        }
+    }
+    
+    private func calculateHogeki(baseKeyPath: String, battleFleet: BattleFleet = .normal) {
         
         Debug.print("Start Hougeki \(baseKeyPath)", level: .debug)
         
-        zip(targetPosLists, damageLists).enumerated().forEach { (i, list) in
-            
-            if !isTargetFriend(eFlags: eFlags, index: i) {
+        buildBattleData(baseKeyPath: baseKeyPath)
+            .map(friendDamage)
+            .filter { $0 != .zero }
+            .forEach { posDamage in
                 
-                Debug.excute(level: .debug) {
+                guard validTargetPos(posDamage.position, in: battleFleet) else {
+                    
+                    Logger.shared.log("invalid position \(posDamage.position)")
                     
-                    print("target is enemy")
+                    return
                 }
                 
-                return
-            }
-            
-            zip(list.0, list.1).forEach { (targetPos, damage) in
-                
-                guard validTargetPos(targetPos, in: battleFleet) else { return }
-                
-                guard let damagePos = position(targetPos, in: battleFleet) else {
+                guard let damagePos = position(posDamage.position, in: battleFleet) else {
+                    
+                    Logger.shared.log("damage pos is larger than damage count")
                     
-                    print("damage pos is larger than damage count")
                     return
                 }
                 
-                calcHP(damage: damages[damagePos], receive: damage)
+                calcHP(damage: damages[damagePos], receive: posDamage.damage)
                 
-                Debug.excute(level: .debug) {
-                    
-                    let shipOffset = (battleFleet == .second) ? 6 : 0
-                    print("Hougeki \(targetPos + shipOffset) -> \(damage)")
-                }
-            }
+                Debug.print("Hougeki \(posDamage.position) -> \(posDamage.damage)", level: .debug)
         }
     }
     
@@ -425,7 +484,7 @@ extension DamageCalculator {
         calculateFDam(baseKeyPath: baseKeyPath, battleFleet: bf())
     }
     
-    private func calculateFDam(baseKeyPath: String, battleFleet: BattleFleet = .first) {
+    private func calculateFDam(baseKeyPath: String, battleFleet: BattleFleet = .normal) {
         
         let baseValue = json[baseKeyPath.components(separatedBy: ".")]
         
@@ -436,82 +495,88 @@ extension DamageCalculator {
             return
         }
         
-        guard let IntFdamArray = fdamArray as? [IntConvertable] else {
+        guard let intFdamArray = fdamArray as? [IntConvertable] else {
             
             Debug.print("api_fdam value of \(baseKeyPath) is not [Int].", level: .debug)
             Debug.print(baseValue, level: .debug)
             
             return
         }
-        let frendDamages = IntFdamArray.map { $0.toInt() }
+        
+        let frendDamages = intFdamArray.map { $0.toInt() }
         
         Debug.print("Start FDam \(baseKeyPath)", level: .debug)
         
         frendDamages.enumerated().forEach { (idx, damage) in
             
-            if idx == 0 { return }
-            
-            guard let damagePos = position(idx, in: battleFleet) else { return }
+            guard let damagePos = position(idx, in: battleFleet) else {
+                
+                return
+            }
             
             calcHP(damage: damages[damagePos], receive: damage)
             
-            Debug.excute(level: .debug) {
-                
-                let shipOffset = (battleFleet == .second) ? 6 : 0
-                print("FDam \(idx + shipOffset) -> \(damage)")
-            }
+            Debug.print("FDam \(idx) -> \(damage)", level: .debug)
         }
     }
 }
 
-
 // MARK: - Damage control
 extension DamageCalculator {
     
-    private func damageControlIfPossible(nowhp: Int, ship: Ship) -> Int {
+    private func damageControlIfPossible(ship: Ship) -> Int {
         
-        var nowHp = nowhp
-        if nowHp < 0 { nowHp = 0 }
-        let maxhp = ship.maxhp
         let store = ServerDataStore.default
-        var useDamageControl = false
         
-        ship.equippedItem.forEach {
+        return store.sync {
             
-            if useDamageControl { return }
+            let damageControl = ship
+                .equippedItem
+                .array
+                .lazy
+                .compactMap { $0 as? SlotItem }
+                .map { store.masterSlotItemID(by: $0.id) }
+                .compactMap { DamageControlID(rawValue: $0) }
+                .first
             
-            guard let master = $0 as? SlotItem else { return }
+            if let validDamageControl = damageControl {
+                
+                switch validDamageControl {
+                    
+                case .damageControl:
+                    Debug.print("Damage Control", level: .debug)
+                    
+                    return Int(Double(ship.maxhp) * 0.2)
+                    
+                case .goddes:
+                    Debug.print("Goddes", level: .debug)
+                    
+                    return ship.maxhp
+                    
+                }
+            }
             
-            let masterSlotItemId = store.masterSlotItemID(by: master.id)
+            // check extra slot
+            let exItemId = store.masterSlotItemID(by: ship.slot_ex)
             
-            guard let type = DamageControlID(rawValue: masterSlotItemId) else { return }
+            guard let exType = DamageControlID(rawValue: exItemId) else {
+                
+                return 0
+            }
             
-            switch type {
+            switch exType {
+                
             case .damageControl:
-                nowHp = Int(Double(maxhp) * 0.2)
-                useDamageControl = true
+                Debug.print("Damage Control", level: .debug)
+                
+                return Int(Double(ship.maxhp) * 0.2)
                 
             case .goddes:
-                nowHp = maxhp
-                useDamageControl = true
+                Debug.print("Goddes", level: .debug)
+                
+                return ship.maxhp
+                
             }
         }
-        
-        if useDamageControl { return nowHp }
-        
-        // check extra slot
-        let exItemId = store.masterSlotItemID(by: ship.slot_ex)
-        
-        guard let exType = DamageControlID(rawValue: exItemId) else { return nowHp }
-        
-        switch exType {
-        case .damageControl:
-            nowHp = Int(Double(maxhp) * 0.2)
-            
-        case .goddes:
-            nowHp = maxhp
-        }
-        
-        return nowHp
     }
 }