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: [Damage] {
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? Ship {
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 isTargetFriend(eFlags: [Int]?, index: Int) -> Bool {
149 if let eFlags = eFlags, 0..<eFlags.count ~= index {
150 return eFlags[index] == 1
154 private func calculateHogeki(baseKeyPath: String, _ bf: () -> BattleFleet) {
155 calculateHogeki(baseKeyPath: baseKeyPath, battleFleet: bf())
157 private func calculateHogeki(baseKeyPath: String, battleFleet: BattleFleet = .first) {
158 let j = json as NSDictionary
159 guard let data = j.value(forKeyPath: baseKeyPath) as? [String: Any],
160 let dfList = data["api_df_list"] as? [Any],
161 let damageList = data["api_damage"] as? [Any]
163 guard let targetArraysArray = dfList.filter({ $0 is [Int] }) as? [[Int]],
164 dfList.count - 1 == targetArraysArray.count
165 else { return print("api_df_list is wrong") }
166 guard let hougeki1Damages = damageList.filter({ $0 is [Int] }) as? [[Int]],
167 targetArraysArray.count == hougeki1Damages.count
168 else { return print("api_damage is wrong") }
169 let eFlags: [Int]? = (data["api_at_eflag"] as? [Int])?.filter { $0 != -1 }
171 Debug.print("Start Hougeki \(baseKeyPath)", level: .debug)
172 let shipOffset = (battleFleet == .second) ? 6 : 0
173 targetArraysArray.enumerated().forEach { (i, targetArray) in
174 targetArray.enumerated().forEach { (j, targetPosition) in
175 if !isTargetFriend(eFlags: eFlags, index: i) { return }
177 if battleFleet == .each {
178 guard 1...12 ~= targetPosition else { return }
180 guard 1...6 ~= targetPosition else { return }
183 let damagePos = targetPosition - 1 + shipOffset
184 guard 0..<damages.count ~= damagePos
185 else { return print("damage pos is larger than damage count") }
186 let damageObject = damages[damagePos]
187 guard 0..<hougeki1Damages[i].count ~= j
188 else { return print("target pos is larger than damages") }
189 let damage = hougeki1Damages[i][j]
190 let hp = damageObject.hp
191 var newHP = (hp as Int) - damage
193 let shipId = damageObject.shipID
194 if let ship = ServerDataStore.default.ship(byId: shipId) {
195 let efectiveHP = damageControlIfPossible(nowhp: newHP, ship: ship)
196 if efectiveHP != 0, efectiveHP != newHP {
197 damageObject.useDamageControl = true
202 damageObject.hp = newHP
204 Debug.print("Hougeki \(targetPosition + shipOffset) -> \(damage)", level: .debug)
208 private func calculateFDam(baseKeyPath: String, _ bf: () -> BattleFleet) {
209 calculateFDam(baseKeyPath: baseKeyPath, battleFleet: bf())
211 private func calculateFDam(baseKeyPath: String, battleFleet: BattleFleet = .first) {
212 let j = json as NSDictionary
213 guard let data = j.value(forKeyPath: baseKeyPath) as? [String: Any],
214 let koukuDamages = data["api_fdam"] as? [Int]
217 Debug.print("Start FDam \(baseKeyPath)", level: .debug)
219 let shipOffset = (battleFleet == .second) ? 6 : 0
220 koukuDamages.enumerated().forEach { (idx, damage) in
221 if idx == 0 { return }
223 let damagePos = idx - 1 + shipOffset
224 guard 0..<damages.count ~= damagePos
226 let damageObject = damages[damagePos]
227 var newHP = damageObject.hp - damage
229 let shipId = damageObject.shipID
230 if let ship = ServerDataStore.default.ship(byId: shipId) {
231 let efectiveHP = damageControlIfPossible(nowhp: newHP, ship: ship)
232 if efectiveHP != 0 && efectiveHP != newHP {
233 damageObject.useDamageControl = true
237 damageObject.hp = newHP
239 damageObject.hp = newHP
241 Debug.print("FDam \(idx + shipOffset) -> \(damage)", level: .debug)
245 // MARK: - Battle phase
246 private func calcKouku() {
247 calculateFDam(baseKeyPath: "api_data.api_kouku.api_stage3")
248 calculateFDam(baseKeyPath: "api_data.api_kouku2.api_stage3")
253 // 連合vs連合(水上) 第2 全体 use kouku nor kouku2
254 // 連合vs連合(機動) 第1 全体 use kouku nor kouku2
255 let bf: () -> BattleFleet = {
256 switch self.battleType {
257 case .combinedWater, .combinedAir,
258 .eachCombinedWater, .eachCombinedAir:
260 default: return .first
263 calculateFDam(baseKeyPath: "api_data.api_kouku.api_stage3_combined", bf)
264 calculateFDam(baseKeyPath: "api_data.api_kouku2.api_stage3_combined", bf)
267 private func calcOpeningAttack() {
273 calculateFDam(baseKeyPath: "api_data.api_opening_atack") {
275 case .combinedWater, .combinedAir: return .second
276 case .eachCombinedWater, .eachCombinedAir: return .each
277 default: return .first
281 private func calcOpeningTaisen() {
282 calculateHogeki(baseKeyPath: "api_data.api_opening_taisen") {
283 isCombinedBattle ? .second : .first
286 private func calcHougeki1() {
292 calculateHogeki(baseKeyPath: "api_data.api_hougeki1") {
294 case .combinedAir: return .second
295 default: return .first
299 private func calcHougeki2() {
305 calculateHogeki(baseKeyPath: "api_data.api_hougeki2") {
307 case .eachCombinedWater: return .each
308 // case .eachCombinedAir: return .second // 1~12
309 default: return .first
313 private func calcHougeki3() {
319 calculateHogeki(baseKeyPath: "api_data.api_hougeki3") {
321 case .combinedWater: return .second
322 // case .eachCombinedWater: return .second // 1~12
323 case .eachCombinedAir: return .each
324 default: return .first
328 private func calcRaigeki() {
334 calculateFDam(baseKeyPath: "api_data.api_raigeki") {
336 case .combinedWater, .combinedAir: return .second
337 case .eachCombinedWater, .eachCombinedAir: return .each
338 default: return .first
343 private func calculateMidnightBattle() {
349 calculateHogeki(baseKeyPath: "api_data.api_hougeki") {
350 isCombinedBattle ? .second : .first
354 // MARK: - Battle type
355 private func calculateBattle() {
366 private func calcCombinedBattleAir() {
377 private func calcEachBattleAir() {
388 private func calcEnemyCombinedBattle() {
389 // same phase as combined air
390 calcCombinedBattleAir()
393 // MARK: - Damage control
394 private func damageControlIfPossible(nowhp: Int, ship: Ship) -> Int {
396 if nowHp < 0 { nowHp = 0 }
397 let maxhp = ship.maxhp
398 let store = ServerDataStore.default
399 var useDamageControl = false
400 ship.equippedItem.forEach {
401 if useDamageControl { return }
402 guard let master = $0 as? SlotItem else { return }
403 let masterSlotItemId = store.masterSlotItemID(bySlotItemId: master.id)
404 guard let type = DamageControlID(rawValue: masterSlotItemId)
408 nowHp = Int(Double(maxhp) * 0.2)
409 useDamageControl = true
412 useDamageControl = true
415 if useDamageControl { return nowHp }
417 let exItemId = store.masterSlotItemID(bySlotItemId: ship.slot_ex)
418 guard let exType = DamageControlID(rawValue: exItemId)
419 else { return nowHp }
422 nowHp = Int(Double(maxhp) * 0.2)
428 private func removeFirstDamageControl(of ship: Ship) {
429 let equiped = ship.equippedItem
430 let newEquiped = equiped.array
431 let store = ServerDataStore.default
432 var useDamageControl = false
434 if useDamageControl { return }
435 guard let master = $0 as? SlotItem else { return }
436 let masterSlotItemId = store.masterSlotItemID(bySlotItemId: master.id)
437 guard let type = DamageControlID(rawValue: masterSlotItemId)
441 ship.fuel = ship.maxFuel
442 ship.bull = ship.maxBull
445 if var equiped = newEquiped as? [SlotItem],
446 let index = equiped.index(of: master) {
447 equiped[index...index] = []
448 ship.equippedItem = NSOrderedSet(array: equiped)
449 useDamageControl = true
453 if useDamageControl {
457 let exItemId = store.masterSlotItemID(bySlotItemId: ship.slot_ex)
458 guard let exType = DamageControlID(rawValue: exItemId)
462 ship.fuel = ship.maxFuel
463 ship.bull = ship.maxBull