OSDN Git Service

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