OSDN Git Service

ShipLabelを再構築するブランチをマージする
[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<ShipLabels> _friendLabels = new List<ShipLabels>();\r
30         private readonly List<ShipLabels> _enemyLabels = new List<ShipLabels>();\r
31         private readonly List<ShipLabel.Hp> _hpLabels = new List<ShipLabel.Hp>();\r
32         private readonly BattleInfo.BattleResult[] _result = new BattleInfo.BattleResult[2];\r
33         private Label _phaseLabel, _rankLabel, _supportLabel, _cellLabel;\r
34         private readonly BattleResultRank[] _rank = new BattleResultRank[2];\r
35         private readonly InformationPanel _informationPanel;\r
36         private CellInfo _cellInfo;\r
37         private string _supportType;\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             _supportType = sniffer.Battle.SupportType;\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 labels in _friendLabels)\r
183                 labels.BackPanel.Visible = false;\r
184             _informationPanel.Visible = false;\r
185             _rankLabel.Text = "";\r
186             _supportLabel.Text = "";\r
187         }\r
188 \r
189         private void ShowResult(BattleInfo.BattleResult result)\r
190         {\r
191             SuspendLayout();\r
192             AutoScrollPosition = _scrollPosition;\r
193             var friend = result.Friend;\r
194             var enemy = result.Enemy;\r
195             for (var i = 0; i < friend.Main.Length; i++)\r
196             {\r
197                 var labels = _friendLabels[i];\r
198                 var ship = friend.Main[i];\r
199                 labels.Name.Set(ship);\r
200                 labels.Hp.Set(ship);\r
201                 ToolTip.SetToolTip(labels.Name, GetEquipString(ship));\r
202             }\r
203             if (friend.Guard.Length > 0)\r
204             {\r
205                 _friendLabels[friend.Main.Length].Name.Text = "護衛";\r
206                 _friendLabels[friend.Main.Length].Hp.Reset();\r
207                 for (var i = 0; i < friend.Guard.Length; i++)\r
208                 {\r
209                     var labels = _friendLabels[friend.Main.Length + 1 + i];\r
210                     var ship = friend.Guard[i];\r
211                     labels.Name.Set(ship);\r
212                     labels.Hp.Set(ship);\r
213                     ToolTip.SetToolTip(labels.Name, GetEquipString(ship));\r
214                 }\r
215             }\r
216             var friendLines = friend.Main.Length + (friend.Guard.Length > 0 ? friend.Guard.Length + 1 : 0);\r
217             for (var i = friendLines; i < _friendLabels.Count; i++)\r
218             {\r
219                 _friendLabels[i].Reset();\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.Name.SetName(ShortenName(ship.Name));\r
226                 labels.Hp.Set(ship);\r
227                 ToolTip.SetToolTip(labels.Name, GetEquipString(ship));\r
228             }\r
229             if (enemy.Guard.Length > 0)\r
230             {\r
231                 _enemyLabels[enemy.Main.Length].Name.Text = "護衛";\r
232                 _enemyLabels[enemy.Main.Length].Hp.Reset();\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.Name.SetName(ShortenName(ship.Name));\r
238                     labels.Hp.Set(ship);\r
239                     ToolTip.SetToolTip(labels.Name, 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].Reset();\r
246             }\r
247             var lines = Max(friendLines, enemyLines);\r
248             for (var i = 0; i < lines; i++)\r
249             {\r
250                 var panel = _friendLabels[i].BackPanel;\r
251                 panel.Visible = true;\r
252             }\r
253             for (var i = lines; i < _friendLabels.Count; i++)\r
254                 _friendLabels[i].BackPanel.Visible = false;\r
255             ResumeLayout(); // スクロールバーの有無を決定する\r
256             var panelWidth = Max(ClientSize.Width, // スクロールバーの有無を反映した横幅\r
257                 _enemyLabels[0].Name.Location.X + _enemyLabels.Max(labels => labels.Name.Size.Width) - 1); // 敵の名前の右端\r
258             for (var i = 0; i < lines; i++)\r
259                 _friendLabels[i].BackPanel.Width = panelWidth;\r
260             _informationPanel.Location = Scaler.Move(AutoScrollPosition.X, AutoScrollPosition.Y, 0, 20);\r
261             _informationPanel.Visible = true;\r
262             UpdateCellInfo(_cellInfo);\r
263         }\r
264 \r
265         private string GetEquipString(ShipStatus ship)\r
266         {\r
267             var result =\r
268                 (from i in Enumerable.Range(0, ship.Slot.Count)\r
269                     let item = ship.Slot[i]\r
270                     where !item.Empty\r
271                     select item.Spec.Name +\r
272                            (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             _supportLabel.Text = _supportType;\r
291         }\r
292 \r
293         public void UpdateCellInfo(CellInfo cellInfo)\r
294         {\r
295             _cellLabel.Text = (Spoilers & Spoiler.NextCell) == 0 ? cellInfo.Current : cellInfo.Next;\r
296             _cellLabel.Location = new Point(ClientSize.Width - _cellLabel.Width - 2, 4);\r
297         }\r
298 \r
299         private void CreateLabels()\r
300         {\r
301             _phaseLabel = new Label\r
302             {\r
303                 Location = new Point(4, 4),\r
304                 Size = new Size(31, 14)\r
305             };\r
306             _phaseLabel.Click += PhaseLabelClick;\r
307             Controls.Add(_phaseLabel);\r
308             _rankLabel = new Label\r
309             {\r
310                 Location = new Point(37, 4),\r
311                 Size = new Size(42, 12)\r
312             };\r
313             Controls.Add(_rankLabel);\r
314             _supportLabel = new Label\r
315             {\r
316                 Location = new Point(77, 4),\r
317                 AutoSize = true\r
318             };\r
319             Controls.Add(_supportLabel);\r
320             _cellLabel = new Label\r
321             {\r
322                 Location = new Point(0, 4),\r
323                 AutoSize = true\r
324             };\r
325             Controls.Add(_cellLabel);\r
326             for (var i = 0; i < 13; i++)\r
327             {\r
328                 var y = LineHeight * i + 38;\r
329                 var friend = new ShipLabels\r
330                 {\r
331                     Name = new ShipLabel.Name(new Point(1, 2), ShipNameWidth.BattleResult),\r
332                     Hp = new ShipLabel.Hp(new Point(101, 0), LineHeight),\r
333                     BackPanel = new Panel\r
334                     {\r
335                         Location = new Point(0, y),\r
336                         Size = new Size(0, LineHeight),\r
337                         BackColor = CustomColors.ColumnColors.DarkFirst(i),\r
338                     }\r
339                 };\r
340                 _friendLabels.Add(friend);\r
341                 friend.Arrange(this, CustomColors.ColumnColors.DarkFirst(i));\r
342                 _hpLabels.Add(friend.Hp);\r
343                 friend.Hp.Click += HpLabelClickHandler;\r
344                 var enemy = new ShipLabels\r
345                 {\r
346                     Name = new ShipLabel.Name(new Point(164, 2), ShipNameWidth.Max),\r
347                     Hp = new ShipLabel.Hp\r
348                     {\r
349                         Location = new Point(119, 0),\r
350                         AutoSize = true,\r
351                         MinimumSize = new Size(0, LineHeight),\r
352                         TextAlign = ContentAlignment.MiddleLeft\r
353                     }\r
354                 };\r
355                 _enemyLabels.Add(enemy);\r
356                 enemy.Arrange(friend.BackPanel, CustomColors.ColumnColors.DarkFirst(i));\r
357             }\r
358         }\r
359 \r
360         protected override void OnMouseWheel(MouseEventArgs e)\r
361         {\r
362             if (VScroll && (ModifierKeys & Keys.Shift) == Keys.Shift)\r
363             {\r
364                 VScroll = false;\r
365                 OnMouseWheel(e);\r
366                 VScroll = true;\r
367                 return;\r
368             }\r
369             base.OnMouseWheel(e);\r
370         }\r
371 \r
372         private class InformationPanel : PanelWithToolTip\r
373         {\r
374             private readonly Label[] _formation;\r
375             private readonly Label[] _fighterPower;\r
376 \r
377             public InformationPanel()\r
378             {\r
379                 Visible = false;\r
380                 Size = new Size(206, 16);\r
381                 Controls.AddRange(_formation = new[]\r
382                 {\r
383                     new Label\r
384                     {\r
385                         Location = new Point(47, 2),\r
386                         Size = new Size(29, 12)\r
387                     },\r
388                     new Label\r
389                     {\r
390                         Location = new Point(75, 2),\r
391                         Size = new Size(29, 12)\r
392                     },\r
393                     new Label\r
394                     {\r
395                         Location = new Point(1, 2),\r
396                         Size = new Size(48, 12),\r
397                         TextAlign = ContentAlignment.MiddleCenter\r
398                     }\r
399                 });\r
400                 Controls.AddRange(_fighterPower = new[]\r
401                 {\r
402                     new Label\r
403                     {\r
404                         Location = new Point(162, 2),\r
405                         Size = new Size(23, 12),\r
406                         TextAlign = ContentAlignment.MiddleRight\r
407                     },\r
408                     new Label\r
409                     {\r
410                         Location = new Point(183, 2),\r
411                         Size = new Size(23, 12),\r
412                         TextAlign = ContentAlignment.MiddleRight\r
413                     },\r
414                     new Label\r
415                     {\r
416                         Location = new Point(110, 2),\r
417                         Size = new Size(53, 12)\r
418                     }\r
419                 });\r
420                 // ReSharper disable once VirtualMemberCallInConstructor\r
421                 BackColor = CustomColors.ColumnColors.Bright;\r
422             }\r
423 \r
424             public void SetInformation(BattleInfo battleInfo)\r
425             {\r
426                 _formation[0].Text = FormationName(battleInfo.Formation[0]);\r
427                 _formation[1].Text = FormationName(battleInfo.Formation[1]);\r
428                 _formation[2].Text = new[] {"同航戦", "反航戦", "T字有利", "T字不利"}[battleInfo.Formation[2] - 1];\r
429 \r
430                 if (battleInfo.AirControlLevel == -1)\r
431                 {\r
432                     if (battleInfo.BattleState == BattleState.Night)\r
433                         return;\r
434                     foreach (var label in _fighterPower)\r
435                         label.Visible = false;\r
436                     return;\r
437                 }\r
438                 var fp = battleInfo.FighterPower;\r
439                 _fighterPower[0].Text = fp[0].ToString("D");\r
440                 ToolTip.SetToolTip(_fighterPower[0], fp[0] == fp[1] ? "" : $"{fp[0]}~{fp[1]}");\r
441                 var efp = battleInfo.EnemyFighterPower;\r
442                 _fighterPower[1].Text = efp.AirCombat + efp.UnknownMark;\r
443                 ToolTip.SetToolTip(_fighterPower[1],\r
444                     efp.AirCombat == efp.Interception ? "" : "防空:" + efp.Interception + efp.UnknownMark);\r
445                 _fighterPower[2].Text =\r
446                     new[] {"", "制空均衡", "制空確保", "航空優勢", "航空劣勢", "制空喪失"}[battleInfo.AirControlLevel + 1];\r
447                 foreach (var label in _fighterPower)\r
448                     label.Visible = true;\r
449             }\r
450 \r
451             private string FormationName(int formation)\r
452             {\r
453                 switch (formation)\r
454                 {\r
455                     case 1:\r
456                         return "単縦";\r
457                     case 2:\r
458                         return "複縦";\r
459                     case 3:\r
460                         return "輪形";\r
461                     case 4:\r
462                         return "梯形";\r
463                     case 5:\r
464                         return "単横";\r
465                     case 6:\r
466                         return "警戒";\r
467                     case 11:\r
468                         return "第一";\r
469                     case 12:\r
470                         return "第二";\r
471                     case 13:\r
472                         return "第三";\r
473                     case 14:\r
474                         return "第四";\r
475                     default:\r
476                         return "";\r
477                 }\r
478             }\r
479         }\r
480     }\r
481 }