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