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