OSDN Git Service

各種報告書の表を列単位で検索可能にする
[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.Collections.Generic;\r
16 using System.Linq;\r
17 using KancolleSniffer.Util;\r
18 \r
19 namespace KancolleSniffer.Model\r
20 {\r
21     public class ShipInfo\r
22     {\r
23         public const int FleetCount = 4;\r
24         public const int MemberCount = 6;\r
25 \r
26         private readonly IReadOnlyList<Fleet> _fleets;\r
27         private readonly ShipMaster _shipMaster;\r
28         private readonly ShipInventory _shipInventory;\r
29         private readonly ItemInventory _itemInventory;\r
30         private ShipStatus[] _battleResult = new ShipStatus[0];\r
31         private readonly NumEquipsChecker _numEquipsChecker = new NumEquipsChecker();\r
32         private int _hqLevel;\r
33         public AlarmCounter Counter { get; }\r
34         public ShipStatusPair[] BattleResultDiff { get; private set; } = new ShipStatusPair[0];\r
35         public bool IsBattleResultError => BattleResultDiff.Length > 0;\r
36         public ShipStatus[] BattleStartStatus { get; private set; } = new ShipStatus[0];\r
37         public int DropShipId { private get; set; } = -1;\r
38 \r
39         private class NumEquipsChecker\r
40         {\r
41             public int MaxId { private get; set; } = int.MaxValue;\r
42 \r
43             public void Check(ShipStatus ship)\r
44             {\r
45                 var spec = ship.Spec;\r
46                 if (spec.NumEquips != -1 || ship.Id <= MaxId)\r
47                     return;\r
48                 spec.NumEquips = ship.Slot.Count(item => !item.Empty);\r
49             }\r
50         }\r
51 \r
52         public class ShipStatusPair\r
53         {\r
54             public ShipStatus Assumed { get; set; }\r
55             public ShipStatus Actual { get; set; }\r
56 \r
57             public ShipStatusPair(ShipStatus assumed, ShipStatus actual)\r
58             {\r
59                 Assumed = assumed;\r
60                 Actual = actual;\r
61             }\r
62         }\r
63 \r
64         public ShipInfo(ShipMaster shipMaster, ShipInventory shipInventory, ItemInventory itemInventory)\r
65         {\r
66             _shipMaster = shipMaster;\r
67             _shipInventory = shipInventory;\r
68             _fleets = Enumerable.Range(0, FleetCount).Select((x, i) => new Fleet(_shipInventory, i, () => _hqLevel)).ToArray();\r
69             _itemInventory = itemInventory;\r
70             Counter = new AlarmCounter(() => _shipInventory.Count) {Margin = 5};\r
71         }\r
72 \r
73         public void InspectMaster(dynamic json)\r
74         {\r
75             _shipMaster.Inspect(json);\r
76             _shipInventory.Clear();\r
77         }\r
78 \r
79         public void InspectShip(string url, dynamic json)\r
80         {\r
81             if (url.Contains("port"))\r
82             {\r
83                 HandlePort(json);\r
84             }\r
85             else if (url.Contains("ship2"))\r
86             {\r
87                 SetShipAndDeck(json.api_data, json.api_data_deck);\r
88             }\r
89             else if (url.Contains("ship3"))\r
90             {\r
91                 SetShipAndDeck(json.api_ship_data, json.api_deck_data);\r
92             }\r
93             else if (url.Contains("ship_deck"))\r
94             {\r
95                 HandleShipDeck(json);\r
96             }\r
97             else if (url.Contains("getship")) // getship\r
98             {\r
99                 HandleGetShip(json);\r
100             }\r
101             DropShipId = -1;\r
102         }\r
103 \r
104         private void HandlePort(dynamic json)\r
105         {\r
106             _shipInventory.Clear();\r
107             for (var i = 0; i < FleetCount; i++)\r
108                 _fleets[i].State = FleetState.Port;\r
109             SetShipAndDeckForPort(json);\r
110             InspectBasic(json.api_basic);\r
111             if (json.api_combined_flag())\r
112                 _fleets[0].CombinedType = _fleets[1].CombinedType = (CombinedType)(int)json.api_combined_flag;\r
113             VerifyBattleResult();\r
114         }\r
115 \r
116         private void SetShipAndDeckForPort(dynamic json)\r
117         {\r
118             foreach (var entry in json.api_ship)\r
119             {\r
120                 var ship = (ShipStatus)CreateShipStatus(entry);\r
121                 _shipInventory.Add(ship);\r
122                 _numEquipsChecker.Check(ship);\r
123             }\r
124             _numEquipsChecker.MaxId = _shipInventory.MaxId;\r
125             InspectDeck(json.api_deck_port);\r
126         }\r
127 \r
128         private void HandleShipDeck(dynamic json)\r
129         {\r
130             SetShipAndDeckForShipDeck(json);\r
131             VerifyBattleResult();\r
132             // ドロップ艦を反映する\r
133             if (DropShipId != -1)\r
134             {\r
135                 _shipInventory.InflateCount(1);\r
136                 var num = _shipMaster.GetSpec(DropShipId).NumEquips;\r
137                 if (num > 0)\r
138                     _itemInventory.InflateCount(num);\r
139             }\r
140         }\r
141 \r
142         private void SetShipAndDeckForShipDeck(dynamic json)\r
143         {\r
144             foreach (var entry in json.api_ship_data)\r
145             {\r
146                 var ship = (ShipStatus)CreateShipStatus(entry);\r
147                 var org = _shipInventory[ship.Id];\r
148                 ship.Escaped = org.Escaped; // 出撃中は継続する\r
149                 ship.SpecialAttack = org.SpecialAttack;\r
150                 _shipInventory.Add(ship);\r
151             }\r
152             InspectDeck(json.api_deck_data);\r
153         }\r
154 \r
155         private void SetShipAndDeck(dynamic ship, dynamic deck)\r
156         {\r
157             SetShips(ship);\r
158             InspectDeck(deck); // FleetのDeckを設定した時点でShipStatusを取得するので必ずdeckが後\r
159         }\r
160 \r
161         private void HandleGetShip(dynamic json)\r
162         {\r
163             var ship = CreateShipStatus(json.api_ship);\r
164             _shipInventory.Add(ship);\r
165             _numEquipsChecker.Check(ship);\r
166             _numEquipsChecker.MaxId = _shipInventory.MaxId;\r
167         }\r
168 \r
169         private void SetShips(dynamic json)\r
170         {\r
171             foreach (var entry in json)\r
172                 _shipInventory.Add(CreateShipStatus(entry));\r
173         }\r
174 \r
175         public void SaveBattleResult()\r
176         {\r
177             _battleResult = _fleets.Where(fleet =>\r
178                     fleet.State >= FleetState.Sortie && !fleet.Ships[0].Spec.IsRepairShip)\r
179                 .SelectMany(fleet => fleet.Ships).ToArray();\r
180         }\r
181 \r
182         public void ClearBattleResult()\r
183         {\r
184             _battleResult = new ShipStatus[0];\r
185         }\r
186 \r
187         private void VerifyBattleResult()\r
188         {\r
189             BattleResultDiff = (from assumed in _battleResult\r
190                 let actual = GetShip(assumed.Id)\r
191                 where !assumed.Escaped && assumed.NowHp != actual.NowHp\r
192                 select new ShipStatusPair(assumed, actual)).ToArray();\r
193             ClearBattleResult();\r
194         }\r
195 \r
196         public void SaveBattleStartStatus()\r
197         {\r
198             BattleStartStatus = _fleets.Where(fleet => fleet.State >= FleetState.Sortie)\r
199                 .SelectMany(fleet => fleet.Ships.Select(ship => (ShipStatus)ship.Clone())).ToArray();\r
200         }\r
201 \r
202         public void InspectDeck(dynamic json)\r
203         {\r
204             foreach (var entry in json)\r
205             {\r
206                 var fleet = (int)entry.api_id - 1;\r
207                 _fleets[fleet].Deck = (int[])entry.api_ship;\r
208                 if ((int)entry.api_mission[0] != 0)\r
209                     _fleets[fleet].State = FleetState.Mission;\r
210             }\r
211         }\r
212 \r
213         private ShipStatus CreateShipStatus(dynamic entry)\r
214         {\r
215             return new ShipStatus\r
216             {\r
217                 Id = (int)entry.api_id,\r
218                 Spec = _shipMaster.GetSpec((int)entry.api_ship_id),\r
219                 Level = (int)entry.api_lv,\r
220                 ExpToNext = (int)entry.api_exp[1],\r
221                 MaxHp = (int)entry.api_maxhp,\r
222                 NowHp = (int)entry.api_nowhp,\r
223                 Speed = entry.api_soku() ? (int)entry.api_soku : 0,\r
224                 Cond = (int)entry.api_cond,\r
225                 Fuel = (int)entry.api_fuel,\r
226                 Bull = (int)entry.api_bull,\r
227                 OnSlot = (int[])entry.api_onslot,\r
228                 GetItem = item => _itemInventory[item.Id],\r
229                 Slot = ((int[])entry.api_slot).Select(item => new ItemStatus(item)).ToArray(),\r
230                 SlotEx = entry.api_slot_ex() ? new ItemStatus((int)entry.api_slot_ex) : new ItemStatus(0),\r
231                 NdockTime = (int)entry.api_ndock_time,\r
232                 NdockItem = (int[])entry.api_ndock_item,\r
233                 LoS = (int)entry.api_sakuteki[0],\r
234                 Firepower = (int)entry.api_karyoku[0],\r
235                 Torpedo = (int)entry.api_raisou[0],\r
236                 AntiSubmarine = (int)entry.api_taisen[0],\r
237                 AntiAir = (int)entry.api_taiku[0],\r
238                 Lucky = (int)entry.api_lucky[0],\r
239                 Locked = entry.api_locked() && entry.api_locked == 1\r
240             };\r
241         }\r
242 \r
243         private void InspectBasic(dynamic json)\r
244         {\r
245             _hqLevel = (int)json.api_level;\r
246             Counter.Max = (int)json.api_max_chara;\r
247         }\r
248 \r
249         public void InspectCharge(dynamic json)\r
250         {\r
251             foreach (var entry in json.api_ship)\r
252             {\r
253                 var status = _shipInventory[(int)entry.api_id];\r
254                 status.Bull = (int)entry.api_bull;\r
255                 status.Fuel = (int)entry.api_fuel;\r
256                 status.OnSlot = (from num in (dynamic[])entry.api_onslot select (int)num).ToArray();\r
257             }\r
258         }\r
259 \r
260         public void InspectChange(string request)\r
261         {\r
262             var values = HttpUtility.ParseQueryString(request);\r
263             var dstFleet = _fleets[int.Parse(values["api_id"]) - 1];\r
264             var dstIdx = int.Parse(values["api_ship_idx"]);\r
265             var shipId = int.Parse(values["api_ship_id"]);\r
266 \r
267             if (shipId == -2)\r
268             {\r
269                 dstFleet.WithdrawAccompanyingShips();\r
270                 return;\r
271             }\r
272             if (shipId == -1)\r
273             {\r
274                 dstFleet.WithdrawShip(dstIdx);\r
275                 return;\r
276             }\r
277             var srcFleet = FindFleet(shipId, out var srcIdx);\r
278             var prevShipId = dstFleet.SetShip(dstIdx, shipId);\r
279             if (srcFleet == null)\r
280                 return;\r
281             // 入れ替えの場合\r
282             srcFleet.SetShip(srcIdx, prevShipId);\r
283             if (prevShipId == -1)\r
284                 srcFleet.WithdrawShip(srcIdx);\r
285         }\r
286 \r
287         private Fleet FindFleet(int ship, out int idx)\r
288         {\r
289             foreach (var fleet in _fleets)\r
290             {\r
291                 idx = fleet.Deck.ToList().IndexOf(ship);\r
292                 if (idx < 0)\r
293                     continue;\r
294                 return fleet;\r
295             }\r
296             idx = -1;\r
297             return null;\r
298         }\r
299 \r
300         public void InspectPowerUp(string request, dynamic json)\r
301         {\r
302             var values = HttpUtility.ParseQueryString(request);\r
303             var ships = values["api_id_items"].Split(',').Select(int.Parse).ToArray();\r
304             if (!_shipInventory.Contains(ships[0])) // 二重に実行された場合\r
305                 return;\r
306             _itemInventory.Remove(ships.SelectMany(id => _shipInventory[id].Slot));\r
307             _shipInventory.Remove(ships);\r
308             SetShipAndDeck(new[] {json.api_ship}, json.api_deck);\r
309         }\r
310 \r
311         public void InspectSlotExchange(dynamic json)\r
312         {\r
313             UpdateShips(new[] {json.api_ship_data});\r
314         }\r
315 \r
316         public void InspectSlotDeprive(dynamic json)\r
317         {\r
318             UpdateShips(new[] {json.api_ship_data.api_set_ship, json.api_ship_data.api_unset_ship});\r
319         }\r
320 \r
321         public void InspectMarriage(dynamic json)\r
322         {\r
323             UpdateShips(new[] {json});\r
324         }\r
325 \r
326         private void UpdateShips(dynamic json)\r
327         {\r
328             SetShips(json);\r
329             foreach (var fleet in _fleets)\r
330                 fleet.SetDeck(); // ShipStatusの差し替え\r
331         }\r
332 \r
333         public void InspectDestroyShip(string request, dynamic json)\r
334         {\r
335             var values = HttpUtility.ParseQueryString(request);\r
336             var delItem = int.Parse(values["api_slot_dest_flag"] ?? "0") == 1;\r
337             foreach (var shipId in values["api_ship_id"].Split(',').Select(int.Parse))\r
338             {\r
339                 if (delItem)\r
340                     _itemInventory.Remove(_shipInventory[shipId].AllSlot);\r
341                 var srcFleet = FindFleet(shipId, out var srcIdx);\r
342                 srcFleet?.WithdrawShip(srcIdx);\r
343                 _shipInventory.Remove(shipId);\r
344             }\r
345         }\r
346 \r
347         public void InspectCombined(string request)\r
348         {\r
349             var values = HttpUtility.ParseQueryString(request);\r
350             _fleets[0].CombinedType = _fleets[1].CombinedType = (CombinedType)int.Parse(values["api_combined_type"]);\r
351         }\r
352 \r
353         public void InspectMapStart(string request)\r
354         {\r
355             var values = HttpUtility.ParseQueryString(request);\r
356             var fleet = int.Parse(values["api_deck_id"]) - 1;\r
357             if (_fleets[0].CombinedType == 0 || fleet > 1)\r
358             {\r
359                 _fleets[fleet].State = FleetState.Sortie;\r
360             }\r
361             else\r
362             {\r
363                 _fleets[0].State = _fleets[1].State = FleetState.Sortie;\r
364             }\r
365             SetBadlyDamagedShips();\r
366         }\r
367 \r
368         public void StartPractice(string request)\r
369         {\r
370             var values = HttpUtility.ParseQueryString(request);\r
371             var fleet = int.Parse(values["api_deck_id"]) - 1;\r
372             _fleets[fleet].State = FleetState.Practice;\r
373         }\r
374 \r
375         public IReadOnlyList<Fleet> Fleets => _fleets;\r
376 \r
377         public ShipStatus GetShip(int id) => _shipInventory[id];\r
378 \r
379         public void SetItemHolder()\r
380         {\r
381             foreach (var ship in _shipInventory.AllShips)\r
382             {\r
383                 foreach (var item in ship.Slot)\r
384                     _itemInventory[item.Id].Holder = ship;\r
385                 _itemInventory[ship.SlotEx.Id].Holder = ship;\r
386             }\r
387         }\r
388 \r
389         public ShipSpec GetSpec(int id) => _shipMaster.GetSpec(id);\r
390 \r
391         public ShipStatus[] ShipList => _shipInventory.AllShips.ToArray();\r
392 \r
393         public ShipStatus[] GetRepairList(DockInfo dockInfo)\r
394             => (from s in ShipList\r
395                 where s.NowHp < s.MaxHp && !dockInfo.InNDock(s.Id) &&\r
396                       (s.Fleet == null || s.Fleet.State != FleetState.Practice)\r
397                 select s).OrderByDescending(s => s.RepairTime).ToArray();\r
398 \r
399         public string[] BadlyDamagedShips { get; private set; } = new string[0];\r
400 \r
401         public void SetBadlyDamagedShips()\r
402         {\r
403             BadlyDamagedShips =\r
404             (from s in _fleets.Where(fleet => fleet.State == FleetState.Sortie)\r
405                     .SelectMany(fleet => fleet.CombinedType != 0 && fleet.Number == 1\r
406                         ? fleet.ActualShips.Skip(1) // 第二艦隊の旗艦を除く\r
407                         : fleet.ActualShips)\r
408                 where !s.Escaped && s.DamageLevel == ShipStatus.Damage.Badly\r
409                 select s.Name).ToArray();\r
410         }\r
411 \r
412         public void ClearBadlyDamagedShips()\r
413         {\r
414             BadlyDamagedShips = new string[0];\r
415         }\r
416 \r
417         public void SetEscapedShips(List<int> ids)\r
418         {\r
419             foreach (var id in ids)\r
420                 _shipInventory[id].Escaped = true;\r
421         }\r
422     }\r
423 }