OSDN Git Service

Model、View、Net、Utilの名前空間にクラスを分類する
[kancollesniffer/KancolleSniffer.git] / KancolleSniffer / Model / ShipInfo.cs
1 // Copyright (C) 2013, 2014, 2015 Kazuhiro Fujieda <fujieda@users.osdn.me>\r
2 // \r
3 // Licensed under the Apache License, Version 2.0 (the "License");\r
4 // you may not use this file except in compliance with the License.\r
5 // You may obtain a copy of the License at\r
6 //\r
7 //    http://www.apache.org/licenses/LICENSE-2.0\r
8 //\r
9 // Unless required by applicable law or agreed to in writing, software\r
10 // distributed under the License is distributed on an "AS IS" BASIS,\r
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
12 // See the License for the specific language governing permissions and\r
13 // limitations under the License.\r
14 \r
15 using System;\r
16 using System.Collections.Generic;\r
17 using System.Linq;\r
18 using KancolleSniffer.Util;\r
19 using static System.Math;\r
20 \r
21 namespace KancolleSniffer.Model\r
22 {\r
23     public class ShipInfo\r
24     {\r
25         public const int FleetCount = 4;\r
26         public const int MemberCount = 6;\r
27 \r
28         private readonly Fleet[] _fleets;\r
29         private readonly Dictionary<int, ShipStatus> _shipInfo = new Dictionary<int, ShipStatus>();\r
30         private readonly ShipMaster _shipMaster;\r
31         private readonly ItemInfo _itemInfo;\r
32         private readonly List<int> _escapedShips = new List<int>();\r
33         private ShipStatus[] _battleResult = new ShipStatus[0];\r
34         private readonly NumEquipsChecker _numEquipsChecker = new NumEquipsChecker();\r
35         public int HqLevel { get; private set; }\r
36         public ShipStatusPair[] BattleResultDiff { get; private set; } = new ShipStatusPair[0];\r
37         public bool IsBattleResultError => BattleResultDiff.Length > 0;\r
38         public ShipStatus[] BattleStartStatus { get; private set; } = new ShipStatus[0];\r
39         public int DropShipId { private get; set; } = -1;\r
40 \r
41         private class NumEquipsChecker\r
42         {\r
43             public int MaxId { private get; set; } = int.MaxValue;\r
44 \r
45             public void Check(ShipStatus ship)\r
46             {\r
47                 var spec = ship.Spec;\r
48                 if (spec.NumEquips != -1 || ship.Id <= MaxId)\r
49                     return;\r
50                 spec.NumEquips = ship.Slot.Count(item => item.Id != -1);\r
51             }\r
52         }\r
53 \r
54         public class ShipStatusPair\r
55         {\r
56             public ShipStatus Assumed { get; set; }\r
57             public ShipStatus Actual { get; set; }\r
58 \r
59             public ShipStatusPair(ShipStatus assumed, ShipStatus actual)\r
60             {\r
61                 Assumed = assumed;\r
62                 Actual = actual;\r
63             }\r
64         }\r
65 \r
66         public ShipInfo(ShipMaster shipMaster, ItemInfo itemInfo)\r
67         {\r
68             _shipMaster = shipMaster;\r
69             _fleets = Enumerable.Range(0, FleetCount).Select((x, i) => new Fleet(this, i)).ToArray();\r
70             _itemInfo = itemInfo;\r
71             ClearShipInfo();\r
72         }\r
73 \r
74         public void InspectMaster(dynamic json)\r
75         {\r
76             _shipMaster.Inspect(json);\r
77             ClearShipInfo();\r
78         }\r
79 \r
80         public void InspectShip(dynamic json)\r
81         {\r
82             if (json.api_deck_port()) // port\r
83             {\r
84                 ClearShipInfo();\r
85                 for (var i = 0; i < FleetCount; i++)\r
86                     _fleets[i].State = FleetState.Port;\r
87                 InspectDeck(json.api_deck_port);\r
88                 InspectShipData(json.api_ship);\r
89                 InspectBasic(json.api_basic);\r
90                 if (json.api_combined_flag())\r
91                     _fleets[0].CombinedType = _fleets[1].CombinedType = (CombinedType)(int)json.api_combined_flag;\r
92                 _itemInfo.NowShips = ((object[])json.api_ship).Length;\r
93                 VerifyBattleResult();\r
94             }\r
95             else if (json.api_data()) // ship2\r
96             {\r
97                 InspectDeck(json.api_data_deck);\r
98                 InspectShipData(json.api_data);\r
99             }\r
100             else if (json.api_ship_data()) // ship3とship_deck\r
101             {\r
102                 InspectDeck(json.api_deck_data);\r
103                 InspectShipData(json.api_ship_data);\r
104                 VerifyBattleResult();\r
105                 // ship_deckでドロップ艦を反映する\r
106                 if (DropShipId != -1)\r
107                 {\r
108                     _itemInfo.NowShips++;\r
109                     var num = _shipMaster.GetSpec(DropShipId).NumEquips;\r
110                     if (num > 0)\r
111                         _itemInfo.NowEquips += num;\r
112                 }\r
113             }\r
114             else if (json.api_ship()) // getshipとpowerup\r
115             {\r
116                 InspectShipData(new[] {json.api_ship});\r
117             }\r
118             DropShipId = -1;\r
119         }\r
120 \r
121         public void SaveBattleResult()\r
122         {\r
123             _battleResult = _fleets.Where(fleet =>\r
124                     fleet.State >= FleetState.Sortie && !GetStatus(fleet.Deck[0]).Spec.IsRepairShip)\r
125                 .SelectMany(fleet => fleet.Deck.Select(GetStatus)).ToArray();\r
126         }\r
127 \r
128         private void VerifyBattleResult()\r
129         {\r
130             BattleResultDiff = (from assumed in _battleResult\r
131                 let actual = GetStatus(assumed.Id)\r
132                 where !assumed.Escaped && assumed.NowHp != actual.NowHp\r
133                 select new ShipStatusPair(assumed, actual)).ToArray();\r
134             _battleResult = new ShipStatus[0];\r
135         }\r
136 \r
137         public void SaveBattleStartStatus()\r
138         {\r
139             BattleStartStatus = _fleets.Where(fleet => fleet.State >= FleetState.Sortie)\r
140                 .SelectMany(fleet => fleet.Deck.Select(id => (ShipStatus)GetStatus(id).Clone())).ToArray();\r
141         }\r
142 \r
143         private void ClearShipInfo()\r
144         {\r
145             _shipInfo.Clear();\r
146             _shipInfo[-1] = new ShipStatus();\r
147         }\r
148 \r
149         public void InspectDeck(dynamic json)\r
150         {\r
151             foreach (var entry in json)\r
152             {\r
153                 var fleet = (int)entry.api_id - 1;\r
154                 _fleets[fleet].Deck = (int[])entry.api_ship;\r
155                 if ((int)entry.api_mission[0] != 0)\r
156                     _fleets[fleet].State = FleetState.Mission;\r
157             }\r
158         }\r
159 \r
160         private void InspectShipData(dynamic json)\r
161         {\r
162             foreach (var entry in json)\r
163             {\r
164                 var id = (int)entry.api_id;\r
165                 var ship = new ShipStatus\r
166                 {\r
167                     Id = id,\r
168                     Spec = _shipMaster.GetSpec((int)entry.api_ship_id),\r
169                     Level = (int)entry.api_lv,\r
170                     ExpToNext = (int)entry.api_exp[1],\r
171                     MaxHp = (int)entry.api_maxhp,\r
172                     NowHp = (int)entry.api_nowhp,\r
173                     Cond = (int)entry.api_cond,\r
174                     Fuel = (int)entry.api_fuel,\r
175                     Bull = (int)entry.api_bull,\r
176                     OnSlot = (int[])entry.api_onslot,\r
177                     Slot = ((int[])entry.api_slot).Select(item => new ItemStatus(item)).ToArray(),\r
178                     SlotEx = entry.api_slot_ex() ? new ItemStatus((int)entry.api_slot_ex) : new ItemStatus(0),\r
179                     NdockTime = (int)entry.api_ndock_time,\r
180                     NdockItem = (int[])entry.api_ndock_item,\r
181                     LoS = (int)entry.api_sakuteki[0],\r
182                     Firepower = (int)entry.api_karyoku[0],\r
183                     Torpedo = (int)entry.api_raisou[0],\r
184                     AntiSubmarine = (int)entry.api_taisen[0],\r
185                     AntiAir = (int)entry.api_taiku[0],\r
186                     Lucky = (int)entry.api_lucky[0],\r
187                     Locked = entry.api_locked() && entry.api_locked == 1\r
188                 };\r
189                 _shipInfo[id] = ship;\r
190                 _numEquipsChecker.Check(ship);\r
191             }\r
192             _numEquipsChecker.MaxId = _shipInfo.Keys.Max();\r
193         }\r
194 \r
195         private void InspectBasic(dynamic json)\r
196         {\r
197             HqLevel = (int)json.api_level;\r
198         }\r
199 \r
200         public void InspectCharge(dynamic json)\r
201         {\r
202             foreach (var entry in json.api_ship)\r
203             {\r
204                 var status = _shipInfo[(int)entry.api_id];\r
205                 status.Bull = (int)entry.api_bull;\r
206                 status.Fuel = (int)entry.api_fuel;\r
207                 status.OnSlot = (from num in (dynamic[])entry.api_onslot select (int)num).ToArray();\r
208             }\r
209         }\r
210 \r
211         public void InspectChange(string request)\r
212         {\r
213             var values = HttpUtility.ParseQueryString(request);\r
214             var fleet = _fleets[int.Parse(values["api_id"]) - 1];\r
215             var idx = int.Parse(values["api_ship_idx"]);\r
216             var ship = int.Parse(values["api_ship_id"]);\r
217 \r
218             if (idx == -1)\r
219             {\r
220                 var deck = fleet.Deck;\r
221                 for (var i = 1; i < deck.Length; i++)\r
222                     deck[i] = -1;\r
223                 return;\r
224             }\r
225             if (ship == -1)\r
226             {\r
227                 WithdrowShip(fleet, idx);\r
228                 return;\r
229             }\r
230             var of = FindFleet(ship, out var oi);\r
231             var orig = fleet.Deck[idx];\r
232             fleet.Deck[idx] = ship;\r
233             if (of == null)\r
234                 return;\r
235             // 入れ替えの場合\r
236             if ((of.Deck[oi] = orig) == -1)\r
237                 WithdrowShip(of, oi);\r
238         }\r
239 \r
240         private Fleet FindFleet(int ship, out int idx)\r
241         {\r
242             foreach (var fleet in _fleets)\r
243             {\r
244                 idx = Array.FindIndex(fleet.Deck, id => id == ship);\r
245                 if (idx < 0)\r
246                     continue;\r
247                 return fleet;\r
248             }\r
249             idx = -1;\r
250             return null;\r
251         }\r
252 \r
253         private void WithdrowShip(Fleet fleet, int idx)\r
254         {\r
255             var deck = fleet.Deck;\r
256             var j = idx;\r
257             for (var i = idx + 1; i < deck.Length; i++)\r
258             {\r
259                 if (deck[i] != -1)\r
260                     deck[j++] = deck[i];\r
261             }\r
262             for (; j < deck.Length; j++)\r
263                 deck[j] = -1;\r
264         }\r
265 \r
266         public void InspectPowerup(string request, dynamic json)\r
267         {\r
268             var values = HttpUtility.ParseQueryString(request);\r
269             var ships = values["api_id_items"].Split(',').Select(int.Parse).ToArray();\r
270             if (!_shipInfo.ContainsKey(ships[0])) // 二重に実行された場合\r
271                 return;\r
272             _itemInfo.NowShips -= ships.Length;\r
273             _itemInfo.DeleteItems(ships.SelectMany(id => _shipInfo[id].Slot).ToArray());\r
274             foreach (var id in ships)\r
275                 _shipInfo.Remove(id);\r
276             InspectDeck(json.api_deck);\r
277             InspectShip(json);\r
278         }\r
279 \r
280         public void InspectSlotExchange(string request, dynamic json)\r
281         {\r
282             var values = HttpUtility.ParseQueryString(request);\r
283             var ship = int.Parse(values["api_id"]);\r
284             _shipInfo[ship].Slot = ((int[])json.api_slot).Select(id => new ItemStatus(id)).ToArray();\r
285         }\r
286 \r
287         public void InspectSlotDeprive(dynamic json)\r
288         {\r
289             InspectShipData(new[] {json.api_ship_data.api_set_ship, json.api_ship_data.api_unset_ship});\r
290         }\r
291 \r
292         public void InspectDestroyShip(string request, dynamic json)\r
293         {\r
294             var values = HttpUtility.ParseQueryString(request);\r
295             var delitem = int.Parse(values["api_slot_dest_flag"] ?? "0") == 1;\r
296             foreach (var ship in values["api_ship_id"].Split(',').Select(int.Parse))\r
297             {\r
298                 _itemInfo.NowShips--;\r
299                 if (delitem)\r
300                     _itemInfo.DeleteItems(_shipInfo[ship].AllSlot);\r
301                 var of = FindFleet(ship, out var oi);\r
302                 if (of != null)\r
303                     WithdrowShip(of, oi);\r
304                 _shipInfo.Remove(ship);\r
305             }\r
306         }\r
307 \r
308         public void InspectCombined(string request)\r
309         {\r
310             var values = HttpUtility.ParseQueryString(request);\r
311             _fleets[0].CombinedType = _fleets[1].CombinedType = (CombinedType)int.Parse(values["api_combined_type"]);\r
312         }\r
313 \r
314         public void InspectMapStart(string request)\r
315         {\r
316             var values = HttpUtility.ParseQueryString(request);\r
317             var fleet = int.Parse(values["api_deck_id"]) - 1;\r
318             if (_fleets[0].CombinedType == 0 || fleet > 1)\r
319             {\r
320                 _fleets[fleet].State = FleetState.Sortie;\r
321             }\r
322             else\r
323             {\r
324                 _fleets[0].State = _fleets[1].State = FleetState.Sortie;\r
325             }\r
326             SetBadlyDamagedShips();\r
327         }\r
328 \r
329         public void StartPractice(string request)\r
330         {\r
331             var values = HttpUtility.ParseQueryString(request);\r
332             var fleet = int.Parse(values["api_deck_id"]) - 1;\r
333             _fleets[fleet].State = FleetState.Practice;\r
334         }\r
335 \r
336         public void RepairShip(int id)\r
337         {\r
338             var s = _shipInfo[id];\r
339             s.NowHp = s.MaxHp;\r
340             s.Cond = Max(40, s.Cond);\r
341         }\r
342 \r
343         public Fleet[] Fleets => _fleets;\r
344 \r
345         public ShipStatus GetStatus(int id)\r
346         {\r
347             if (!_shipInfo.TryGetValue(id, out var s))\r
348                 return new ShipStatus();\r
349             s.Slot = s.Slot.Select(item => _itemInfo.GetStatus(item.Id)).ToArray();\r
350             s.SlotEx = _itemInfo.GetStatus(s.SlotEx.Id);\r
351             s.Escaped = _escapedShips.Contains(id);\r
352             s.Fleet = FindFleet(s.Id, out var idx);\r
353             s.DeckIndex = idx;\r
354             return s;\r
355         }\r
356 \r
357         public void SetItemHolder()\r
358         {\r
359             foreach (var ship in _shipInfo.Values)\r
360             {\r
361                 foreach (var item in ship.Slot)\r
362                     _itemInfo.GetStatus(item.Id).Holder = ship;\r
363                 _itemInfo.GetStatus(ship.SlotEx.Id).Holder = ship;\r
364             }\r
365         }\r
366 \r
367         public ShipSpec GetSpec(int id) => _shipMaster.GetSpec(id);\r
368 \r
369         public ShipStatus[] ShipList => _shipInfo.Keys.Where(id => id != -1).Select(GetStatus).ToArray();\r
370 \r
371         public ShipStatus[] GetRepairList(DockInfo dockInfo)\r
372             => (from s in ShipList\r
373                 where s.NowHp < s.MaxHp && !dockInfo.InNDock(s.Id) &&\r
374                       (s.Fleet == null || s.Fleet.State != FleetState.Practice)\r
375                 select s).OrderByDescending(s => s.RepairTime).ToArray();\r
376 \r
377         public string[] BadlyDamagedShips { get; private set; } = new string[0];\r
378 \r
379         public void SetBadlyDamagedShips()\r
380         {\r
381             BadlyDamagedShips =\r
382             (from s in _fleets.Where(fleet => fleet.State == FleetState.Sortie)\r
383                     .SelectMany(fleet => fleet.Deck.Where(id => id != -1).Select(GetStatus))\r
384                 where !s.Escaped && s.DamageLevel == ShipStatus.Damage.Badly &&\r
385                       !(s.Fleet.CombinedType != 0 && s.Fleet.Number == 1 && s.DeckIndex == 0) // 第二艦隊の旗艦を除く\r
386                 select s.Name).ToArray();\r
387         }\r
388 \r
389         public void ClearBadlyDamagedShips()\r
390         {\r
391             BadlyDamagedShips = new string[0];\r
392         }\r
393 \r
394         public void SetEscapedShips(List<int> ships)\r
395         {\r
396             _escapedShips.AddRange(ships);\r
397         }\r
398 \r
399         public void ClearEscapedShips()\r
400         {\r
401             _escapedShips.Clear();\r
402         }\r
403 \r
404         public void InjectShips(dynamic battle, dynamic item)\r
405         {\r
406             var deck = (int)battle.api_deck_id - 1;\r
407             InjectShips(deck, (int[])battle.api_f_nowhps, (int[])battle.api_f_maxhps, (int[][])item[0]);\r
408             if (battle.api_f_nowhps_combined())\r
409                 InjectShips(1, (int[])battle.api_f_nowhps_combined, (int[])battle.api_f_maxhps_combined,\r
410                     (int[][])item[1]);\r
411             foreach (var enemy in (int[])battle.api_ship_ke)\r
412                 _shipMaster.InjectSpec(enemy);\r
413             if (battle.api_ship_ke_combined())\r
414             {\r
415                 foreach (var enemy in (int[])battle.api_ship_ke_combined)\r
416                     _shipMaster.InjectSpec(enemy);\r
417             }\r
418             _itemInfo.InjectItems(((int[][])battle.api_eSlot).SelectMany(x => x));\r
419             if (battle.api_eSlot_combined())\r
420                 _itemInfo.InjectItems(((int[][])battle.api_eSlot_combined).SelectMany(x => x));\r
421         }\r
422 \r
423         private void InjectShips(int deck, int[] nowhps, int[] maxhps, int[][] slots)\r
424         {\r
425             var id = _shipInfo.Keys.Count + 1;\r
426             var ships = nowhps.Zip(maxhps,\r
427                 (now, max) => new ShipStatus {Id = id++, NowHp = now, MaxHp = max}).ToArray();\r
428             _fleets[deck].Deck = (from ship in ships select ship.Id).ToArray();\r
429             foreach (var ship in ships)\r
430                 _shipInfo[ship.Id] = ship;\r
431             foreach (var entry in ships.Zip(slots, (ship, slot) => new {ship, slot}))\r
432             {\r
433                 entry.ship.Slot = _itemInfo.InjectItems(entry.slot.Take(5));\r
434                 if (entry.slot.Length >= 6)\r
435                     entry.ship.SlotEx = _itemInfo.InjectItems(entry.slot.Skip(5)).First();\r
436             }\r
437         }\r
438     }\r
439 }