OSDN Git Service

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