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