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                 InspectShipData(json.api_ship);\r
128                 InspectDeck(json.api_deck_port);\r
129                 _itemInfo.NowShips = ((object[])json.api_ship).Length;\r
130             }\r
131             else if (json.api_data()) // ship2\r
132             {\r
133                 InspectShipData(json.api_data);\r
134                 InspectDeck(json.api_data_deck);\r
135                 _itemInfo.NowShips = ((object[])json.api_data).Length;\r
136             }\r
137             else if (json.api_ship_data()) // ship3\r
138             {\r
139                 // 一隻分のデータしか来ないことがあるので艦娘数を数えない\r
140                 InspectShipData(json.api_ship_data);\r
141                 InspectDeck(json.api_deck_data);\r
142             }\r
143             else if (json.api_ship()) // getship\r
144             {\r
145                 InspectShipData(new[] {json.api_ship});\r
146             }\r
147         }\r
148 \r
149         private void InspectDeck(dynamic json)\r
150         {\r
151             foreach (var entry in json)\r
152             {\r
153                 var fleet = (int)entry.api_id - 1;\r
154                 var deck = _decks[fleet];\r
155                 for (var i = 0; i < deck.Length; i++)\r
156                     deck[i] = (int)entry.api_ship[i];\r
157             }\r
158         }\r
159 \r
160         private void InspectShipData(dynamic json)\r
161         {\r
162             foreach (var entry in json)\r
163             {\r
164                 _shipInfo[(int)entry.api_id] = new ShipStatus\r
165                 {\r
166                     Spec = _shipMaster[(int)entry.api_ship_id],\r
167                     Level = (int)entry.api_lv,\r
168                     ExpToNext = (int)entry.api_exp[1],\r
169                     MaxHp = (int)entry.api_maxhp,\r
170                     NowHp = (int)entry.api_nowhp,\r
171                     Cond = (int)entry.api_cond,\r
172                     Fuel = (int)entry.api_fuel,\r
173                     Bull = (int)entry.api_bull,\r
174                     OnSlot = (from num in (dynamic[])entry.api_onslot select (int)num).ToArray(),\r
175                     Slot = (from num in (dynamic[])entry.api_slot select (int)num).ToArray()\r
176                 };\r
177                 _shipInfo[-1] = new ShipStatus {Spec = _shipMaster[-1]};\r
178             }\r
179             _conditionTimer.SetTimer();\r
180         }\r
181 \r
182         public void InspectCharge(dynamic json)\r
183         {\r
184             foreach (var entry in json.api_ship)\r
185             {\r
186                 var status = _shipInfo[(int)entry.api_id];\r
187                 status.Bull = (int)entry.api_bull;\r
188                 status.Fuel = (int)entry.api_fuel;\r
189                 status.OnSlot = (from num in (dynamic[])entry.api_onslot select (int)num).ToArray();\r
190             }\r
191             var material = (int[])json.api_material;\r
192             for (var i = 0; i < material.Length; i++)\r
193                 _itemInfo.MaterialHistory[i].Now = material[i];\r
194         }\r
195 \r
196         public void InspectChange(string request)\r
197         {\r
198             var values = HttpUtility.ParseQueryString(request);\r
199             var fleet = int.Parse(values["api_id"]) - 1;\r
200             var idx = int.Parse(values["api_ship_idx"]);\r
201             var ship = int.Parse(values["api_ship_id"]);\r
202             if (idx == -1)\r
203             {\r
204                 var deck = _decks[fleet];\r
205                 for (var i = 1; i < deck.Length; i++)\r
206                     deck[i] = -1;\r
207                 _conditionTimer.Invalidate(fleet);\r
208                 return;\r
209             }\r
210             if (ship == -1)\r
211             {\r
212                 WithdrowShip(fleet, idx);\r
213                 return;\r
214             }\r
215             int oi;\r
216             var of = FindFleet(ship, out oi);\r
217             if (of != -1)\r
218             {\r
219                 // 入れ替えの場合\r
220                 if ((_decks[of][oi] = _decks[fleet][idx]) == -1)\r
221                     WithdrowShip(of, oi);\r
222                 if (of != fleet)\r
223                     _conditionTimer.Invalidate(of);\r
224             }\r
225             _decks[fleet][idx] = ship;\r
226             _conditionTimer.Invalidate(fleet);\r
227         }\r
228 \r
229         private int FindFleet(int ship, out int idx)\r
230         {\r
231             for (var f = 0; f < _decks.Length; f++)\r
232             {\r
233                 idx = Array.FindIndex(_decks[f], id => id == ship);\r
234                 if (idx < 0)\r
235                     continue;\r
236                 return f;\r
237             }\r
238             idx = -1;\r
239             return -1;\r
240         }\r
241 \r
242         private void WithdrowShip(int fleet, int idx)\r
243         {\r
244             var deck = _decks[fleet];\r
245             for (var i = idx; i < deck.Length - 1; i++)\r
246                 deck[i] = deck[i + 1];\r
247             deck[deck.Length - 1] = -1;\r
248             _conditionTimer.Invalidate(fleet);\r
249         }\r
250 \r
251         public void InspectPowerup(string request, dynamic json)\r
252         {\r
253             var values = HttpUtility.ParseQueryString(request);\r
254             var ships = values["api_id_items"].Split(',');\r
255             _itemInfo.NowShips -= ships.Length;\r
256             _itemInfo.NowItems -= (from s in ships select SlotItemCount(int.Parse(s))).Sum();\r
257             foreach (var ship in ships)\r
258                 _shipInfo.Remove(int.Parse(ship));\r
259             InspectDeck(json.api_deck);\r
260             InspectShip(json.api_ship);\r
261         }\r
262 \r
263         public void InspectDestroyShip(string request, dynamic json)\r
264         {\r
265             var values = HttpUtility.ParseQueryString(request);\r
266             var ship = int.Parse(values["api_ship_id"]);\r
267             _itemInfo.NowShips -= 1;\r
268             _itemInfo.NowItems -= SlotItemCount(ship);\r
269             int oi;\r
270             var of = FindFleet(ship, out oi);\r
271             if (of != -1)\r
272                 WithdrowShip(of, oi);\r
273             _shipInfo.Remove(ship);\r
274 \r
275             var material = (int[])json.api_material;\r
276             for (var i = 0; i < material.Length; i++)\r
277                 _itemInfo.MaterialHistory[i].Now = material[i];\r
278         }\r
279 \r
280         private int SlotItemCount(int id)\r
281         {\r
282             return _shipInfo[id].Slot.Count(item => item != -1);\r
283         }\r
284 \r
285         public void ApplyBucket(int id)\r
286         {\r
287             var s = _shipInfo[id];\r
288             s.NowHp = s.MaxHp;\r
289             s.Cond = Math.Max(40, s.Cond);\r
290             _conditionTimer.SetTimer();\r
291         }\r
292 \r
293         public ShipStatus[] GetShipStatuses(int fleet)\r
294         {\r
295             return (from id in _decks[fleet] where id != -1 select _shipInfo[id]).ToArray();\r
296         }\r
297 \r
298         public int[] GetDeck(int fleet)\r
299         {\r
300             return _decks[fleet];\r
301         }\r
302 \r
303         public ShipStatus this[int idx]\r
304         {\r
305             get { return _shipInfo[idx]; }\r
306         }\r
307 \r
308         public string[] GetConditionTimers(int fleet)\r
309         {\r
310             return _conditionTimer.GetTimerStrings(fleet);\r
311         }\r
312 \r
313         public ChargeStatus[] ChargeStatuses\r
314         {\r
315             get\r
316             {\r
317                 return (from deck in _decks\r
318                     select (from id in deck\r
319                         where id != -1\r
320                         let status = _shipInfo[id]\r
321                         select new {status.Bull, status.Fuel, status.Spec.BullMax, status.Spec.FuelMax})\r
322                         .Aggregate(\r
323                             new ChargeStatus(), (result, next) => new ChargeStatus\r
324                             {\r
325                                 Bull = Math.Max(result.Bull, CalcChargeState(next.Bull, next.BullMax)),\r
326                                 Fuel = Math.Max(result.Fuel, CalcChargeState(next.Fuel, next.FuelMax))\r
327                             })).ToArray();\r
328             }\r
329         }\r
330 \r
331         private int CalcChargeState(int now, int full)\r
332         {\r
333             if (full == 0 || now == full)\r
334                 return 0;\r
335             var ratio = (double)now / full;\r
336             if (ratio >= 7.0 / 9)\r
337                 return 1;\r
338             if (ratio >= 3.0 / 9)\r
339                 return 2;\r
340             if (ratio > 0)\r
341                 return 3;\r
342             return 4;\r
343         }\r
344 \r
345         public int GetAirSuperiority(int fleet)\r
346         {\r
347             return (from id in _decks[fleet]\r
348                 where id != -1\r
349                 let ship = _shipInfo[id]\r
350                 from slot in ship.Slot.Zip(ship.OnSlot, (s, o) => new {slot = s, onslot = o})\r
351                 select (int)Math.Floor(_itemInfo[slot.slot].TyKu * Math.Sqrt(slot.onslot))).Sum();\r
352         }\r
353 \r
354         public RepairStatus[] GetDamagedShipList(DockInfo dockInfo)\r
355         {\r
356             int oi;\r
357             return (from entry in _shipInfo\r
358                 let status = entry.Value\r
359                 where status.NowHp < status.MaxHp && !dockInfo.InNDock(entry.Key)\r
360                 select new RepairStatus(FindFleet(entry.Key, out oi), status.Name, status.RepairTime())).\r
361                 OrderByDescending(entry => entry.Time).ToArray();\r
362         }\r
363     }\r
364 }