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