5 // Created by Hori,Masaki on 2017/01/29.
6 // Copyright © 2017年 Hori,Masaki. All rights reserved.
11 // swiftlint:disable variable_name
12 final class Ship: KCManagedObject {
14 @NSManaged dynamic var bull: Int
15 @NSManaged dynamic var cond: Int
16 @NSManaged dynamic var exp: Int
17 @NSManaged dynamic var fleet: NSNumber?
18 @NSManaged dynamic var fuel: Int
19 @NSManaged dynamic var id: Int
20 @NSManaged dynamic var kaihi_0: Int
21 @NSManaged dynamic var kaihi_1: NSNumber?
22 @NSManaged dynamic var karyoku_0: Int
23 @NSManaged dynamic var karyoku_1: Int
24 @NSManaged dynamic var kyouka_0: Int
25 @NSManaged dynamic var kyouka_1: Int
26 @NSManaged dynamic var kyouka_2: Int
27 @NSManaged dynamic var kyouka_3: Int
28 @NSManaged dynamic var kyouka_4: Int
29 @NSManaged dynamic var locked: Int
30 @NSManaged dynamic var locked_equip: NSNumber?
31 @NSManaged dynamic var lucky_0: Int
32 @NSManaged dynamic var lucky_1: Int
33 @NSManaged dynamic var lv: Int
34 @NSManaged dynamic var maxhp: Int
35 @NSManaged dynamic var ndock_time: NSNumber?
36 @NSManaged dynamic var nowhp: Int
37 @NSManaged dynamic var onslot_0: Int
38 @NSManaged dynamic var onslot_1: Int
39 @NSManaged dynamic var onslot_2: Int
40 @NSManaged dynamic var onslot_3: Int
41 @NSManaged dynamic var onslot_4: Int
42 @NSManaged dynamic var raisou_0: Int
43 @NSManaged dynamic var raisou_1: Int
44 @NSManaged dynamic var sakuteki_0: Int
45 @NSManaged dynamic var sakuteki_1: NSNumber?
46 @NSManaged dynamic var sally_area: NSNumber?
47 @NSManaged dynamic var ship_id: Int
48 @NSManaged dynamic var slot_0: Int
49 @NSManaged dynamic var slot_1: Int
50 @NSManaged dynamic var slot_2: Int
51 @NSManaged dynamic var slot_3: Int
52 @NSManaged dynamic var slot_4: Int
53 @NSManaged dynamic var slot_ex: Int
54 @NSManaged dynamic var sortno: NSNumber?
55 @NSManaged dynamic var soukou_0: Int
56 @NSManaged dynamic var soukou_1: Int
57 @NSManaged dynamic var srate: NSNumber?
58 @NSManaged dynamic var taiku_0: Int
59 @NSManaged dynamic var taiku_1: Int
60 @NSManaged dynamic var taisen_0: Int
61 @NSManaged dynamic var taisen_1: NSNumber?
62 @NSManaged dynamic var equippedItem: NSOrderedSet
63 @NSManaged dynamic var master_ship: MasterShip
64 @NSManaged dynamic var extraItem: SlotItem?
66 // swiftlint:eable variable_name
68 fileprivate let shortSTypeNames: [String] = {
70 guard let url = Bundle.main.url(forResource: "STypeShortName", withExtension: "plist"),
71 let array = NSArray(contentsOf: url) as? [String]
73 print("Can not load STypeShortName.plist.")
80 fileprivate let levelUpExps: [Int] = {
82 guard let url = Bundle.main.url(forResource: "LevelUpExp", withExtension: "plist"),
83 let array = NSArray(contentsOf: url) as? [Int]
85 print("Can not load LevelUpExp.plist.")
94 class func keyPathsForValuesAffectingName() -> Set<String> {
98 dynamic var name: String { return master_ship.name }
100 class func keyPathsForValuesAffectingShortTypeName() -> Set<String> {
104 dynamic var shortTypeName: String? {
106 let index = master_ship.stype.id - 1
108 guard case 0..<shortSTypeNames.count = index
111 return shortSTypeNames[index]
114 class func keyPathsForValuesAffectingNext() -> Set<String> {
118 dynamic var next: NSNumber? {
120 guard case 0..<levelUpExps.count = lv
123 if lv == 99 { return nil }
125 let nextExp = levelUpExps[lv]
127 if lv < 99 { return (nextExp - exp) as NSNumber }
129 return (1_000_000 + nextExp - exp) as NSNumber
132 class func keyPathsForValuesAffectingStatus() -> Set<String> {
134 return ["nowhp", "maxph"]
136 dynamic var status: Int {
138 let stat = Double(nowhp) / Double(maxhp)
140 if stat <= 0.25 { return 3 }
141 if stat <= 0.5 { return 2 }
142 if stat <= 0.75 { return 1 }
147 class func keyPathsForValuesAffectingStatusColor() -> Set<String> {
151 dynamic var statusColor: NSColor {
154 case 0: return NSColor.controlTextColor
155 case 1: return NSColor.yellow
156 case 2: return NSColor.orange
157 case 3: return NSColor.red
158 default: fatalError("status is unknown value")
162 // class func keyPathsForValuesAffectingConditionColor() -> Set<String> {
165 dynamic var conditionColor: NSColor { return NSColor.controlTextColor }
167 class func keyPathsForValuesAffectingPlanColor() -> Set<String> {
169 return ["sally_area"]
171 dynamic var planColor: NSColor {
173 if !UserDefaults.standard[.showsPlanColor] { return NSColor.controlTextColor }
175 guard let sally = sally_area
176 else { return NSColor.controlTextColor }
179 case 1: return UserDefaults.standard[.plan01Color]
180 case 2: return UserDefaults.standard[.plan02Color]
181 case 3: return UserDefaults.standard[.plan03Color]
182 case 4: return UserDefaults.standard[.plan04Color]
183 case 5: return UserDefaults.standard[.plan05Color]
184 case 6: return UserDefaults.standard[.plan06Color]
185 default: return NSColor.controlTextColor
193 dynamic var maxBull: Int { return master_ship.bull_max }
194 dynamic var maxFuel: Int { return master_ship.fuel_max }
196 class func keyPathsForValuesAffectingIsMaxKaryoku() -> Set<String> {
198 return ["karyoku_1", "kyouka_0"]
200 dynamic var isMaxKaryoku: Bool {
202 let initial = master_ship.houg_0
204 let growth = kyouka_0
206 return initial + growth >= max
209 class func keyPathsForValuesAffectingIsMaxRaisou() -> Set<String> {
211 return ["raisou_1", "kyouka_1"]
213 dynamic var isMaxRaisou: Bool {
215 let initial = master_ship.raig_0
217 let growth = kyouka_1
219 return initial + growth >= max
222 class func keyPathsForValuesAffectingIsMaxTaiku() -> Set<String> {
224 return ["taiku_1", "kyouka_2"]
226 dynamic var isMaxTaiku: Bool {
228 let initial = master_ship.tyku_0
230 let growth = kyouka_2
232 return initial + growth >= max
235 class func keyPathsForValuesAffectingIsMaxSoukou() -> Set<String> {
237 return ["soukou_1", "kyouka_3"]
239 dynamic var isMaxSoukou: Bool {
241 let initial = master_ship.souk_0
243 let growth = kyouka_3
245 return initial + growth >= max
248 class func keyPathsForValuesAffectingIsMaxLucky() -> Set<String> {
250 return ["lucky_1", "kyouka_4"]
252 dynamic var isMaxLucky: Bool {
254 let initial = master_ship.luck_0
256 let growth = kyouka_4
258 return initial + growth >= max
261 class func keyPathsForValuesAffectingUpgradeLevel() -> Set<String> {
265 dynamic var upgradeLevel: Int { return master_ship.afterlv }
267 class func keyPathsForValuesAffectingUpgradeExp() -> Set<String> {
271 dynamic var upgradeExp: NSNumber? {
273 let upgradeLv = upgradeLevel
275 if upgradeLv <= 0 { return nil }
276 if levelUpExps.count < upgradeLv { return nil }
278 let upExp = levelUpExps[upgradeLv - 1] - exp
280 return upExp < 0 ? 0 : upExp as NSNumber
283 dynamic var guardEscaped: Bool {
285 let store = TemporaryDataStore.default
287 guard let _ = store.ensuredGuardEscaped(byShipId: id)
288 else { return false }
293 class func keyPathsForValuesAffectingSteelRequiredInRepair() -> Set<String> {
297 dynamic var steelRequiredInRepair: Int {
299 return Int(Double(maxFuel) * 0.06 * Double(maxhp - nowhp))
302 class func keyPathsForValuesAffectingFuelRequiredInRepair() -> Set<String> {
306 dynamic var fuelRequiredInRepair: Int {
308 return Int(Double(maxFuel) * 0.032 * Double(maxhp - nowhp))
312 fileprivate let allPlaneTypes: [Int] = [6, 7, 8, 9, 10, 11, 25, 26, 41, 45, 56, 57, 58, 59]
316 func setItem(_ id: Int, for slot: Int) {
324 default: fatalError("Ship: setItem out of bounds.")
328 func slotItemId(_ index: Int) -> Int {
331 case 0: return slot_0
332 case 1: return slot_1
333 case 2: return slot_2
334 case 3: return slot_3
335 case 4: return slot_4
340 func slotItemCount(_ index: Int) -> Int {
343 case 0: return onslot_0
344 case 1: return onslot_1
345 case 2: return onslot_2
346 case 3: return onslot_3
347 case 4: return onslot_4
352 private func slotItemMax(_ index: Int) -> Int {
355 case 0: return master_ship.maxeq_0
356 case 1: return master_ship.maxeq_1
357 case 2: return master_ship.maxeq_2
358 case 3: return master_ship.maxeq_3
359 case 4: return master_ship.maxeq_4
364 private func slotItem(_ index: Int) -> SlotItem? {
366 return ServerDataStore.default.slotItem(by: slotItemId(index))
369 dynamic var totalEquipment: Int {
371 return (0...4).map(slotItemMax).reduce(0, +)
374 class func keyPathsForValuesAffectingSeiku() -> Set<String> {
376 return ["slot_0", "slot_1", "slot_2", "slot_3", "slot_4",
377 "onslot_0", "onslot_1", "onslot_2", "onslot_3", "onslot_4"]
379 dynamic var seiku: Int {
381 return SeikuCalclator(ship: self).seiku
384 class func keyPathsForValuesAffectingTotalSeiku() -> Set<String> {
388 dynamic var totalSeiku: Int {
390 return SeikuCalclator(ship: self).totalSeiku
393 var totalDrums: Int {
395 return (0...4).flatMap(slotItem).filter { $0.slotitem_id == 75 }.count
398 // MARK: - Plane count strings
399 private enum PlaneState {
403 case equiped(Int, Int)
406 private func planState(_ index: Int) -> PlaneState {
408 let itemId = slotItemId(index)
409 let maxCount = slotItemMax(index)
411 if maxCount == 0 { return .cannotEquip }
412 if itemId == -1 { return .notEquip(maxCount) }
414 if let item = slotItem(index),
415 allPlaneTypes.contains(item.master_slotItem.type_2) {
417 return .equiped(slotItemCount(index), maxCount)
420 return .notEquip(maxCount)
423 private func planeString(_ index: Int) -> String? {
425 switch planState(index) {
428 case .notEquip(let max):
430 case .equiped(let count, let max):
431 return "\(count)/\(max)"
435 class func keyPathsForValuesAffectingPlaneString0() -> Set<String> {
437 return ["onslot_0", "master_ship.maxeq_0", "equippedItem"]
439 dynamic var planeString0: String? { return planeString(0) }
441 class func keyPathsForValuesAffectingPlaneString1() -> Set<String> {
443 return ["onslot_1", "master_ship.maxeq_1", "equippedItem"]
445 dynamic var planeString1: String? { return planeString(1) }
447 class func keyPathsForValuesAffectingPlaneString2() -> Set<String> {
449 return ["onslot_2", "master_ship.maxeq_2", "equippedItem"]
451 dynamic var planeString2: String? { return planeString(2) }
453 class func keyPathsForValuesAffectingPlaneString3() -> Set<String> {
455 return ["onslot_3", "master_ship.maxeq_3", "equippedItem"]
457 dynamic var planeString3: String? { return planeString(3) }
459 class func keyPathsForValuesAffectingPlaneString4() -> Set<String> {
461 return ["onslot_4", "master_ship.maxeq_4", "equippedItem"]
463 dynamic var planeString4: String? { return planeString(4) }
465 // MARK: - Plane count string color
466 private func planeStringColor(_ index: Int) -> NSColor {
467 switch planState(index) {
468 case .cannotEquip: return NSColor.controlTextColor
469 case .notEquip: return NSColor.disabledControlTextColor
470 case .equiped: return NSColor.controlTextColor
474 class func keyPathsForValuesAffectingPlaneString0Color() -> Set<String> {
476 return ["onslot_0", "master_ship.maxeq_0", "equippedItem"]
478 dynamic var planeString0Color: NSColor { return planeStringColor(0) }
480 class func keyPathsForValuesAffectingPlaneString1Color() -> Set<String> {
482 return ["onslot_1", "master_ship.maxeq_1", "equippedItem"]
484 dynamic var planeString1Color: NSColor { return planeStringColor(1) }
486 class func keyPathsForValuesAffectingPlaneString2Color() -> Set<String> {
488 return ["onslot_2", "master_ship.maxeq_2", "equippedItem"]
490 dynamic var planeString2Color: NSColor { return planeStringColor(2) }
492 class func keyPathsForValuesAffectingPlaneString3Color() -> Set<String> {
494 return ["onslot_3", "master_ship.maxeq_3", "equippedItem"]
496 dynamic var planeString3Color: NSColor { return planeStringColor(3) }
498 class func keyPathsForValuesAffectingPlaneString4Color() -> Set<String> {
500 return ["onslot_4", "master_ship.maxeq_4", "equippedItem"]
502 dynamic var planeString4Color: NSColor { return planeStringColor(4) }