OSDN Git Service

DPIスケーリング時に連合艦隊表示がずれるのを直す
[kancollesniffer/KancolleSniffer.git] / KancolleSniffer / ShipLabels.cs
1 // Copyright (C) 2014, 2015 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 ShipLabels\r
26     {\r
27         private readonly ShipLabel[][] _labels = new ShipLabel[ShipInfo.MemberCount][];\r
28         private readonly ShipLabel[][] _combinedLabels = new ShipLabel[ShipInfo.MemberCount * 2][];\r
29         private readonly ShipLabel[] _akashiTimers = new ShipLabel[ShipInfo.MemberCount];\r
30         private readonly ShipLabel[][] _damagedShipList = new ShipLabel[16][];\r
31         private Control _panelDamagedShipList;\r
32         private readonly ShipLabel[][] _ndockLabels = new ShipLabel[DockInfo.DockCount][];\r
33         public static Color[] ColumnColors = {SystemColors.Control, Color.FromArgb(255, 250, 250, 250)};\r
34 \r
35         public void CreateLabels(Control parent, EventHandler onClick)\r
36         {\r
37             parent.SuspendLayout();\r
38             const int top = 3, height = 12, lh = 16;\r
39             ShipLabel[] headings;\r
40             parent.Controls.AddRange(headings = new[]\r
41             {\r
42                 new ShipLabel {Location = new Point(109, top), Text = "HP", AutoSize = true},\r
43                 new ShipLabel {Location = new Point(128, top), Text = "cond", AutoSize = true},\r
44                 new ShipLabel {Location = new Point(163, top), Text = "Lv", AutoSize = true},\r
45                 new ShipLabel {Location = new Point(194, top), Text = "Exp", AutoSize = true},\r
46                 new ShipLabel {Location = new Point(0, 1), Size = new Size(parent.Width, lh - 1)}\r
47             });\r
48             foreach (var label in headings)\r
49             {\r
50                 label.Scale();\r
51                 label.BackColor = ColumnColors[1];\r
52             }\r
53             for (var i = 0; i < _labels.Length; i++)\r
54             {\r
55                 var y = top + lh * (i + 1);\r
56                 parent.Controls.AddRange(_labels[i] = new[]\r
57                 {\r
58                     new ShipLabel {Location = new Point(129, y), AutoSize = true, AnchorRight = true},\r
59                     new ShipLabel\r
60                     {\r
61                         Location = new Point(132, y),\r
62                         Size = new Size(23, height),\r
63                         TextAlign = ContentAlignment.MiddleRight\r
64                     },\r
65                     new ShipLabel\r
66                     {\r
67                         Location = new Point(157, y),\r
68                         Size = new Size(23, height),\r
69                         TextAlign = ContentAlignment.MiddleRight\r
70                     },\r
71                     new ShipLabel\r
72                     {\r
73                         Location = new Point(177, y),\r
74                         Size = new Size(41, height),\r
75                         TextAlign = ContentAlignment.MiddleRight\r
76                     },\r
77                     new ShipLabel {Location = new Point(2, y), AutoSize = true}, // 名前のZ-orderを下に\r
78                     new ShipLabel {Location = new Point(0, y - 2), Size = new Size(parent.Width, lh - 1)}\r
79                 });\r
80                 foreach (var label in _labels[i])\r
81                 {\r
82                     label.Scale();\r
83                     label.PresetColor = label.BackColor = ColumnColors[i % 2];\r
84                     label.Tag = i;\r
85                     label.Click += onClick;\r
86                 }\r
87             }\r
88             parent.ResumeLayout();\r
89         }\r
90 \r
91         public void SetShipInfo(ShipStatus[] statuses)\r
92         {\r
93             for (var i = 0; i < _labels.Length; i++)\r
94             {\r
95                 var labels = _labels[i];\r
96                 if (i < statuses.Length)\r
97                 {\r
98                     var s = statuses[i];\r
99                     labels[0].SetHp(s);\r
100                     labels[1].SetCond(s);\r
101                     labels[2].SetLevel(s);\r
102                     labels[3].SetExpToNext(s);\r
103                     labels[4].SetName(s);\r
104                 }\r
105                 else\r
106                 {\r
107                     labels[0].Text = labels[1].Text = labels[2].Text = labels[3].Text = "";\r
108                     labels[4].SetName("");\r
109                     labels[0].BackColor = labels[1].BackColor = labels[0].PresetColor;\r
110                 }\r
111             }\r
112         }\r
113 \r
114         public void CreateCombinedShipLabels(Control parent, EventHandler onClick)\r
115         {\r
116             parent.SuspendLayout();\r
117             const int top = 3, height = 12, lh = 16;\r
118             const int parentWidth = 220; // parent.Widthを使うとDPIスケーリング時に計算がくるうので\r
119             ShipLabel[] headings;\r
120             parent.Controls.AddRange(headings = new[]\r
121             {\r
122                 new ShipLabel {Location = new Point(68, top), Text = "HP", AutoSize = true},\r
123                 new ShipLabel {Location = new Point(86, top), Text = "cnd", AutoSize = true},\r
124                 new ShipLabel {Location = new Point(177, top), Text = "HP", AutoSize = true},\r
125                 new ShipLabel {Location = new Point(195, top), Text = "cnd", AutoSize = true},\r
126                 new ShipLabel {Location = new Point(0, 1), Size = new Size(parentWidth, lh - 1)}\r
127             });\r
128             foreach (var label in headings)\r
129             {\r
130                 label.Scale();\r
131                 label.BackColor = ColumnColors[1];\r
132             }\r
133             for (var i = 0; i < _combinedLabels.Length; i++)\r
134             {\r
135                 var x = parentWidth / 2 * (i / ShipInfo.MemberCount);\r
136                 var y = top + lh * ((i % ShipInfo.MemberCount) + 1);\r
137                 parent.Controls.AddRange(_combinedLabels[i] = new[]\r
138                 {\r
139                     new ShipLabel {Location = new Point(x + 88, y), AutoSize = true, AnchorRight = true},\r
140                     new ShipLabel\r
141                     {\r
142                         Location = new Point(x + 86, y),\r
143                         Size = new Size(23, height),\r
144                         TextAlign = ContentAlignment.MiddleRight\r
145                     },\r
146                     new ShipLabel {Location = new Point(x + 2, y), AutoSize = true}, // 名前のZ-orderを下に\r
147                     new ShipLabel {Location = new Point(x, y - 2), Size = new Size(parentWidth / 2, lh - 1)}\r
148                 });\r
149                 foreach (var label in _combinedLabels[i])\r
150                 {\r
151                     label.Scale();\r
152                     label.PresetColor = label.BackColor = ColumnColors[i % 2];\r
153                     label.Tag = i;\r
154                     label.Click += onClick;\r
155                 }\r
156             }\r
157             parent.ResumeLayout();\r
158         }\r
159 \r
160         public void SetCombinedShipInfo(ShipStatus[] first, ShipStatus[] second)\r
161         {\r
162             for (var i = 0; i < _combinedLabels.Length; i++)\r
163             {\r
164                 var idx = i % ShipInfo.MemberCount;\r
165                 var statuses = i < ShipInfo.MemberCount ? first : second;\r
166                 var labels = _combinedLabels[i];\r
167                 if (idx < statuses.Length)\r
168                 {\r
169                     var s = statuses[idx];\r
170                     labels[0].SetHp(s);\r
171                     labels[1].SetCond(s);\r
172                     labels[2].SetName(s);\r
173                 }\r
174                 else\r
175                 {\r
176                     labels[0].Text = labels[1].Text = "";\r
177                     labels[2].SetName("");\r
178                     labels[0].BackColor = labels[1].BackColor = labels[0].PresetColor;\r
179                 }\r
180             }\r
181         }\r
182 \r
183         public void CreateAkashiTimers(Control parent)\r
184         {\r
185             parent.SuspendLayout();\r
186             for (var i = 0; i < _akashiTimers.Length; i++)\r
187             {\r
188                 const int x = 55;\r
189                 var y = 3 + 16 * (i + 1);\r
190                 ShipLabel label;\r
191                 parent.Controls.Add(\r
192                     label = _akashiTimers[i] =\r
193                         new ShipLabel\r
194                         {\r
195                             Location = new Point(x, y),\r
196                             Size = new Size(34, 12),\r
197                             TextAlign = ContentAlignment.TopRight\r
198                         });\r
199                 label.BackColor = ColumnColors[i % 2];\r
200             }\r
201             foreach (var label in _akashiTimers)\r
202                 label.Scale();\r
203             parent.ResumeLayout();\r
204         }\r
205 \r
206         public void SetAkashiTimer(ShipStatus[] statuses, AkashiTimer.RepairSpan[] timers)\r
207         {\r
208             var shortest = -1;\r
209             for (var i = 0; i < timers.Length; i++)\r
210             {\r
211                 if (timers[i].Span <= TimeSpan.Zero)\r
212                     continue;\r
213                 if (shortest == -1 || timers[i].Span < timers[shortest].Span)\r
214                     shortest = i;\r
215             }\r
216             for (var i = 0; i < _akashiTimers.Length; i++)\r
217             {\r
218                 var label = _akashiTimers[i];\r
219                 var labelHp = _labels[i][0];\r
220                 var labelName = _labels[i][4];\r
221                 if (i >= timers.Length || timers[i].Span == TimeSpan.MinValue)\r
222                 {\r
223                     label.Visible = false;\r
224                     labelHp.ForeColor = Control.DefaultForeColor;\r
225                     continue;\r
226                 }\r
227                 var timer = timers[i];\r
228                 var stat = statuses[i];\r
229                 label.Visible = true;\r
230                 label.Text = timer.Span.ToString(@"mm\:ss");\r
231                 label.ForeColor = Control.DefaultForeColor;\r
232                 labelName.SetName(stat, _shortNameDict);\r
233                 if (timer.Diff == 0)\r
234                 {\r
235                     labelHp.ForeColor = Control.DefaultForeColor;\r
236                     continue;\r
237                 }\r
238                 if (i == shortest)\r
239                     label.ForeColor = Color.Red;\r
240                 labelHp.ForeColor = Color.DimGray;\r
241                 labelHp.SetHp(stat.NowHp + timer.Diff, stat.MaxHp);\r
242             }\r
243         }\r
244 \r
245         private readonly Dictionary<string, string> _shortNameDict = new Dictionary<string, string>\r
246         {\r
247             {"千代田航改", "千代田航"},\r
248             {"千代田航改二", "千代田航"},\r
249             {"千歳航改二", "千歳航改"},\r
250             {"五十鈴改二", "五十鈴改"},\r
251             {"あきつ丸改", "あきつ丸"},\r
252             {"Bismarck改", "Bismarck"},\r
253             {"Bismarck twei", "Bismarck"},\r
254             {"Bismarck drei", "Bismarck"},\r
255             {"Prinz Eugen", "Prinz Eug"},\r
256             {"Prinz Eugen改", "Prinz Eug"},\r
257             {"Graf Zeppelin", "Graf Zep"},\r
258             {"Graf Zeppelin改", "Graf Zep"},\r
259             {"Libeccio改", "Libeccio"},\r
260             {"阿武隈改二", "阿武隈改"},\r
261             {"瑞鶴改二甲", "瑞鶴改二"},\r
262             {"翔鶴改二甲", "翔鶴改二"},\r
263         };\r
264 \r
265         public void CreateDamagedShipList(Control parent, EventHandler onClick)\r
266         {\r
267             parent.SuspendLayout();\r
268             for (var i = 0; i < _damagedShipList.Length; i++)\r
269             {\r
270                 var y = 3 + i * 16;\r
271                 const int height = 12;\r
272                 parent.Controls.AddRange(_damagedShipList[i] = new[]\r
273                 {\r
274                     new ShipLabel {Location = new Point(0, y), Size = new Size(11, height)},\r
275                     new ShipLabel {Location = new Point(119, y), Size = new Size(5, height - 1)},\r
276                     new ShipLabel {Location = new Point(75, y), AutoSize = true},\r
277                     new ShipLabel {Location = new Point(9, y), AutoSize = true},\r
278                     new ShipLabel {Location = new Point(0, y - 2), Size = new Size(parent.Width, height + 3)}\r
279                 });\r
280                 foreach (var label in _damagedShipList[i])\r
281                 {\r
282                     label.Scale();\r
283                     label.PresetColor = label.BackColor = ColumnColors[(i + 1) % 2];\r
284                     label.Click += onClick;\r
285                 }\r
286             }\r
287             _panelDamagedShipList = parent;\r
288             parent.ResumeLayout();\r
289         }\r
290 \r
291         public void SetDamagedShipList(ShipStatus[] list)\r
292         {\r
293             const int fleet = 0, name = 3, time = 2, damage = 1;\r
294             var parent = _panelDamagedShipList;\r
295             var num = Min(list.Length, _damagedShipList.Length);\r
296             if (num == 0)\r
297             {\r
298                 parent.Size = new Size(parent.Width, (int)Round(ShipLabel.ScaleFactor.Height * 19));\r
299                 var labels = _damagedShipList[0];\r
300                 labels[fleet].Text = "";\r
301                 labels[name].SetName("なし");\r
302                 labels[time].Text = "";\r
303                 labels[damage].BackColor = labels[damage].PresetColor;\r
304                 return;\r
305             }\r
306             parent.Size = new Size(parent.Width, (int)Round(ShipLabel.ScaleFactor.Height * (num * 16 + 3)));\r
307             for (var i = 0; i < num; i++)\r
308             {\r
309                 var s = list[i];\r
310                 var labels = _damagedShipList[i];\r
311                 labels[fleet].SetFleet(s);\r
312                 labels[name].SetName(s, new Dictionary<string, string>\r
313                 {\r
314                     {"Graf Zeppelin", "Graf Zeppeli"},\r
315                     {"Graf Zeppelin改", "Graf Zeppeli"},\r
316                     {"千代田航改二", "千代田航改"}\r
317                 });\r
318                 labels[time].SetRepairTime(s);\r
319                 labels[damage].BackColor = ShipLabel.DamageColor(s, labels[damage].PresetColor);\r
320             }\r
321         }\r
322 \r
323         public void CreateNDockLabels(Control parent, EventHandler onClick)\r
324         {\r
325             for (var i = 0; i < _ndockLabels.Length; i++)\r
326             {\r
327                 var y = 3 + i * 15;\r
328                 parent.Controls.AddRange(\r
329                     _ndockLabels[i] = new[]\r
330                     {\r
331                         new ShipLabel\r
332                         {\r
333                             Location = new Point(138, y),\r
334                             AutoSize = true,\r
335                             AnchorRight = true\r
336                         },\r
337                         new ShipLabel {Location = new Point(29, y), AutoSize = true} // 名前のZ-orderを下に\r
338                     });\r
339                 foreach (var label in _ndockLabels[i])\r
340                 {\r
341                     label.Scale();\r
342                     label.Click += onClick;\r
343                 }\r
344             }\r
345         }\r
346 \r
347         public void SetNDockLabels(NameAndTimer[] ndock)\r
348         {\r
349             var dict = new Dictionary<string, string>\r
350             {\r
351                 {"Graf Zeppelin", "Graf Zeppeli"},\r
352                 {"Graf Zeppelin改", "Graf Zeppeli"},\r
353                 {"千代田航改二", "千代田航改"}\r
354             };\r
355             for (var i = 0; i < _ndockLabels.Length; i++)\r
356             {\r
357                 string name;\r
358                 _ndockLabels[i][1].SetName(dict.TryGetValue(ndock[i].Name, out name) ? name : ndock[i].Name);\r
359             }\r
360         }\r
361 \r
362         public void SetNDockTimer(int dock, RingTimer timer, bool finishTime)\r
363         {\r
364             var label = _ndockLabels[dock][0];\r
365             label.ForeColor = timer.IsFinished ? Color.Red : Color.Black;\r
366             label.Text = timer.ToString(finishTime);\r
367         }\r
368     }\r
369 \r
370     [System.ComponentModel.DesignerCategory("Code")]\r
371     public class ShipLabel : Label\r
372     {\r
373         public static SizeF ScaleFactor { get; set; }\r
374         public static Font LatinFont { get; set; } = new Font("Tahoma", 8f);\r
375         public Color PresetColor { get; set; }\r
376         public bool AnchorRight { get; set; }\r
377         private int _right = int.MinValue;\r
378         private int _left;\r
379 \r
380         public void SetName(ShipStatus status, Dictionary<string, string> convDict = null)\r
381         {\r
382             string name;\r
383             if (convDict == null || !convDict.TryGetValue(status.Name, out name))\r
384                 name = status.Name;\r
385             var empty = status.Id != -1 && status.Slot.All(e => e.Id == -1) ? "▫" : "";\r
386             var dc = status.PreparedDamageControl;\r
387             var dcname = dc == 42 ? "[ダ]" : dc == 43 ? "[メ]" : "";\r
388             SetName((status.Escaped ? "[避]" : dcname) + name + empty);\r
389         }\r
390 \r
391         public void SetName(string name)\r
392         {\r
393             var lu = name != null && new Regex(@"^(?:\[.\])?\p{Lu}").IsMatch(name);\r
394             var shift = (int)Round(ScaleFactor.Height);\r
395             if (lu && Font.Equals(Parent.Font))\r
396             {\r
397                 Location += new Size(0, -shift);\r
398                 Font = LatinFont;\r
399             }\r
400             else if (!lu && !Font.Equals(Parent.Font))\r
401             {\r
402                 Location += new Size(0, shift);\r
403                 Font = Parent.Font;\r
404             }\r
405             Text = name;\r
406         }\r
407 \r
408         public void SetHp(ShipStatus status)\r
409         {\r
410             Text = $"{status.NowHp:D}/{status.MaxHp:D}";\r
411             BackColor = DamageColor(status, PresetColor);\r
412         }\r
413 \r
414         public void SetHp(int now, int max)\r
415         {\r
416             SetHp(new ShipStatus {NowHp = now, MaxHp = max});\r
417         }\r
418 \r
419         public static Color DamageColor(ShipStatus status, Color backcolor)\r
420         {\r
421             switch (status.DamageLevel)\r
422             {\r
423                 case ShipStatus.Damage.Badly:\r
424                     return Color.Red;\r
425                 case ShipStatus.Damage.Half:\r
426                     return Color.Orange;\r
427                 case ShipStatus.Damage.Small:\r
428                     return Color.FromArgb(240, 240, 0);\r
429                 default:\r
430                     return backcolor;\r
431             }\r
432         }\r
433 \r
434         public void SetCond(ShipStatus status)\r
435         {\r
436             var cond = status.Cond;\r
437             Text = cond.ToString("D");\r
438             BackColor = cond >= 50\r
439                 ? Color.Yellow\r
440                 : cond >= 30\r
441                     ? PresetColor\r
442                     : cond >= 20 ? Color.Orange : Color.Red;\r
443         }\r
444 \r
445         public void SetLevel(ShipStatus status)\r
446         {\r
447             Text = status.Level.ToString("D");\r
448         }\r
449 \r
450         public void SetExpToNext(ShipStatus status)\r
451         {\r
452             Text = status.ExpToNext.ToString("D");\r
453         }\r
454 \r
455         public void SetRepairTime(ShipStatus status)\r
456         {\r
457             SetRepairTime(status.RepairTime);\r
458         }\r
459 \r
460         public void SetRepairTime(TimeSpan span)\r
461         {\r
462             Text = $@"{(int)span.TotalHours:d2}:{span:mm\:ss}";\r
463         }\r
464 \r
465         public void SetFleet(ShipStatus status)\r
466         {\r
467             Text = new[] {"", "1", "2", "3", "4"}[status.Fleet + 1];\r
468         }\r
469 \r
470         protected override void OnLayout(LayoutEventArgs levent)\r
471         {\r
472             base.OnLayout(levent);\r
473             if (!AnchorRight)\r
474                 return;\r
475             if (_right == int.MinValue || _left != Left)\r
476             {\r
477                 _right = Right;\r
478                 _left = Left;\r
479                 return;\r
480             }\r
481             if (_right == Right)\r
482                 return;\r
483             _left -= Right - _right;\r
484             Location = new Point(_left, Top);\r
485         }\r
486 \r
487         public void Scale()\r
488         {\r
489             Scale(ScaleFactor);\r
490         }\r
491     }\r
492 }