OSDN Git Service

NSManagedObjectIDを明に使用しないように変更
[kcd/KCD.git] / KCD / ShipMapper.swift
1 //
2 //  ShipMapper.swift
3 //  KCD
4 //
5 //  Created by Hori,Masaki on 2017/02/23.
6 //  Copyright © 2017年 Hori,Masaki. All rights reserved.
7 //
8
9 import Cocoa
10 import SwiftyJSON
11
12 final class ShipMapper: JSONMapper {
13     
14     private static let ignoreKeys = ["api_gomes", "api_gomes2", "api_broken", "api_powup",
15                                      "api_voicef", "api_afterlv", "api_aftershipid", "api_backs",
16                                      "api_slotnum", "api_stype", "api_name", "api_yomi",
17                                      "api_raig", "api_luck", "api_saku", "api_raim", "api_baku",
18                                      "api_taik", "api_houg", "api_houm", "api_tyku",
19                                      "api_ndock_item", "api_star",
20                                      "api_ndock_time_str", "api_member_id",
21                                      "api_fuel_max", "api_bull_max"]
22     
23     let apiResponse: APIResponse
24     let configuration: MappingConfiguration<Ship>
25     
26     required init(_ apiResponse: APIResponse) {
27         
28         self.apiResponse = apiResponse
29         self.configuration = MappingConfiguration(entity: Ship.entity,
30                                                   dataKeys: ShipMapper.dataKeys(apiResponse),
31                                                   editorStore: ServerDataStore.oneTimeEditor(),
32                                                   ignoreKeys: ShipMapper.ignoreKeys)
33     }
34     
35     // slotDepriveの時に2種類のデータが来るため
36     init(forSlotDepriveUnset apiResponse: APIResponse) {
37         
38         self.apiResponse = apiResponse
39         self.configuration = MappingConfiguration(entity: Ship.entity,
40                                                   dataKeys: ["api_data", "api_ship_data", "api_unset_ship"],
41                                                   editorStore: ServerDataStore.oneTimeEditor(),
42                                                   ignoreKeys: ShipMapper.ignoreKeys)
43     }
44     
45     private class func dataKeys(_ apiResponse: APIResponse) -> [String] {
46         
47         switch apiResponse.api.endpoint {
48             
49         case .port, .getShip, .powerup: return ["api_data", "api_ship"]
50             
51         case .ship3, .shipDeck: return ["api_data", "api_ship_data"]
52             
53         case .slotDeprive: return ["api_data", "api_ship_data", "api_set_ship"]
54             
55         case .ship, .ship2: return ["api_data"]
56             
57         default: return Logger.shared.log("Missing API: \(apiResponse.api)", value: ["api_data"])
58         }
59     }
60     
61     private var registerIds: [Int] = []
62     private lazy var masterShips: [MasterShip] = {
63         
64         guard let store = configuration.editorStore as? ServerDataStore else { return [] }
65         
66         return store.sortedMasterShipsById()
67         
68     }()
69     private lazy var slotItems: [SlotItem] = {
70         
71         guard let store = configuration.editorStore as? ServerDataStore else { return [] }
72         
73         return store.sortedSlotItemsById()
74     }()
75     
76     private var needsDeleteUnregisteredShip: Bool {
77         
78         switch apiResponse.api.endpoint {
79         case .ship3, .getShip, .shipDeck, .powerup, .slotDeprive:
80             return false
81             
82         case .ship2:
83             // 特殊任務のクリア時にship2がapi_shipid付きでリクエストされ、その艦娘のデータしかない時があるため
84             return !apiResponse.parameter["api_shipid"].valid
85             
86         default:
87             return true
88         }
89     }
90     
91     private var store: ServerDataStore? {
92         
93         return configuration.editorStore as? ServerDataStore
94     }
95     
96     func beginRegister(_ ship: Ship) {
97         
98         ship.sally_area = nil
99     }
100     
101     func handleExtraValue(_ value: JSON, forKey key: String, to ship: Ship) -> Bool {
102         
103         // 取得後破棄した装備のデータを削除するため保有IDを保存
104         if key == "api_id" {
105             
106             guard let id = value.int else { return false }
107             
108             registerIds.append(id)
109             
110             return false
111         }
112         
113         if key == "api_ship_id" {
114             
115             guard let masterId = value.int else { return false }
116             
117             if ship.ship_id == masterId { return true }
118             
119             setMaster(masterId, to: ship)
120             
121             return true
122         }
123         
124         if key == "api_exp" {
125             
126             guard let exp = value[0].int else { return false }
127             
128             if ship.exp == exp { return true }
129             
130             ship.exp = exp
131             
132             return true
133         }
134         
135         if key == "api_slot" {
136             
137             setSlot(value, to: ship)
138             
139             return false
140         }
141         
142         if key == "api_slot_ex" {
143             
144             guard let ex = value.int else {
145                 
146                 ship.extraItem = nil
147                 return false
148             }
149             
150             if ship.slot_ex == ex { return true }
151             
152             setExtraSlot(ex, to: ship)
153             
154             ship.slot_ex = ex
155             
156             return true
157         }
158         
159         return false
160     }
161     
162     func finishOperating() {
163         
164         if !needsDeleteUnregisteredShip { return }
165         
166         store?.ships(exclude: registerIds).forEach { store?.delete($0) }
167     }
168     
169     private func setMaster(_ masterId: Int, to ship: Ship) {
170         
171         guard let mShip = masterShips.binarySearch(comparator: { $0.id ==? masterId }),
172             let masterShip = store?.exchange(mShip) else {
173                 
174                 return Logger.shared.log("Can not convert to current moc object masterShip")
175         }
176         
177         ship.master_ship = masterShip
178         ship.ship_id = masterId
179     }
180     
181     private func setSlot(_ slotItems: JSON, to ship: Ship) {
182         
183         guard let convertedSlotItems = slotItems.arrayObject as? [Int] else { return }
184         guard let store = store else { return }
185         
186         let newItems: [SlotItem] = convertedSlotItems
187             .filter { $0 != 0 && $0 != -1 }
188             .flatMap { item in
189                 
190                 guard let found = self.slotItems.binarySearch(comparator: { $0.id ==? item }),
191                     let slotItem = store.exchange(found) else {
192                         
193                         let maxV = convertedSlotItems.last
194                         if maxV != nil, maxV! < item {
195                             
196                             Debug.print("item is maybe unregistered, so it is new ship's equipment.")
197                             return nil
198                         }
199                         Logger.shared.log("Can not convert to current moc object slotItem")
200                         return nil
201                 }
202                 
203                 return slotItem
204         }
205         
206         ship.equippedItem = NSOrderedSet(array: newItems)
207     }
208     
209     private func setExtraSlot(_ exSlotItem: Int, to ship: Ship) {
210         
211         guard exSlotItem != -1, exSlotItem != 0 else {
212             ship.extraItem = nil
213             return
214         }
215         guard let found = slotItems.binarySearch(comparator: { $0.id ==? exSlotItem }),
216             let ex = store?.exchange(found) else {
217                 
218                 return Logger.shared.log("Can not convert to current moc object")
219         }
220         
221         ship.extraItem = ex
222     }
223 }