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 Id { get; set; }\r
28         public int Fleet { get; set; } // ShipListだけで使う\r
29         public ShipSpec Spec { get; set; }\r
30 \r
31         public string Name\r
32         {\r
33             get { return Spec.Name; }\r
34         }\r
35 \r
36         public int Level { get; set; }\r
37         public int ExpToNext { get; set; }\r
38         public int MaxHp { get; set; }\r
39         public int NowHp { get; set; }\r
40         public int Cond { get; set; }\r
41         public int Fuel { get; set; }\r
42         public int Bull { get; set; }\r
43         public int[] OnSlot { get; set; }\r
44         public int[] Slot { get; set; }\r
45         public int LoS { get; set; }\r
46 \r
47         public Damage DamageLevel\r
48         {\r
49             get { return CalcDamage(NowHp, MaxHp); }\r
50         }\r
51 \r
52         public enum Damage\r
53         {\r
54             Minor,\r
55             Small,\r
56             Half,\r
57             Badly\r
58         }\r
59 \r
60         public static Damage CalcDamage(int now, int max)\r
61         {\r
62             var ratio = max == 0 ? 1 : (double)now / max;\r
63             return ratio > 0.75 ? Damage.Minor : ratio > 0.5 ? Damage.Small : ratio > 0.25 ? Damage.Half : Damage.Badly;\r
64         }\r
65 \r
66         public TimeSpan RepairTime\r
67         {\r
68             get { return CalcRepairTime(MaxHp - NowHp); }\r
69         }\r
70 \r
71         public TimeSpan CalcRepairTime(int damage)\r
72         {\r
73             var weight = Spec.RepairWeight;\r
74             var level = Level < 12 ? Level * 10 : Level * 5 + Math.Floor(Math.Sqrt(Level - 11)) * 10 + 50;\r
75             return TimeSpan.FromSeconds(Math.Floor(level * weight * damage) + 30);\r
76         }\r
77     }\r
78 \r
79     public struct ChargeStatus\r
80     {\r
81         public int Fuel { get; set; }\r
82         public int Bull { get; set; }\r
83 \r
84         public ChargeStatus(ShipStatus status) : this()\r
85         {\r
86             Fuel = CalcChargeState(status.Fuel, status.Spec.FuelMax);\r
87             Bull = CalcChargeState(status.Bull, status.Spec.BullMax);\r
88         }\r
89 \r
90         public ChargeStatus(int fuel, int bull) : this()\r
91         {\r
92             Fuel = fuel;\r
93             Bull = bull;\r
94         }\r
95 \r
96         private int CalcChargeState(int now, int full)\r
97         {\r
98             if (full == 0 || now == full)\r
99                 return 0;\r
100             var ratio = (double)now / full;\r
101             if (ratio >= 7.0 / 9)\r
102                 return 1;\r
103             if (ratio >= 3.0 / 9)\r
104                 return 2;\r
105             if (ratio > 0)\r
106                 return 3;\r
107             return 4;\r
108         }\r
109     }\r
110 \r
111     public class ShipInfo\r
112     {\r
113         public const int FleetCount = 4;\r
114         public const int MemberCount = 6;\r
115 \r
116         private readonly int[][] _decks = new int[FleetCount][];\r
117         private readonly Dictionary<int, ShipStatus> _shipInfo = new Dictionary<int, ShipStatus>();\r
118         private readonly ConditionTimer _conditionTimer;\r
119         private readonly ShipMaster _shipMaster;\r
120         private readonly ItemInfo _itemInfo;\r
121         private readonly bool[] _inMission = new bool[FleetCount];\r
122         private readonly bool[] _inSortie = new bool[FleetCount];\r
123         private int _hqLevel;\r
124         private readonly List<int> _escapedShips = new List<int>();\r
125         private int _combinedFleetType;\r
126 \r
127         public ShipInfo(ShipMaster shipMaster, ItemInfo itemInfo)\r
128         {\r
129             _shipMaster = shipMaster;\r
130             _itemInfo = itemInfo;\r
131             _conditionTimer = new ConditionTimer(this);\r
132 \r
133             for (var fleet = 0; fleet < FleetCount; fleet++)\r
134             {\r
135                 var deck = new int[MemberCount];\r
136                 for (var i = 0; i < deck.Length; i++)\r
137                     deck[i] = -1;\r
138                 _decks[fleet] = deck;\r
139             }\r
140         }\r
141 \r
142         public void InspectShip(dynamic json)\r
143         {\r
144             if (json.api_deck_port()) // port\r
145             {\r
146                 ClearShipInfo();\r
147                 for (var i = 0; i < FleetCount; i++)\r
148                     _inSortie[i] = false;\r
149                 InspectDeck(json.api_deck_port);\r
150                 InspectShipData(json.api_ship);\r
151                 InspectBasic(json.api_basic);\r
152                 _combinedFleetType = json.api_combined_flag() ? (int)json.api_combined_flag : 0;\r
153                 _itemInfo.NowShips = ((object[])json.api_ship).Length;\r
154                 _conditionTimer.SetTimer();\r
155             }\r
156             else if (json.api_data()) // ship2\r
157             {\r
158                 ClearShipInfo();\r
159                 InspectDeck(json.api_data_deck);\r
160                 InspectShipData(json.api_data);\r
161                 _itemInfo.NowShips = ((object[])json.api_data).Length;\r
162                 _conditionTimer.SetTimer();\r
163             }\r
164             else if (json.api_ship_data()) // ship3\r
165             {\r
166                 // 一隻分のデータしか来ないことがあるので艦娘数を数えない\r
167                 InspectDeck(json.api_deck_data);\r
168                 InspectShipData(json.api_ship_data);\r
169             }\r
170             else if (json.api_ship()) // getship\r
171             {\r
172                 InspectShipData(new[] {json.api_ship});\r
173             }\r
174         }\r
175 \r
176         private void ClearShipInfo()\r
177         {\r
178             _shipInfo.Clear();\r
179             _shipInfo[-1] = new ShipStatus {Spec = _shipMaster[-1], Slot = new int[0], OnSlot = new int[0]};\r
180         }\r
181 \r
182         public void InspectDeck(dynamic json)\r
183         {\r
184             foreach (var entry in json)\r
185             {\r
186                 var fleet = (int)entry.api_id - 1;\r
187                 var deck = _decks[fleet];\r
188                 for (var i = 0; i < deck.Length; i++)\r
189                     deck[i] = (int)entry.api_ship[i];\r
190                 _inMission[fleet] = (int)entry.api_mission[0] != 0;\r
191                 if (_inMission[fleet])\r
192                     _conditionTimer.Disable(fleet);\r
193             }\r
194         }\r
195 \r
196         private void InspectShipData(dynamic json)\r
197         {\r
198             foreach (var entry in json)\r
199             {\r
200                 _shipInfo[(int)entry.api_id] = new ShipStatus\r
201                 {\r
202                     Id = (int)entry.api_id,\r
203                     Spec = _shipMaster[(int)entry.api_ship_id],\r
204                     Level = (int)entry.api_lv,\r
205                     ExpToNext = (int)entry.api_exp[1],\r
206                     MaxHp = (int)entry.api_maxhp,\r
207                     NowHp = (int)entry.api_nowhp,\r
208                     Cond = (int)entry.api_cond,\r
209                     Fuel = (int)entry.api_fuel,\r
210                     Bull = (int)entry.api_bull,\r
211                     OnSlot = (from num in (dynamic[])entry.api_onslot select (int)num).ToArray(),\r
212                     Slot = (from num in (dynamic[])entry.api_slot select (int)num).ToArray(),\r
213                     LoS = (int)entry.api_sakuteki[0]\r
214                 };\r
215             }\r
216         }\r
217 \r
218         private void InspectBasic(dynamic json)\r
219         {\r
220             _hqLevel = (int)json.api_level;\r
221         }\r
222 \r
223         public void InspectCharge(dynamic json)\r
224         {\r
225             foreach (var entry in json.api_ship)\r
226             {\r
227                 var status = _shipInfo[(int)entry.api_id];\r
228                 status.Bull = (int)entry.api_bull;\r
229                 status.Fuel = (int)entry.api_fuel;\r
230                 status.OnSlot = (from num in (dynamic[])entry.api_onslot select (int)num).ToArray();\r
231             }\r
232             var material = (int[])json.api_material;\r
233             for (var i = 0; i < material.Length; i++)\r
234                 _itemInfo.MaterialHistory[i].Now = material[i];\r
235         }\r
236 \r
237         public void InspectChange(string request)\r
238         {\r
239             var values = HttpUtility.ParseQueryString(request);\r
240             var fleet = int.Parse(values["api_id"]) - 1;\r
241             var idx = int.Parse(values["api_ship_idx"]);\r
242             var ship = int.Parse(values["api_ship_id"]);\r
243             if (idx == -1)\r
244             {\r
245                 var deck = _decks[fleet];\r
246                 for (var i = 1; i < deck.Length; i++)\r
247                     deck[i] = -1;\r
248                 _conditionTimer.Invalidate(fleet);\r
249                 return;\r
250             }\r
251             if (ship == -1)\r
252             {\r
253                 WithdrowShip(fleet, idx);\r
254                 return;\r
255             }\r
256             int oi;\r
257             var of = FindFleet(ship, out oi);\r
258             if (of != -1)\r
259             {\r
260                 // 入れ替えの場合\r
261                 if ((_decks[of][oi] = _decks[fleet][idx]) == -1)\r
262                     WithdrowShip(of, oi);\r
263                 if (of != fleet)\r
264                     _conditionTimer.Invalidate(of);\r
265             }\r
266             _decks[fleet][idx] = ship;\r
267             _conditionTimer.Invalidate(fleet);\r
268         }\r
269 \r
270         private int FindFleet(int ship, out int idx)\r
271         {\r
272             for (var f = 0; f < _decks.Length; f++)\r
273             {\r
274                 idx = Array.FindIndex(_decks[f], id => id == ship);\r
275                 if (idx < 0)\r
276                     continue;\r
277                 return f;\r
278             }\r
279             idx = -1;\r
280             return -1;\r
281         }\r
282 \r
283         private void WithdrowShip(int fleet, int idx)\r
284         {\r
285             var deck = _decks[fleet];\r
286             for (var i = idx; i < deck.Length - 1; i++)\r
287                 deck[i] = deck[i + 1];\r
288             deck[deck.Length - 1] = -1;\r
289             _conditionTimer.Invalidate(fleet);\r
290         }\r
291 \r
292         public void InspectPowerup(string request, dynamic json)\r
293         {\r
294             var values = HttpUtility.ParseQueryString(request);\r
295             var ships = values["api_id_items"].Split(',');\r
296             _itemInfo.NowShips -= ships.Length;\r
297             _itemInfo.NowEquips -= (from s in ships select SlotItemCount(int.Parse(s))).Sum();\r
298             foreach (var ship in ships)\r
299                 _shipInfo.Remove(int.Parse(ship));\r
300             InspectDeck(json.api_deck);\r
301             InspectShip(json.api_ship);\r
302         }\r
303 \r
304         public void InspectDestroyShip(string request, dynamic json)\r
305         {\r
306             var values = HttpUtility.ParseQueryString(request);\r
307             var ship = int.Parse(values["api_ship_id"]);\r
308             _itemInfo.NowShips -= 1;\r
309             _itemInfo.NowEquips -= SlotItemCount(ship);\r
310             int oi;\r
311             var of = FindFleet(ship, out oi);\r
312             if (of != -1)\r
313                 WithdrowShip(of, oi);\r
314             _shipInfo.Remove(ship);\r
315 \r
316             var material = (int[])json.api_material;\r
317             for (var i = 0; i < material.Length; i++)\r
318                 _itemInfo.MaterialHistory[i].Now = material[i];\r
319         }\r
320 \r
321         private int SlotItemCount(int id)\r
322         {\r
323             return _shipInfo[id].Slot.Count(item => item != -1);\r
324         }\r
325 \r
326         public void StartSortie(string request)\r
327         {\r
328             var values = HttpUtility.ParseQueryString(request);\r
329             var fleet = int.Parse(values["api_deck_id"]) - 1;\r
330             if (_combinedFleetType == 0)\r
331             {\r
332                 _conditionTimer.Disable(fleet);\r
333                 _inSortie[fleet] = true;\r
334             }\r
335             else\r
336             {\r
337                 _conditionTimer.Disable(0);\r
338                 _conditionTimer.Disable(1);\r
339                 _inSortie[0] = _inSortie[1] = true;\r
340             }\r
341         }\r
342 \r
343         public void RepairShip(int id)\r
344         {\r
345             var s = _shipInfo[id];\r
346             s.NowHp = s.MaxHp;\r
347             s.Cond = Math.Max(40, s.Cond);\r
348             _conditionTimer.SetTimer();\r
349         }\r
350 \r
351         public ShipStatus[] GetShipStatuses(int fleet)\r
352         {\r
353             return (from id in _decks[fleet] where id != -1 select _escapedShips.Contains(id) ? new ShipStatus() : _shipInfo[id]).ToArray();\r
354         }\r
355 \r
356         public int[] GetDeck(int fleet)\r
357         {\r
358             return _decks[fleet];\r
359         }\r
360 \r
361         public ShipStatus this[int idx]\r
362         {\r
363             get { return _shipInfo[idx]; }\r
364         }\r
365 \r
366         public bool InMission(int fleet)\r
367         {\r
368             return _inMission[fleet];\r
369         }\r
370 \r
371         public bool InSortie(int fleet)\r
372         {\r
373             return _inSortie[fleet];\r
374         }\r
375 \r
376         public ShipStatus[] ShipList\r
377         {\r
378             get\r
379             {\r
380                 return _shipInfo.Values.Where(s => s.Level != 0).Select(s =>\r
381                 {\r
382                     int oi;\r
383                     var f = FindFleet(s.Id, out oi);\r
384                     s.Fleet = f;\r
385                     return s;\r
386                 }).ToArray();\r
387             }\r
388         }\r
389 \r
390         public DateTime GetConditionTiemr(int fleet)\r
391         {\r
392             return _conditionTimer.GetTimer(fleet);\r
393         }\r
394 \r
395         public int[] GetConditionNotice()\r
396         {\r
397             return _conditionTimer.GetNotice();\r
398         }\r
399 \r
400         public ChargeStatus[] ChargeStatuses\r
401         {\r
402             get\r
403             {\r
404                 return (from deck in _decks\r
405                     let flag = new ChargeStatus(_shipInfo[deck[0]])\r
406                     let others = (from id in deck.Skip(1)\r
407                         select new ChargeStatus(_shipInfo[id]))\r
408                         .Aggregate(\r
409                             (result, next) =>\r
410                                 new ChargeStatus(Math.Max(result.Fuel, next.Fuel), Math.Max(result.Bull, next.Bull)))\r
411                     select new ChargeStatus(flag.Fuel != 0 ? flag.Fuel : others.Fuel + 5,\r
412                         flag.Bull != 0 ? flag.Bull : others.Bull + 5)).ToArray();\r
413             }\r
414         }\r
415 \r
416         public int GetAirSuperiority(int fleet)\r
417         {\r
418             return (from id in _decks[fleet]\r
419                 let ship = _shipInfo[id]\r
420                 from slot in ship.Slot.Zip(ship.OnSlot, (s, o) => new {slot = s, onslot = o})\r
421                 let item = _itemInfo[slot.slot]\r
422                 where item.CanAirCombat()\r
423                 select (int)Math.Floor(item.AntiAir * Math.Sqrt(slot.onslot))).DefaultIfEmpty().Sum();\r
424         }\r
425 \r
426         public ShipStatus[] GetDamagedShipList(DockInfo dockInfo)\r
427         {\r
428             return (from s in ShipList\r
429                 where s.NowHp < s.MaxHp && !dockInfo.InNDock(s.Id)\r
430                 select s).OrderByDescending(s => s.RepairTime).ToArray();\r
431         }\r
432 \r
433         public double GetLineOfSights(int fleet)\r
434         {\r
435             var result = 0.0;\r
436             foreach (var s in _decks[fleet].Select(id => _shipInfo[id]))\r
437             {\r
438                 var items = 0;\r
439                 foreach (var spec in s.Slot.Select(t => _itemInfo[t]))\r
440                 {\r
441                     items += spec.LoS;\r
442                     result += spec.LoS * spec.LoSScaleFactor();\r
443                 }\r
444                 result += Math.Sqrt(s.LoS - items) * 1.6841056;\r
445 \r
446             }\r
447             return result > 0 ? result + (_hqLevel + 4) / 5 * 5 * -0.6142467 : 0.0;\r
448         }\r
449 \r
450         public void SetEscapedShips(List<int> ships)\r
451         {\r
452             _escapedShips.Clear();\r
453             _escapedShips.AddRange(ships);\r
454         }\r
455 \r
456         public void ClearEscapedShips()\r
457         {\r
458             _escapedShips.Clear();\r
459         }\r
460     }\r
461 }