OSDN Git Service

c03e83cc9972c7dce7eabe9ee6794753b0ae9f30
[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.self,
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.self,
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:
58             
59             Logger.shared.log("Missing API: \(apiResponse.api)")
60             
61             return ["api_data"]
62         }
63     }
64     
65     private var registerIds: [Int] = []
66     private lazy var masterShips: [MasterShip] = {
67         
68         guard let store = configuration.editorStore as? ServerDataStore else {
69             
70             return []
71         }
72         
73         return store.sortedMasterShipsById()
74         
75     }()
76     private lazy var slotItems: [SlotItem] = {
77         
78         guard let store = configuration.editorStore as? ServerDataStore else {
79             
80             return []
81         }
82         
83         return store.sortedSlotItemsById()
84     }()
85     
86     private var needsDeleteUnregisteredShip: Bool {
87         
88         switch apiResponse.api.endpoint {
89             
90         case .ship3, .getShip, .shipDeck, .powerup, .slotDeprive:
91             
92             return false
93             
94         case .ship2:
95             // 特殊任務のクリア時にship2がapi_shipid付きでリクエストされ、その艦娘のデータしかない時があるため
96             
97             return !apiResponse.parameter["api_shipid"].valid
98             
99         default:
100             
101             return true
102             
103         }
104     }
105     
106     private var store: ServerDataStore? {
107         
108         return configuration.editorStore as? ServerDataStore
109     }
110     
111     func beginRegister(_ ship: Ship) {
112         
113         ship.sally_area = nil
114     }
115     
116     func handleExtraValue(_ value: JSON, forKey key: String, to ship: Ship) -> Bool {
117         
118         // 取得後破棄した装備のデータを削除するため保有IDを保存
119         if key == "api_id" {
120             
121             guard let id = value.int else {
122                 
123                 return false
124             }
125             
126             registerIds.append(id)
127             
128             return false
129         }
130         
131         if key == "api_ship_id" {
132             
133             guard let masterId = value.int else {
134                 
135                 return false
136             }
137             
138             if ship.ship_id == masterId {
139                 
140                 return true
141             }
142             
143             setMaster(masterId, to: ship)
144             
145             return true
146         }
147         
148         if key == "api_exp" {
149             
150             guard let exp = value[0].int else {
151                 
152                 return false
153             }
154             
155             if ship.exp == exp {
156                 
157                 return true
158             }
159             
160             ship.exp = exp
161             
162             return true
163         }
164         
165         if key == "api_slot" {
166             
167             setSlot(value, to: ship)
168             
169             return false
170         }
171         
172         if key == "api_slot_ex" {
173             
174             guard let ex = value.int else {
175                 
176                 ship.extraItem = nil
177                 
178                 return false
179             }
180             
181             if ship.slot_ex == ex {
182                 
183                 return true
184             }
185             
186             setExtraSlot(ex, to: ship)
187             
188             ship.slot_ex = ex
189             
190             return true
191         }
192         
193         return false
194     }
195     
196     func finishOperating() {
197         
198         if !needsDeleteUnregisteredShip {
199             
200             return
201         }
202         
203         store?.ships(exclude: registerIds).forEach { store?.delete($0) }
204     }
205     
206     private func setMaster(_ masterId: Int, to ship: Ship) {
207         
208         guard let mShip = masterShips.binarySearch(comparator: { $0.id ==? masterId }),
209             let masterShip = store?.exchange(mShip) else {
210                 
211                 Logger.shared.log("Can not convert to current moc object masterShip")
212                 
213                 return
214         }
215         
216         ship.master_ship = masterShip
217         ship.ship_id = masterId
218     }
219     
220     private func setSlot(_ slotItems: JSON, to ship: Ship) {
221         
222         guard let convertedSlotItems = slotItems.arrayObject as? [Int] else {
223             
224             return
225         }
226         guard let store = store else {
227             
228             return
229         }
230         
231         let newItems: [SlotItem] = convertedSlotItems
232             .filter { $0 != 0 && $0 != -1 }
233             .compactMap { item in
234                 
235                 guard let found = self.slotItems.binarySearch(comparator: { $0.id ==? item }),
236                     let slotItem = store.exchange(found) else {
237                         
238                         let maxV = convertedSlotItems.last
239                         if maxV != nil, maxV! < item {
240                             
241                             Debug.print("item is maybe unregistered, so it is new ship's equipment.")
242                             
243                             return nil
244                         }
245                         Logger.shared.log("Can not convert to current moc object slotItem")
246                         
247                         return nil
248                 }
249                 
250                 return slotItem
251         }
252         
253         ship.equippedItem = NSOrderedSet(array: newItems)
254     }
255     
256     private func setExtraSlot(_ exSlotItem: Int, to ship: Ship) {
257         
258         guard exSlotItem != -1, exSlotItem != 0 else {
259             
260             ship.extraItem = nil
261             
262             return
263         }
264         guard let found = slotItems.binarySearch(comparator: { $0.id ==? exSlotItem }),
265             let ex = store?.exchange(found) else {
266                 
267                 Logger.shared.log("Can not convert to current moc object")
268                 
269                 return
270         }
271         
272         ship.extraItem = ex
273     }
274 }