2 // ShipDetailViewController.swift
5 // Created by Hori,Masaki on 2016/12/25.
6 // Copyright © 2016年 Hori,Masaki. All rights reserved.
11 enum ShipDetailViewType {
18 private func nibNameFor(_ type: ShipDetailViewType) -> NSNib.Name {
21 case .full: return ShipDetailViewController.nibName
22 case .medium: return NSNib.Name("MediumShipViewController")
23 case .minimum: return NSNib.Name("MediumShipViewController")
27 private var shipContext: Int = 0
28 private var equippedItem0Context: Int = 0
29 private var equippedItem1Context: Int = 0
30 private var equippedItem2Context: Int = 0
31 private var equippedItem3Context: Int = 0
32 private var equippedItem4Context: Int = 0
34 final class ShipDetailViewController: NSViewController {
36 let type: ShipDetailViewType
37 @objc let managedObjectContext = ServerDataStore.default.context
39 init?(type: ShipDetailViewType) {
43 super.init(nibName: nibNameFor(type), bundle: nil)
47 .addObserver(forName: .DidUpdateGuardEscape, object: nil, queue: nil) { [weak self] _ in
49 guard let `self` = self else { return }
51 self.guardEscaped = self.ship?.guardEscaped ?? false
55 required init?(coder: NSCoder) {
57 fatalError("not implemented")
62 NotificationCenter.default.removeObserver(self)
63 damageView.unbind(NSBindingName(#keyPath(DamageView.damageType)))
64 supply.unbind(NSBindingName(#keyPath(SuppliesView.ship)))
65 [slot00Field, slot01Field, slot02Field, slot03Field]
66 .forEach { $0?.unbind(NSBindingName(#keyPath(SlotItemLevelView.slotItemID))) }
68 shipController.removeObserver(self, forKeyPath: "selection")
70 shipController.removeObserver(self, forKeyPath: "selection.slot_0")
71 shipController.removeObserver(self, forKeyPath: "selection.onslot_0")
72 shipController.removeObserver(self, forKeyPath: "selection.master_ship.maxeq_0")
74 shipController.removeObserver(self, forKeyPath: "selection.slot_1")
75 shipController.removeObserver(self, forKeyPath: "selection.onslot_1")
76 shipController.removeObserver(self, forKeyPath: "selection.master_ship.maxeq_1")
78 shipController.removeObserver(self, forKeyPath: "selection.slot_2")
79 shipController.removeObserver(self, forKeyPath: "selection.onslot_2")
80 shipController.removeObserver(self, forKeyPath: "selection.master_ship.maxeq_2")
82 shipController.removeObserver(self, forKeyPath: "selection.slot_3")
83 shipController.removeObserver(self, forKeyPath: "selection.onslot_3")
84 shipController.removeObserver(self, forKeyPath: "selection.master_ship.maxeq_3")
86 shipController.removeObserver(self, forKeyPath: "selection.slot_4")
87 shipController.removeObserver(self, forKeyPath: "selection.onslot_4")
88 shipController.removeObserver(self, forKeyPath: "selection.master_ship.maxeq_4")
93 @IBOutlet weak var supply: SuppliesView!
94 @IBOutlet weak var guardEscapedView: GuardEscapedView!
95 @IBOutlet weak var damageView: DamageView!
96 @IBOutlet weak var slot00Field: SlotItemLevelView!
97 @IBOutlet weak var slot01Field: SlotItemLevelView!
98 @IBOutlet weak var slot02Field: SlotItemLevelView!
99 @IBOutlet weak var slot03Field: SlotItemLevelView!
100 @IBOutlet var shipController: NSObjectController!
102 @objc dynamic var guardEscaped: Bool = false {
105 guardEscapedView.isHidden = !guardEscaped
109 @objc dynamic var ship: Ship? {
111 get { return shipController.content as? Ship }
113 shipController.fetchPredicate = NSPredicate(#keyPath(Ship.id), equal: newValue?.id ?? 0)
117 override func viewDidLoad() {
121 damageView.setFrameOrigin(.zero)
122 view.addSubview(damageView)
123 damageView.bind(NSBindingName(#keyPath(DamageView.damageType)),
125 withKeyPath: "selection.status", options: nil)
127 supply.bind(NSBindingName(#keyPath(SuppliesView.ship)),
129 withKeyPath: "selection.self", options: nil)
131 guardEscapedView.setFrameOrigin(.zero)
132 view.addSubview(guardEscapedView)
134 case .medium, .minimum:
135 guardEscapedView.controlSize = .mini
140 let fields = [slot00Field, slot01Field, slot02Field, slot03Field]
141 let keypath = ["selection.slot_0", "selection.slot_1", "selection.slot_2", "selection.slot_3"]
142 zip(fields, keypath).forEach {
144 $0.0?.bind(NSBindingName(#keyPath(SlotItemLevelView.slotItemID)), to: shipController, withKeyPath: $0.1, options: nil)
147 // observe slotitems count
148 shipController.addObserver(self, forKeyPath: "selection", context: &shipContext)
150 shipController.addObserver(self, forKeyPath: "selection.slot_0", context: &equippedItem0Context)
151 shipController.addObserver(self, forKeyPath: "selection.onslot_0", context: &equippedItem0Context)
152 shipController.addObserver(self, forKeyPath: "selection.master_ship.maxeq_0", context: &equippedItem0Context)
154 shipController.addObserver(self, forKeyPath: "selection.slot_1", context: &equippedItem1Context)
155 shipController.addObserver(self, forKeyPath: "selection.onslot_1", context: &equippedItem1Context)
156 shipController.addObserver(self, forKeyPath: "selection.master_ship.maxeq_1", context: &equippedItem1Context)
158 shipController.addObserver(self, forKeyPath: "selection.slot_2", context: &equippedItem2Context)
159 shipController.addObserver(self, forKeyPath: "selection.onslot_2", context: &equippedItem2Context)
160 shipController.addObserver(self, forKeyPath: "selection.master_ship.maxeq_2", context: &equippedItem2Context)
162 shipController.addObserver(self, forKeyPath: "selection.slot_3", context: &equippedItem3Context)
163 shipController.addObserver(self, forKeyPath: "selection.onslot_3", context: &equippedItem3Context)
164 shipController.addObserver(self, forKeyPath: "selection.master_ship.maxeq_3", context: &equippedItem3Context)
166 shipController.addObserver(self, forKeyPath: "selection.slot_4", context: &equippedItem4Context)
167 shipController.addObserver(self, forKeyPath: "selection.onslot_4", context: &equippedItem4Context)
168 shipController.addObserver(self, forKeyPath: "selection.master_ship.maxeq_4", context: &equippedItem4Context)
172 override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey: Any]?, context: UnsafeMutableRawPointer?) {
174 if context == &shipContext {
176 notifyChangeValue(forKey: #keyPath(planeString0))
177 notifyChangeValue(forKey: #keyPath(planeString0Color))
178 notifyChangeValue(forKey: #keyPath(planeString1))
179 notifyChangeValue(forKey: #keyPath(planeString1Color))
180 notifyChangeValue(forKey: #keyPath(planeString2))
181 notifyChangeValue(forKey: #keyPath(planeString2Color))
182 notifyChangeValue(forKey: #keyPath(planeString3))
183 notifyChangeValue(forKey: #keyPath(planeString3Color))
184 notifyChangeValue(forKey: #keyPath(planeString4))
185 notifyChangeValue(forKey: #keyPath(planeString4Color))
189 if context == &equippedItem0Context {
191 notifyChangeValue(forKey: #keyPath(planeString0))
192 notifyChangeValue(forKey: #keyPath(planeString0Color))
196 if context == &equippedItem1Context {
198 notifyChangeValue(forKey: #keyPath(planeString1))
199 notifyChangeValue(forKey: #keyPath(planeString1Color))
203 if context == &equippedItem2Context {
205 notifyChangeValue(forKey: #keyPath(planeString2))
206 notifyChangeValue(forKey: #keyPath(planeString2Color))
210 if context == &equippedItem3Context {
212 notifyChangeValue(forKey: #keyPath(planeString3))
213 notifyChangeValue(forKey: #keyPath(planeString3Color))
217 if context == &equippedItem4Context {
219 notifyChangeValue(forKey: #keyPath(planeString4))
220 notifyChangeValue(forKey: #keyPath(planeString4Color))
225 super.observeValue(forKeyPath: keyPath, of: object, change: change, context: context)
230 private let allPlaneTypes: [Int] = [6, 7, 8, 9, 10, 11, 25, 26, 41, 45, 56, 57, 58, 59]
233 extension ShipDetailViewController {
235 // MARK: - Plane count strings
236 private enum PlaneState {
240 case equiped(Int, Int)
243 private func planState(_ index: Int) -> PlaneState {
245 guard let ship = ship else { return .cannotEquip }
247 let itemId = ship.slotItemId(index)
248 let maxCount = ship.slotItemMax(index)
250 if maxCount == 0 { return .cannotEquip }
251 if itemId == -1 { return .notEquip(maxCount) }
253 if let item = ship.slotItem(index),
254 allPlaneTypes.contains(item.master_slotItem.type_2) {
256 return .equiped(ship.slotItemCount(index), maxCount)
259 return .notEquip(maxCount)
262 private func planeString(_ index: Int) -> String? {
264 switch planState(index) {
267 case .notEquip(let max):
269 case .equiped(let count, let max):
270 return "\(count)/\(max)"
274 @objc dynamic var planeString0: String? { return planeString(0) }
276 @objc dynamic var planeString1: String? { return planeString(1) }
278 @objc dynamic var planeString2: String? { return planeString(2) }
280 @objc dynamic var planeString3: String? { return planeString(3) }
282 @objc dynamic var planeString4: String? { return planeString(4) }
284 // MARK: - Plane count string color
285 private func planeStringColor(_ index: Int) -> NSColor {
287 switch planState(index) {
288 case .cannotEquip: return NSColor.controlTextColor
289 case .notEquip: return NSColor.disabledControlTextColor
290 case .equiped: return NSColor.controlTextColor
294 @objc dynamic var planeString0Color: NSColor { return planeStringColor(0) }
296 @objc dynamic var planeString1Color: NSColor { return planeStringColor(1) }
298 @objc dynamic var planeString2Color: NSColor { return planeStringColor(2) }
300 @objc dynamic var planeString3Color: NSColor { return planeStringColor(3) }
302 @objc dynamic var planeString4Color: NSColor { return planeStringColor(4) }
305 extension ShipDetailViewController: NibLoadable {}