OSDN Git Service

2aaae712db266559923e5f5df67dd423ff1db646
[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 using static System.Math;\r
20 \r
21 namespace KancolleSniffer.Model\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 ShipMaster _shipMaster;\r
30         private readonly ShipInventry _shipInventry;\r
31         private readonly ItemInventry _itemInventry;\r
32         private readonly List<int> _escapedShips = new List<int>();\r
33         private ShipStatus[] _battleResult = new ShipStatus[0];\r
34         private readonly NumEquipsChecker _numEquipsChecker = new NumEquipsChecker();\r
35         public AlarmCounter Counter { get; }\r
36         public int HqLevel { get; private set; }\r
37         public ShipStatusPair[] BattleResultDiff { get; private set; } = new ShipStatusPair[0];\r
38         public bool IsBattleResultError => BattleResultDiff.Length > 0;\r
39         public ShipStatus[] BattleStartStatus { get; private set; } = new ShipStatus[0];\r
40         public int DropShipId { private get; set; } = -1;\r
41 \r
42         private class NumEquipsChecker\r
43         {\r
44             public int MaxId { private get; set; } = int.MaxValue;\r
45 \r
46             public void Check(ShipStatus ship)\r
47             {\r
48                 var spec = ship.Spec;\r
49                 if (spec.NumEquips != -1 || ship.Id <= MaxId)\r
50                     return;\r
51                 spec.NumEquips = ship.Slot.Count(item => !item.Empty);\r
52             }\r
53         }\r
54 \r
55         public class ShipStatusPair\r
56         {\r
57             public ShipStatus Assumed { get; set; }\r
58             public ShipStatus Actual { get; set; }\r
59 \r
60             public ShipStatusPair(ShipStatus assumed, ShipStatus actual)\r
61             {\r
62                 Assumed = assumed;\r
63                 Actual = actual;\r
64             }\r
65         }\r
66 \r
67         public ShipInfo(ShipMaster shipMaster, ShipInventry shipInventry, ItemInventry itemInventry)\r
68         {\r
69             _shipMaster = shipMaster;\r
70             _shipInventry = shipInventry;\r
71             _fleets = Enumerable.Range(0, FleetCount).Select((x, i) => new Fleet(this, i)).ToArray();\r
72             _itemInventry = itemInventry;\r
73             Counter = new AlarmCounter(() => _shipInventry.Count){Margin = 4};\r
74         }\r
75 \r
76         public void InspectMaster(dynamic json)\r
77         {\r
78             _shipMaster.Inspect(json);\r
79             _shipInventry.Clear();\r
80         }\r
81 \r
82         public void InspectShip(dynamic json)\r
83         {\r
84             if (json.api_deck_port()) // port\r
85             {\r
86                 _shipInventry.Clear();\r
87                 for (var i = 0; i < FleetCount; i++)\r
88                     _fleets[i].State = FleetState.Port;\r
89                 InspectDeck(json.api_deck_port);\r
90                 InspectShipData(json.api_ship);\r
91                 InspectBasic(json.api_basic);\r
92                 if (json.api_combined_flag())\r
93                     _fleets[0].CombinedType = _fleets[1].CombinedType = (CombinedType)(int)json.api_combined_flag;\r
94                 VerifyBattleResult();\r
95             }\r
96             else if (json.api_data()) // ship2\r
97             {\r
98                 InspectDeck(json.api_data_deck);\r
99                 InspectShipData(json.api_data);\r
100             }\r
101             else if (json.api_ship_data()) // ship3とship_deck\r
102             {\r
103                 InspectDeck(json.api_deck_data);\r
104                 InspectShipData(json.api_ship_data);\r
105                 VerifyBattleResult();\r
106                 // ship_deckでドロップ艦を反映する\r
107                 if (DropShipId != -1)\r
108                 {\r
109                     _shipInventry.InflateCount(1);\r
110                     var num = _shipMaster.GetSpec(DropShipId).NumEquips;\r
111                     if (num > 0)\r
112                         _itemInventry.InflateCount(num);\r
113                 }\r
114             }\r
115             else if (json.api_ship()) // getshipとpowerup\r
116             {\r
117                 InspectShipData(new[] {json.api_ship});\r
118             }\r
119             DropShipId = -1;\r
120         }\r
121 \r
122         public void SaveBattleResult()\r
123         {\r
124             _battleResult = _fleets.Where(fleet =>\r
125                     fleet.State >= FleetState.Sortie && !GetStatus(fleet.Deck[0]).Spec.IsRepairShip)\r
126                 .SelectMany(fleet => fleet.Deck.Select(GetStatus)).ToArray();\r
127         }\r
128 \r
129         private void VerifyBattleResult()\r
130         {\r
131             BattleResultDiff = (from assumed in _battleResult\r
132                 let actual = GetStatus(assumed.Id)\r
133                 where !assumed.Escaped && assumed.NowHp != actual.NowHp\r
134                 select new ShipStatusPair(assumed, actual)).ToArray();\r
135             _battleResult = new ShipStatus[0];\r
136         }\r
137 \r
138         public void SaveBattleStartStatus()\r
139         {\r
140             BattleStartStatus = _fleets.Where(fleet => fleet.State >= FleetState.Sortie)\r
141                 .SelectMany(fleet => fleet.Deck.Select(id => (ShipStatus)GetStatus(id).Clone())).ToArray();\r
142         }\r
143 \r
144         public void InspectDeck(dynamic json)\r
145         {\r
146             foreach (var entry in json)\r
147             {\r
148                 var fleet = (int)entry.api_id - 1;\r
149                 _fleets[fleet].Deck = (int[])entry.api_ship;\r
150                 if ((int)entry.api_mission[0] != 0)\r
151                     _fleets[fleet].State = FleetState.Mission;\r
152             }\r
153         }\r
154 \r
155         private void InspectShipData(dynamic json)\r
156         {\r
157             foreach (var entry in json)\r
158             {\r
159                 var id = (int)entry.api_id;\r
160                 var ship = new ShipStatus\r
161                 {\r
162                     Id = id,\r
163                     Spec = _shipMaster.GetSpec((int)entry.api_ship_id),\r
164                     Level = (int)entry.api_lv,\r
165                     ExpToNext = (int)entry.api_exp[1],\r
166                     MaxHp = (int)entry.api_maxhp,\r
167                     NowHp = (int)entry.api_nowhp,\r
168                     Cond = (int)entry.api_cond,\r
169                     Fuel = (int)entry.api_fuel,\r
170                     Bull = (int)entry.api_bull,\r
171                     OnSlot = (int[])entry.api_onslot,\r
172                     GetItem = item => _itemInventry[item.Id],\r
173                     Slot = ((int[])entry.api_slot).Select(item => new ItemStatus(item)).ToArray(),\r
174                     SlotEx = entry.api_slot_ex() ? new ItemStatus((int)entry.api_slot_ex) : new ItemStatus(0),\r
175                     NdockTime = (int)entry.api_ndock_time,\r
176                     NdockItem = (int[])entry.api_ndock_item,\r
177                     LoS = (int)entry.api_sakuteki[0],\r
178                     Firepower = (int)entry.api_karyoku[0],\r
179                     Torpedo = (int)entry.api_raisou[0],\r
180                     AntiSubmarine = (int)entry.api_taisen[0],\r
181                     AntiAir = (int)entry.api_taiku[0],\r
182                     Lucky = (int)entry.api_lucky[0],\r
183                     Locked = entry.api_locked() && entry.api_locked == 1\r
184                 };\r
185                 _shipInventry.Add(ship);\r
186                 _numEquipsChecker.Check(ship);\r
187             }\r
188             _numEquipsChecker.MaxId = _shipInventry.MaxId;\r
189         }\r
190 \r
191         private void InspectBasic(dynamic json)\r
192         {\r
193             HqLevel = (int)json.api_level;\r
194             Counter.Max = (int)json.api_max_chara;\r
195         }\r
196 \r
197         public void InspectCharge(dynamic json)\r
198         {\r
199             foreach (var entry in json.api_ship)\r
200             {\r
201                 var status = _shipInventry[(int)entry.api_id];\r
202                 status.Bull = (int)entry.api_bull;\r
203                 status.Fuel = (int)entry.api_fuel;\r
204                 status.OnSlot = (from num in (dynamic[])entry.api_onslot select (int)num).ToArray();\r
205             }\r
206         }\r
207 \r
208         public void InspectChange(string request)\r
209         {\r
210             var values = HttpUtility.ParseQueryString(request);\r
211             var fleet = _fleets[int.Parse(values["api_id"]) - 1];\r
212             var idx = int.Parse(values["api_ship_idx"]);\r
213             var ship = int.Parse(values["api_ship_id"]);\r
214 \r
215             if (idx == -1)\r
216             {\r
217                 var deck = fleet.Deck;\r
218                 for (var i = 1; i < deck.Length; i++)\r
219                     deck[i] = -1;\r
220                 return;\r
221             }\r
222             if (ship == -1)\r
223             {\r
224                 WithdrowShip(fleet, idx);\r
225                 return;\r
226             }\r
227             var of = FindFleet(ship, out var oi);\r
228             var orig = fleet.Deck[idx];\r
229             fleet.Deck[idx] = ship;\r
230             if (of == null)\r
231                 return;\r
232             // 入れ替えの場合\r
233             if ((of.Deck[oi] = orig) == -1)\r
234                 WithdrowShip(of, oi);\r
235         }\r
236 \r
237         private Fleet FindFleet(int ship, out int idx)\r
238         {\r
239             foreach (var fleet in _fleets)\r
240             {\r
241                 idx = Array.FindIndex(fleet.Deck, id => id == ship);\r
242                 if (idx < 0)\r
243                     continue;\r
244                 return fleet;\r
245             }\r
246             idx = -1;\r
247             return null;\r
248         }\r
249 \r
250         private void WithdrowShip(Fleet fleet, int idx)\r
251         {\r
252             var deck = fleet.Deck;\r
253             var j = idx;\r
254             for (var i = idx + 1; i < deck.Length; i++)\r
255             {\r
256                 if (deck[i] != -1)\r
257                     deck[j++] = deck[i];\r
258             }\r
259             for (; j < deck.Length; j++)\r
260                 deck[j] = -1;\r
261         }\r
262 \r
263         public void InspectPowerup(string request, dynamic json)\r
264         {\r
265             var values = HttpUtility.ParseQueryString(request);\r
266             var ships = values["api_id_items"].Split(',').Select(int.Parse).ToArray();\r
267             if (!_shipInventry.Contains(ships[0])) // 二重に実行された場合\r
268                 return;\r
269             _itemInventry.Remove(ships.SelectMany(id => _shipInventry[id].Slot));\r
270             _shipInventry.Remove(ships);\r
271             InspectDeck(json.api_deck);\r
272             InspectShip(json);\r
273         }\r
274 \r
275         public void InspectSlotExchange(string request, dynamic json)\r
276         {\r
277             var values = HttpUtility.ParseQueryString(request);\r
278             var ship = int.Parse(values["api_id"]);\r
279             _shipInventry[ship].Slot = ((int[])json.api_slot).Select(id => new ItemStatus(id)).ToArray();\r
280         }\r
281 \r
282         public void InspectSlotDeprive(dynamic json)\r
283         {\r
284             InspectShipData(new[] {json.api_ship_data.api_set_ship, json.api_ship_data.api_unset_ship});\r
285         }\r
286 \r
287         public void InspectDestroyShip(string request, dynamic json)\r
288         {\r
289             var values = HttpUtility.ParseQueryString(request);\r
290             var delitem = int.Parse(values["api_slot_dest_flag"] ?? "0") == 1;\r
291             foreach (var id in values["api_ship_id"].Split(',').Select(int.Parse))\r
292             {\r
293                 if (delitem)\r
294                     _itemInventry.Remove(_shipInventry[id].AllSlot);\r
295                 var of = FindFleet(id, out var oi);\r
296                 if (of != null)\r
297                     WithdrowShip(of, oi);\r
298                 _shipInventry.Remove(id);\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 void RepairShip(int id)\r
331         {\r
332             var s = _shipInventry[id];\r
333             s.NowHp = s.MaxHp;\r
334             s.Cond = Max(40, s.Cond);\r
335         }\r
336 \r
337         public Fleet[] Fleets => _fleets;\r
338 \r
339         public ShipStatus GetStatus(int id) => FillUp(_shipInventry[id]);\r
340 \r
341         private ShipStatus FillUp(ShipStatus ship)\r
342         {\r
343             if (ship.Empty)\r
344                 return ship;\r
345             ship.Escaped = _escapedShips.Contains(ship.Id);\r
346             ship.Fleet = FindFleet(ship.Id, out var idx);\r
347             ship.DeckIndex = idx;\r
348             return ship;\r
349         }\r
350 \r
351         public void SetItemHolder()\r
352         {\r
353             foreach (var ship in _shipInventry.AllShips)\r
354             {\r
355                 foreach (var item in ship.Slot)\r
356                     _itemInventry[item.Id].Holder = ship;\r
357                 _itemInventry[ship.SlotEx.Id].Holder = ship;\r
358             }\r
359         }\r
360 \r
361         public ShipSpec GetSpec(int id) => _shipMaster.GetSpec(id);\r
362 \r
363         public ShipStatus[] ShipList => _shipInventry.AllShips.Select(FillUp).ToArray();\r
364 \r
365         public ShipStatus[] GetRepairList(DockInfo dockInfo)\r
366             => (from s in ShipList\r
367                 where s.NowHp < s.MaxHp && !dockInfo.InNDock(s.Id) &&\r
368                       (s.Fleet == null || s.Fleet.State != FleetState.Practice)\r
369                 select s).OrderByDescending(s => s.RepairTime).ToArray();\r
370 \r
371         public string[] BadlyDamagedShips { get; private set; } = new string[0];\r
372 \r
373         public void SetBadlyDamagedShips()\r
374         {\r
375             BadlyDamagedShips =\r
376             (from s in _fleets.Where(fleet => fleet.State == FleetState.Sortie)\r
377                     .SelectMany(fleet => fleet.Deck.Where(id => id != -1).Select(GetStatus))\r
378                 where !s.Escaped && s.DamageLevel == ShipStatus.Damage.Badly &&\r
379                       !(s.Fleet.CombinedType != 0 && s.Fleet.Number == 1 && s.DeckIndex == 0) // 第二艦隊の旗艦を除く\r
380                 select s.Name).ToArray();\r
381         }\r
382 \r
383         public void ClearBadlyDamagedShips()\r
384         {\r
385             BadlyDamagedShips = new string[0];\r
386         }\r
387 \r
388         public void SetEscapedShips(List<int> ships)\r
389         {\r
390             _escapedShips.AddRange(ships);\r
391         }\r
392 \r
393         public void ClearEscapedShips()\r
394         {\r
395             _escapedShips.Clear();\r
396         }\r
397     }\r
398 }