2 // CalculateDamageCommand.swift
5 // Created by Hori,Masaki on 2017/01/15.
6 // Copyright © 2017年 Hori,Masaki. All rights reserved.
12 fileprivate enum BattleType {
17 case eachCombinedWater
20 fileprivate enum DamageControlID: Int {
21 case damageControl = 42
24 fileprivate enum BattleFleet {
30 class CalculateDamageCommand: JSONCommand {
31 private let store = TemporaryDataStore.oneTimeEditor()
33 fileprivate var battleType: BattleType = .normal
34 fileprivate var damages: [Damage] {
35 let array = store.sortedDamagesById()
36 if array.count != 12 {
38 let newDamages = store.sortedDamagesById()
39 guard newDamages.count == 12
41 print("ERROR!!!! CAN NOT CREATE DAMAGE OBJECT")
48 fileprivate var isCombinedBattle: Bool {
50 case .combinedAir, .combinedWater, .eachCombinedAir, .eachCombinedWater:
57 override func execute() {
58 guard let battleApi = BattleAPI(rawValue: api)
62 case .battle, .airBattle, .ldAirBattle:
64 case .combinedEcBattle:
65 battleType = .enemyCombined
66 calcEnemyCombinedBattle()
67 case .combinedBattle, .combinedAirBattle:
68 battleType = .combinedAir
69 calcCombinedBattleAir()
70 case .combinedBattleWater, .combinedLdAirBattle:
71 battleType = .combinedWater
73 case .combinedEachBattle:
74 battleType = .eachCombinedAir
76 case .combinedEachBattleWater:
77 battleType = .eachCombinedWater
79 case .midnightBattle, .midnightSpMidnight, .combinedMidnightBattle, .combinedSpMidnight:
80 calculateMidnightBattle()
81 case .combinedEcMidnightBattle:
82 battleType = .eachCombinedAir
83 calculateMidnightBattle()
84 case .battleResult, .combinedBattleResult:
90 private func resetDamage() {
91 store.damages().forEach { store.delete($0) }
93 private func applyDamage() {
94 let totalDamages = store.sortedDamagesById()
95 guard totalDamages.count == 12
96 else { return print("Damages count is invalid. count is \(totalDamages.count).") }
97 let aStore = ServerDataStore.oneTimeEditor()
98 totalDamages.forEach {
99 guard let ship = aStore.ship(byId: $0.shipID)
102 if ship.nowhp != $0.hp {
103 Debug.print("\(ship.name)(\(ship.id)),HP \(ship.nowhp) -> \($0.hp)", level: .debug)
107 if $0.useDamageControl { removeFirstDamageControl(of: ship) }
110 private func buildDamagedEntity() {
111 guard let battle = store.battle()
112 else { return print("Battle is invalid.") }
114 let aStore = ServerDataStore.default
115 var ships: [Any] = []
118 let firstFleetShips = aStore.ships(byDeckId: battle.deckId)
119 ships += (firstFleetShips as [Any])
120 while ships.count != 6 {
125 let secondFleetShips = aStore.ships(byDeckId: 2)
126 ships += (secondFleetShips as [Any])
127 while ships.count != 12 {
130 ships.enumerated().forEach {
131 guard let damage = store.createDamage()
132 else { return print("Can not create Damage") }
133 damage.battle = battle
134 damage.id = $0.offset
135 if let ship = $0.element as? Ship {
136 damage.hp = ship.nowhp
137 damage.shipID = ship.id
142 fileprivate func updateBattleCell() {
143 guard let battle = store.battle()
144 else { return print("Battle is invalid.") }
145 battle.battleCell = (battle.no == 0 ? nil : battle.no as NSNumber)
148 // MARK: - Primitive Calclator
149 extension CalculateDamageCommand {
151 private func hogekiTargets(_ list: JSON) -> [[Int]]? {
152 guard let targetArraysArray = list
154 .flatMap({ $0.array?.flatMap { $0.int } })
156 guard list.count - 1 == targetArraysArray.count
158 print("api_df_list is wrong")
161 return targetArraysArray
163 private func hogekiDamages(_ list: JSON) -> [[Int]]? {
164 guard let hougeki1Damages = list
166 .flatMap({ $0.array?.flatMap { $0.int } })
168 return hougeki1Damages
170 private func enemyFlags(_ list: JSON) -> [Int]? {
171 return list.array?.flatMap { $0.int }.filter { $0 != -1 }
173 private func isTargetFriend(eFlags: [Int]?, index: Int) -> Bool {
174 if let eFlags = eFlags, 0..<eFlags.count ~= index {
175 return eFlags[index] == 1
179 fileprivate func calculateHogeki(baseKeyPath: String, _ bf: () -> BattleFleet) {
180 calculateHogeki(baseKeyPath: baseKeyPath, battleFleet: bf())
182 fileprivate func calculateHogeki(baseKeyPath: String, battleFleet: BattleFleet = .first) {
183 let baseValue = json[baseKeyPath.components(separatedBy: ".")]
184 guard let targetArraysArray = hogekiTargets(baseValue["api_df_list"]),
185 let hougeki1Damages = hogekiDamages(baseValue["api_damage"])
187 guard targetArraysArray.count == hougeki1Damages.count
188 else { return print("api_damage is wrong.") }
190 let eFlags: [Int]? = enemyFlags(baseValue["api_at_eflag"])
192 Debug.print("Start Hougeki \(baseKeyPath)", level: .debug)
193 let shipOffset = (battleFleet == .second) ? 6 : 0
194 targetArraysArray.enumerated().forEach { (i, targetArray) in
195 targetArray.enumerated().forEach { (j, targetPosition) in
196 if !isTargetFriend(eFlags: eFlags, index: i) { return }
198 if battleFleet == .each {
199 guard 1...12 ~= targetPosition else { return }
201 guard 1...6 ~= targetPosition else { return }
204 let damagePos = targetPosition - 1 + shipOffset
205 guard 0..<damages.count ~= damagePos
206 else { return print("damage pos is larger than damage count") }
207 let damageObject = damages[damagePos]
208 guard 0..<hougeki1Damages[i].count ~= j
209 else { return print("target pos is larger than damages") }
210 let damage = hougeki1Damages[i][j]
211 let hp = damageObject.hp
212 var newHP = (hp as Int) - damage
214 let shipId = damageObject.shipID
215 if let ship = ServerDataStore.default.ship(byId: shipId) {
216 let efectiveHP = damageControlIfPossible(nowhp: newHP, ship: ship)
217 if efectiveHP != 0, efectiveHP != newHP {
218 damageObject.useDamageControl = true
223 damageObject.hp = newHP
225 Debug.print("Hougeki \(targetPosition + shipOffset) -> \(damage)", level: .debug)
229 fileprivate func calculateFDam(baseKeyPath: String, _ bf: () -> BattleFleet) {
230 calculateFDam(baseKeyPath: baseKeyPath, battleFleet: bf())
232 fileprivate func calculateFDam(baseKeyPath: String, battleFleet: BattleFleet = .first) {
233 let baseValue = json[baseKeyPath.components(separatedBy: ".")]
234 guard let koukuDamages = baseValue["api_fdam"].arrayObject as? [Int]
237 Debug.print("Start FDam \(baseKeyPath)", level: .debug)
239 let shipOffset = (battleFleet == .second) ? 6 : 0
240 koukuDamages.enumerated().forEach { (idx, damage) in
241 if idx == 0 { return }
243 let damagePos = idx - 1 + shipOffset
244 guard 0..<damages.count ~= damagePos
246 let damageObject = damages[damagePos]
247 var newHP = damageObject.hp - damage
249 let shipId = damageObject.shipID
250 if let ship = ServerDataStore.default.ship(byId: shipId) {
251 let efectiveHP = damageControlIfPossible(nowhp: newHP, ship: ship)
252 if efectiveHP != 0 && efectiveHP != newHP {
253 damageObject.useDamageControl = true
257 damageObject.hp = newHP
259 damageObject.hp = newHP
261 Debug.print("FDam \(idx + shipOffset) -> \(damage)", level: .debug)
265 // MARK: - Battle phase
266 extension CalculateDamageCommand {
267 fileprivate func calcKouku() {
268 calculateFDam(baseKeyPath: "api_data.api_kouku.api_stage3")
269 calculateFDam(baseKeyPath: "api_data.api_kouku2.api_stage3")
274 // 連合vs連合(水上) 第2 全体 use kouku nor kouku2
275 // 連合vs連合(機動) 第1 全体 use kouku nor kouku2
276 let bf: () -> BattleFleet = {
277 switch self.battleType {
278 case .combinedWater, .combinedAir,
279 .eachCombinedWater, .eachCombinedAir:
281 default: return .first
284 calculateFDam(baseKeyPath: "api_data.api_kouku.api_stage3_combined", bf)
285 calculateFDam(baseKeyPath: "api_data.api_kouku2.api_stage3_combined", bf)
288 fileprivate func calcOpeningAttack() {
294 calculateFDam(baseKeyPath: "api_data.api_opening_atack") {
296 case .combinedWater, .combinedAir: return .second
297 case .eachCombinedWater, .eachCombinedAir: return .each
298 default: return .first
302 fileprivate func calcOpeningTaisen() {
303 calculateHogeki(baseKeyPath: "api_data.api_opening_taisen") {
304 isCombinedBattle ? .second : .first
307 fileprivate func calcHougeki1() {
313 calculateHogeki(baseKeyPath: "api_data.api_hougeki1") {
315 case .combinedAir: return .second
316 default: return .first
320 fileprivate func calcHougeki2() {
326 calculateHogeki(baseKeyPath: "api_data.api_hougeki2") {
328 case .eachCombinedWater: return .each
329 // case .eachCombinedAir: return .second // 1~12
330 default: return .first
334 fileprivate func calcHougeki3() {
340 calculateHogeki(baseKeyPath: "api_data.api_hougeki3") {
342 case .combinedWater: return .second
343 // case .eachCombinedWater: return .second // 1~12
344 case .eachCombinedAir: return .each
345 default: return .first
349 fileprivate func calcRaigeki() {
355 calculateFDam(baseKeyPath: "api_data.api_raigeki") {
357 case .combinedWater, .combinedAir: return .second
358 case .eachCombinedWater, .eachCombinedAir: return .each
359 default: return .first
364 fileprivate func calculateMidnightBattle() {
370 calculateHogeki(baseKeyPath: "api_data.api_hougeki") {
371 isCombinedBattle ? .second : .first
375 // MARK: - Battle type
376 extension CalculateDamageCommand {
377 fileprivate func calculateBattle() {
388 fileprivate func calcCombinedBattleAir() {
399 fileprivate func calcEachBattleAir() {
410 fileprivate func calcEnemyCombinedBattle() {
411 // same phase as combined air
412 calcCombinedBattleAir()
415 extension CalculateDamageCommand {
417 // MARK: - Damage control
418 fileprivate func damageControlIfPossible(nowhp: Int, ship: Ship) -> Int {
420 if nowHp < 0 { nowHp = 0 }
421 let maxhp = ship.maxhp
422 let store = ServerDataStore.default
423 var useDamageControl = false
424 ship.equippedItem.forEach {
425 if useDamageControl { return }
426 guard let master = $0 as? SlotItem else { return }
427 let masterSlotItemId = store.masterSlotItemID(bySlotItemId: master.id)
428 guard let type = DamageControlID(rawValue: masterSlotItemId)
432 nowHp = Int(Double(maxhp) * 0.2)
433 useDamageControl = true
436 useDamageControl = true
439 if useDamageControl { return nowHp }
441 let exItemId = store.masterSlotItemID(bySlotItemId: ship.slot_ex)
442 guard let exType = DamageControlID(rawValue: exItemId)
443 else { return nowHp }
446 nowHp = Int(Double(maxhp) * 0.2)
452 fileprivate func removeFirstDamageControl(of ship: Ship) {
453 let equiped = ship.equippedItem
454 let newEquiped = equiped.array
455 let store = ServerDataStore.default
456 var useDamageControl = false
458 if useDamageControl { return }
459 guard let master = $0 as? SlotItem else { return }
460 let masterSlotItemId = store.masterSlotItemID(bySlotItemId: master.id)
461 guard let type = DamageControlID(rawValue: masterSlotItemId)
465 ship.fuel = ship.maxFuel
466 ship.bull = ship.maxBull
469 if var equiped = newEquiped as? [SlotItem],
470 let index = equiped.index(of: master) {
471 equiped[index...index] = []
472 ship.equippedItem = NSOrderedSet(array: equiped)
473 useDamageControl = true
477 if useDamageControl {
481 let exItemId = store.masterSlotItemID(bySlotItemId: ship.slot_ex)
482 guard let exType = DamageControlID(rawValue: exItemId)
486 ship.fuel = ship.maxFuel
487 ship.bull = ship.maxBull