OSDN Git Service

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