OSDN Git Service

交戦形がT字のときに敵制空値の表示タイミングがずれていたのを直す
[kancollesniffer/KancolleSniffer.git] / KancolleSniffer / BattleInfo.cs
1 // Copyright (C) 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.Linq;\r
20 \r
21 namespace KancolleSniffer\r
22 {\r
23     public class BattleInfo\r
24     {\r
25         private readonly ShipMaster _shipMaster;\r
26         private readonly ShipInfo _shipInfo;\r
27         private readonly ItemInfo _itemInfo;\r
28         public bool InBattle { get; set; }\r
29         public string Formation { get; private set; }\r
30         public int DelayInFormation { get; private set; }\r
31         public int EnemyAirSuperiority { get; private set; }\r
32         public int DelayInAirSuperiority { get; private set; }\r
33         public bool HasDamagedShip { get; set; }\r
34         public string[] DamagedShipNames { get; private set; }\r
35 \r
36         private struct Delay\r
37         {\r
38             public const int Basic = 4100;\r
39             public const int Formation = 1100;\r
40             public const int Tau = 200;\r
41             public const int SearchAirSuccess = 5700;\r
42             public const int SearchAirFailure = 4900;\r
43             public const int SearchSuccess = 5400;\r
44             public const int Submarine = 2500;\r
45             public const int Emergence = 2000;\r
46             public const int AirFight = 8700;\r
47             public const int AirFightBoth = 2700;\r
48             public const int Support = 9800;\r
49             public const int OpeningAttack = 3500;\r
50             public const int Cutin = 4500;\r
51         }\r
52 \r
53         public BattleInfo(ShipMaster shipMaster, ShipInfo shipInfo, ItemInfo itemInfo)\r
54         {\r
55             _shipMaster = shipMaster;\r
56             _shipInfo = shipInfo;\r
57             _itemInfo = itemInfo;\r
58         }\r
59 \r
60         public void InspectBattle(dynamic json)\r
61         {\r
62             InBattle = true;\r
63             Formation = FormationName(json);\r
64             EnemyAirSuperiority = CalcEnemyAirSuperiority(json);\r
65             SetDelay(json);\r
66             CauseDamage(json);\r
67         }\r
68 \r
69         private string FormationName(dynamic json)\r
70         {\r
71             if (!json.api_formation()) // 演習の夜戦\r
72                 return "";\r
73             switch ((int)json.api_formation[2])\r
74             {\r
75                 case 1:\r
76                     return "同航戦";\r
77                 case 2:\r
78                     return "反航戦";\r
79                 case 3:\r
80                     return "T字有利";\r
81                 case 4:\r
82                     return "T字不利";\r
83             }\r
84             return "";\r
85         }\r
86 \r
87         private void SetDelay(dynamic json)\r
88         {\r
89             // 敵出現まで\r
90             var delay = Delay.Basic;\r
91             if (json.api_hougeki()) // 夜戦\r
92             {\r
93                 DelayInAirSuperiority = DelayInFormation = delay;\r
94                 return;\r
95             }\r
96             var subm = (SubmarineFlags)CheckSubmarine(json);\r
97             bool success;\r
98             delay += SearchDelay(json, out success) + subm.AddtionalDelay;\r
99             DelayInAirSuperiority = delay + (success ? 0 : Delay.Emergence); // 失敗すると出現が遅れる\r
100             // 敵艦隊発見以降\r
101             delay += Delay.Emergence + Delay.Formation + SupportDelay(json) + CutinDelay(json);\r
102             if ((int)json.api_formation[2] >= 3)\r
103                 delay += Delay.Tau;\r
104             if (!subm.PreventAirFight)\r
105                 delay += AirFightDelay(json);\r
106             if (!subm.PreventOpeningAttack)\r
107                 delay += OpeningAttackDelay(json);\r
108             DelayInFormation = delay;\r
109         }\r
110 \r
111         private int SearchDelay(dynamic json, out bool success)\r
112         {\r
113             success = false;\r
114             switch ((int)json.api_search[0])\r
115             {\r
116                 case 1: // 索敵機による索敵成功\r
117                 case 2: // 索敵機未帰還あり\r
118                     success = true;\r
119                     return Delay.SearchAirSuccess;\r
120                 case 3: // 索敵機未帰還\r
121                 case 4: // 索敵機による索敵失敗\r
122                     return Delay.SearchAirFailure;\r
123                 case 5: // 索敵力による索敵成功\r
124                     success = true;\r
125                     return Delay.SearchSuccess;\r
126             }\r
127             return 0;\r
128         }\r
129 \r
130         private int OpeningAttackDelay(dynamic json)\r
131         {\r
132             return json.api_opening_flag == 1 ? Delay.OpeningAttack : 0;\r
133         }\r
134 \r
135         private class SubmarineFlags\r
136         {\r
137             public bool[] Friend;\r
138             public bool[] Enemy;\r
139 \r
140             public int AddtionalDelay\r
141             {\r
142                 get { return Friend.Any(x => x) || Enemy.Any(x => x) ? Delay.Submarine : 0; }\r
143                 // どちらかに潜水艦                \r
144             }\r
145 \r
146             public bool PreventAirFight\r
147             {\r
148                 get { return Friend.All(x => x) || Enemy.All(x => x); } // 一方がすべて潜水艦\r
149             }\r
150 \r
151             public bool PreventOpeningAttack\r
152             {\r
153                 get { return Friend.All(x => x) && Enemy.All(x => x); } // 双方すべて潜水艦\r
154             }\r
155         }\r
156 \r
157         private SubmarineFlags CheckSubmarine(dynamic json)\r
158         {\r
159             return new SubmarineFlags\r
160             {\r
161                 Friend = (from status in _shipInfo.GetShipStatuses((int)json.api_dock_id - 1)\r
162                     select _shipMaster[status.ShipId].IsSubmarine).ToArray(),\r
163                 Enemy = (from id in (int[])json.api_ship_ke where id != -1 select _shipMaster[id].IsSubmarine).ToArray()\r
164             };\r
165         }\r
166 \r
167         private int AirFightDelay(dynamic json)\r
168         {\r
169             // 僚艦が偵察機だけだと航空戦に参加しないので、\r
170             // 艦載機の配備ではなく遭遇戦の状況を調べる。\r
171             var f = json.api_kouku.api_stage1.api_f_count > 0;\r
172             var e = json.api_kouku.api_stage1.api_e_count > 0;\r
173             var result = 0;\r
174             if (f || e) // 艦載機発艦\r
175                 result += Delay.AirFight;\r
176             if (f && e) // 双方とも\r
177                 result += Delay.AirFightBoth;\r
178             return result;\r
179         }\r
180 \r
181         private int SupportDelay(dynamic json)\r
182         {\r
183             return json.api_support_flag() && json.api_support_flag == 1 ? Delay.Support : 0;\r
184         }\r
185 \r
186         private int CutinDelay(dynamic json)\r
187         {\r
188             var cutin = 0;\r
189             var maxHps = (int[])json.api_nowhps;\r
190             var nowHps = (int[])json.api_nowhps;\r
191             var planeFrom = (int[][])json.api_kouku.api_plane_from;\r
192             if ((planeFrom[0][0] != -1 || planeFrom[1][0] != -1) && // どちらかに艦載機あり\r
193                 json.api_kouku.api_stage3 != null) // 敵艦載機が全滅しているとnull\r
194             {\r
195                 // 航空戦による中破大破の判定\r
196                 var damages = (int[])json.api_kouku.api_stage3.api_fdam;\r
197                 var newHps = nowHps.Zip(damages, (hp, dmg) => hp - dmg).ToArray();\r
198                 if (IsCutinShown(nowHps, newHps, maxHps))\r
199                     cutin++;\r
200                 nowHps = newHps;\r
201             }\r
202             if ((int)json.api_opening_flag != 0)\r
203             {\r
204                 // 開幕雷撃による中破大破の判定\r
205                 var damages = (int[])json.api_opening_atack.api_fdam;\r
206                 var newHps = nowHps.Zip(damages, (hp, dmg) => hp - dmg).ToArray();\r
207                 if (IsCutinShown(nowHps, newHps, maxHps))\r
208                     cutin++;\r
209             }\r
210             return Delay.Cutin * cutin;\r
211         }\r
212 \r
213         private bool IsCutinShown(int[] nowHps, int[] newHps, int[] maxHps)\r
214         {\r
215             for (var i = 1; i < ShipInfo.MemberCount + 1; i++)\r
216             {\r
217                 if (ShipStatus.CalcDamage(nowHps[i], maxHps[i]) <= ShipStatus.Damage.Small &&\r
218                     ShipStatus.CalcDamage(newHps[i], maxHps[i]) >= ShipStatus.Damage.Half)\r
219                     return true;\r
220             }\r
221             return false;\r
222         }\r
223 \r
224         private int CalcEnemyAirSuperiority(dynamic json)\r
225         {\r
226             var maxEq = ((int[])json.api_ship_ke).Skip(1).SelectMany(id => _shipMaster[id].MaxEq);\r
227             var equips = ((int[][])json.api_eSlot).SelectMany(x => x);\r
228             return (from slot in equips.Zip(maxEq, (id, max) => new {id, max})\r
229                 select (int)Math.Floor(_itemInfo.GetSpecByItemId(slot.id).TyKu * Math.Sqrt(slot.max))).Sum();\r
230         }\r
231 \r
232         private void CauseDamage(dynamic json)\r
233         {\r
234             bool midnight = json.api_hougeki();\r
235             var ships = _shipInfo.GetShipStatuses((int)(midnight ? json.api_deck_id : json.api_dock_id) - 1); // 昼戦はtypoしている\r
236             if (midnight)\r
237             {\r
238                 CauseHougekiDamage(ships, json.api_hougeki);\r
239             }\r
240             else // 昼戦\r
241             {\r
242                 ships = _shipInfo.GetShipStatuses((int)json.api_dock_id - 1);\r
243                 if (json.api_kouku.api_stage3 != null)\r
244                     CauseSimpleDamage(ships, json.api_kouku.api_stage3.api_fdam);\r
245                 if (json.api_opening_atack != null)\r
246                     CauseSimpleDamage(ships, json.api_opening_atack.api_fdam);\r
247                 if (json.api_hougeki1 != null)\r
248                     CauseHougekiDamage(ships, json.api_hougeki1);\r
249                 if (json.api_hougeki2 != null)\r
250                     CauseHougekiDamage(ships, json.api_hougeki2);\r
251                 if (json.api_raigeki != null)\r
252                     CauseSimpleDamage(ships, json.api_raigeki.api_fdam);\r
253             }\r
254             DamagedShipNames =\r
255                 (from ship in ships where ship.DamageLevel == ShipStatus.Damage.Badly select ship.Name).ToArray();\r
256             HasDamagedShip = DamagedShipNames.Any();\r
257         }\r
258 \r
259         private void CauseSimpleDamage(ShipStatus[] ships, dynamic rawDamage)\r
260         {\r
261             var damage = (int[])rawDamage;\r
262             for (var i = 0; i < ships.Length; i++)\r
263                 ships[i].NowHp -= damage[i + 1];\r
264         }\r
265 \r
266         private void CauseHougekiDamage(ShipStatus[] ships, dynamic hougeki)\r
267         {\r
268             var targets = ((dynamic[])hougeki.api_df_list).Skip(1).SelectMany(x => (int[])x);\r
269             var damages = ((dynamic[])hougeki.api_damage).Skip(1).SelectMany(x => (double[])x);\r
270             foreach (var hit in targets.Zip(damages, (t, d) => new {t, d}))\r
271             {\r
272                 if (hit.t - 1 < ships.Length)\r
273                     ships[hit.t - 1].NowHp -= (int)hit.d;\r
274             }\r
275         }\r
276     }\r
277 }