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