OSDN Git Service

入渠の終わった艦娘が要修復リストに載るのを直す
[kancollesniffer/KancolleSniffer.git] / KancolleSniffer / ShipInfo.cs
1 // Copyright (C) 2013, 2014 Kazuhiro Fujieda <fujieda@users.sourceforge.jp>\r
2 // \r
3 // This program is part of KancolleSniffer.\r
4 //\r
5 // KancolleSniffer is free software: you can redistribute it and/or modify\r
6 // it under the terms of the GNU General Public License as published by\r
7 // the Free Software Foundation, either version 3 of the License, or\r
8 // (at your option) any later version.\r
9 //\r
10 // This program is distributed in the hope that it will be useful,\r
11 // but WITHOUT ANY WARRANTY; without even the implied warranty of\r
12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
13 // GNU General Public License for more details.\r
14 //\r
15 // You should have received a copy of the GNU General Public License\r
16 // along with this program; if not, see <http://www.gnu.org/licenses/>.\r
17 \r
18 using System;\r
19 using System.Collections.Generic;\r
20 using System.Linq;\r
21 using System.Web;\r
22 \r
23 namespace KancolleSniffer\r
24 {\r
25     public class ShipStatus\r
26     {\r
27         public ShipSpec Spec { get; set; }\r
28 \r
29         public string Name\r
30         {\r
31             get { return Spec.Name; }\r
32         }\r
33 \r
34         public int Level { get; set; }\r
35         public int ExpToNext { get; set; }\r
36         public int MaxHp { get; set; }\r
37         public int NowHp { get; set; }\r
38         public int Cond { get; set; }\r
39         public int Fuel { get; set; }\r
40         public int Bull { get; set; }\r
41         public int[] OnSlot { get; set; }\r
42         public int[] Slot { get; set; }\r
43 \r
44         public Damage DamageLevel\r
45         {\r
46             get { return CalcDamage(NowHp, MaxHp); }\r
47         }\r
48 \r
49         public enum Damage\r
50         {\r
51             Minor,\r
52             Small,\r
53             Half,\r
54             Badly\r
55         }\r
56 \r
57         public static Damage CalcDamage(int now, int max)\r
58         {\r
59             var ratio = max == 0 ? 1 : (double)now / max;\r
60             return ratio > 0.75 ? Damage.Minor : ratio > 0.5 ? Damage.Small : ratio > 0.25 ? Damage.Half : Damage.Badly;\r
61         }\r
62 \r
63         public TimeSpan RepairTime()\r
64         {\r
65             return RepairTime(MaxHp - NowHp);\r
66         }\r
67 \r
68         public TimeSpan RepairTime(int damage)\r
69         {\r
70             var weight = Spec.RepairWeight;\r
71             var level = Level < 12 ? Level * 10 : Level * 5 + Math.Floor(Math.Sqrt(Level - 11)) * 10 + 50;\r
72             return TimeSpan.FromSeconds(Math.Floor(level * weight * damage) + 30);\r
73         }\r
74     }\r
75 \r
76     public struct ChargeStatus\r
77     {\r
78         public int Fuel { get; set; }\r
79         public int Bull { get; set; }\r
80     }\r
81 \r
82     public class RepairStatus\r
83     {\r
84         public int Fleet { private set; get; }\r
85         public string Name { private set; get; }\r
86         public TimeSpan Time { private set; get; }\r
87 \r
88         public RepairStatus(int fleet, string name, TimeSpan time)\r
89         {\r
90             Fleet = fleet;\r
91             Name = name;\r
92             Time = time;\r
93         }\r
94     }\r
95 \r
96     public class ShipInfo\r
97     {\r
98         public const int FleetCount = 4;\r
99         public const int MemberCount = 6;\r
100 \r
101         private readonly int[][] _decks = new int[FleetCount][];\r
102         private readonly Dictionary<int, ShipStatus> _shipInfo = new Dictionary<int, ShipStatus>();\r
103         private readonly ConditionTimer _conditionTimer;\r
104 \r
105         private readonly ShipMaster _shipMaster;\r
106         private readonly ItemInfo _itemInfo;\r
107 \r
108         public ShipInfo(ShipMaster shipMaster, ItemInfo itemInfo)\r
109         {\r
110             _shipMaster = shipMaster;\r
111             _itemInfo = itemInfo;\r
112             _conditionTimer = new ConditionTimer(this);\r
113 \r
114             for (var fleet = 0; fleet < FleetCount; fleet++)\r
115             {\r
116                 var deck = new int[MemberCount];\r
117                 for (var i = 0; i < deck.Length; i++)\r
118                     deck[i] = -1;\r
119                 _decks[fleet] = deck;\r
120             }\r
121         }\r
122 \r
123         public void InspectShip(dynamic json)\r
124         {\r
125             if (json.api_deck_port()) // port\r
126             {\r
127                 _shipInfo.Clear();\r
128                 InspectDeck(json.api_deck_port);\r
129                 InspectShipData(json.api_ship);\r
130                 _itemInfo.NowShips = ((object[])json.api_ship).Length;\r
131             }\r
132             else if (json.api_data()) // ship2\r
133             {\r
134                 _shipInfo.Clear();\r
135                 InspectDeck(json.api_data_deck);\r
136                 InspectShipData(json.api_data);\r
137                 _itemInfo.NowShips = ((object[])json.api_data).Length;\r
138             }\r
139             else if (json.api_ship_data()) // ship3\r
140             {\r
141                 // 一隻分のデータしか来ないことがあるので艦娘数を数えない\r
142                 InspectDeck(json.api_deck_data);\r
143                 InspectShipData(json.api_ship_data);\r
144             }\r
145             else if (json.api_ship()) // getship\r
146             {\r
147                 InspectShipData(new[] {json.api_ship});\r
148             }\r
149         }\r
150 \r
151         private 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                 var deck = _decks[fleet];\r
157                 for (var i = 0; i < deck.Length; i++)\r
158                     deck[i] = (int)entry.api_ship[i];\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                 _shipInfo[(int)entry.api_id] = new ShipStatus\r
167                 {\r
168                     Spec = _shipMaster[(int)entry.api_ship_id],\r
169                     Level = (int)entry.api_lv,\r
170                     ExpToNext = (int)entry.api_exp[1],\r
171                     MaxHp = (int)entry.api_maxhp,\r
172                     NowHp = (int)entry.api_nowhp,\r
173                     Cond = (int)entry.api_cond,\r
174                     Fuel = (int)entry.api_fuel,\r
175                     Bull = (int)entry.api_bull,\r
176                     OnSlot = (from num in (dynamic[])entry.api_onslot select (int)num).ToArray(),\r
177                     Slot = (from num in (dynamic[])entry.api_slot select (int)num).ToArray()\r
178                 };\r
179                 _shipInfo[-1] = new ShipStatus {Spec = _shipMaster[-1]};\r
180             }\r
181             _conditionTimer.SetTimer();\r
182         }\r
183 \r
184         public void InspectCharge(dynamic json)\r
185         {\r
186             foreach (var entry in json.api_ship)\r
187             {\r
188                 var status = _shipInfo[(int)entry.api_id];\r
189                 status.Bull = (int)entry.api_bull;\r
190                 status.Fuel = (int)entry.api_fuel;\r
191                 status.OnSlot = (from num in (dynamic[])entry.api_onslot select (int)num).ToArray();\r
192             }\r
193             var material = (int[])json.api_material;\r
194             for (var i = 0; i < material.Length; i++)\r
195                 _itemInfo.MaterialHistory[i].Now = material[i];\r
196         }\r
197 \r
198         public void InspectChange(string request)\r
199         {\r
200             var values = HttpUtility.ParseQueryString(request);\r
201             var fleet = int.Parse(values["api_id"]) - 1;\r
202             var idx = int.Parse(values["api_ship_idx"]);\r
203             var ship = int.Parse(values["api_ship_id"]);\r
204             if (idx == -1)\r
205             {\r
206                 var deck = _decks[fleet];\r
207                 for (var i = 1; i < deck.Length; i++)\r
208                     deck[i] = -1;\r
209                 _conditionTimer.Invalidate(fleet);\r
210                 return;\r
211             }\r
212             if (ship == -1)\r
213             {\r
214                 WithdrowShip(fleet, idx);\r
215                 return;\r
216             }\r
217             int oi;\r
218             var of = FindFleet(ship, out oi);\r
219             if (of != -1)\r
220             {\r
221                 // 入れ替えの場合\r
222                 if ((_decks[of][oi] = _decks[fleet][idx]) == -1)\r
223                     WithdrowShip(of, oi);\r
224                 if (of != fleet)\r
225                     _conditionTimer.Invalidate(of);\r
226             }\r
227             _decks[fleet][idx] = ship;\r
228             _conditionTimer.Invalidate(fleet);\r
229         }\r
230 \r
231         private int FindFleet(int ship, out int idx)\r
232         {\r
233             for (var f = 0; f < _decks.Length; f++)\r
234             {\r
235                 idx = Array.FindIndex(_decks[f], id => id == ship);\r
236                 if (idx < 0)\r
237                     continue;\r
238                 return f;\r
239             }\r
240             idx = -1;\r
241             return -1;\r
242         }\r
243 \r
244         private void WithdrowShip(int fleet, int idx)\r
245         {\r
246             var deck = _decks[fleet];\r
247             for (var i = idx; i < deck.Length - 1; i++)\r
248                 deck[i] = deck[i + 1];\r
249             deck[deck.Length - 1] = -1;\r
250             _conditionTimer.Invalidate(fleet);\r
251         }\r
252 \r
253         public void InspectPowerup(string request, dynamic json)\r
254         {\r
255             var values = HttpUtility.ParseQueryString(request);\r
256             var ships = values["api_id_items"].Split(',');\r
257             _itemInfo.NowShips -= ships.Length;\r
258             _itemInfo.NowItems -= (from s in ships select SlotItemCount(int.Parse(s))).Sum();\r
259             foreach (var ship in ships)\r
260                 _shipInfo.Remove(int.Parse(ship));\r
261             InspectDeck(json.api_deck);\r
262             InspectShip(json.api_ship);\r
263         }\r
264 \r
265         public void InspectDestroyShip(string request, dynamic json)\r
266         {\r
267             var values = HttpUtility.ParseQueryString(request);\r
268             var ship = int.Parse(values["api_ship_id"]);\r
269             _itemInfo.NowShips -= 1;\r
270             _itemInfo.NowItems -= SlotItemCount(ship);\r
271             int oi;\r
272             var of = FindFleet(ship, out oi);\r
273             if (of != -1)\r
274                 WithdrowShip(of, oi);\r
275             _shipInfo.Remove(ship);\r
276 \r
277             var material = (int[])json.api_material;\r
278             for (var i = 0; i < material.Length; i++)\r
279                 _itemInfo.MaterialHistory[i].Now = material[i];\r
280         }\r
281 \r
282         private int SlotItemCount(int id)\r
283         {\r
284             return _shipInfo[id].Slot.Count(item => item != -1);\r
285         }\r
286 \r
287         public void RepairShip(int id)\r
288         {\r
289             var s = _shipInfo[id];\r
290             s.NowHp = s.MaxHp;\r
291             s.Cond = Math.Max(40, s.Cond);\r
292             _conditionTimer.SetTimer();\r
293         }\r
294 \r
295         public ShipStatus[] GetShipStatuses(int fleet)\r
296         {\r
297             return (from id in _decks[fleet] where id != -1 select _shipInfo[id]).ToArray();\r
298         }\r
299 \r
300         public int[] GetDeck(int fleet)\r
301         {\r
302             return _decks[fleet];\r
303         }\r
304 \r
305         public ShipStatus this[int idx]\r
306         {\r
307             get { return _shipInfo[idx]; }\r
308         }\r
309 \r
310         public string[] GetConditionTimers(int fleet)\r
311         {\r
312             return _conditionTimer.GetTimerStrings(fleet);\r
313         }\r
314 \r
315         public ChargeStatus[] ChargeStatuses\r
316         {\r
317             get\r
318             {\r
319                 return (from deck in _decks\r
320                     select (from id in deck\r
321                         where id != -1\r
322                         let status = _shipInfo[id]\r
323                         select new {status.Bull, status.Fuel, status.Spec.BullMax, status.Spec.FuelMax})\r
324                         .Aggregate(\r
325                             new ChargeStatus(), (result, next) => new ChargeStatus\r
326                             {\r
327                                 Bull = Math.Max(result.Bull, CalcChargeState(next.Bull, next.BullMax)),\r
328                                 Fuel = Math.Max(result.Fuel, CalcChargeState(next.Fuel, next.FuelMax))\r
329                             })).ToArray();\r
330             }\r
331         }\r
332 \r
333         private int CalcChargeState(int now, int full)\r
334         {\r
335             if (full == 0 || now == full)\r
336                 return 0;\r
337             var ratio = (double)now / full;\r
338             if (ratio >= 7.0 / 9)\r
339                 return 1;\r
340             if (ratio >= 3.0 / 9)\r
341                 return 2;\r
342             if (ratio > 0)\r
343                 return 3;\r
344             return 4;\r
345         }\r
346 \r
347         public int GetAirSuperiority(int fleet)\r
348         {\r
349             return (from id in _decks[fleet]\r
350                 where id != -1\r
351                 let ship = _shipInfo[id]\r
352                 from slot in ship.Slot.Zip(ship.OnSlot, (s, o) => new {slot = s, onslot = o})\r
353                 select (int)Math.Floor(_itemInfo[slot.slot].TyKu * Math.Sqrt(slot.onslot))).Sum();\r
354         }\r
355 \r
356         public RepairStatus[] GetDamagedShipList(DockInfo dockInfo)\r
357         {\r
358             int oi;\r
359             return (from entry in _shipInfo\r
360                 let status = entry.Value\r
361                 where status.NowHp < status.MaxHp && !dockInfo.InNDock(entry.Key)\r
362                 select new RepairStatus(FindFleet(entry.Key, out oi), status.Name, status.RepairTime())).\r
363                 OrderByDescending(entry => entry.Time).ToArray();\r
364         }\r
365     }\r
366 }