OSDN Git Service

艦隊に関する責務をShipInfoからFleetクラスに移す
[kancollesniffer/KancolleSniffer.git] / KancolleSniffer / 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 static System.Math;\r
19 \r
20 namespace KancolleSniffer\r
21 {\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 = new ShipMaster();\r
31         private readonly ItemInfo _itemInfo;\r
32         private readonly List<int> _escapedShips = new List<int>();\r
33         private int _combinedFleetType;\r
34         private ShipStatus[] _battleResult = new ShipStatus[0];\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 \r
40         public class ShipStatusPair\r
41         {\r
42             public ShipStatus Assumed { get; set; }\r
43             public ShipStatus Actual { get; set; }\r
44 \r
45             public ShipStatusPair(ShipStatus assumed, ShipStatus actual)\r
46             {\r
47                 Assumed = assumed;\r
48                 Actual = actual;\r
49             }\r
50         }\r
51 \r
52         public ShipInfo(ItemInfo itemInfo)\r
53         {\r
54             _fleets = Enumerable.Range(0, FleetCount).Select(x => new Fleet(this)).ToArray();\r
55             _itemInfo = itemInfo;\r
56             ClearShipInfo();\r
57         }\r
58 \r
59         public void InspectMaster(dynamic json)\r
60         {\r
61             _shipMaster.Inspect(json);\r
62         }\r
63 \r
64         public void InspectShip(dynamic json)\r
65         {\r
66             if (json.api_deck_port()) // port\r
67             {\r
68                 ClearShipInfo();\r
69                 for (var i = 0; i < FleetCount; i++)\r
70                     _fleets[i].State = FleetState.Port;\r
71                 InspectDeck(json.api_deck_port);\r
72                 InspectShipData(json.api_ship);\r
73                 InspectBasic(json.api_basic);\r
74                 if (json.api_combined_flag())\r
75                     _combinedFleetType = (int)json.api_combined_flag;\r
76                 _itemInfo.NowShips = ((object[])json.api_ship).Length;\r
77                 VerifyBattleResult();\r
78             }\r
79             else if (json.api_data()) // ship2\r
80             {\r
81                 // 一隻分のデータしか来ないことがあるので艦娘数を数えない\r
82                 InspectDeck(json.api_data_deck);\r
83                 InspectShipData(json.api_data);\r
84             }\r
85             else if (json.api_ship_data()) // ship3とship_deck\r
86             {\r
87                 // 一隻分のデータしか来ないことがあるので艦娘数を数えない\r
88                 InspectDeck(json.api_deck_data);\r
89                 InspectShipData(json.api_ship_data);\r
90                 VerifyBattleResult();\r
91             }\r
92             else if (json.api_ship()) // getshipとpowerup\r
93             {\r
94                 InspectShipData(new[] {json.api_ship});\r
95             }\r
96         }\r
97 \r
98         public void SaveBattleResult()\r
99         {\r
100             _battleResult = _fleets.Where(fleet =>\r
101                     fleet.State >= FleetState.Sortie && !GetStatus(fleet.Deck[0]).Spec.IsRepairShip)\r
102                 .SelectMany(fleet => fleet.Deck.Select(GetStatus)).ToArray();\r
103         }\r
104 \r
105         private void VerifyBattleResult()\r
106         {\r
107             BattleResultDiff = (from assumed in _battleResult\r
108                 let actual = GetStatus(assumed.Id)\r
109                 where !assumed.Escaped && assumed.NowHp != actual.NowHp\r
110                 select new ShipStatusPair(assumed, actual)).ToArray();\r
111             _battleResult = new ShipStatus[0];\r
112         }\r
113 \r
114         public void SaveBattleStartStatus()\r
115         {\r
116             BattleStartStatus = _fleets.Where(fleet => fleet.State >= FleetState.Sortie)\r
117                 .SelectMany(fleet => fleet.Deck.Select(id => (ShipStatus)GetStatus(id).Clone())).ToArray();\r
118         }\r
119 \r
120         private void ClearShipInfo()\r
121         {\r
122             _shipInfo.Clear();\r
123             _shipInfo[-1] = new ShipStatus();\r
124         }\r
125 \r
126         public void InspectDeck(dynamic json)\r
127         {\r
128             foreach (var entry in json)\r
129             {\r
130                 var fleet = (int)entry.api_id - 1;\r
131                 _fleets[fleet].Deck = (int[])entry.api_ship;\r
132                 if ((int)entry.api_mission[0] != 0)\r
133                     _fleets[fleet].State = FleetState.Mission;\r
134             }\r
135         }\r
136 \r
137         private void InspectShipData(dynamic json)\r
138         {\r
139             foreach (var entry in json)\r
140             {\r
141                 _shipInfo[(int)entry.api_id] = new ShipStatus\r
142                 {\r
143                     Id = (int)entry.api_id,\r
144                     Spec = _shipMaster.GetSpec((int)entry.api_ship_id),\r
145                     Level = (int)entry.api_lv,\r
146                     ExpToNext = (int)entry.api_exp[1],\r
147                     MaxHp = (int)entry.api_maxhp,\r
148                     NowHp = (int)entry.api_nowhp,\r
149                     Cond = (int)entry.api_cond,\r
150                     Fuel = (int)entry.api_fuel,\r
151                     Bull = (int)entry.api_bull,\r
152                     OnSlot = (int[])entry.api_onslot,\r
153                     Slot = ((int[])entry.api_slot).Select(id => new ItemStatus(id)).ToArray(),\r
154                     SlotEx = entry.api_slot_ex() ? new ItemStatus((int)entry.api_slot_ex) : new ItemStatus(0),\r
155                     NdockTime = (int)entry.api_ndock_time,\r
156                     NdockItem = (int[])entry.api_ndock_item,\r
157                     LoS = (int)entry.api_sakuteki[0],\r
158                     Firepower = (int)entry.api_karyoku[0],\r
159                     Torpedo = (int)entry.api_raisou[0],\r
160                     AntiSubmarine = (int)entry.api_taisen[0],\r
161                     AntiAir = (int)entry.api_taiku[0],\r
162                     Lucky = (int)entry.api_lucky[0],\r
163                     Locked = entry.api_locked() && entry.api_locked == 1\r
164                 };\r
165             }\r
166         }\r
167 \r
168         private void InspectBasic(dynamic json)\r
169         {\r
170             HqLevel = (int)json.api_level;\r
171         }\r
172 \r
173         public void InspectCharge(dynamic json)\r
174         {\r
175             foreach (var entry in json.api_ship)\r
176             {\r
177                 var status = _shipInfo[(int)entry.api_id];\r
178                 status.Bull = (int)entry.api_bull;\r
179                 status.Fuel = (int)entry.api_fuel;\r
180                 status.OnSlot = (from num in (dynamic[])entry.api_onslot select (int)num).ToArray();\r
181             }\r
182         }\r
183 \r
184         public void InspectChange(string request)\r
185         {\r
186             var values = HttpUtility.ParseQueryString(request);\r
187             var fleet = int.Parse(values["api_id"]) - 1;\r
188             var idx = int.Parse(values["api_ship_idx"]);\r
189             var ship = int.Parse(values["api_ship_id"]);\r
190             if (idx == -1)\r
191             {\r
192                 var deck = _fleets[fleet].Deck;\r
193                 for (var i = 1; i < deck.Length; i++)\r
194                     deck[i] = -1;\r
195                 return;\r
196             }\r
197             if (ship == -1)\r
198             {\r
199                 WithdrowShip(fleet, idx);\r
200                 return;\r
201             }\r
202             var of = FindFleet(ship, out var oi);\r
203             var orig = _fleets[fleet].Deck[idx];\r
204             _fleets[fleet].Deck[idx] = ship;\r
205             if (of == -1)\r
206                 return;\r
207             // 入れ替えの場合\r
208             if ((_fleets[of].Deck[oi] = orig) == -1)\r
209                 WithdrowShip(of, oi);\r
210         }\r
211 \r
212         private int FindFleet(int ship, out int idx)\r
213         {\r
214             for (var f = 0; f < _fleets.Length; f++)\r
215             {\r
216                 idx = Array.FindIndex(_fleets[f].Deck, id => id == ship);\r
217                 if (idx < 0)\r
218                     continue;\r
219                 return f;\r
220             }\r
221             idx = -1;\r
222             return -1;\r
223         }\r
224 \r
225         private void WithdrowShip(int fleet, int idx)\r
226         {\r
227             var deck = _fleets[fleet].Deck;\r
228             var j = idx;\r
229             for (var i = idx + 1; i < deck.Length; i++)\r
230             {\r
231                 if (deck[i] != -1)\r
232                     deck[j++] = deck[i];\r
233             }\r
234             for (; j < deck.Length; j++)\r
235                 deck[j] = -1;\r
236         }\r
237 \r
238         public void InspectPowerup(string request, dynamic json)\r
239         {\r
240             var values = HttpUtility.ParseQueryString(request);\r
241             var ships = values["api_id_items"].Split(',').Select(int.Parse).ToArray();\r
242             if (!_shipInfo.ContainsKey(ships[0])) // 二重に実行された場合\r
243                 return;\r
244             _itemInfo.NowShips -= ships.Length;\r
245             _itemInfo.DeleteItems(ships.SelectMany(id => _shipInfo[id].Slot).ToArray());\r
246             foreach (var id in ships)\r
247                 _shipInfo.Remove(id);\r
248             InspectDeck(json.api_deck);\r
249             InspectShip(json);\r
250         }\r
251 \r
252         public void InspectSlotExchange(string request, dynamic json)\r
253         {\r
254             var values = HttpUtility.ParseQueryString(request);\r
255             var ship = int.Parse(values["api_id"]);\r
256             _shipInfo[ship].Slot = ((int[])json.api_slot).Select(id => new ItemStatus(id)).ToArray();\r
257         }\r
258 \r
259         public void InspectSlotDeprive(dynamic json)\r
260         {\r
261             InspectShipData(new[] {json.api_ship_data.api_set_ship, json.api_ship_data.api_unset_ship});\r
262         }\r
263 \r
264         public void InspectDestroyShip(string request, dynamic json)\r
265         {\r
266             var values = HttpUtility.ParseQueryString(request);\r
267             var delitem = int.Parse(values["api_slot_dest_flag"] ?? "0") == 1;\r
268             foreach (var ship in values["api_ship_id"].Split(',').Select(int.Parse))\r
269             {\r
270                 _itemInfo.NowShips--;\r
271                 if (delitem)\r
272                     _itemInfo.DeleteItems(_shipInfo[ship].AllSlot);\r
273                 var of = FindFleet(ship, out var oi);\r
274                 if (of != -1)\r
275                     WithdrowShip(of, oi);\r
276                 _shipInfo.Remove(ship);\r
277             }\r
278         }\r
279 \r
280         public void InspectCombined(string request)\r
281         {\r
282             var values = HttpUtility.ParseQueryString(request);\r
283             _combinedFleetType = int.Parse(values["api_combined_type"]);\r
284         }\r
285 \r
286         public void InspectMapStart(string request)\r
287         {\r
288             var values = HttpUtility.ParseQueryString(request);\r
289             var fleet = int.Parse(values["api_deck_id"]) - 1;\r
290             if (_combinedFleetType == 0 || fleet > 1)\r
291             {\r
292                 _fleets[fleet].State = FleetState.Sortie;\r
293             }\r
294             else\r
295             {\r
296                 _fleets[0].State = _fleets[1].State = FleetState.Sortie;\r
297             }\r
298             SetBadlyDamagedShips();\r
299         }\r
300 \r
301         public void StartPractice(string request)\r
302         {\r
303             var values = HttpUtility.ParseQueryString(request);\r
304             var fleet = int.Parse(values["api_deck_id"]) - 1;\r
305             _fleets[fleet].State = FleetState.Practice;\r
306         }\r
307 \r
308         public void RepairShip(int id)\r
309         {\r
310             var s = _shipInfo[id];\r
311             s.NowHp = s.MaxHp;\r
312             s.Cond = Max(40, s.Cond);\r
313         }\r
314 \r
315         public Fleet[] Fleets => _fleets;\r
316 \r
317         public ShipStatus GetStatus(int id)\r
318         {\r
319             if (!_shipInfo.TryGetValue(id, out var s))\r
320                 return new ShipStatus();\r
321             s.Slot = s.Slot.Select(item => _itemInfo.GetStatus(item.Id)).ToArray();\r
322             s.SlotEx = _itemInfo.GetStatus(s.SlotEx.Id);\r
323             s.Escaped = _escapedShips.Contains(id);\r
324             s.Fleet = FindFleet(s.Id, out var idx);\r
325             s.DeckIndex = idx;\r
326             s.CombinedFleetType = s.Fleet < 2 ? _combinedFleetType : 0;\r
327             return s;\r
328         }\r
329 \r
330         public void SetItemHolder()\r
331         {\r
332             foreach (var ship in _shipInfo.Values)\r
333             {\r
334                 foreach (var item in ship.Slot)\r
335                     _itemInfo.GetStatus(item.Id).Holder = ship;\r
336                 _itemInfo.GetStatus(ship.SlotEx.Id).Holder = ship;\r
337             }\r
338         }\r
339 \r
340         public ShipSpec GetSpec(int id) => _shipMaster.GetSpec(id);\r
341 \r
342         public int CombinedFleetType => _combinedFleetType;\r
343 \r
344         public ShipStatus[] ShipList => _shipInfo.Keys.Where(id => id != -1).Select(GetStatus).ToArray();\r
345 \r
346         public ShipStatus[] GetRepairList(DockInfo dockInfo)\r
347             => (from s in ShipList\r
348                 where s.NowHp < s.MaxHp && !dockInfo.InNDock(s.Id)\r
349                 select s).OrderByDescending(s => s.RepairTime).ToArray();\r
350 \r
351         public string[] BadlyDamagedShips { get; private set; } = new string[0];\r
352 \r
353         public void SetBadlyDamagedShips()\r
354         {\r
355             BadlyDamagedShips =\r
356             (from s in _fleets.Where(fleet => fleet.State == FleetState.Sortie)\r
357                     .SelectMany(fleet => fleet.Deck.Where(id => id != -1).Select(GetStatus))\r
358                 where !s.Escaped && s.DamageLevel == ShipStatus.Damage.Badly &&\r
359                       !(s.CombinedFleetType > 0 && s.Fleet == 1 && s.DeckIndex == 0) // 第二艦隊の旗艦を除く\r
360                 select s.Name).ToArray();\r
361         }\r
362 \r
363         public void ClearBadlyDamagedShips()\r
364         {\r
365             BadlyDamagedShips = new string[0];\r
366         }\r
367 \r
368         public void SetEscapedShips(List<int> ships)\r
369         {\r
370             _escapedShips.AddRange(ships);\r
371         }\r
372 \r
373         public void ClearEscapedShips()\r
374         {\r
375             _escapedShips.Clear();\r
376         }\r
377 \r
378         public void InjectShips(dynamic battle, dynamic item)\r
379         {\r
380             var deck = (int)battle.api_deck_id - 1;\r
381             InjectShips(deck, (int[])battle.api_f_nowhps, (int[])battle.api_f_maxhps, (int[][])item[0]);\r
382             if (battle.api_f_nowhps_combined())\r
383                 InjectShips(1, (int[])battle.api_f_nowhps_combined, (int[])battle.api_f_maxhps_combined, (int[][])item[1]);\r
384             foreach (var enemy in (int[])battle.api_ship_ke)\r
385                 _shipMaster.InjectSpec(enemy);\r
386             if (battle.api_ship_ke_combined())\r
387             {\r
388                 foreach (var enemy in (int[])battle.api_ship_ke_combined)\r
389                     _shipMaster.InjectSpec(enemy);\r
390             }\r
391             _itemInfo.InjectItems(((int[][])battle.api_eSlot).SelectMany(x => x));\r
392             if (battle.api_eSlot_combined())\r
393                 _itemInfo.InjectItems(((int[][])battle.api_eSlot_combined).SelectMany(x => x));\r
394         }\r
395 \r
396         private void InjectShips(int deck, int[] nowhps, int[] maxhps, int[][] slots)\r
397         {\r
398             var id = _shipInfo.Keys.Count + 1;\r
399             var ships = nowhps.Zip(maxhps,\r
400                 (now, max) => new ShipStatus {Id = id++, NowHp = now, MaxHp = max}).ToArray();\r
401             _fleets[deck].Deck = (from ship in ships select ship.Id).ToArray();\r
402             foreach (var ship in ships)\r
403                 _shipInfo[ship.Id] = ship;\r
404             foreach (var entry in ships.Zip(slots, (ship, slot) =>new {ship, slot}))\r
405             {\r
406                 entry.ship.Slot = _itemInfo.InjectItems(entry.slot.Take(5));\r
407                 if (entry.slot.Length >= 6)\r
408                     entry.ship.SlotEx = _itemInfo.InjectItems(entry.slot.Skip(5)).First();\r
409             }\r
410         }\r
411     }\r
412 }