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 int ShipId { get; set; }\r
28         public string Name { get; set; }\r
29         public int Level { get; set; }\r
30         public int ExpToNext { get; set; }\r
31         public int MaxHp { get; set; }\r
32         public int NowHp { get; set; }\r
33         public int Cond { get; set; }\r
34         public int Fuel { get; set; }\r
35         public int Bull { get; set; }\r
36         public int[] OnSlot { get; set; }\r
37         public int[] Slot { get; set; }\r
38     }\r
39 \r
40     public struct ChargeStatus\r
41     {\r
42         public int Fuel { get; set; }\r
43         public int Bull { get; set; }\r
44     }\r
45 \r
46     public class ShipInfo\r
47     {\r
48         private readonly int[][] _decks = new int[4][];\r
49         private readonly Dictionary<int, ShipStatus> _shipInfo = new Dictionary<int, ShipStatus>();\r
50         private readonly DateTime[][] _recoveryTimes = {new DateTime[3], new DateTime[3], new DateTime[3], new DateTime[3]};\r
51         private readonly ShipMaster _shipMaster;\r
52         private readonly ItemInfo _itemInfo;\r
53 \r
54         public ShipInfo(ShipMaster shipMaster, ItemInfo itemInfo)\r
55         {\r
56             _shipMaster = shipMaster;\r
57             _itemInfo = itemInfo;\r
58 \r
59             for (var i = 0; i < _decks.Length; i++)\r
60                 _decks[i] = new[] {-1, -1, -1, -1, -1, -1};\r
61         }\r
62 \r
63         public DateTime[] GetRecoveryTimes(int fleet)\r
64         {\r
65             return _recoveryTimes[fleet];\r
66         }\r
67 \r
68         public void InspectShip(dynamic json)\r
69         {\r
70             if (json.api_deck_port()) // port\r
71             {\r
72                 InspectShipData(json.api_ship);\r
73                 InspectDeck(json.api_deck_port);\r
74                 _itemInfo.NowShips = ((object[])json.api_ship).Length;\r
75             }\r
76             else if (json.api_data()) // ship2\r
77             {\r
78                 InspectShipData(json.api_data);\r
79                 InspectDeck(json.api_data_deck);\r
80                 _itemInfo.NowShips = ((object[])json.api_data).Length;\r
81             }\r
82             else if (json.api_ship_data()) // ship3\r
83             {\r
84                 // 一隻分のデータしか来ないことがあるので艦娘数を数えない\r
85                 InspectShipData(json.api_ship_data);\r
86                 InspectDeck(json.api_deck_data);\r
87             }\r
88             else if (json.api_ship()) // getship\r
89             {\r
90                 InspectShipData(new[] {json.api_ship});\r
91             }\r
92         }\r
93 \r
94         private void InspectDeck(dynamic json)\r
95         {\r
96             foreach (var entry in json)\r
97             {\r
98                 var fleet = (int)entry.api_id;\r
99                 var deck = _decks[fleet - 1];\r
100                 for (var i = 0; i < deck.Length; i++)\r
101                     deck[i] = (int)entry.api_ship[i];\r
102             }\r
103         }\r
104 \r
105         private void InspectShipData(dynamic json)\r
106         {\r
107             if (!json.IsArray)\r
108                 json = new[] {json};\r
109             foreach (var entry in json)\r
110             {\r
111                 _shipInfo[(int)entry.api_id] = new ShipStatus\r
112                 {\r
113                     ShipId = (int)entry.api_ship_id,\r
114                     Name = _shipMaster[(int)entry.api_ship_id].Name,\r
115                     Level = (int)entry.api_lv,\r
116                     ExpToNext = (int)entry.api_exp[1],\r
117                     MaxHp = (int)entry.api_maxhp,\r
118                     NowHp = (int)entry.api_nowhp,\r
119                     Cond = (int)entry.api_cond,\r
120                     Fuel = (int)entry.api_fuel,\r
121                     Bull = (int)entry.api_bull,\r
122                     OnSlot = (from num in (dynamic[])entry.api_onslot select (int)num).ToArray(),\r
123                     Slot = (from num in (dynamic[])entry.api_slot select (int)num).ToArray()\r
124                 };\r
125             }\r
126             SetRecoveryTime();\r
127         }\r
128 \r
129         public void InspectCharge(dynamic json)\r
130         {\r
131             foreach (var entry in json.api_ship)\r
132             {\r
133                 var status = _shipInfo[(int)entry.api_id];\r
134                 status.Bull = (int)entry.api_bull;\r
135                 status.Fuel = (int)entry.api_fuel;\r
136                 status.OnSlot = (from num in (dynamic[])entry.api_onslot select (int)num).ToArray();\r
137             }\r
138         }\r
139 \r
140         public void InspectChange(string request)\r
141         {\r
142             var values = HttpUtility.ParseQueryString(request);\r
143             var fleet = int.Parse(values["api_id"]) - 1;\r
144             var idx = int.Parse(values["api_ship_idx"]);\r
145             var ship = int.Parse(values["api_ship_id"]);\r
146             if (idx == -1)\r
147             {\r
148                 var deck = _decks[fleet];\r
149                 for (var i = 1; i < deck.Length; i++)\r
150                     deck[i] = -1;\r
151                 return;\r
152             }\r
153             if (ship == -1)\r
154             {\r
155                 RemoveShip(fleet, idx);\r
156                 return;\r
157             }\r
158             for (var f = 0; f < _decks.Length; f++)\r
159                 for (var i = 0; i < _decks[f].Length; i++)\r
160                     if (_decks[f][i] == ship) // 入れ替えの場合\r
161                     {\r
162                         if ((_decks[f][i] = _decks[fleet][idx]) == -1)\r
163                             RemoveShip(f, i);\r
164                         goto last;\r
165                     }\r
166             last:\r
167             _decks[fleet][idx] = ship;\r
168         }\r
169 \r
170         private void RemoveShip(int fleet, int idx)\r
171         {\r
172             var deck = _decks[fleet];\r
173             for (var i = idx; i < deck.Length - 1; i++)\r
174                 deck[i] = deck[i + 1];\r
175             deck[deck.Length - 1] = -1;\r
176         }\r
177 \r
178         public void InspectPowerup(string request, dynamic json)\r
179         {\r
180             var values = HttpUtility.ParseQueryString(request);\r
181             var ships = values["api_id_items"].Split(',');\r
182             _itemInfo.NowShips -= ships.Length;\r
183             _itemInfo.NowItems -= (from s in ships select SlotItemCount(int.Parse(s))).Sum();\r
184             InspectDeck(json.api_deck);\r
185             InspectShip(json.api_ship);\r
186         }\r
187 \r
188         public void InspectDestroyShip(string request)\r
189         {\r
190             var values = HttpUtility.ParseQueryString(request);\r
191             var id = int.Parse(values["api_ship_id"]);\r
192             _itemInfo.NowShips -= 1;\r
193             _itemInfo.NowItems -= SlotItemCount(id);\r
194             foreach (var deck in _decks)\r
195             {\r
196                 for (var i = 0; i < deck.Length; i++)\r
197                     if (deck[i] == id)\r
198                     {\r
199                         for (var j = i; j < deck.Length - 1; j++)\r
200                             deck[j] = deck[j + 1];\r
201                         deck[deck.Length - 1] = -1;\r
202                     }\r
203             }\r
204         }\r
205 \r
206         public void InspectNyukyo(string request)\r
207         {\r
208             var values = HttpUtility.ParseQueryString(request);\r
209             var id = int.Parse(values["api_ship_id"]);\r
210             if (int.Parse(values["api_highspeed"]) == 0)\r
211                 return;\r
212             var ship = _shipInfo[id];\r
213             ship.NowHp = ship.MaxHp;\r
214             if (ship.Cond < 40)\r
215                 ship.Cond = 40;\r
216             _itemInfo.NumBuckets--;\r
217         }\r
218 \r
219         private int SlotItemCount(int id)\r
220         {\r
221             return _shipInfo[id].Slot.Count(item => item != -1);\r
222         }\r
223 \r
224         private void SetRecoveryTime()\r
225         {\r
226             for (var fleet = 0; fleet < 4; fleet++)\r
227             {\r
228                 var cond =\r
229                     (from id in _decks[fleet] where _shipInfo.ContainsKey(id) select _shipInfo[id].Cond).DefaultIfEmpty(49)\r
230                         .Min();\r
231                 if (cond < 49 && _recoveryTimes[fleet][2] != DateTime.MinValue) // 計時中\r
232                 {\r
233                     // コンディション値から推定される残り時刻と経過時間の差\r
234                     var diff = TimeSpan.FromMinutes((49 - cond + 2) / 3 * 3) - (_recoveryTimes[fleet][2] - DateTime.Now);\r
235                     if (diff >= TimeSpan.Zero && diff <= TimeSpan.FromMinutes(3)) // 差が0以上3分以内ならタイマーを更新しない。\r
236                         return;\r
237                 }\r
238                 var thresh = new[] {30, 40, 49};\r
239                 for (var i = 0; i < thresh.Length; i++)\r
240                     _recoveryTimes[fleet][i] = cond < thresh[i]\r
241                         ? DateTime.Now.AddMinutes((thresh[i] - cond + 2) / 3 * 3)\r
242                         : DateTime.MinValue;\r
243             }\r
244         }\r
245 \r
246         public ShipStatus[] GetShipStatuses(int fleet)\r
247         {\r
248             return _decks[fleet].Select(id => (id == -1) ? new ShipStatus() : _shipInfo[id]).ToArray();\r
249         }\r
250 \r
251         public ShipStatus this[int idx]\r
252         {\r
253             get { return _shipInfo[idx]; }\r
254         }\r
255 \r
256         public ChargeStatus[] ChargeStatuses\r
257         {\r
258             get\r
259             {\r
260                 return (from deck in _decks\r
261                     select (from id in deck\r
262                         where id != -1\r
263                         let status = _shipInfo[id]\r
264                         let spec = _shipMaster[status.ShipId]\r
265                         select new {status.Bull, status.Fuel, spec.BullMax, spec.FuelMax})\r
266                         .Aggregate(\r
267                             new ChargeStatus(), (result, next) => new ChargeStatus()\r
268                             {\r
269                                 Bull = Math.Max(result.Bull, CalcChargeState(next.Bull, next.BullMax)),\r
270                                 Fuel = Math.Max(result.Fuel, CalcChargeState(next.Fuel, next.FuelMax))\r
271                             })).ToArray();\r
272             }\r
273         }\r
274 \r
275         private int CalcChargeState(int now, int full)\r
276         {\r
277             if (full == 0 || now == full)\r
278                 return 0;\r
279             var ratio = (double)now / full;\r
280             if (ratio >= 7.0 / 9)\r
281                 return 1;\r
282             if (ratio >= 3.0 / 9)\r
283                 return 2;\r
284             if (ratio > 0)\r
285                 return 3;\r
286             return 4;\r
287         }\r
288 \r
289         public int GetAirSuperiority(int fleet)\r
290         {\r
291             return (from id in _decks[fleet]\r
292                 where _shipInfo.ContainsKey(id)\r
293                     let ship = _shipInfo[id]\r
294                 from slot in ship.Slot.Zip(ship.OnSlot, (s, o) => new {slot = s, onslot = o})\r
295                 select (int)Math.Floor(_itemInfo.GetTyKu(slot.slot) * Math.Sqrt(slot.onslot))).Sum();\r
296         }\r
297     }\r
298 }