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 readonly List<int> _escapedShips = new List<int>();\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 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)).ToArray();\r
70             _itemInventory = itemInventory;\r
71             Counter = new AlarmCounter(() => _shipInventory.Count) {Margin = 5};\r
72         }\r
73 \r
74         public void InspectMaster(dynamic json)\r
75         {\r
76             _shipMaster.Inspect(json);\r
77             _shipInventory.Clear();\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                 _shipInventory.Clear();\r
85                 _escapedShips.Clear();\r
86                 for (var i = 0; i < FleetCount; i++)\r
87                     _fleets[i].State = FleetState.Port;\r
88                 InspectShipDataAndDeck(json.api_ship, json.api_deck_port);\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                 VerifyBattleResult();\r
93             }\r
94             else if (json.api_data()) // ship2\r
95             {\r
96                 InspectShipDataAndDeck(json.api_data, json.api_data_deck);\r
97             }\r
98             else if (json.api_ship_data()) // ship3とship_deck\r
99             {\r
100                 InspectShipDataAndDeck(json.api_ship_data, json.api_deck_data);\r
101                 VerifyBattleResult();\r
102                 // ship_deckでドロップ艦を反映する\r
103                 if (DropShipId != -1)\r
104                 {\r
105                     _shipInventory.InflateCount(1);\r
106                     var num = _shipMaster.GetSpec(DropShipId).NumEquips;\r
107                     if (num > 0)\r
108                         _itemInventory.InflateCount(num);\r
109                 }\r
110             }\r
111             else if (json.api_ship()) // getship\r
112             {\r
113                 InspectShipData(new[] {json.api_ship});\r
114             }\r
115             DropShipId = -1;\r
116         }\r
117 \r
118         public void SaveBattleResult()\r
119         {\r
120             _battleResult = _fleets.Where(fleet =>\r
121                     fleet.State >= FleetState.Sortie && !fleet.Ships[0].Spec.IsRepairShip)\r
122                 .SelectMany(fleet => fleet.Ships).ToArray();\r
123         }\r
124 \r
125         public void ClearBattleResult()\r
126         {\r
127             _battleResult = new ShipStatus[0];\r
128         }\r
129 \r
130         private void VerifyBattleResult()\r
131         {\r
132             BattleResultDiff = (from assumed in _battleResult\r
133                 let actual = GetShip(assumed.Id)\r
134                 where !assumed.Escaped && assumed.NowHp != actual.NowHp\r
135                 select new ShipStatusPair(assumed, actual)).ToArray();\r
136             ClearBattleResult();\r
137         }\r
138 \r
139         public void SaveBattleStartStatus()\r
140         {\r
141             BattleStartStatus = _fleets.Where(fleet => fleet.State >= FleetState.Sortie)\r
142                 .SelectMany(fleet => fleet.Ships.Select(ship => (ShipStatus)ship.Clone())).ToArray();\r
143         }\r
144 \r
145         private void InspectShipDataAndDeck(dynamic ship, dynamic deck)\r
146         {\r
147             InspectShipData(ship);\r
148             InspectDeck(deck); // FleetのDeckを設定した時点でShipStatusを取得するので必ずdeckが後\r
149         }\r
150 \r
151         public void InspectDeck(dynamic json)\r
152         {\r
153             foreach (var entry in json)\r
154             {\r
155                 var fleet = (int)entry.api_id - 1;\r
156                 _fleets[fleet].Deck = (int[])entry.api_ship;\r
157                 if ((int)entry.api_mission[0] != 0)\r
158                     _fleets[fleet].State = FleetState.Mission;\r
159             }\r
160         }\r
161 \r
162         private void InspectShipData(dynamic json)\r
163         {\r
164             foreach (var entry in json)\r
165             {\r
166                 var id = (int)entry.api_id;\r
167                 var ship = new ShipStatus\r
168                 {\r
169                     Id = id,\r
170                     Spec = _shipMaster.GetSpec((int)entry.api_ship_id),\r
171                     Level = (int)entry.api_lv,\r
172                     ExpToNext = (int)entry.api_exp[1],\r
173                     MaxHp = (int)entry.api_maxhp,\r
174                     NowHp = (int)entry.api_nowhp,\r
175                     Cond = (int)entry.api_cond,\r
176                     Fuel = (int)entry.api_fuel,\r
177                     Bull = (int)entry.api_bull,\r
178                     OnSlot = (int[])entry.api_onslot,\r
179                     GetItem = item => _itemInventory[item.Id],\r
180                     Slot = ((int[])entry.api_slot).Select(item => new ItemStatus(item)).ToArray(),\r
181                     SlotEx = entry.api_slot_ex() ? new ItemStatus((int)entry.api_slot_ex) : new ItemStatus(0),\r
182                     NdockTime = (int)entry.api_ndock_time,\r
183                     NdockItem = (int[])entry.api_ndock_item,\r
184                     LoS = (int)entry.api_sakuteki[0],\r
185                     Firepower = (int)entry.api_karyoku[0],\r
186                     Torpedo = (int)entry.api_raisou[0],\r
187                     AntiSubmarine = (int)entry.api_taisen[0],\r
188                     AntiAir = (int)entry.api_taiku[0],\r
189                     Lucky = (int)entry.api_lucky[0],\r
190                     Locked = entry.api_locked() && entry.api_locked == 1,\r
191                     Escaped = _escapedShips.Contains(id)\r
192                 };\r
193                 _shipInventory.Add(ship);\r
194                 _numEquipsChecker.Check(ship);\r
195             }\r
196             _numEquipsChecker.MaxId = _shipInventory.MaxId;\r
197         }\r
198 \r
199         private void InspectBasic(dynamic json)\r
200         {\r
201             _hqLevel = (int)json.api_level;\r
202             Counter.Max = (int)json.api_max_chara;\r
203         }\r
204 \r
205         public void InspectCharge(dynamic json)\r
206         {\r
207             foreach (var entry in json.api_ship)\r
208             {\r
209                 var status = _shipInventory[(int)entry.api_id];\r
210                 status.Bull = (int)entry.api_bull;\r
211                 status.Fuel = (int)entry.api_fuel;\r
212                 status.OnSlot = (from num in (dynamic[])entry.api_onslot select (int)num).ToArray();\r
213             }\r
214         }\r
215 \r
216         public void InspectChange(string request)\r
217         {\r
218             var values = HttpUtility.ParseQueryString(request);\r
219             var dstFleet = _fleets[int.Parse(values["api_id"]) - 1];\r
220             var dstIdx = int.Parse(values["api_ship_idx"]);\r
221             var shipId = int.Parse(values["api_ship_id"]);\r
222 \r
223             if (shipId == -2)\r
224             {\r
225                 dstFleet.WithdrawAccompanyingShips();\r
226                 return;\r
227             }\r
228             if (shipId == -1)\r
229             {\r
230                 dstFleet.WithdrawShip(dstIdx);\r
231                 return;\r
232             }\r
233             var srcFleet = FindFleet(shipId, out var srcIdx);\r
234             var prevShipId = dstFleet.SetShip(dstIdx, shipId);\r
235             if (srcFleet == null)\r
236                 return;\r
237             // 入れ替えの場合\r
238             srcFleet.SetShip(srcIdx, prevShipId);\r
239             if (prevShipId == -1)\r
240                 srcFleet.WithdrawShip(srcIdx);\r
241         }\r
242 \r
243         private Fleet FindFleet(int ship, out int idx)\r
244         {\r
245             foreach (var fleet in _fleets)\r
246             {\r
247                 idx = fleet.Deck.ToList().IndexOf(ship);\r
248                 if (idx < 0)\r
249                     continue;\r
250                 return fleet;\r
251             }\r
252             idx = -1;\r
253             return null;\r
254         }\r
255 \r
256         public void InspectPowerUp(string request, dynamic json)\r
257         {\r
258             var values = HttpUtility.ParseQueryString(request);\r
259             var ships = values["api_id_items"].Split(',').Select(int.Parse).ToArray();\r
260             if (!_shipInventory.Contains(ships[0])) // 二重に実行された場合\r
261                 return;\r
262             _itemInventory.Remove(ships.SelectMany(id => _shipInventory[id].Slot));\r
263             _shipInventory.Remove(ships);\r
264             InspectShipDataAndDeck(new[]{json.api_ship}, json.api_deck);\r
265         }\r
266 \r
267         public void InspectSlotExchange(string request, dynamic json)\r
268         {\r
269             var values = HttpUtility.ParseQueryString(request);\r
270             var ship = int.Parse(values["api_id"]);\r
271             _shipInventory[ship].Slot = ((int[])json.api_slot).Select(id => new ItemStatus(id)).ToArray();\r
272         }\r
273 \r
274         public void InspectSlotDeprive(dynamic json)\r
275         {\r
276             InspectShipData(new[] {json.api_ship_data.api_set_ship, json.api_ship_data.api_unset_ship});\r
277             foreach (var fleet in _fleets)\r
278                 fleet.SetDeck(); // ShipStatusの差し替え\r
279         }\r
280 \r
281         public void InspectMarriage(dynamic json)\r
282         {\r
283             InspectShipData(new[]{json});\r
284             foreach (var fleet in _fleets)\r
285                 fleet.SetDeck(); // ShipStatusの差し替え\r
286         }\r
287 \r
288         public void InspectDestroyShip(string request, dynamic json)\r
289         {\r
290             var values = HttpUtility.ParseQueryString(request);\r
291             var delItem = int.Parse(values["api_slot_dest_flag"] ?? "0") == 1;\r
292             foreach (var shipId in values["api_ship_id"].Split(',').Select(int.Parse))\r
293             {\r
294                 if (delItem)\r
295                     _itemInventory.Remove(_shipInventory[shipId].AllSlot);\r
296                 var srcFleet = FindFleet(shipId, out var srcIdx);\r
297                 srcFleet?.WithdrawShip(srcIdx);\r
298                 _shipInventory.Remove(shipId);\r
299             }\r
300         }\r
301 \r
302         public void InspectCombined(string request)\r
303         {\r
304             var values = HttpUtility.ParseQueryString(request);\r
305             _fleets[0].CombinedType = _fleets[1].CombinedType = (CombinedType)int.Parse(values["api_combined_type"]);\r
306         }\r
307 \r
308         public void InspectMapStart(string request)\r
309         {\r
310             var values = HttpUtility.ParseQueryString(request);\r
311             var fleet = int.Parse(values["api_deck_id"]) - 1;\r
312             if (_fleets[0].CombinedType == 0 || fleet > 1)\r
313             {\r
314                 _fleets[fleet].State = FleetState.Sortie;\r
315             }\r
316             else\r
317             {\r
318                 _fleets[0].State = _fleets[1].State = FleetState.Sortie;\r
319             }\r
320             SetBadlyDamagedShips();\r
321         }\r
322 \r
323         public void StartPractice(string request)\r
324         {\r
325             var values = HttpUtility.ParseQueryString(request);\r
326             var fleet = int.Parse(values["api_deck_id"]) - 1;\r
327             _fleets[fleet].State = FleetState.Practice;\r
328         }\r
329 \r
330         public IReadOnlyList<Fleet> Fleets => _fleets;\r
331 \r
332         public ShipStatus GetShip(int id) => _shipInventory[id];\r
333 \r
334         public void SetItemHolder()\r
335         {\r
336             foreach (var ship in _shipInventory.AllShips)\r
337             {\r
338                 foreach (var item in ship.Slot)\r
339                     _itemInventory[item.Id].Holder = ship;\r
340                 _itemInventory[ship.SlotEx.Id].Holder = ship;\r
341             }\r
342         }\r
343 \r
344         public ShipSpec GetSpec(int id) => _shipMaster.GetSpec(id);\r
345 \r
346         public ShipStatus[] ShipList => _shipInventory.AllShips.ToArray();\r
347 \r
348         public ShipStatus[] GetRepairList(DockInfo dockInfo)\r
349             => (from s in ShipList\r
350                 where s.NowHp < s.MaxHp && !dockInfo.InNDock(s.Id) &&\r
351                       (s.Fleet == null || s.Fleet.State != FleetState.Practice)\r
352                 select s).OrderByDescending(s => s.RepairTime).ToArray();\r
353 \r
354         public string[] BadlyDamagedShips { get; private set; } = new string[0];\r
355 \r
356         public void SetBadlyDamagedShips()\r
357         {\r
358             BadlyDamagedShips =\r
359             (from s in _fleets.Where(fleet => fleet.State == FleetState.Sortie)\r
360                     .SelectMany(fleet => fleet.CombinedType != 0 && fleet.Number == 1\r
361                         ? fleet.ActualShips.Skip(1) // 第二艦隊の旗艦を除く\r
362                         : fleet.ActualShips)\r
363                 where !s.Escaped && s.DamageLevel == ShipStatus.Damage.Badly\r
364                 select s.Name).ToArray();\r
365         }\r
366 \r
367         public void ClearBadlyDamagedShips()\r
368         {\r
369             BadlyDamagedShips = new string[0];\r
370         }\r
371 \r
372         public void SetEscapedShips(List<int> ids)\r
373         {\r
374             _escapedShips.AddRange(ids);\r
375             foreach (var id in ids)\r
376                 _shipInventory[id].Escaped = true;\r
377         }\r
378     }\r
379 }