OSDN Git Service

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