OSDN Git Service

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