OSDN Git Service

補正された制空値が更新んされない問題に対応
[kcd/KCD.git] / KCD / Ship.swift
1 //
2 //  [
3 //  KCD
4 //
5 //  Created by Hori,Masaki on 2017/01/29.
6 //  Copyright © 2017年 Hori,Masaki. All rights reserved.
7 //
8
9 import Cocoa
10
11 // swiftlint:disable variable_name
12 final class Ship: KCManagedObject {
13     
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?
65 }
66 // swiftlint:eable variable_name
67
68 fileprivate let shortSTypeNames: [String] = {
69     
70     guard let url = Bundle.main.url(forResource: "STypeShortName", withExtension: "plist"),
71         let array = NSArray(contentsOf: url) as? [String]
72         else {
73             print("Can not load STypeShortName.plist.")
74             return []
75     }
76     
77     return array
78 }()
79
80 fileprivate let levelUpExps: [Int] = {
81     
82     guard let url = Bundle.main.url(forResource: "LevelUpExp", withExtension: "plist"),
83         let array = NSArray(contentsOf: url) as? [Int]
84         else {
85             print("Can not load LevelUpExp.plist.")
86             return []
87     }
88     
89     return array
90 }()
91
92 extension Ship {
93     
94     class func keyPathsForValuesAffectingName() -> Set<String> {
95         
96         return ["ship_id"]
97     }
98     dynamic var name: String { return master_ship.name }
99     
100     class func keyPathsForValuesAffectingShortTypeName() -> Set<String> {
101         
102         return ["ship_id"]
103     }
104     dynamic var shortTypeName: String? {
105         
106         let index = master_ship.stype.id - 1
107         
108         guard case 0..<shortSTypeNames.count = index
109             else { return nil }
110         
111         return shortSTypeNames[index]
112     }
113     
114     class func keyPathsForValuesAffectingNext() -> Set<String> {
115         
116         return ["exp"]
117     }
118     dynamic var next: NSNumber? {
119         
120         guard case 0..<levelUpExps.count = lv
121             else { return nil }
122         
123         if lv == 99 { return nil }
124         
125         let nextExp = levelUpExps[lv]
126         
127         if lv < 99 { return (nextExp - exp) as NSNumber }
128         
129         return (1_000_000 + nextExp - exp) as NSNumber
130     }
131     
132     class func keyPathsForValuesAffectingStatus() -> Set<String> {
133         
134         return ["nowhp", "maxph"]
135     }
136     dynamic var status: Int {
137         
138         let stat = Double(nowhp) / Double(maxhp)
139         
140         if stat <= 0.25 { return 3 }
141         if stat <= 0.5 { return 2 }
142         if stat <= 0.75 { return 1 }
143         
144         return 0
145     }
146     
147     class func keyPathsForValuesAffectingStatusColor() -> Set<String> {
148         
149         return ["status"]
150     }
151     dynamic var statusColor: NSColor {
152         
153         switch status {
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")
159         }
160     }
161     
162 //    class func keyPathsForValuesAffectingConditionColor() -> Set<String> {
163 //        return ["cond"]
164 //    }
165     dynamic var conditionColor: NSColor { return NSColor.controlTextColor }
166     
167     class func keyPathsForValuesAffectingPlanColor() -> Set<String> {
168         
169         return ["sally_area"]
170     }
171     dynamic var planColor: NSColor {
172         
173         if !UserDefaults.standard[.showsPlanColor] { return NSColor.controlTextColor }
174         
175         guard let sally = sally_area
176             else { return NSColor.controlTextColor }
177         
178         switch sally {
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
186         }
187     }
188 }
189
190
191 extension Ship {
192     
193     dynamic var maxBull: Int { return master_ship.bull_max }
194     dynamic var maxFuel: Int { return master_ship.fuel_max }
195     
196     class func keyPathsForValuesAffectingIsMaxKaryoku() -> Set<String> {
197         
198         return ["karyoku_1", "kyouka_0"]
199     }
200     dynamic var isMaxKaryoku: Bool {
201         
202         let initial = master_ship.houg_0
203         let max = karyoku_1
204         let growth = kyouka_0
205         
206         return initial + growth >= max
207     }
208     
209     class func keyPathsForValuesAffectingIsMaxRaisou() -> Set<String> {
210         
211         return ["raisou_1", "kyouka_1"]
212     }
213     dynamic var isMaxRaisou: Bool {
214         
215         let initial = master_ship.raig_0
216         let max = raisou_1
217         let growth = kyouka_1
218         
219         return initial + growth >= max
220     }
221     
222     class func keyPathsForValuesAffectingIsMaxTaiku() -> Set<String> {
223         
224         return ["taiku_1", "kyouka_2"]
225     }
226     dynamic var isMaxTaiku: Bool {
227         
228         let initial = master_ship.tyku_0
229         let max = taiku_1
230         let growth = kyouka_2
231         
232         return initial + growth >= max
233     }
234     
235     class func keyPathsForValuesAffectingIsMaxSoukou() -> Set<String> {
236         
237         return ["soukou_1", "kyouka_3"]
238     }
239     dynamic var isMaxSoukou: Bool {
240         
241         let initial = master_ship.souk_0
242         let max = soukou_1
243         let growth = kyouka_3
244         
245         return initial + growth >= max
246     }
247     
248     class func keyPathsForValuesAffectingIsMaxLucky() -> Set<String> {
249         
250         return ["lucky_1", "kyouka_4"]
251     }
252     dynamic var isMaxLucky: Bool {
253         
254         let initial = master_ship.luck_0
255         let max = lucky_1
256         let growth = kyouka_4
257         
258         return initial + growth >= max
259     }
260     
261     class func keyPathsForValuesAffectingUpgradeLevel() -> Set<String> {
262         
263         return ["ship_id"]
264     }
265     dynamic var upgradeLevel: Int { return master_ship.afterlv }
266     
267     class func keyPathsForValuesAffectingUpgradeExp() -> Set<String> {
268         
269         return ["exp"]
270     }
271     dynamic var upgradeExp: NSNumber? {
272         
273         let upgradeLv = upgradeLevel
274         
275         if upgradeLv <= 0 { return nil }
276         if levelUpExps.count < upgradeLv { return nil }
277         
278         let upExp = levelUpExps[upgradeLv - 1] - exp
279         
280         return upExp < 0 ? 0 : upExp as NSNumber
281     }
282     
283     dynamic var guardEscaped: Bool {
284         
285         let store = TemporaryDataStore.default
286         
287         guard let _ = store.ensuredGuardEscaped(byShipId: id)
288             else { return false }
289         
290         return true
291     }
292     
293     class func keyPathsForValuesAffectingSteelRequiredInRepair() -> Set<String> {
294         
295         return ["nowhp"]
296     }
297     dynamic var steelRequiredInRepair: Int {
298         
299         return Int(Double(maxFuel) * 0.06 * Double(maxhp - nowhp))
300     }
301     
302     class func keyPathsForValuesAffectingFuelRequiredInRepair() -> Set<String> {
303         
304         return ["nowhp"]
305     }
306     dynamic var fuelRequiredInRepair: Int {
307         
308         return Int(Double(maxFuel) * 0.032 * Double(maxhp - nowhp))
309     }
310 }
311
312 fileprivate let allPlaneTypes: [Int] = [6, 7, 8, 9, 10, 11, 25, 26, 41, 45, 56, 57, 58, 59]
313
314 extension Ship {
315     
316     func setItem(_ id: Int, for slot: Int) {
317         
318         switch slot {
319         case 0: slot_0 = id
320         case 1: slot_1 = id
321         case 2: slot_2 = id
322         case 3: slot_3 = id
323         case 4: slot_4 = id
324         default: fatalError("Ship: setItem out of bounds.")
325         }
326     }
327     
328     func slotItemId(_ index: Int) -> Int {
329         
330         switch index {
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
336         default: return 0
337         }
338     }
339     
340     func slotItemCount(_ index: Int) -> Int {
341         
342         switch index {
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
348         default: return 0
349         }
350     }
351     
352     private func slotItemMax(_ index: Int) -> Int {
353         
354         switch index {
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
360         default: return 0
361         }
362     }
363     
364     private func slotItem(_ index: Int) -> SlotItem? {
365         
366         return ServerDataStore.default.slotItem(by: slotItemId(index))
367     }
368     
369     dynamic var totalEquipment: Int {
370         
371         return (0...4).map(slotItemMax).reduce(0, +)
372     }
373     
374     class func keyPathsForValuesAffectingSeiku() -> Set<String> {
375         
376         return ["slot_0", "slot_1", "slot_2", "slot_3", "slot_4",
377                    "onslot_0", "onslot_1", "onslot_2", "onslot_3", "onslot_4"]
378     }
379     dynamic var seiku: Int {
380         
381         return SeikuCalclator(ship: self).seiku
382     }
383     
384     class func keyPathsForValuesAffectingTotalSeiku() -> Set<String> {
385         
386         return ["seiku"]
387     }
388     dynamic var totalSeiku: Int {
389         
390         return SeikuCalclator(ship: self).totalSeiku
391     }
392     
393     var totalDrums: Int {
394         
395         return (0...4).flatMap(slotItem).filter { $0.slotitem_id == 75 }.count
396     }
397     
398     // MARK: - Plane count strings
399     private enum PlaneState {
400         
401         case cannotEquip
402         case notEquip(Int)
403         case equiped(Int, Int)
404     }
405     
406     private func planState(_ index: Int) -> PlaneState {
407         
408         let itemId = slotItemId(index)
409         let maxCount = slotItemMax(index)
410         
411         if maxCount == 0 { return .cannotEquip }
412         if itemId == -1 { return .notEquip(maxCount) }
413         
414         if let item = slotItem(index),
415             allPlaneTypes.contains(item.master_slotItem.type_2) {
416             
417             return .equiped(slotItemCount(index), maxCount)
418         }
419         
420         return .notEquip(maxCount)
421     }
422     
423     private func planeString(_ index: Int) -> String? {
424         
425         switch planState(index) {
426         case .cannotEquip:
427             return nil
428         case .notEquip(let max):
429             return "\(max)"
430         case .equiped(let count, let max):
431             return "\(count)/\(max)"
432         }
433     }
434     
435     class func keyPathsForValuesAffectingPlaneString0() -> Set<String> {
436         
437         return ["onslot_0", "master_ship.maxeq_0", "equippedItem"]
438     }
439     dynamic var planeString0: String? { return planeString(0) }
440     
441     class func keyPathsForValuesAffectingPlaneString1() -> Set<String> {
442         
443         return ["onslot_1", "master_ship.maxeq_1", "equippedItem"]
444     }
445     dynamic var planeString1: String? { return planeString(1) }
446     
447     class func keyPathsForValuesAffectingPlaneString2() -> Set<String> {
448         
449         return ["onslot_2", "master_ship.maxeq_2", "equippedItem"]
450     }
451     dynamic var planeString2: String? { return planeString(2) }
452     
453     class func keyPathsForValuesAffectingPlaneString3() -> Set<String> {
454         
455         return ["onslot_3", "master_ship.maxeq_3", "equippedItem"]
456     }
457     dynamic var planeString3: String? { return planeString(3) }
458     
459     class func keyPathsForValuesAffectingPlaneString4() -> Set<String> {
460         
461         return ["onslot_4", "master_ship.maxeq_4", "equippedItem"]
462     }
463     dynamic var planeString4: String? { return planeString(4) }
464     
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
471         }
472     }
473     
474     class func keyPathsForValuesAffectingPlaneString0Color() -> Set<String> {
475         
476         return ["onslot_0", "master_ship.maxeq_0", "equippedItem"]
477     }
478     dynamic var planeString0Color: NSColor { return planeStringColor(0) }
479     
480     class func keyPathsForValuesAffectingPlaneString1Color() -> Set<String> {
481         
482         return ["onslot_1", "master_ship.maxeq_1", "equippedItem"]
483     }
484     dynamic var planeString1Color: NSColor { return planeStringColor(1) }
485     
486     class func keyPathsForValuesAffectingPlaneString2Color() -> Set<String> {
487         
488         return ["onslot_2", "master_ship.maxeq_2", "equippedItem"]
489     }
490     dynamic var planeString2Color: NSColor { return planeStringColor(2) }
491     
492     class func keyPathsForValuesAffectingPlaneString3Color() -> Set<String> {
493         
494         return ["onslot_3", "master_ship.maxeq_3", "equippedItem"]
495     }
496     dynamic var planeString3Color: NSColor { return planeStringColor(3) }
497     
498     class func keyPathsForValuesAffectingPlaneString4Color() -> Set<String> {
499         
500         return ["onslot_4", "master_ship.maxeq_4", "equippedItem"]
501     }
502     dynamic var planeString4Color: NSColor { return planeStringColor(4) }
503 }