OSDN Git Service

CoreDataエンティティのクラスをエンティティ名と同じにした
[kcd/KCD.git] / KCD / KCShipObject.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 class Ship: KCManagedObject {
12     @NSManaged dynamic var bull: Int
13     @NSManaged dynamic var cond: Int
14     @NSManaged dynamic var exp: Int
15     @NSManaged dynamic var fleet: NSNumber?
16     @NSManaged dynamic var fuel: Int
17     @NSManaged dynamic var id: Int
18     @NSManaged dynamic var kaihi_0: Int
19     @NSManaged dynamic var kaihi_1: NSNumber?
20     @NSManaged dynamic var karyoku_0: Int
21     @NSManaged dynamic var karyoku_1: Int
22     @NSManaged dynamic var kyouka_0: Int
23     @NSManaged dynamic var kyouka_1: Int
24     @NSManaged dynamic var kyouka_2: Int
25     @NSManaged dynamic var kyouka_3: Int
26     @NSManaged dynamic var kyouka_4: Int
27     @NSManaged dynamic var locked: Int
28     @NSManaged dynamic var locked_equip: NSNumber?
29     @NSManaged dynamic var lucky_0: Int
30     @NSManaged dynamic var lucky_1: Int
31     @NSManaged dynamic var lv: Int
32     @NSManaged dynamic var maxhp: Int
33     @NSManaged dynamic var ndock_time: NSNumber?
34     @NSManaged dynamic var nowhp: Int
35     @NSManaged dynamic var onslot_0: Int
36     @NSManaged dynamic var onslot_1: Int
37     @NSManaged dynamic var onslot_2: Int
38     @NSManaged dynamic var onslot_3: Int
39     @NSManaged dynamic var onslot_4: Int
40     @NSManaged dynamic var raisou_0: Int
41     @NSManaged dynamic var raisou_1: Int
42     @NSManaged dynamic var sakuteki_0: Int
43     @NSManaged dynamic var sakuteki_1: NSNumber?
44     @NSManaged dynamic var sally_area: NSNumber?
45     @NSManaged dynamic var ship_id: Int
46     @NSManaged dynamic var slot_0: Int
47     @NSManaged dynamic var slot_1: Int
48     @NSManaged dynamic var slot_2: Int
49     @NSManaged dynamic var slot_3: Int
50     @NSManaged dynamic var slot_4: Int
51     @NSManaged dynamic var slot_ex: Int
52     @NSManaged dynamic var sortno: NSNumber?
53     @NSManaged dynamic var soukou_0: Int
54     @NSManaged dynamic var soukou_1: Int
55     @NSManaged dynamic var srate: NSNumber?
56     @NSManaged dynamic var taiku_0: Int
57     @NSManaged dynamic var taiku_1: Int
58     @NSManaged dynamic var taisen_0: Int
59     @NSManaged dynamic var taisen_1: NSNumber?
60     @NSManaged dynamic var equippedItem: NSOrderedSet
61     @NSManaged dynamic var master_ship: MasterShip
62     @NSManaged dynamic var extraItem: SlotItem?
63 }
64
65 fileprivate let shortSTypeNames: [String] = {
66     guard let url = Bundle.main.url(forResource: "STypeShortName", withExtension: "plist"),
67         let array = NSArray(contentsOf: url) as? [String]
68         else {
69             print("Can not load STypeShortName.plist.")
70             return []
71     }
72     return array
73 }()
74 fileprivate let levelUpExps: [Int] = {
75     guard let url = Bundle.main.url(forResource: "LevelUpExp", withExtension: "plist"),
76         let array = NSArray(contentsOf: url) as? [Int]
77         else {
78             print("Can not load LevelUpExp.plist.")
79             return []
80     }
81     return array
82 }()
83
84 extension Ship {
85     class func keyPathsForValuesAffectingName() -> Set<String> {
86         return ["ship_id"]
87     }
88     dynamic var name: String { return master_ship.name }
89     
90     class func keyPathsForValuesAffectingShortTypeName() -> Set<String> {
91         return ["ship_id"]
92     }
93     dynamic var shortTypeName: String? {
94         if shortSTypeNames.count < master_ship.stype.id { return nil }
95         return shortSTypeNames[master_ship.stype.id - 1]
96     }
97     
98     class func keyPathsForValuesAffectingNext() -> Set<String> {
99         return ["exp"]
100     }
101     dynamic var next: NSNumber? {
102         if lv >= levelUpExps.count { return nil }
103         if lv == 99 { return nil }
104         let nextExp = levelUpExps[lv]
105         if lv < 99 { return (nextExp - exp) as NSNumber }
106         return (1_000_000 + nextExp - exp) as NSNumber
107     }
108     
109     class func keyPathsForValuesAffectingStatus() -> Set<String> {
110         return ["nowhp", "maxph"]
111     }
112     dynamic var status: Int {
113         let n = Double(nowhp)
114         let m = Double(maxhp)
115         let stat = n / m
116         if stat <= 0.25 { return 3 }
117         if stat <= 0.5 { return 2 }
118         if stat <= 0.75 { return 1 }
119         return 0
120     }
121     
122     class func keyPathsForValuesAffectingStatusColor() -> Set<String> {
123         return ["status"]
124     }
125     dynamic var statusColor: NSColor {
126         switch status {
127         case 0: return NSColor.controlTextColor
128         case 1: return NSColor.yellow
129         case 2: return NSColor.orange
130         case 3: return NSColor.red
131         default: fatalError("status is unknown value")
132         }
133     }
134     
135 //    class func keyPathsForValuesAffectingConditionColor() -> Set<String> {
136 //        return ["cond"]
137 //    }
138     dynamic var conditionColor: NSColor { return NSColor.controlTextColor }
139     
140     class func keyPathsForValuesAffectingPlanColor() -> Set<String> {
141         return ["sally_area"]
142     }
143     dynamic var planColor: NSColor {
144         if !UserDefaults.standard.showsPlanColor { return NSColor.controlTextColor }
145         guard let sally = sally_area else { return NSColor.controlTextColor }
146         
147         switch sally {
148         case 1: return UserDefaults.standard.plan01Color
149         case 2: return UserDefaults.standard.plan02Color
150         case 3: return UserDefaults.standard.plan03Color
151         case 4: return UserDefaults.standard.plan04Color
152         case 5: return UserDefaults.standard.plan05Color
153         case 6: return UserDefaults.standard.plan06Color
154         default: return NSColor.controlTextColor
155         }
156     }
157 }
158
159
160 extension Ship {
161     dynamic var maxBull: Int { return master_ship.bull_max }
162     dynamic var maxFuel: Int { return master_ship.fuel_max }
163     
164     class func keyPathsForValuesAffectingIsMaxKaryoku() -> Set<String> {
165         return ["karyoku_1", "kyouka_0"]
166     }
167     dynamic var isMaxKaryoku: Bool {
168         let initial = master_ship.houg_0
169         let max = karyoku_1
170         let growth = kyouka_0
171         return initial + growth >= max
172     }
173     
174     class func keyPathsForValuesAffectingIsMaxRaisou() -> Set<String> {
175         return ["raisou_1", "kyouka_1"]
176     }
177     dynamic var isMaxRaisou: Bool {
178         let initial = master_ship.raig_0
179         let max = raisou_1
180         let growth = kyouka_1
181         return initial + growth >= max
182     }
183     
184     class func keyPathsForValuesAffectingIsMaxTaiku() -> Set<String> {
185         return ["taiku_1", "kyouka_2"]
186     }
187     dynamic var isMaxTaiku: Bool {
188         let initial = master_ship.tyku_0
189         let max = taiku_1
190         let growth = kyouka_2
191         return initial + growth >= max
192     }
193     
194     class func keyPathsForValuesAffectingIsMaxSoukou() -> Set<String> {
195         return ["soukou_1", "kyouka_3"]
196     }
197     dynamic var isMaxSoukou: Bool {
198         let initial = master_ship.souk_0
199         let max = soukou_1
200         let growth = kyouka_3
201         return initial + growth >= max
202     }
203     
204     class func keyPathsForValuesAffectingIsMaxLucky() -> Set<String> {
205         return ["lucky_1", "kyouka_4"]
206     }
207     dynamic var isMaxLucky: Bool {
208         let initial = master_ship.luck_0
209         let max = lucky_1
210         let growth = kyouka_4
211         return initial + growth >= max
212     }
213     
214     class func keyPathsForValuesAffectingUpgradeLevel() -> Set<String> {
215         return ["ship_id"]
216     }
217     dynamic var upgradeLevel: Int { return master_ship.afterlv }
218     
219     class func keyPathsForValuesAffectingUpgradeExp() -> Set<String> {
220         return ["exp"]
221     }
222     dynamic var upgradeExp: NSNumber? {
223         let upgradeLv = upgradeLevel
224         if upgradeLv <= 0 { return nil }
225         if levelUpExps.count < upgradeLv { return nil }
226         let upExp = levelUpExps[upgradeLv - 1] - exp
227         return upExp < 0 ? 0 : upExp as NSNumber
228     }
229     
230     dynamic var guardEscaped: Bool {
231         let store = TemporaryDataStore.default
232         guard let _ = store.ensuredGuardEscaped(byShipId: id)
233             else { return false }
234         return true
235     }
236     
237     class func keyPathsForValuesAffectingSteelRequiredInRepair() -> Set<String> {
238         return ["nowhp"]
239     }
240     dynamic var steelRequiredInRepair: Int {
241         return Int(Double(maxFuel) * 0.06 * Double(maxhp - nowhp))
242     }
243     
244     class func keyPathsForValuesAffectingFuelRequiredInRepair() -> Set<String> {
245         return ["nowhp"]
246     }
247     dynamic var fuelRequiredInRepair: Int {
248         return Int(Double(maxFuel) * 0.032 * Double(maxhp - nowhp))
249     }
250 }
251
252
253 fileprivate let seikuEffectiveTypes: [Int] = [6, 7, 8, 11, 45]
254
255 fileprivate let fighterTypes: [Int] = [6]
256 fileprivate let bomberTypes: [Int] = [7]
257 fileprivate let attackerTypes: [Int] = [8]
258 fileprivate let floatplaneBomberTypes: [Int] = [11]
259 fileprivate let floatplaneFighterTypes: [Int] = [45]
260 fileprivate let jetFighter: [Int] = [56]
261 fileprivate let jetBomberTypes: [Int] = [57]
262 fileprivate let jetAttackerTypes: [Int] = [58]
263
264 fileprivate let allPlaneTypes: [Int] = [6, 7, 8, 9, 10, 11, 25, 26, 41, 45, 56, 57, 58, 59]
265
266 fileprivate let fighterBonus: [Double] = [0, 0, 2, 5, 9, 14, 14, 22]
267 fileprivate let bomberBonus: [Double] = [0, 0, 0, 0, 0, 0, 0, 0]
268 fileprivate let attackerBonus: [Double] = [0, 0, 0, 0, 0, 0, 0, 0]
269 fileprivate let floatplaneBomberBonus: [Double] = [0, 0, 1, 1, 1, 3, 3, 6]
270 fileprivate let floatplaneFighterBonus: [Double] = [0, 0, 2, 5, 9, 14, 14, 22]
271 fileprivate let jetBomberBonus: [Double] = [0, 0, 0, 0, 0, 0, 0, 0]
272
273 //                            sqrt 0, 1,     2.5,   4,     5.5,   7,     8.5,   10
274 fileprivate let bonus: [Double] = [0, 1.000, 1.581, 2.000, 2.345, 2.645, 2.915, 3.162]
275
276
277 extension Ship {
278     
279     private func slotItemCount(_ index: Int) -> Int {
280         switch index {
281         case 0: return onslot_0
282         case 1: return onslot_1
283         case 2: return onslot_2
284         case 3: return onslot_3
285         case 4: return onslot_4
286         default: return 0
287         }
288     }
289     private func slotItemId(_ index: Int) -> Int {
290         switch index {
291         case 0: return slot_0
292         case 1: return slot_1
293         case 2: return slot_2
294         case 3: return slot_3
295         case 4: return slot_4
296         default: return 0
297         }
298     }
299     private func slotItemMax(_ index: Int) -> Int {
300         switch index {
301         case 0: return master_ship.maxeq_0
302         case 1: return master_ship.maxeq_1
303         case 2: return master_ship.maxeq_2
304         case 3: return master_ship.maxeq_3
305         case 4: return master_ship.maxeq_4
306         default: return 0
307         }
308     }
309     
310     private func slotItem(_ index: Int) -> SlotItem? {
311         return ServerDataStore.default.slotItem(byId: slotItemId(index))
312     }
313     private func typeBonus(_ type: Int) -> [Double]? {
314         switch type {
315         case let t where fighterTypes.contains(t): return fighterBonus
316         case let t where bomberTypes.contains(t): return bomberBonus
317         case let t where attackerTypes.contains(t): return attackerBonus
318         case let t where floatplaneBomberTypes.contains(t): return floatplaneBomberBonus
319         case let t where floatplaneFighterTypes.contains(t): return floatplaneFighterBonus
320         case let t where jetBomberTypes.contains(t): return jetBomberBonus
321         default: return nil
322         }
323     }
324     private func normalSeiku(_ index: Int) -> Double {
325         let itemCount = slotItemCount(index)
326         if itemCount == 0 { return 0 }
327         guard let item = slotItem(index) else { return 0 }
328         let type2 = item.master_slotItem.type_2
329         guard seikuEffectiveTypes.contains(type2) else { return 0 }
330         let taiku = Double(item.master_slotItem.tyku)
331         let lv = Double(item.level)
332         let rate = bomberTypes.contains(type2) ? 0.25 : 0.2
333         return (taiku + lv * rate) * sqrt(Double(itemCount))
334         
335     }
336     private func extraSeiku(_ index: Int) -> Double {
337         let itemCount = slotItemCount(index)
338         if itemCount == 0 { return 0 }
339         guard let item = slotItem(index) else { return 0 }
340         let type2 = item.master_slotItem.type_2
341         guard let typeBonus = typeBonus(type2) else { return 0 }
342         let airLevel = item.alv
343         return typeBonus[airLevel] + bonus[airLevel]
344     }
345     private func seiku(_ index: Int) -> Int {
346         return Int(normalSeiku(index) + extraSeiku(index))
347     }
348     
349     dynamic var totalEquipment: Int {
350         return (0...4).reduce(0) { $0 + slotItemMax($1) }
351     }
352     
353     class func keyPathsForValuesAffectingSeiku() -> Set<String> {
354         return ["slot_0", "slot_1", "slot_2", "slot_3", "slot_4",
355                    "onslot_0", "onslot_1", "onslot_2", "onslot_3", "onslot_4"]
356     }
357     dynamic var seiku: Int {
358         return (0...4).reduce(0) { $0 + Int(normalSeiku($1)) }
359     }
360     
361     class func keyPathsForValuesAffectingExtraSeiku() -> Set<String> {
362         return ["seiku"]
363     }
364     dynamic var extraSeiku: Int {
365         return (0...4).reduce(0) { $0 + Int(extraSeiku($1)) }
366     }
367     
368     class func keyPathsForValuesAffectingTotalSeiku() -> Set<String> {
369         return ["seiku", "extraSeiku"]
370     }
371     dynamic var totalSeiku: Int {
372         return (0...4).reduce(0) { $0 + seiku($1) }
373     }
374     
375     class func keyPathsForValuesAffectingTotalDrums() -> Set<String> {
376         return ["slot_0", "slot_1", "slot_2", "slot_3", "slot_4"]
377     }
378     dynamic var totalDrums: Int {
379         return (0...4).reduce(0) {
380             guard let item = slotItem($1),
381                 item.slotitem_id == 75
382                 else { return $0 }
383             return $0 + 1
384         }
385     }
386     
387     // MARK: - Plane count strings
388     private enum PlaneState {
389         case cannotEquip
390         case notEquip(Int)
391         case equiped(Int, Int)
392     }
393     private func planState(_ index: Int) -> PlaneState {
394         let itemId = slotItemId(index)
395         let maxCount = slotItemMax(index)
396         if maxCount == 0 { return .cannotEquip }
397         if itemId == -1 { return .notEquip(maxCount) }
398         
399         if let item = slotItem(index),
400             allPlaneTypes.contains(item.master_slotItem.type_2)
401         { return .equiped(slotItemCount(index), maxCount) }
402         return .notEquip(maxCount)
403     }
404     private func planeString(_ index: Int) -> String? {
405         switch planState(index) {
406         case .cannotEquip:
407             return nil
408         case .notEquip(let max):
409             return "\(max)"
410         case .equiped(let count, let max):
411             return "\(count)/\(max)"
412         }
413     }
414     
415     class func keyPathsForValuesAffectingPlaneString0() -> Set<String> {
416         return ["onslot_0", "master_ship.maxeq_0", "equippedItem"]
417     }
418     dynamic var planeString0: String? { return planeString(0) }
419     
420     class func keyPathsForValuesAffectingPlaneString1() -> Set<String> {
421         return ["onslot_1", "master_ship.maxeq_1", "equippedItem"]
422     }
423     dynamic var planeString1: String? { return planeString(1) }
424     
425     class func keyPathsForValuesAffectingPlaneString2() -> Set<String> {
426         return ["onslot_2", "master_ship.maxeq_2", "equippedItem"]
427     }
428     dynamic var planeString2: String? { return planeString(2) }
429     
430     class func keyPathsForValuesAffectingPlaneString3() -> Set<String> {
431         return ["onslot_3", "master_ship.maxeq_3", "equippedItem"]
432     }
433     dynamic var planeString3: String? { return planeString(3) }
434     
435     class func keyPathsForValuesAffectingPlaneString4() -> Set<String> {
436         return ["onslot_4", "master_ship.maxeq_4", "equippedItem"]
437     }
438     dynamic var planeString4: String? { return planeString(4) }
439     
440     // MARK: - Plane count string color
441     private func planeStringColor(_ index: Int) -> NSColor {
442         switch planState(index) {
443         case .cannotEquip: return NSColor.controlTextColor
444         case .notEquip: return NSColor.disabledControlTextColor
445         case .equiped: return NSColor.controlTextColor
446         }
447     }
448     
449     class func keyPathsForValuesAffectingPlaneString0Color() -> Set<String> {
450         return ["onslot_0", "master_ship.maxeq_0", "equippedItem"]
451     }
452     dynamic var planeString0Color: NSColor { return planeStringColor(0) }
453     
454     class func keyPathsForValuesAffectingPlaneString1Color() -> Set<String> {
455         return ["onslot_1", "master_ship.maxeq_1", "equippedItem"]
456     }
457     dynamic var planeString1Color: NSColor { return planeStringColor(1) }
458     
459     class func keyPathsForValuesAffectingPlaneString2Color() -> Set<String> {
460         return ["onslot_2", "master_ship.maxeq_2", "equippedItem"]
461     }
462     dynamic var planeString2Color: NSColor { return planeStringColor(2) }
463     
464     class func keyPathsForValuesAffectingPlaneString3Color() -> Set<String> {
465         return ["onslot_3", "master_ship.maxeq_3", "equippedItem"]
466     }
467     dynamic var planeString3Color: NSColor { return planeStringColor(3) }
468     
469     class func keyPathsForValuesAffectingPlaneString4Color() -> Set<String> {
470         return ["onslot_4", "master_ship.maxeq_4", "equippedItem"]
471     }
472     dynamic var planeString4Color: NSColor { return planeStringColor(4) }
473 }