2 // CalculateDamageCommand.swift
5 // Created by Hori,Masaki on 2017/01/15.
6 // Copyright © 2017年 Hori,Masaki. All rights reserved.
11 fileprivate enum BattleType {
16 case eachCombinedWater
19 fileprivate enum DamageControlID: Int {
20 case damageControl = 42
23 fileprivate enum BattleFleet {
29 class CalculateDamageCommand: JSONCommand {
30 private let store = TemporaryDataStore.oneTimeEditor()
32 private var battleType: BattleType = .normal
33 private var damages: [KCDamage] {
34 let array = store.sortedDamagesById()
35 if array.count != 12 {
37 let newDamages = store.sortedDamagesById()
38 guard newDamages.count == 12
40 print("ERROR!!!! CAN NOT CREATE DAMAGE OBJECT")
47 private var isCombinedBattle: Bool {
49 case .combinedAir, .combinedWater, .eachCombinedAir, .eachCombinedWater:
56 override func execute() {
57 guard let battleApi = BattleAPI(rawValue: api)
61 case .battle, .airBattle, .ldAirBattle:
63 case .combinedEcBattle:
64 battleType = .enemyCombined
65 calcEnemyCombinedBattle()
66 case .combinedBattle, .combinedAirBattle:
67 battleType = .combinedAir
68 calcCombinedBattleAir()
69 case .combinedBattleWater, .combinedLdAirBattle:
70 battleType = .combinedWater
72 case .combinedEachBattle:
73 battleType = .eachCombinedAir
75 case .combinedEachBattleWater:
76 battleType = .eachCombinedWater
78 case .midnightBattle, .midnightSpMidnight, .combinedMidnightBattle, .combinedSpMidnight:
79 calculateMidnightBattle()
80 case .combinedEcMidnightBattle:
81 battleType = .eachCombinedAir
82 calculateMidnightBattle()
83 case .battleResult, .combinedBattleResult:
89 private func resetDamage() {
90 store.damages().forEach { store.delete($0) }
92 private func applyDamage() {
93 let totalDamages = store.sortedDamagesById()
94 guard totalDamages.count == 12
95 else { return print("Damages count is invalid") }
96 let aStore = ServerDataStore.oneTimeEditor()
97 totalDamages.forEach {
98 guard let ship = aStore.ship(byId: $0.shipID)
101 if ship.nowhp != $0.hp {
102 Debug.print("\(ship.name)(\(ship.id)),HP \(ship.nowhp) -> \($0.hp)", level: .debug)
106 if $0.useDamageControl { removeFirstDamageControl(of: ship) }
109 private func buildDamagedEntity() {
110 guard let battle = store.battle()
111 else { return print("Battle is invalid.") }
113 let aStore = ServerDataStore.default
114 var ships: [Any] = []
117 let firstFleetShips = aStore.ships(byDeckId: battle.deckId)
118 ships += (firstFleetShips as [Any])
119 while ships.count != 6 {
124 let secondFleetShips = aStore.ships(byDeckId: 2)
125 ships += (secondFleetShips as [Any])
126 while ships.count != 12 {
129 ships.enumerated().forEach {
130 guard let damage = store.createDamage()
131 else { return print("Can not create Damage") }
132 damage.battle = battle
133 damage.id = $0.offset
134 if let ship = $0.element as? KCShipObject {
135 damage.hp = ship.nowhp
136 damage.shipID = ship.id
141 private func updateBattleCell() {
142 guard let battle = store.battle()
143 else { return print("Battle is invalid.") }
144 battle.battleCell = (battle.no == 0 ? nil : battle.no as NSNumber)
147 // MARK: - Primitive Calclator
148 private func calculateHogeki(baseKeyPath: String, _ bf: () -> BattleFleet) {
149 calculateHogeki(baseKeyPath: baseKeyPath, battleFleet: bf())
151 private func calculateHogeki(baseKeyPath: String, battleFleet: BattleFleet = .first) {
152 let j = json as NSDictionary
153 guard let data = j.value(forKeyPath: baseKeyPath) as? [String: Any],
154 let tt = data["api_df_list"] as? [Any],
155 let dd = data["api_damage"] as? [Any]
157 let ttt = tt.filter { $0 is [Int] }
158 let ddd = dd.filter { $0 is [Int] }
159 guard let targetArraysArray = ttt as? [[Int]],
160 tt.count - 1 == targetArraysArray.count
161 else { return print("api_df_list is wrong") }
162 guard let hougeki1Damages = ddd as? [[Int]],
163 targetArraysArray.count == hougeki1Damages.count
164 else { return print("api_damage is wrong") }
166 let eFlags: [Int]? = (data["api_at_eflag"] as? [Int])?.filter { $0 != -1 }
168 Debug.print("Start Hougeki \(baseKeyPath)", level: .debug)
169 let shipOffset = (battleFleet == .second) ? 6 : 0
170 targetArraysArray.enumerated().forEach { (i, targetArray) in
171 targetArray.enumerated().forEach { (j, targetPosition) in
173 if let e = eFlags, 0..<e.count ~= i {
174 if e[i] != 1 { return }
177 if battleFleet == .each {
178 guard 1...12 ~= targetPosition else { return }
180 guard 1...6 ~= targetPosition else { return }
184 let damagePos = targetPosition - 1 + shipOffset
185 guard 0..<damages.count ~= damagePos
186 else { return print("damage pos is larger than damage count") }
187 let damageObject = damages[damagePos]
188 guard 0..<hougeki1Damages[i].count ~= j
189 else { return print("target pos is larger than damages") }
190 let damage = hougeki1Damages[i][j]
191 let hp = damageObject.hp
192 var newHP = (hp as Int) - damage
194 let shipId = damageObject.shipID
195 if let ship = ServerDataStore.default.ship(byId: shipId) {
196 let efectiveHP = damageControlIfPossible(nowhp: newHP, ship: ship)
197 if efectiveHP != 0 && efectiveHP != newHP {
198 damageObject.useDamageControl = true
203 damageObject.hp = newHP
205 Debug.print("Hougeki \(targetPosition + shipOffset) -> \(damage)", level: .debug)
209 private func calculateFDam(baseKeyPath: String, _ bf: () -> BattleFleet) {
210 calculateFDam(baseKeyPath: baseKeyPath, battleFleet: bf())
212 private func calculateFDam(baseKeyPath: String, battleFleet: BattleFleet = .first) {
213 let j = json as NSDictionary
214 guard let data = j.value(forKeyPath: baseKeyPath) as? [String: Any],
215 let koukuDamages = data["api_fdam"] as? [Int]
218 Debug.print("Start FDam \(baseKeyPath)", level: .debug)
220 let shipOffset = (battleFleet == .second) ? 6 : 0
221 koukuDamages.enumerated().forEach { (idx, damage) in
222 if idx == 0 { return }
224 let damagePos = idx - 1 + shipOffset
225 guard 0..<damages.count ~= damagePos
227 let damageObject = damages[damagePos]
228 var newHP = damageObject.hp - damage
230 let shipId = damageObject.shipID
231 if let ship = ServerDataStore.default.ship(byId: shipId) {
232 let efectiveHP = damageControlIfPossible(nowhp: newHP, ship: ship)
233 if efectiveHP != 0 && efectiveHP != newHP {
234 damageObject.useDamageControl = true
238 damageObject.hp = newHP
240 damageObject.hp = newHP
242 Debug.print("FDam \(idx + shipOffset) -> \(damage)", level: .debug)
246 // MARK: - Battle phase
247 private func calcKouku() {
248 calculateFDam(baseKeyPath: "api_data.api_kouku.api_stage3")
249 calculateFDam(baseKeyPath: "api_data.api_kouku2.api_stage3")
254 // 連合vs連合(水上) 第2 全体 use kouku nor kouku2
255 // 連合vs連合(機動) 第1 全体 use kouku nor kouku2
256 let bf: () -> BattleFleet = {
257 switch self.battleType {
258 case .combinedWater, .combinedAir,
259 .eachCombinedWater, .eachCombinedAir:
261 default: return .first
264 calculateFDam(baseKeyPath: "api_data.api_kouku.api_stage3_combined", bf)
265 calculateFDam(baseKeyPath: "api_data.api_kouku2.api_stage3_combined", bf)
268 private func calcOpeningAttack() {
274 calculateFDam(baseKeyPath: "api_data.api_opening_atack") {
276 case .combinedWater, .combinedAir: return .second
277 case .eachCombinedWater, .eachCombinedAir: return .each
278 default: return .first
282 private func calcOpeningTaisen() {
283 calculateHogeki(baseKeyPath: "api_data.api_opening_taisen") {
284 isCombinedBattle ? .second : .first
287 private func calcHougeki1() {
293 calculateHogeki(baseKeyPath: "api_data.api_hougeki1") {
295 case .combinedAir: return .second
296 default: return .first
300 private func calcHougeki2() {
306 calculateHogeki(baseKeyPath: "api_data.api_hougeki2") {
308 case .eachCombinedWater: return .each
309 // case .eachCombinedAir: return .second // 1~12
310 default: return .first
314 private func calcHougeki3() {
320 calculateHogeki(baseKeyPath: "api_data.api_hougeki3") {
322 case .combinedWater: return .second
323 // case .eachCombinedWater: return .second // 1~12
324 case .eachCombinedAir: return .each
325 default: return .first
329 private func calcRaigeki() {
335 calculateFDam(baseKeyPath: "api_data.api_raigeki") {
337 case .combinedWater, .combinedAir: return .second
338 case .eachCombinedWater, .eachCombinedAir: return .each
339 default: return .first
344 private func calculateMidnightBattle() {
350 calculateHogeki(baseKeyPath: "api_data.api_hougeki") {
351 isCombinedBattle ? .second : .first
355 // MARK: - Battle type
356 private func calculateBattle() {
367 private func calcCombinedBattleAir() {
378 private func calcEachBattleAir() {
389 private func calcEnemyCombinedBattle() {
390 // same phase as combined air
391 calcCombinedBattleAir()
394 // MARK: - Damage control
395 private func damageControlIfPossible(nowhp: Int, ship: KCShipObject) -> Int {
397 if nowHp < 0 { nowHp = 0 }
398 let maxhp = ship.maxhp
399 let store = ServerDataStore.default
400 var useDamageControl = false
401 ship.equippedItem.forEach {
402 if useDamageControl { return }
403 guard let master = $0 as? KCSlotItemObject else { return }
404 let masterSlotItemId = store.masterSlotItemID(bySlotItemId: master.id)
405 guard let type = DamageControlID(rawValue: masterSlotItemId)
409 nowHp = Int(Double(maxhp) * 0.2)
410 useDamageControl = true
413 useDamageControl = true
416 if useDamageControl { return nowHp }
418 let exItemId = store.masterSlotItemID(bySlotItemId: ship.slot_ex)
419 guard let exType = DamageControlID(rawValue: exItemId)
420 else { return nowHp }
423 nowHp = Int(Double(maxhp) * 0.2)
429 private func removeFirstDamageControl(of ship: KCShipObject) {
430 let equiped = ship.equippedItem
431 let newEquiped = equiped.mutableCopy() as! NSMutableOrderedSet
432 let store = ServerDataStore.default
433 var useDamageControl = false
435 if useDamageControl { return }
436 guard let master = $0 as? KCSlotItemObject else { return }
437 let masterSlotItemId = store.masterSlotItemID(bySlotItemId: master.id)
438 guard let type = DamageControlID(rawValue: masterSlotItemId)
442 ship.fuel = ship.maxFuel
443 ship.bull = ship.maxBull
446 newEquiped.remove(master)
447 useDamageControl = true
450 if useDamageControl {
451 ship.equippedItem = newEquiped
455 let exItemId = store.masterSlotItemID(bySlotItemId: ship.slot_ex)
456 guard let exType = DamageControlID(rawValue: exItemId)
460 ship.fuel = ship.maxFuel
461 ship.bull = ship.maxBull