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 {
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() {
func calcMidnight() {
- calculateMidnightBattle()
+ store.sync {
+
+ self.calculateMidnightBattle()
+ }
}
}
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)
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")
}
}
let array = store.sortedDamagesById()
- if array.count != 12 {
+ if array.isEmpty {
buildDamagedEntity()
- let newDamages = store.sortedDamagesById()
-
- guard newDamages.count == 12 else {
-
- return Logger.shared.log("ERROR!!!! CAN NOT CREATE DAMAGE OBJECT", value: [])
- }
-
- return newDamages
+ return store.sortedDamagesById()
}
return array
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 Logger.shared.log("fleet must 0 or 1.") }
- guard let battle = store.battle() else { return Logger.shared.log("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 Logger.shared.log("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 Logger.shared.log("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)
+ }
}
}
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 {
- return Logger.shared.log("api_df_list is wrong", value: nil)
+ Logger.shared.log("api_df_list is wrong")
+
+ return nil
}
return targetArraysArray
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) {
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: ".")]
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 Logger.shared.log("api_damage is wrong.") }
+ guard targetPosLists.count == damageLists.count else {
+
+ Logger.shared.log("api_damage is wrong.")
+
+ return []
+ }
+
+ guard let eFlags = enemyFlags(baseValue["api_at_eflag"]) else {
+
+ return zip(targetPosLists, damageLists).map { HogekiBattleData(targetPositionList: $0.0, damageList: $0.1, enemyFlags: false) }
+ }
- let eFlags = enemyFlags(baseValue["api_at_eflag"])
+ 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 {
- print("target is enemy")
+ Logger.shared.log("invalid position \(posDamage.position)")
+
+ 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 {
- return Logger.shared.log("damage pos is larger than damage count")
+ Logger.shared.log("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)
}
}
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: ".")]
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)
}
}
}
let store = ServerDataStore.default
- let damageControl = ship
- .equippedItem
- .lazy
- .flatMap { $0 as? SlotItem }
- .map { store.masterSlotItemID(by: $0.id) }
- .flatMap { DamageControlID(rawValue: $0) }
- .first
-
- if let validDamageControl = damageControl {
+ return store.sync {
+
+ let damageControl = ship
+ .equippedItem
+ .array
+ .lazy
+ .compactMap { $0 as? SlotItem }
+ .map { store.masterSlotItemID(by: $0.id) }
+ .compactMap { DamageControlID(rawValue: $0) }
+ .first
+
+ 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
+
+ }
+ }
+
+ // check extra slot
+ let exItemId = store.masterSlotItemID(by: ship.slot_ex)
- switch validDamageControl {
+ guard let exType = DamageControlID(rawValue: exItemId) else {
+
+ return 0
+ }
+
+ switch exType {
+
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
+
}
}
-
- // check extra slot
- let exItemId = store.masterSlotItemID(by: ship.slot_ex)
-
- guard let exType = DamageControlID(rawValue: exItemId) else { return 0 }
-
- switch exType {
- case .damageControl:
- return Int(Double(ship.maxhp) * 0.2)
-
- case .goddes:
- return ship.maxhp
- }
}
}