OSDN Git Service

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