OSDN Git Service

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