OSDN Git Service

43ab804af59b8cd590322be372aadba472893da5
[kancollesniffer/KancolleSniffer.git] / KancolleSniffer / BattleInfo.cs
1 // Copyright (C) 2014, 2015 Kazuhiro Fujieda <fujieda@users.osdn.me>\r
2 // \r
3 // Licensed under the Apache License, Version 2.0 (the "License");\r
4 // you may not use this file except in compliance with the License.\r
5 // You may obtain a copy of the License at\r
6 //\r
7 //    http://www.apache.org/licenses/LICENSE-2.0\r
8 //\r
9 // Unless required by applicable law or agreed to in writing, software\r
10 // distributed under the License is distributed on an "AS IS" BASIS,\r
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
12 // See the License for the specific language governing permissions and\r
13 // limitations under the License.\r
14 \r
15 using System.Collections.Generic;\r
16 using System.Linq;\r
17 using static System.Math;\r
18 \r
19 namespace KancolleSniffer\r
20 {\r
21     public enum BattleResultRank\r
22     {\r
23         P,\r
24         S,\r
25         A,\r
26         B,\r
27         C,\r
28         D,\r
29         E,\r
30     }\r
31 \r
32     public enum BattleState\r
33     {\r
34         None,\r
35         Day,\r
36         Night,\r
37         Result\r
38     }\r
39 \r
40     public class BattleInfo\r
41     {\r
42         private readonly ShipInfo _shipInfo;\r
43         private readonly ItemInfo _itemInfo;\r
44         private int _fleet;\r
45         private Record[] _friend;\r
46         private Record[] _guard;\r
47         private int[] _enemyHp;\r
48         private int[] _enemyGuardHp;\r
49         private int[] _enemyStartHp;\r
50         private int[] _enemyGuardStartHp;\r
51         private readonly List<int> _escapingShips = new List<int>();\r
52         private int _flagshipRecoveryType;\r
53 \r
54         public BattleState BattleState { get; set; }\r
55         public string Formation { get; private set; }\r
56         public string EnemyFighterPower { get; private set; }\r
57         public int AirControlLevel { get; private set; }\r
58         public BattleResultRank ResultRank { get; private set; }\r
59         public ShipStatus[] EnemyResultStatus { get; private set; }\r
60         public List<AirBattleResult> AirBattleResults { get; } = new List<AirBattleResult>();\r
61 \r
62 \r
63         public BattleInfo(ShipInfo shipInfo, ItemInfo itemInfo)\r
64         {\r
65             _shipInfo = shipInfo;\r
66             _itemInfo = itemInfo;\r
67         }\r
68 \r
69         public void InspectBattle(dynamic json, string url)\r
70         {\r
71             Formation = FormationName(json);\r
72             EnemyFighterPower = CalcEnemyFighterPower(json);\r
73             AirControlLevel = CheckAirControlLevel(json);\r
74             ShowResult(false); // 昼戦の結果を夜戦のときに表示する\r
75             SetupResult(json);\r
76             if (IsNightBattle(json))\r
77             {\r
78                 BattleState = BattleState.Night;\r
79                 CalcHougekiDamage(json.api_hougeki,\r
80                     _guard.Length > 0 ? _guard : _friend,\r
81                     json.api_active_deck() && json.api_active_deck[1] != 1 ? _enemyGuardHp : _enemyHp);\r
82             }\r
83             else\r
84             {\r
85                 BattleState = BattleState.Day;\r
86                 CalcDamage(json, url.EndsWith("battle_water"));\r
87             }\r
88             ClearEnemyOverKill();\r
89             ResultRank = url.EndsWith("ld_airbattle") ? CalcLdAirBattleRank() : CalcResultRank();\r
90         }\r
91 \r
92         private void ClearEnemyOverKill()\r
93         {\r
94             _enemyHp = _enemyHp.Select(hp => hp < 0 ? 0 : hp).ToArray();\r
95             _enemyGuardHp = _enemyGuardHp.Select(hp => hp < 0 ? 0 : hp).ToArray();\r
96         }\r
97 \r
98         public void InspectMapNext(string request)\r
99         {\r
100             var type = HttpUtility.ParseQueryString(request)["api_recovery_type"];\r
101             if (type == null)\r
102                 return;\r
103             _flagshipRecoveryType = int.Parse(type);\r
104         }\r
105 \r
106         private bool IsNightBattle(dynamic json) => json.api_hougeki();\r
107 \r
108         private int DeckId(dynamic json)\r
109         {\r
110             if (json.api_dock_id()) // 昼戦はtypoしている\r
111                 return (int)json.api_dock_id - 1;\r
112             if (json.api_deck_id is string) // 通常の夜戦では文字列\r
113                 return int.Parse(json.api_deck_id) - 1;\r
114             return (int)json.api_deck_id - 1;\r
115         }\r
116 \r
117         private string FormationName(dynamic json)\r
118         {\r
119             if (!json.api_formation()) // 演習の夜戦\r
120                 return "";\r
121             switch ((int)json.api_formation[2])\r
122             {\r
123                 case 1:\r
124                     return "同航戦";\r
125                 case 2:\r
126                     return "反航戦";\r
127                 case 3:\r
128                     return "T字有利";\r
129                 case 4:\r
130                     return "T字不利";\r
131             }\r
132             return "";\r
133         }\r
134 \r
135         private void SetupResult(dynamic json)\r
136         {\r
137             if (_friend != null)\r
138                 return;\r
139             var nowhps = (int[])json.api_nowhps;\r
140             _fleet = DeckId(json);\r
141             var fstats = _shipInfo.GetShipStatuses(_fleet);\r
142             FlagshipRecovery(fstats[0]);\r
143             _friend = Record.Setup(fstats);\r
144             _enemyHp = nowhps.Skip(7).TakeWhile(hp => hp != -1).ToArray();\r
145             _enemyStartHp = (int[])_enemyHp.Clone();\r
146             EnemyResultStatus =\r
147             (from id in (int[])json.api_ship_ke\r
148                 where id != -1\r
149                 select new ShipStatus {Id = id, Spec = _shipInfo.GetSpec(id)}).ToArray();\r
150             _guard = new Record[0];\r
151             _enemyGuardHp = new int[0];\r
152             _enemyGuardStartHp = new int[0];\r
153             if (!json.api_nowhps_combined())\r
154                 return;\r
155             var combined = (int[])json.api_nowhps_combined;\r
156             if (combined[1] != -1) // 味方が連合艦隊\r
157                 _guard = Record.Setup(_shipInfo.GetShipStatuses(1));\r
158             if (combined.Length > 7) // 敵が連合艦隊\r
159             {\r
160                 _enemyGuardHp =\r
161                     ((int[])json.api_nowhps_combined).\r
162                         Skip(7).TakeWhile(hp => hp != -1).ToArray();\r
163                 _enemyGuardStartHp = (int[])_enemyGuardHp.Clone();\r
164             }\r
165         }\r
166 \r
167         private void FlagshipRecovery(ShipStatus flagship)\r
168         {\r
169             switch (_flagshipRecoveryType)\r
170             {\r
171                 case 0:\r
172                     return;\r
173                 case 1:\r
174                     flagship.NowHp = flagship.MaxHp / 2;\r
175                     ConsumeSlotItem(flagship, 42); // ダメコン\r
176                     break;\r
177                 case 2:\r
178                     flagship.NowHp = flagship.MaxHp;\r
179                     ConsumeSlotItem(flagship, 43); // 女神\r
180                     break;\r
181             }\r
182             if (_flagshipRecoveryType != 0)\r
183                 _shipInfo.SetBadlyDamagedShips();\r
184             _flagshipRecoveryType = 0;\r
185         }\r
186 \r
187         private static void ConsumeSlotItem(ShipStatus ship, int id)\r
188         {\r
189             if (ship.SlotEx.Spec.Id == id)\r
190             {\r
191                 ship.SlotEx = new ItemStatus();\r
192                 return;\r
193             }\r
194             for (var i = 0; i < ship.Slot.Length; i++)\r
195             {\r
196                 if (ship.Slot[i].Spec.Id == id)\r
197                 {\r
198                     ship.Slot[i] = new ItemStatus();\r
199                     break;\r
200                 }\r
201             }\r
202         }\r
203 \r
204         public void CleanupResult()\r
205         {\r
206             _friend = null;\r
207         }\r
208 \r
209         private int CheckAirControlLevel(dynamic json)\r
210         {\r
211             if (!json.api_kouku())\r
212                 return -1;\r
213             var stage1 = json.api_kouku.api_stage1;\r
214             if (stage1 == null)\r
215                 return -1;\r
216             if (stage1.api_f_count == 0 && stage1.api_e_count == 0)\r
217                 return -1;\r
218             return (int)stage1.api_disp_seiku;\r
219         }\r
220 \r
221         private string CalcEnemyFighterPower(dynamic json)\r
222         {\r
223             var missing = "";\r
224             var maxEq = ((int[])json.api_ship_ke).Skip(1).SelectMany(id =>\r
225             {\r
226                 var r = _shipInfo.GetSpec(id).MaxEq;\r
227                 if (r != null)\r
228                     return r;\r
229                 missing = "+";\r
230                 return new int[5];\r
231             }).ToArray();\r
232             var equips = ((int[][])json.api_eSlot).SelectMany(x => x);\r
233             return (from slot in equips.Zip(maxEq, (id, max) => new {id, max})\r
234                        let spec = _itemInfo.GetSpecByItemId(slot.id)\r
235                        where spec.CanAirCombat\r
236                        select (int)Floor(spec.AntiAir * Sqrt(slot.max))).DefaultIfEmpty().Sum() + missing;\r
237         }\r
238 \r
239         private void CalcDamage(dynamic json, bool surfaceFleet = false)\r
240         {\r
241             AirBattleResults.Clear();\r
242             var fc = _guard.Length > 0;\r
243             var ec = _enemyGuardHp.Length > 0;\r
244             var both = fc && ec;\r
245             if (json.api_air_base_injection())\r
246             {\r
247                 AddAirBattleResult(json.api_air_base_injection, "AB噴式");\r
248                 CalcKoukuDamage(json.api_air_base_injection);\r
249             }\r
250             if (json.api_injection_kouku())\r
251             {\r
252                 AddAirBattleResult(json.api_injection_kouku, "噴式");\r
253                 CalcKoukuDamage(json.api_injection_kouku);\r
254             }\r
255             if (json.api_air_base_attack())\r
256                 CalcAirBaseAttackDamage(json.api_air_base_attack);\r
257             if (json.api_kouku())\r
258             {\r
259                 AddAirBattleResult(json.api_kouku, "航空戦");\r
260                 CalcKoukuDamage(json.api_kouku);\r
261             }\r
262             if (json.api_kouku2()) // 航空戦2回目\r
263             {\r
264                 AddAirBattleResult(json.api_kouku2, "航空戦2");\r
265                 CalcKoukuDamage(json.api_kouku2);\r
266             }\r
267             if (!json.api_opening_atack()) // 航空戦のみ\r
268                 return;\r
269             if (json.api_support_info() && json.api_support_info != null)\r
270                 CalcSupportDamage(json.api_support_info);\r
271             if (json.api_opening_taisen() && json.api_opening_taisen != null)\r
272             {\r
273                 if (json.api_opening_taisen.api_at_eflag())\r
274                 {\r
275                     CalcCombinedHougekiDamage(json.api_opening_taisen, _friend, _guard, _enemyHp, _enemyGuardHp);\r
276                 }\r
277                 else\r
278                 {\r
279                     CalcHougekiDamage(json.api_opening_taisen,\r
280                         fc ? _guard : _friend, // 先制対潜攻撃の対象は護衛\r
281                         _enemyHp);\r
282                 }\r
283             }\r
284             if (json.api_opening_atack != null)\r
285             {\r
286                 if (both)\r
287                 {\r
288                     CalcSimpleDamage(json.api_opening_atack, _friend, _guard, _enemyHp, _enemyGuardHp);\r
289                 }\r
290                 else\r
291                 {\r
292                     CalcSimpleDamage(json.api_opening_atack,\r
293                         fc ? _guard : _friend, // 雷撃の対象は護衛\r
294                         _enemyHp, _enemyGuardHp);\r
295                 }\r
296             }\r
297             if (json.api_hougeki1() && json.api_hougeki1 != null)\r
298             {\r
299                 if (json.api_hougeki1.api_at_eflag())\r
300                 {\r
301                     CalcCombinedHougekiDamage(json.api_hougeki1, _friend, _guard, _enemyHp, _enemyGuardHp);\r
302                 }\r
303                 else\r
304                 {\r
305                     CalcHougekiDamage(json.api_hougeki1,\r
306                         fc && !surfaceFleet ? _guard : _friend, // 空母機動部隊は一巡目が護衛\r
307                         ec ? _enemyGuardHp : _enemyHp); // 敵連合艦隊は一巡目が護衛\r
308                 }\r
309             }\r
310             if (json.api_hougeki2() && json.api_hougeki2 != null)\r
311             {\r
312                 if (json.api_hougeki2.api_at_eflag())\r
313                 {\r
314                     CalcCombinedHougekiDamage(json.api_hougeki2, _friend, _guard, _enemyHp, _enemyGuardHp);\r
315                 }\r
316                 else\r
317                 {\r
318                     CalcHougekiDamage(json.api_hougeki2, _friend, _enemyHp);\r
319                 }\r
320             }\r
321             if (json.api_hougeki3() && json.api_hougeki3 != null)\r
322             {\r
323                 if (json.api_hougeki3.api_at_eflag())\r
324                 {\r
325                     CalcCombinedHougekiDamage(json.api_hougeki3, _friend, _guard, _enemyHp, _enemyGuardHp);\r
326                 }\r
327                 else\r
328                 {\r
329                     CalcHougekiDamage(json.api_hougeki3,\r
330                         fc && surfaceFleet ? _guard : _friend, // 水上打撃部隊は三順目が護衛\r
331                         _enemyHp);\r
332                 }\r
333             }\r
334             if (json.api_raigeki() && json.api_raigeki != null)\r
335             {\r
336                 if (both)\r
337                 {\r
338                     CalcSimpleDamage(json.api_raigeki, _friend, _guard, _enemyHp, _enemyGuardHp);\r
339                 }\r
340                 else\r
341                 {\r
342                     CalcSimpleDamage(json.api_raigeki,\r
343                         fc ? _guard : _friend, // 雷撃の対象は護衛\r
344                         _enemyHp, _enemyGuardHp);\r
345                 }\r
346             }\r
347         }\r
348 \r
349         private void CalcSupportDamage(dynamic json)\r
350         {\r
351             if (json.api_support_hourai != null)\r
352             {\r
353                 CalcSimpleDamage(json.api_support_hourai.api_damage, _enemyHp, _enemyGuardHp);\r
354             }\r
355             else if (json.api_support_airatack != null)\r
356             {\r
357                 CalcSimpleDamage(json.api_support_airatack.api_stage3.api_edam, _enemyHp, _enemyGuardHp);\r
358             }\r
359         }\r
360 \r
361         private void CalcAirBaseAttackDamage(dynamic json)\r
362         {\r
363             var i = 1;\r
364             foreach (var entry in json)\r
365             {\r
366                 AddAirBattleResult(entry, "基地" + i++);\r
367                 CalcKoukuDamage(entry);\r
368             }\r
369         }\r
370 \r
371         private void AddAirBattleResult(dynamic json, string phaseName)\r
372         {\r
373             var stage1 = json.api_stage1;\r
374             if (stage1 == null || (stage1.api_f_count == 0 && stage1.api_e_count == 0))\r
375                 return;\r
376             AirBattleResults.Add(new AirBattleResult\r
377             {\r
378                 PhaseName = phaseName,\r
379                 AirControlLevel = json.api_stage1.api_disp_seiku() ? (int)json.api_stage1.api_disp_seiku : 0,\r
380                 Stage1 = new AirBattleResult.StageResult\r
381                 {\r
382                     FriendCount = (int)json.api_stage1.api_f_count,\r
383                     FriendLost = (int)json.api_stage1.api_f_lostcount,\r
384                     EnemyCount = (int)json.api_stage1.api_e_count,\r
385                     EnemyLost = (int)json.api_stage1.api_e_lostcount\r
386                 },\r
387                 Stage2 = json.api_stage2 == null\r
388                     ? new AirBattleResult.StageResult\r
389                     {\r
390                         FriendCount = 0,\r
391                         FriendLost = 0,\r
392                         EnemyCount = 0,\r
393                         EnemyLost = 0\r
394                     }\r
395                     : new AirBattleResult.StageResult\r
396                     {\r
397                         FriendCount = (int)json.api_stage2.api_f_count,\r
398                         FriendLost = (int)json.api_stage2.api_f_lostcount,\r
399                         EnemyCount = (int)json.api_stage2.api_e_count,\r
400                         EnemyLost = (int)json.api_stage2.api_e_lostcount\r
401                     }\r
402             });\r
403         }\r
404 \r
405         private void CalcKoukuDamage(dynamic json)\r
406         {\r
407             if (!json.api_stage3() || json.api_stage3 == null)\r
408                 return;\r
409             CalcSimpleDamage(json.api_stage3, _friend, _enemyHp);\r
410             if (json.api_stage3_combined())\r
411                 CalcSimpleDamage(json.api_stage3_combined, _guard, _enemyGuardHp);\r
412         }\r
413 \r
414         private void CalcSimpleDamage(dynamic json, Record[] friend, int[] enemy)\r
415         {\r
416             if (json.api_fdam())\r
417                 CalcSimpleDamage(json.api_fdam, friend);\r
418             if (json.api_edam())\r
419                 CalcSimpleDamage(json.api_edam, enemy);\r
420         }\r
421 \r
422         private void CalcSimpleDamage(dynamic json, Record[] friend, int[] enemy, int[] enemyGuard)\r
423         {\r
424             CalcSimpleDamage(json.api_fdam, friend);\r
425             CalcSimpleDamage(json.api_edam, enemy, enemyGuard);\r
426         }\r
427 \r
428         private void CalcSimpleDamage(dynamic json, Record[] friend, Record[] guard, int[] enemy, int[] enemyGuard)\r
429         {\r
430             CalcSimpleDamage(json.api_fdam, friend, guard);\r
431             CalcSimpleDamage(json.api_edam, enemy, enemyGuard);\r
432         }\r
433 \r
434         private void CalcSimpleDamage(dynamic rawDamage, Record[] friend, Record[] guard)\r
435         {\r
436             var damage = (int[])rawDamage;\r
437             for (var i = 0; i < friend.Length; i++)\r
438                 friend[i].ApplyDamage(damage[i + 1]);\r
439             for (var i = 0; i < guard.Length; i++)\r
440                 guard[i].ApplyDamage(damage[i + 6 + 1]);\r
441         }\r
442 \r
443         private void CalcSimpleDamage(dynamic rawDamage, Record[] friend)\r
444         {\r
445             var damage = (int[])rawDamage;\r
446             for (var i = 0; i < friend.Length; i++)\r
447                 friend[i].ApplyDamage(damage[i + 1]);\r
448         }\r
449 \r
450         private void CalcSimpleDamage(dynamic rawDamage, int[] enemy, int[] enemyGuard)\r
451         {\r
452             var damage = (int[])rawDamage;\r
453             for (var i = 0; i < enemy.Length; i++)\r
454                 enemy[i] -= damage[i + 1];\r
455             for (var i = 0; i < enemyGuard.Length; i++)\r
456                 enemyGuard[i] -= damage[i + 6 + 1];\r
457         }\r
458 \r
459         private void CalcSimpleDamage(dynamic rawDamage, int[] result)\r
460         {\r
461             var damage = (int[])rawDamage;\r
462             for (var i = 0; i < result.Length; i++)\r
463                 result[i] -= damage[i + 1];\r
464         }\r
465 \r
466         private void CalcHougekiDamage(dynamic hougeki, Record[] friend, int[] enemy)\r
467         {\r
468             var targets = ((dynamic[])hougeki.api_df_list).Skip(1).SelectMany(x => (int[])x);\r
469             var damages = ((dynamic[])hougeki.api_damage).Skip(1).SelectMany(x => (int[])x);\r
470             foreach (var hit in targets.Zip(damages, (t, d) => new {t, d}))\r
471             {\r
472                 if (hit.t == -1)\r
473                     continue;\r
474                 if (hit.t <= 6)\r
475                     friend[hit.t - 1].ApplyDamage(hit.d);\r
476                 else\r
477                     enemy[(hit.t - 1) % 6] -= hit.d;\r
478             }\r
479         }\r
480 \r
481         private void CalcCombinedHougekiDamage(dynamic hougeki, Record[] friend, Record[] guard,\r
482             int[] enemy, int[] enemyGuard)\r
483         {\r
484             var targets = ((dynamic[])hougeki.api_df_list).Skip(1).Select(x => (int[])x);\r
485             var damages = ((dynamic[])hougeki.api_damage).Skip(1).Select(x => (int[])x);\r
486             var eflags = ((int[])hougeki.api_at_eflag).Skip(1);\r
487             foreach (var turn in\r
488                 targets.Zip(damages, (t, d) => new {t, d}).\r
489                     Zip(eflags, (td, e) => new {e, td.t, td.d}))\r
490             {\r
491                 foreach (var hit in turn.t.Zip(turn.d, (t, d) => new {t, d}))\r
492                 {\r
493                     if (turn.e == 1)\r
494                     {\r
495                         if (hit.t <= 6)\r
496                             friend[hit.t - 1].ApplyDamage(hit.d);\r
497                         else\r
498                             guard[(hit.t - 1) % 6].ApplyDamage(hit.d);\r
499                     }\r
500                     else\r
501                     {\r
502                         if (hit.t <= 6)\r
503                             enemy[hit.t - 1] -= hit.d;\r
504                         else\r
505                             enemyGuard[(hit.t - 1) % 6] -= hit.d;\r
506                     }\r
507                 }\r
508             }\r
509         }\r
510 \r
511         public void InspectBattleResult(dynamic json)\r
512         {\r
513             BattleState = BattleState.Result;\r
514             ShowResult();\r
515             CleanupResult();\r
516             SetEscapeShips(json);\r
517         }\r
518 \r
519         public void InspectPracticeResult(dynamic json)\r
520         {\r
521             BattleState = BattleState.Result;\r
522             ShowResult(false);\r
523             CleanupResult();\r
524         }\r
525 \r
526         private void ShowResult(bool warnDamagedShip = true)\r
527         {\r
528             if (_friend == null)\r
529                 return;\r
530             var ships = _guard.Length > 0\r
531                 ? _shipInfo.GetShipStatuses(0).Concat(_shipInfo.GetShipStatuses(1)).ToArray()\r
532                 : _shipInfo.GetShipStatuses(_fleet);\r
533             foreach (var entry in ships.Zip(_friend.Concat(_guard), (ship, now) => new {ship, now}))\r
534                 entry.now.UpdateShipStatus(entry.ship);\r
535             if (warnDamagedShip)\r
536                 _shipInfo.SetBadlyDamagedShips();\r
537             else\r
538                 _shipInfo.ClearBadlyDamagedShips();\r
539             SetEnemyResultStatus();\r
540         }\r
541 \r
542         private void SetEnemyResultStatus()\r
543         {\r
544             for (var i = 0; i < EnemyResultStatus.Length; i++)\r
545             {\r
546                 EnemyResultStatus[i].MaxHp = _enemyStartHp[i];\r
547                 EnemyResultStatus[i].NowHp = _enemyHp[i];\r
548             }\r
549         }\r
550 \r
551         public void SetEscapeShips(dynamic json)\r
552         {\r
553             _escapingShips.Clear();\r
554             if (!json.api_escape_flag() || (int)json.api_escape_flag == 0)\r
555                 return;\r
556             var damaged = (int)json.api_escape.api_escape_idx[0] - 1;\r
557             _escapingShips.Add(_shipInfo.GetDeck(damaged / 6)[damaged % 6]);\r
558             var escort = (int)json.api_escape.api_tow_idx[0] - 1;\r
559             _escapingShips.Add(_shipInfo.GetDeck(escort / 6)[escort % 6]);\r
560         }\r
561 \r
562         public void CauseCombinedBattleEscape()\r
563         {\r
564             _shipInfo.SetEscapedShips(_escapingShips);\r
565             _shipInfo.SetBadlyDamagedShips();\r
566         }\r
567 \r
568         private class Record\r
569         {\r
570             private ShipStatus _status;\r
571             public int NowHp => _status.NowHp;\r
572             public bool Escaped => _status.Escaped;\r
573             public ShipStatus.Damage DamageLevel => _status.DamageLevel;\r
574             public int StartHp;\r
575 \r
576             public static Record[] Setup(ShipStatus[] ships) =>\r
577                 (from s in ships select new Record {_status = (ShipStatus)s.Clone(), StartHp = s.NowHp}).ToArray();\r
578 \r
579             public void ApplyDamage(int damage)\r
580             {\r
581                 if (_status.NowHp > damage)\r
582                 {\r
583                     _status.NowHp -= damage;\r
584                     return;\r
585                 }\r
586                 _status.NowHp = 0;\r
587                 foreach (var item in new[] {_status.SlotEx}.Concat(_status.Slot))\r
588                 {\r
589                     if (item.Spec.Id == 42)\r
590                     {\r
591                         _status.NowHp = (int)(_status.MaxHp * 0.2);\r
592                         ConsumeSlotItem(_status, 42);\r
593                         break;\r
594                     }\r
595                     if (item.Spec.Id == 43)\r
596                     {\r
597                         _status.NowHp = _status.MaxHp;\r
598                         ConsumeSlotItem(_status, 43);\r
599                         break;\r
600                     }\r
601                 }\r
602             }\r
603 \r
604             public void UpdateShipStatus(ShipStatus ship)\r
605             {\r
606                 ship.NowHp = NowHp;\r
607                 ship.Slot = _status.Slot;\r
608                 ship.SlotEx = _status.SlotEx;\r
609             }\r
610         }\r
611 \r
612         private BattleResultRank CalcLdAirBattleRank()\r
613         {\r
614             var combined = _friend.Concat(_guard).ToArray();\r
615             var friendNowShips = combined.Count(r => r.NowHp > 0);\r
616             var friendGauge = combined.Sum(r => r.StartHp - r.NowHp);\r
617             var friendSunk = combined.Count(r => r.NowHp == 0);\r
618             var friendGaugeRate = Floor((double)friendGauge / combined.Sum(r => r.StartHp) * 100);\r
619 \r
620             if (friendSunk == 0)\r
621             {\r
622                 if (friendGauge == 0)\r
623                     return BattleResultRank.P;\r
624                 if (friendGaugeRate < 10)\r
625                     return BattleResultRank.A;\r
626                 if (friendGaugeRate < 20)\r
627                     return BattleResultRank.B;\r
628                 if (friendGaugeRate < 50)\r
629                     return BattleResultRank.C;\r
630                 return BattleResultRank.D;\r
631             }\r
632             if (friendSunk < friendNowShips)\r
633                 return BattleResultRank.D;\r
634             return BattleResultRank.E;\r
635         }\r
636 \r
637         private BattleResultRank CalcResultRank()\r
638         {\r
639             var friend = _friend.Concat(_guard).ToArray();\r
640             var enemyHp = _enemyHp.Concat(_enemyGuardHp).ToArray();\r
641             var enemyStartHp = _enemyStartHp.Concat(_enemyGuardStartHp).ToArray();\r
642 \r
643             var friendCount = friend.Length;\r
644             var friendStartHpTotal = 0;\r
645             var friendNowHpTotal = 0;\r
646             var friendSunk = 0;\r
647             foreach (var ship in friend)\r
648             {\r
649                 if (ship.Escaped)\r
650                     continue;\r
651                 friendStartHpTotal += ship.StartHp;\r
652                 friendNowHpTotal += ship.NowHp;\r
653                 if (ship.NowHp == 0)\r
654                     friendSunk++;\r
655             }\r
656             var friendGaugeRate = (int)((double)(friendStartHpTotal - friendNowHpTotal) / friendStartHpTotal * 100);\r
657 \r
658             var enemyCount = enemyHp.Length;\r
659             var enemyStartHpTotal = enemyStartHp.Sum();\r
660             var enemyNowHpTotal = enemyHp.Sum();\r
661             var enemySunk = enemyHp.Count(hp => hp == 0);\r
662             var enemyGaugeRate = (int)((double)(enemyStartHpTotal - enemyNowHpTotal) / enemyStartHpTotal * 100);\r
663 \r
664             if (friendSunk == 0 && enemySunk == enemyCount)\r
665             {\r
666                 if (friendNowHpTotal >= friendStartHpTotal)\r
667                     return BattleResultRank.P;\r
668                 return BattleResultRank.S;\r
669             }\r
670             if (friendSunk == 0 && enemySunk >= (int)(enemyCount * 0.7) && enemyCount > 1)\r
671                 return BattleResultRank.A;\r
672             if (friendSunk < enemySunk && enemyHp[0] == 0)\r
673                 return BattleResultRank.B;\r
674             if (friendCount == 1 && friend[0].DamageLevel == ShipStatus.Damage.Badly)\r
675                 return BattleResultRank.D;\r
676             if (enemyGaugeRate > friendGaugeRate * 2.5)\r
677                 return BattleResultRank.B;\r
678             if (enemyGaugeRate > friendGaugeRate * 0.9)\r
679                 return BattleResultRank.C;\r
680             if (friendCount > 1 && friendCount - 1 == friendSunk)\r
681                 return BattleResultRank.E;\r
682             return BattleResultRank.D;\r
683         }\r
684     }\r
685 }