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 : Panel\r
27     {\r
28         private const int LineHeight = 16;\r
29         private readonly List<ShipLabel[]> _friendLabels = new List<ShipLabel[]>();\r
30         private readonly List<ShipLabel[]> _enemyLabels = new List<ShipLabel[]>();\r
31         private readonly List<Panel> _panelList = new List<Panel>();\r
32         private readonly List<ShipLabel> _hpLabels = new List<ShipLabel>();\r
33         private readonly ResizableToolTip _toolTip = new ResizableToolTip {ShowAlways = true};\r
34         private readonly BattleInfo.BattleResult[] _result = new BattleInfo.BattleResult[2];\r
35         private Label _phaseLabel, _rankLabel, _cellLabel;\r
36         private readonly BattleResultRank[] _rank = new BattleResultRank[2];\r
37         private readonly InformationPanel _informationPanel;\r
38         private CellInfo _cellInfo;\r
39 \r
40         public Spoiler Spoilers { get; set; }\r
41 \r
42         public BattleResultPanel()\r
43         {\r
44             SuspendLayout();\r
45             CreateLabels();\r
46             _informationPanel = new InformationPanel();\r
47             Controls.Add(_informationPanel);\r
48             ResumeLayout();\r
49         }\r
50 \r
51         public event Action HpLabelClick;\r
52 \r
53         private void HpLabelClickHandler(object sender, EventArgs ev)\r
54         {\r
55             HpLabelClick?.Invoke();\r
56         }\r
57 \r
58         public void ToggleHpPercent()\r
59         {\r
60             foreach (var label in _hpLabels)\r
61                 label.ToggleHpPercent();\r
62         }\r
63 \r
64         public void Update(Sniffer sniffer)\r
65         {\r
66             var state = sniffer.Battle.BattleState;\r
67             _cellInfo = sniffer.CellInfo;\r
68             switch (sniffer.Battle.BattleState)\r
69             {\r
70                 case BattleState.None:\r
71                     if (_result[0] == null && _result[1] == null)\r
72                         return;\r
73                         ClearResult();\r
74                         SetPhase("結果");\r
75                         UpdateCellInfo(_cellInfo);\r
76                         return;\r
77                 case BattleState.Day:\r
78                 case BattleState.SpNight:\r
79                     _result[0] = _result[1] = null;\r
80                     break;\r
81                 case BattleState.Result:\r
82                 case BattleState.Unknown:\r
83                     return;\r
84             }\r
85             if ((Spoilers & Spoiler.BattleResult) != 0)\r
86             {\r
87                 ShowResult(sniffer.Battle.Result);\r
88                 ShowResultRank(sniffer.Battle.ResultRank);\r
89                 switch (state)\r
90                 {\r
91                     case BattleState.Day:\r
92                         _result[0] = sniffer.Battle.Result;\r
93                         _rank[0] = sniffer.Battle.ResultRank;\r
94                         SetPhase("昼戦");\r
95                         break;\r
96                     case BattleState.Night:\r
97                     case BattleState.SpNight:\r
98                         _result[1] = sniffer.Battle.Result;\r
99                         _rank[1] = sniffer.Battle.ResultRank;\r
100                         SetPhase("夜戦");\r
101                         break;\r
102                 }\r
103                 _informationPanel.Visible = true;\r
104             }\r
105             else\r
106             {\r
107                 ClearResult();\r
108                 SetPhase("結果");\r
109                 switch (state)\r
110                 {\r
111                     case BattleState.Day:\r
112                         _result[0] = sniffer.Battle.Result;\r
113                         _rank[0] = sniffer.Battle.ResultRank;\r
114                         break;\r
115                     case BattleState.Night:\r
116                     case BattleState.SpNight:\r
117                         _result[1] = sniffer.Battle.Result;\r
118                         _rank[1] = sniffer.Battle.ResultRank;\r
119                         break;\r
120                 }\r
121             }\r
122             _informationPanel.SetInformation(sniffer.Battle);\r
123         }\r
124 \r
125         private void PhaseLabelClick(object sender, EventArgs ev)\r
126         {\r
127             switch (_phaseLabel.Text)\r
128             {\r
129                 case "結果":\r
130                     if (_result[0] != null)\r
131                     {\r
132                         ShowResult(_result[0]);\r
133                         ShowResultRank(_rank[0]);\r
134                         SetPhase("昼戦");\r
135                     }\r
136                     else if (_result[1] != null)\r
137                     {\r
138                         ShowResult(_result[1]);\r
139                         ShowResultRank(_rank[1]);\r
140                         SetPhase("夜戦");\r
141                     }\r
142                     break;\r
143                 case "昼戦":\r
144                     if (_result[1] != null)\r
145                     {\r
146                         ShowResult(_result[1]);\r
147                         ShowResultRank(_rank[1]);\r
148                         SetPhase("夜戦");\r
149                     }\r
150                     break;\r
151                 case "夜戦":\r
152                     if (_result[0] != null)\r
153                     {\r
154                         ShowResult(_result[0]);\r
155                         ShowResultRank(_rank[0]);\r
156                         SetPhase("昼戦");\r
157                     }\r
158                     break;\r
159             }\r
160         }\r
161 \r
162         private void SetPhase(string phase)\r
163         {\r
164             _phaseLabel.Text = phase;\r
165             if (phase == "結果" || _result[0] != null && _result[1] != null)\r
166             {\r
167                 _phaseLabel.BorderStyle = BorderStyle.FixedSingle;\r
168                 _phaseLabel.Cursor = Cursors.Hand;\r
169             }\r
170             else\r
171             {\r
172                 _phaseLabel.BorderStyle = BorderStyle.None;\r
173                 _phaseLabel.Cursor = Cursors.Default;\r
174             }\r
175         }\r
176 \r
177         private Point _scrollPosition;\r
178 \r
179         private void ClearResult()\r
180         {\r
181             _scrollPosition = AutoScrollPosition;\r
182             foreach (var panel in _panelList)\r
183                 panel.Visible = false;\r
184             _informationPanel.Visible = false;\r
185             _rankLabel.Text = "";\r
186         }\r
187 \r
188         private void ShowResult(BattleInfo.BattleResult result)\r
189         {\r
190             SuspendLayout();\r
191             AutoScrollPosition = _scrollPosition;\r
192             var friend = result.Friend;\r
193             var enemy = result.Enemy;\r
194             for (var i = 0; i < friend.Main.Length; i++)\r
195             {\r
196                 var labels = _friendLabels[i];\r
197                 var ship = friend.Main[i];\r
198                 labels[0].SetHp(ship);\r
199                 labels[1].SetName(ship, ShipNameWidth.BattleResult);\r
200                 _toolTip.SetToolTip(labels[1], GetEquipString(ship));\r
201             }\r
202             if (friend.Guard.Length > 0)\r
203             {\r
204                 _friendLabels[friend.Main.Length][1].Text = "護衛";\r
205                 _friendLabels[friend.Main.Length][0].SetHp(null);\r
206                 for (var i = 0; i < friend.Guard.Length; i++)\r
207                 {\r
208                     var labels = _friendLabels[friend.Main.Length + 1 + i];\r
209                     var ship = friend.Guard[i];\r
210                     labels[0].SetHp(ship);\r
211                     labels[1].SetName(ship, ShipNameWidth.BattleResult);\r
212                     _toolTip.SetToolTip(labels[1], GetEquipString(ship));\r
213                 }\r
214             }\r
215             var friendLines = friend.Main.Length + (friend.Guard.Length > 0 ? friend.Guard.Length + 1 : 0);\r
216             for (var i = friendLines; i < _friendLabels.Count; i++)\r
217             {\r
218                 _friendLabels[i][0].SetHp(null);\r
219                 _friendLabels[i][1].SetName("");\r
220             }\r
221             for (var i = 0; i < enemy.Main.Length; i++)\r
222             {\r
223                 var labels = _enemyLabels[i];\r
224                 var ship = enemy.Main[i];\r
225                 labels[0].SetHp(ship);\r
226                 labels[1].SetName(ShortenName(ship.Name));\r
227                 _toolTip.SetToolTip(labels[1], GetEquipString(ship));\r
228             }\r
229             if (enemy.Guard.Length > 0)\r
230             {\r
231                 _enemyLabels[enemy.Main.Length][1].Text = "護衛";\r
232                 _enemyLabels[enemy.Main.Length][0].SetHp(null);\r
233                 for (var i = 0; i < enemy.Guard.Length; i++)\r
234                 {\r
235                     var labels = _enemyLabels[enemy.Main.Length + 1 + i];\r
236                     var ship = enemy.Guard[i];\r
237                     labels[0].SetHp(ship);\r
238                     labels[1].SetName(ShortenName(ship.Name));\r
239                     _toolTip.SetToolTip(labels[1], GetEquipString(ship));\r
240                 }\r
241             }\r
242             var enemyLines = enemy.Main.Length + (enemy.Guard.Length > 0 ? enemy.Guard.Length + 1 : 0);\r
243             for (var i = enemyLines; i < _enemyLabels.Count; i++)\r
244             {\r
245                 _enemyLabels[i][0].SetHp(null);\r
246                 _enemyLabels[i][1].SetName("");\r
247             }\r
248             var lines = Max(friendLines, enemyLines);\r
249             for (var i = 0; i < lines; i++)\r
250             {\r
251                 var panel = _panelList[i];\r
252                 if (panel.Visible)\r
253                     continue;\r
254                 panel.Location = new Point(AutoScrollPosition.X,\r
255                     (int)Round((int)panel.Tag * ShipLabel.ScaleFactor.Height) + AutoScrollPosition.Y);\r
256                 panel.Visible = true;\r
257             }\r
258             for (var i = lines; i < _panelList.Count; i++)\r
259                 _panelList[i].Visible = false;\r
260             ResumeLayout(); // スクロールバーの有無を決定する\r
261             var panelWidth = Max(ClientSize.Width, // スクロールバーの有無を反映した横幅\r
262                 _enemyLabels[0][1].Location.X + _enemyLabels.Max(labels => labels[1].Size.Width) - 1); // 敵の名前の右端\r
263             for (var i = 0; i < lines; i++)\r
264                 _panelList[i].Width = panelWidth;\r
265             _informationPanel.Location = new Point(\r
266                 (int)Round(0 * ShipLabel.ScaleFactor.Width) + AutoScrollPosition.X,\r
267                 (int)Round(20 * ShipLabel.ScaleFactor.Height) + AutoScrollPosition.Y);\r
268             _informationPanel.Visible = true;\r
269             UpdateCellInfo(_cellInfo);\r
270         }\r
271 \r
272         private string GetEquipString(ShipStatus ship)\r
273         {\r
274             var result =\r
275             (from i in Enumerable.Range(0, ship.Slot.Count)\r
276                 let item = ship.Slot[i]\r
277                 where !item.Empty\r
278                 select item.Spec.Name + (item.Spec.IsAircraft && ship.OnSlot.Length > 0 && ship.Spec.MaxEq.Length > 0\r
279                            ? $"{ship.OnSlot[i]}/{ship.Spec.MaxEq[i]}"\r
280                            : ""));\r
281             if (ship.SlotEx.Id > 0)\r
282                 result = result.Concat(new[] {ship.SlotEx.Spec.Name});\r
283             return string.Join("\r\n", result);\r
284         }\r
285 \r
286         private string ShortenName(string name)\r
287         {\r
288             return new Regex(@"\(elite\)|\(flagship\)").Replace(name,\r
289                 match => match.Value == "(elite)" ? "(e)" : "(f)");\r
290         }\r
291 \r
292         private void ShowResultRank(BattleResultRank rank)\r
293         {\r
294             var result = new[] {"完全S", "勝利S", "勝利A", "勝利B", "敗北C", "敗北D", "敗北E"};\r
295             _rankLabel.Text = result[(int)rank];\r
296         }\r
297 \r
298         public void UpdateCellInfo(CellInfo cellInfo)\r
299         {\r
300             _cellLabel.Text = (Spoilers & Spoiler.NextCell) == 0 ? cellInfo.Current : cellInfo.Next;\r
301             _cellLabel.Location = new Point(ClientSize.Width - _cellLabel.Width - 2, 4);\r
302         }\r
303 \r
304         private void CreateLabels()\r
305         {\r
306             _phaseLabel = new Label\r
307             {\r
308                 Location = new Point(4, 4),\r
309                 Size = new Size(31, 14)\r
310             };\r
311             _phaseLabel.Click += PhaseLabelClick;\r
312             Controls.Add(_phaseLabel);\r
313             _rankLabel = new Label\r
314             {\r
315                 Location = new Point(37, 4),\r
316                 Size = new Size(42, 12)\r
317             };\r
318             Controls.Add(_rankLabel);\r
319             _cellLabel = new Label\r
320             {\r
321                 Location = new Point(0, 4),\r
322                 AutoSize = true\r
323             };\r
324             Controls.Add(_cellLabel);\r
325             for (var i = 0; i < 13; i++)\r
326             {\r
327                 var y = LineHeight * i + 38;\r
328                 var panel = new Panel\r
329                 {\r
330                     Location = new Point(0, y),\r
331                     Size = new Size(0, LineHeight),\r
332                     BackColor = ShipLabel.ColumnColors[i % 2],\r
333                     Visible = false,\r
334                     Tag = y\r
335                 };\r
336                 _panelList.Add(panel);\r
337 \r
338                 var friend = new[]\r
339                 {\r
340                     new ShipLabel\r
341                     {\r
342                         Location = new Point(101, 0),\r
343                         AutoSize = true,\r
344                         AnchorRight = true,\r
345                         MinimumSize = new Size(0, LineHeight),\r
346                         TextAlign = ContentAlignment.MiddleLeft,\r
347                         Cursor = Cursors.Hand\r
348                     },\r
349                     new ShipLabel {Location = new Point(1, 2), AutoSize = true}\r
350                 };\r
351                 _friendLabels.Add(friend);\r
352                 _hpLabels.Add(friend[0]);\r
353                 friend[0].Click += HpLabelClickHandler;\r
354                 var enemy = new[]\r
355                 {\r
356                     new ShipLabel\r
357                     {\r
358                         Location = new Point(119, 0),\r
359                         AutoSize = true,\r
360                         MinimumSize = new Size(0, LineHeight),\r
361                         TextAlign = ContentAlignment.MiddleLeft\r
362                     },\r
363                     new ShipLabel {Location = new Point(164, 2), AutoSize = true}\r
364                 };\r
365                 _enemyLabels.Add(enemy);\r
366                 foreach (var label in friend.Concat(enemy))\r
367                 {\r
368                     panel.Controls.Add(label);\r
369                     label.BackColor = label.PresetColor = ShipLabel.ColumnColors[i % 2];\r
370                 }\r
371                 Controls.Add(panel);\r
372             }\r
373         }\r
374 \r
375         protected override void OnMouseWheel(MouseEventArgs e)\r
376         {\r
377             if (VScroll && (ModifierKeys & Keys.Shift) == Keys.Shift)\r
378             {\r
379                 VScroll = false;\r
380                 OnMouseWheel(e);\r
381                 VScroll = true;\r
382                 return;\r
383             }\r
384             base.OnMouseWheel(e);\r
385         }\r
386 \r
387         protected override void ScaleControl(SizeF factor, BoundsSpecified specified)\r
388         {\r
389             base.ScaleControl(factor, specified);\r
390             if (factor.Height > 1)\r
391                 _toolTip.Font = new Font(_toolTip.Font.FontFamily, _toolTip.Font.Size * factor.Height);\r
392         }\r
393 \r
394         private class InformationPanel : Panel\r
395         {\r
396             private readonly Label[] _formation;\r
397             private readonly Label[] _fighterPower;\r
398             private readonly ResizableToolTip _toolTip = new ResizableToolTip {ShowAlways = true};\r
399 \r
400             public InformationPanel()\r
401             {\r
402                 Visible = false;\r
403                 Size = new Size(206, 16);\r
404                 Controls.AddRange(_formation = new[]\r
405                 {\r
406                     new Label\r
407                     {\r
408                         Location = new Point(47, 2),\r
409                         Size = new Size(29, 12)\r
410                     },\r
411                     new Label\r
412                     {\r
413                         Location = new Point(75, 2),\r
414                         Size = new Size(29, 12)\r
415                     },\r
416                     new Label\r
417                     {\r
418                         Location = new Point(1, 2),\r
419                         Size = new Size(48, 12),\r
420                         TextAlign = ContentAlignment.MiddleCenter\r
421                     }\r
422                 });\r
423                 Controls.AddRange(_fighterPower = new[]\r
424                 {\r
425                     new Label\r
426                     {\r
427                         Location = new Point(162, 2),\r
428                         Size = new Size(23, 12),\r
429                         TextAlign = ContentAlignment.MiddleRight\r
430                     },\r
431                     new Label\r
432                     {\r
433                         Location = new Point(183, 2),\r
434                         Size = new Size(23, 12),\r
435                         TextAlign = ContentAlignment.MiddleRight\r
436                     },\r
437                     new Label\r
438                     {\r
439                         Location = new Point(110, 2),\r
440                         Size = new Size(53, 12)\r
441                     }\r
442                 });\r
443                 // ReSharper disable once VirtualMemberCallInConstructor\r
444                 BackColor = ShipLabel.ColumnColors[1];\r
445             }\r
446 \r
447             public void SetInformation(BattleInfo battleInfo)\r
448             {\r
449                 _formation[0].Text = FormationName(battleInfo.Formation[0]);\r
450                 _formation[1].Text = FormationName(battleInfo.Formation[1]);\r
451                 _formation[2].Text = new[] {"同航戦", "反航戦", "T字有利", "T字不利"}[battleInfo.Formation[2] - 1];\r
452 \r
453                 if (battleInfo.AirControlLevel == -1)\r
454                 {\r
455                     if (battleInfo.BattleState == BattleState.Night)\r
456                         return;\r
457                     foreach (var label in _fighterPower)\r
458                         label.Visible = false;\r
459                     return;\r
460                 }\r
461                 var fp = battleInfo.FighterPower;\r
462                 _fighterPower[0].Text = fp[0].ToString("D");\r
463                 _toolTip.SetToolTip(_fighterPower[0], fp[0] == fp[1] ? "" : $"{fp[0]}~{fp[1]}");\r
464                 var efp = battleInfo.EnemyFighterPower;\r
465                 _fighterPower[1].Text = efp.AirCombat + efp.UnknownMark;\r
466                 _toolTip.SetToolTip(_fighterPower[1],\r
467                     efp.AirCombat == efp.Interception ? "" : "防空:" + efp.Interception + efp.UnknownMark);\r
468                 _fighterPower[2].Text =\r
469                     new[] {"", "制空均衡", "制空確保", "航空優勢", "航空劣勢", "制空喪失"}[battleInfo.AirControlLevel + 1];\r
470                 foreach (var label in _fighterPower)\r
471                     label.Visible = true;\r
472             }\r
473 \r
474             private string FormationName(int formation)\r
475             {\r
476                 switch (formation)\r
477                 {\r
478                     case 1:\r
479                         return "単縦";\r
480                     case 2:\r
481                         return "複縦";\r
482                     case 3:\r
483                         return "輪形";\r
484                     case 4:\r
485                         return "梯形";\r
486                     case 5:\r
487                         return "単横";\r
488                     case 6:\r
489                         return "警戒";\r
490                     case 11:\r
491                         return "第一";\r
492                     case 12:\r
493                         return "第二";\r
494                     case 13:\r
495                         return "第三";\r
496                     case 14:\r
497                         return "第四";\r
498                     default:\r
499                         return "";\r
500                 }\r
501             }\r
502 \r
503             protected override void ScaleControl(SizeF factor, BoundsSpecified specified)\r
504             {\r
505                 base.ScaleControl(factor, specified);\r
506                 if (factor.Height > 1)\r
507                     _toolTip.Font = new Font(_toolTip.Font.FontFamily, _toolTip.Font.Size * factor.Height);\r
508             }\r
509         }\r
510     }\r
511 }