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