OSDN Git Service

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