OSDN Git Service

基地空襲戦の制空値がメインウィンドウに表示されないのを直す
[kancollesniffer/KancolleSniffer.git] / KancolleSniffer / View / BattleResultPanel.cs
1 // Copyright (C) 2017 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.Drawing;\r
18 using System.Linq;\r
19 using System.Text.RegularExpressions;\r
20 using System.Windows.Forms;\r
21 using KancolleSniffer.Model;\r
22 using static System.Math;\r
23 \r
24 namespace KancolleSniffer.View\r
25 {\r
26     public class BattleResultPanel : PanelWithToolTip\r
27     {\r
28         private readonly List<ShipLabels> _friendLabels = new List<ShipLabels>();\r
29         private readonly List<ShipLabels> _enemyLabels = new List<ShipLabels>();\r
30         private readonly List<ShipLabel.Hp> _hpLabels = new List<ShipLabel.Hp>();\r
31         private Label _phaseLabel, _rankLabel, _supportLabel, _cellLabel;\r
32         private readonly InformationPanel _information;\r
33         private CellInfo _cellInfo;\r
34         private readonly BattleData _data = new BattleData();\r
35 \r
36         public Spoiler Spoilers { get; set; }\r
37 \r
38         private class BattleData\r
39         {\r
40             private Result _day;\r
41             private Result _night;\r
42             public int[] Formation;\r
43             public Range FighterPower;\r
44             public EnemyFighterPower EnemyFighterPower;\r
45             public int AirControlLevel;\r
46             public int SupportType;\r
47             public bool HaveDay => _day != null;\r
48             public bool HaveNight => _night != null;\r
49 \r
50             public class Result\r
51             {\r
52                 public BattleInfo.BattleResult Damage;\r
53                 public BattleResultRank Rank;\r
54             }\r
55 \r
56             public Result GetResult(BattleState state)\r
57             {\r
58                 switch (state)\r
59                 {\r
60                     case BattleState.Day:\r
61                     case BattleState.AirRaid:\r
62                         return _day;\r
63                     case BattleState.SpNight:\r
64                     case BattleState.Night:\r
65                         return _night;\r
66                     default:\r
67                         return _day;\r
68                 }\r
69             }\r
70 \r
71             public void SetData(BattleInfo battle)\r
72             {\r
73                 switch (battle.BattleState)\r
74                 {\r
75                     case BattleState.Day:\r
76                     case BattleState.AirRaid:\r
77                         _day = new Result {Damage = battle.Result, Rank = battle.ResultRank};\r
78                         _night = null;\r
79                         break;\r
80                     case BattleState.SpNight:\r
81                         _day = null;\r
82                         goto case BattleState.Night;\r
83                     case BattleState.Night:\r
84                         _night = new Result {Damage = battle.Result, Rank = battle.ResultRank};\r
85                         break;\r
86                 }\r
87                 if (battle.BattleState != BattleState.Night)\r
88                     SetNonStateData(battle);\r
89             }\r
90 \r
91             private void SetNonStateData(BattleInfo battle)\r
92             {\r
93                 Formation = battle.Formation;\r
94                 FighterPower = battle.FighterPower;\r
95                 EnemyFighterPower = battle.EnemyFighterPower;\r
96                 AirControlLevel = battle.AirControlLevel;\r
97                 SupportType = battle.SupportType;\r
98             }\r
99         }\r
100 \r
101         public BattleResultPanel()\r
102         {\r
103             SuspendLayout();\r
104             CreateLabels();\r
105             _information = new InformationPanel();\r
106             Controls.Add(_information);\r
107             ResumeLayout();\r
108         }\r
109 \r
110         public event Action HpLabelClick;\r
111 \r
112         private void HpLabelClickHandler(object sender, EventArgs ev)\r
113         {\r
114             HpLabelClick?.Invoke();\r
115         }\r
116 \r
117         public void ToggleHpPercent()\r
118         {\r
119             foreach (var label in _hpLabels)\r
120                 label.ToggleHpPercent();\r
121         }\r
122 \r
123         public void Update(Sniffer sniffer)\r
124         {\r
125             var state = sniffer.Battle.BattleState;\r
126             _cellInfo = sniffer.CellInfo;\r
127             switch (sniffer.Battle.BattleState)\r
128             {\r
129                 case BattleState.None:\r
130                     if (!_data.HaveDay && !_data.HaveNight)\r
131                         return;\r
132                     ClearResult();\r
133                     SetPhase("結果");\r
134                     UpdateCellInfo(_cellInfo);\r
135                     return;\r
136                 case BattleState.Result:\r
137                 case BattleState.Unknown:\r
138                     return;\r
139             }\r
140             _data.SetData(sniffer.Battle);\r
141             if ((Spoilers & Spoiler.BattleResult) != 0)\r
142             {\r
143                 ShowData(sniffer.Battle.BattleState);\r
144                 switch (state)\r
145                 {\r
146                     case BattleState.Day:\r
147                     case BattleState.AirRaid:\r
148                         SetPhase("昼戦");\r
149                         break;\r
150                     case BattleState.Night:\r
151                     case BattleState.SpNight:\r
152                         SetPhase("夜戦");\r
153                         break;\r
154                 }\r
155             }\r
156             else\r
157             {\r
158                 ClearResult();\r
159                 SetPhase("結果");\r
160             }\r
161         }\r
162 \r
163         private void PhaseLabelClick(object sender, EventArgs ev)\r
164         {\r
165             switch (_phaseLabel.Text)\r
166             {\r
167                 case "結果":\r
168                     if (_data.HaveDay)\r
169                     {\r
170                         ShowData(BattleState.Day);\r
171                         SetPhase("昼戦");\r
172                     }\r
173                     else if (_data.HaveNight)\r
174                     {\r
175                         ShowData(BattleState.Night);\r
176                         SetPhase("夜戦");\r
177                     }\r
178                     break;\r
179                 case "昼戦":\r
180                     if (_data.HaveNight)\r
181                     {\r
182                         ShowData(BattleState.Night);\r
183                         SetPhase("夜戦");\r
184                     }\r
185                     break;\r
186                 case "夜戦":\r
187                     if (_data.HaveDay)\r
188                     {\r
189                         ShowData(BattleState.Day);\r
190                         SetPhase("昼戦");\r
191                     }\r
192                     break;\r
193             }\r
194         }\r
195 \r
196         private void SetPhase(string phase)\r
197         {\r
198             _phaseLabel.Text = phase;\r
199             if (phase == "結果" || _data.HaveDay && _data.HaveNight)\r
200             {\r
201                 _phaseLabel.BorderStyle = BorderStyle.FixedSingle;\r
202                 _phaseLabel.Cursor = Cursors.Hand;\r
203             }\r
204             else\r
205             {\r
206                 _phaseLabel.BorderStyle = BorderStyle.None;\r
207                 _phaseLabel.Cursor = Cursors.Default;\r
208             }\r
209         }\r
210 \r
211         private Point _scrollPosition;\r
212 \r
213         private void ClearResult()\r
214         {\r
215             _scrollPosition = AutoScrollPosition;\r
216             SetPanelVisible(0);\r
217             _information.Visible = false;\r
218             _rankLabel.Text = "";\r
219             _supportLabel.Text = "";\r
220         }\r
221 \r
222         private void ShowData(BattleState state)\r
223         {\r
224             var result = _data.GetResult(state);\r
225             ShowDamage(result.Damage);\r
226             ShowResultRank(result.Rank);\r
227             _information.Show(_data);\r
228             ShowSupportType(_data.SupportType);\r
229             UpdateCellInfo(_cellInfo);\r
230             AutoScrollPosition = new Point(-_scrollPosition.X, -_scrollPosition.Y);\r
231         }\r
232 \r
233         private void ShowDamage(BattleInfo.BattleResult result)\r
234         {\r
235             SuspendLayout();\r
236             SetEachResult(_friendLabels, result.Friend);\r
237             SetEachResult(_enemyLabels, result.Enemy);\r
238             var lines = Max(Ships(result.Friend).Length, Ships(result.Enemy).Length);\r
239             SetPanelVisible(lines);\r
240             ResumeLayout(); // スクロールバーの有無を決定する\r
241             AdjustPanelWidth(lines);\r
242         }\r
243 \r
244         private void SetEachResult(IReadOnlyList<ShipLabels> labelsList, BattleInfo.BattleResult.Combined fleet)\r
245         {\r
246             var ships = Ships(fleet);\r
247             for (var i = 0; i < ships.Length; i++)\r
248             {\r
249                 var labels = labelsList[i];\r
250                 if (i == fleet.Main.Length)\r
251                 {\r
252                     labels.Name.Text = "護衛";\r
253                     labels.Hp.Reset();\r
254                     continue;\r
255                 }\r
256                 var ship = ships[i];\r
257                 if (ShipMaster.IsEnemyId(ship.Spec.Id))\r
258                 {\r
259                     labels.Hp.Set(ship);\r
260                     labels.Name.SetName(ShortenName(ship.Name));\r
261                 }\r
262                 else\r
263                 {\r
264                     labels.Set(ship);\r
265                     if (ship.Name.StartsWith("基地航空隊"))\r
266                         labels.Name.SetName(ship.Name.Substring(2));\r
267                 }\r
268                 ToolTip.SetToolTip(labels.Name, GetEquipString(ship));\r
269             }\r
270             for (var i = ships.Length; i < labelsList.Count; i++)\r
271                 labelsList[i].Reset();\r
272         }\r
273 \r
274         private static ShipStatus[] Ships(BattleInfo.BattleResult.Combined fleet)\r
275         {\r
276             if (fleet.Guard.Length == 0)\r
277                 return fleet.Main;\r
278             return fleet.Main.Concat(new[] {new ShipStatus()}.Concat(fleet.Guard)).ToArray();\r
279         }\r
280 \r
281         private static string GetEquipString(ShipStatus ship)\r
282         {\r
283             var result =\r
284                 (from i in Enumerable.Range(0, ship.Slot.Count)\r
285                     let item = ship.Slot[i]\r
286                     where !item.Empty\r
287                     select item.Spec.Name +\r
288                            (item.Spec.IsAircraft && ship.OnSlot.Length > 0 && ship.Spec.MaxEq.Length > 0\r
289                                ? $"{ship.OnSlot[i]}/{ship.Spec.MaxEq[i]}"\r
290                                : ""));\r
291             if (ship.SlotEx.Id > 0)\r
292                 result = result.Concat(new[] {ship.SlotEx.Spec.Name});\r
293             return string.Join("\r\n", result);\r
294         }\r
295 \r
296         private static string ShortenName(string name)\r
297         {\r
298             return new Regex(@"\(elite\)|\(flagship\)").Replace(name,\r
299                 match => match.Value == "(elite)" ? "(e)" : "(f)");\r
300         }\r
301 \r
302         private void ShowResultRank(BattleResultRank rank)\r
303         {\r
304             var result = new[] {"完全S", "勝利S", "勝利A", "勝利B", "敗北C", "敗北D", "敗北E"};\r
305             _rankLabel.Text = result[(int)rank];\r
306         }\r
307 \r
308         private void ShowSupportType(int support)\r
309         {\r
310             _supportLabel.Text = new[] {"", "空支援", "砲支援", "雷支援", "潜支援"}[support];\r
311         }\r
312 \r
313         public void UpdateCellInfo(CellInfo cellInfo)\r
314         {\r
315             _cellLabel.Text = (Spoilers & Spoiler.NextCell) == 0 ? cellInfo.Current : cellInfo.Next;\r
316             _cellLabel.Location = new Point(ClientSize.Width - _cellLabel.Width - 2, _cellLabel.Location.Y);\r
317         }\r
318 \r
319         private const int LineHeight = 16;\r
320         private const int LabelHeight = 12;\r
321         private const int LabelMargin = (LineHeight - LabelHeight) / 2;\r
322         private const int TopMargin = 4;\r
323 \r
324         private void CreateLabels()\r
325         {\r
326             _phaseLabel = new Label\r
327             {\r
328                 Location = new Point(4, TopMargin),\r
329                 Size = new Size(31, LabelHeight + 2)\r
330             };\r
331             _phaseLabel.Click += PhaseLabelClick;\r
332             Controls.Add(_phaseLabel);\r
333             _rankLabel = new Label\r
334             {\r
335                 Location = new Point(37, TopMargin),\r
336                 Size = new Size(42, LabelHeight)\r
337             };\r
338             Controls.Add(_rankLabel);\r
339             _supportLabel = new Label\r
340             {\r
341                 Location = new Point(77, TopMargin),\r
342                 AutoSize = true\r
343             };\r
344             Controls.Add(_supportLabel);\r
345             _cellLabel = new Label\r
346             {\r
347                 Location = new Point(0, TopMargin),\r
348                 AutoSize = true\r
349             };\r
350             Controls.Add(_cellLabel);\r
351             for (var i = 0; i < 13; i++)\r
352             {\r
353                 var friend = new ShipLabels\r
354                 {\r
355                     Name = new ShipLabel.Name(new Point(1, LabelMargin), ShipNameWidth.BattleResult),\r
356                     Hp = new ShipLabel.Hp(new Point(101, 0), LineHeight),\r
357                     BackPanel = new Panel\r
358                     {\r
359                         Location = new Point(0, LineHeight * i + 38),\r
360                         Size = new Size(0, LineHeight),\r
361                         BackColor = CustomColors.ColumnColors.DarkFirst(i),\r
362                         Visible = false\r
363                     }\r
364                 };\r
365                 _friendLabels.Add(friend);\r
366                 friend.Arrange(this, CustomColors.ColumnColors.DarkFirst(i));\r
367                 _hpLabels.Add(friend.Hp);\r
368                 friend.Hp.Click += HpLabelClickHandler;\r
369                 var enemy = new ShipLabels\r
370                 {\r
371                     Name = new ShipLabel.Name(new Point(164, LabelMargin), ShipNameWidth.Max),\r
372                     Hp = new ShipLabel.Hp\r
373                     {\r
374                         Location = new Point(119, 0),\r
375                         AutoSize = true,\r
376                         MinimumSize = new Size(0, LineHeight),\r
377                         TextAlign = ContentAlignment.MiddleLeft\r
378                     }\r
379                 };\r
380                 _enemyLabels.Add(enemy);\r
381                 enemy.Arrange(friend.BackPanel, CustomColors.ColumnColors.DarkFirst(i));\r
382             }\r
383         }\r
384 \r
385         private void SetPanelVisible(int showPanels)\r
386         {\r
387             for (var i = 0; i < _friendLabels.Count; i++)\r
388                 _friendLabels[i].BackPanel.Visible = i < showPanels;\r
389         }\r
390 \r
391         private void AdjustPanelWidth(int lines)\r
392         {\r
393             var panelWidth = Max(ClientSize.Width, // スクロールバーの有無を反映した横幅\r
394                 _enemyLabels[0].Name.Location.X + _enemyLabels.Max(labels => labels.Name.Size.Width) - 1); // 敵の名前の右端\r
395             for (var i = 0; i < lines; i++)\r
396                 _friendLabels[i].BackPanel.Width = panelWidth;\r
397         }\r
398 \r
399         protected override void OnMouseWheel(MouseEventArgs e)\r
400         {\r
401             if (VScroll && (ModifierKeys & Keys.Shift) == Keys.Shift)\r
402             {\r
403                 VScroll = false;\r
404                 OnMouseWheel(e);\r
405                 VScroll = true;\r
406                 return;\r
407             }\r
408             base.OnMouseWheel(e);\r
409         }\r
410 \r
411         private sealed class InformationPanel : PanelWithToolTip\r
412         {\r
413             private readonly Formation _formation;\r
414             private readonly FighterPower _fighterPower;\r
415 \r
416             public InformationPanel()\r
417             {\r
418                 _formation = new Formation\r
419                 {\r
420                     Friend = new Label\r
421                     {\r
422                         Location = new Point(47, LabelMargin),\r
423                         Size = new Size(29, LabelHeight)\r
424                     },\r
425                     Enemy = new Label\r
426                     {\r
427                         Location = new Point(75, LabelMargin),\r
428                         Size = new Size(29, LabelHeight)\r
429                     },\r
430                     State = new Label\r
431                     {\r
432                         Location = new Point(1, LabelMargin),\r
433                         Size = new Size(48, LabelHeight),\r
434                         TextAlign = ContentAlignment.MiddleCenter\r
435                     }\r
436                 };\r
437                 Controls.AddRange(_formation.Controls);\r
438                 _fighterPower = new FighterPower\r
439                 {\r
440                     ToolTip = ToolTip,\r
441                     Friend = new Label\r
442                     {\r
443                         Location = new Point(162, LabelMargin),\r
444                         Size = new Size(23, LabelHeight),\r
445                         TextAlign = ContentAlignment.MiddleRight\r
446                     },\r
447                     Enemy = new Label\r
448                     {\r
449                         Location = new Point(183, LabelMargin),\r
450                         Size = new Size(23, LabelHeight),\r
451                         TextAlign = ContentAlignment.MiddleRight\r
452                     },\r
453                     State = new Label\r
454                     {\r
455                         Location = new Point(110, LabelMargin),\r
456                         Size = new Size(53, LabelHeight)\r
457                     }\r
458                 };\r
459                 Controls.AddRange(_fighterPower.Controls);\r
460                 Location = new Point(0, 20);\r
461                 Size = new Size(206, 16);\r
462                 BackColor = CustomColors.ColumnColors.Bright;\r
463                 Visible = false;\r
464             }\r
465 \r
466             public void Show(BattleData data)\r
467             {\r
468                 SetData(data);\r
469                 Visible = true;\r
470             }\r
471 \r
472             private void SetData(BattleData data)\r
473             {\r
474                 _formation.SetFormation(data.Formation);\r
475                 _fighterPower.SetFighterPower(data);\r
476             }\r
477 \r
478             private class StateLabels\r
479             {\r
480                 public Label Friend;\r
481                 public Label Enemy;\r
482                 public Label State;\r
483 \r
484                 public Control[] Controls => new Control[] {Friend, Enemy, State};\r
485             }\r
486 \r
487             private class Formation : StateLabels\r
488             {\r
489                 public void SetFormation(int[] formation)\r
490                 {\r
491                     Friend.Text = FormationName(formation[0]);\r
492                     Enemy.Text = FormationName(formation[1]);\r
493                     State.Text = new[] {"同航戦", "反航戦", "T字有利", "T字不利"}[formation[2] - 1];\r
494                 }\r
495 \r
496                 private static string FormationName(int formation)\r
497                 {\r
498                     switch (formation)\r
499                     {\r
500                         case 1:\r
501                             return "単縦";\r
502                         case 2:\r
503                             return "複縦";\r
504                         case 3:\r
505                             return "輪形";\r
506                         case 4:\r
507                             return "梯形";\r
508                         case 5:\r
509                             return "単横";\r
510                         case 6:\r
511                             return "警戒";\r
512                         case 11:\r
513                             return "第一";\r
514                         case 12:\r
515                             return "第二";\r
516                         case 13:\r
517                             return "第三";\r
518                         case 14:\r
519                             return "第四";\r
520                         default:\r
521                             return "";\r
522                     }\r
523                 }\r
524             }\r
525 \r
526             private class FighterPower : StateLabels\r
527             {\r
528                 public ToolTip ToolTip { private get; set; }\r
529 \r
530                 public void SetFighterPower(BattleData data)\r
531                 {\r
532                     if (data.AirControlLevel == -1)\r
533                     {\r
534                         foreach (var control in Controls)\r
535                             control.Text = "";\r
536                         return;\r
537                     }\r
538                     SetFriend(data.FighterPower);\r
539                     SetEnemy(data.EnemyFighterPower);\r
540                     SetAirControlLevel(data.AirControlLevel);\r
541                 }\r
542 \r
543                 private void SetFriend(Range fighterPower)\r
544                 {\r
545                     Friend.Text = fighterPower.Min.ToString();\r
546                     ToolTip.SetToolTip(Friend, fighterPower.Diff ? fighterPower.RangeString : "");\r
547                 }\r
548 \r
549                 private void SetEnemy(EnemyFighterPower enemy)\r
550                 {\r
551                     Enemy.Text = enemy.AirCombat + enemy.UnknownMark;\r
552                     ToolTip.SetToolTip(Enemy,\r
553                         enemy.AirCombat == enemy.Interception ? "" : "防空:" + enemy.Interception + enemy.UnknownMark);\r
554                 }\r
555 \r
556                 private void SetAirControlLevel(int level)\r
557                 {\r
558                     State.Text =\r
559                         new[] {"", "制空均衡", "制空確保", "航空優勢", "航空劣勢", "制空喪失"}[level + 1];\r
560                 }\r
561             }\r
562         }\r
563     }\r
564 }