OSDN Git Service

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