OSDN Git Service

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