OSDN Git Service

艦隊情報で装備名の&が消えるのを直す
[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 \r
266         public void CreateDamagedShipList(Control parent, EventHandler onClick)\r
267         {\r
268             parent.SuspendLayout();\r
269             for (var i = 0; i < _damagedShipList.Length; i++)\r
270             {\r
271                 var y = 3 + i * 16;\r
272                 const int height = 12;\r
273                 parent.Controls.AddRange(_damagedShipList[i] = new[]\r
274                 {\r
275                     new ShipLabel {Location = new Point(0, y), Size = new Size(11, height)},\r
276                     new ShipLabel {Location = new Point(119, y), Size = new Size(5, height - 1)},\r
277                     new ShipLabel {Location = new Point(75, y), AutoSize = true},\r
278                     new ShipLabel {Location = new Point(9, y), AutoSize = true},\r
279                     new ShipLabel {Location = new Point(0, y - 2), Size = new Size(parent.Width, height + 3)}\r
280                 });\r
281                 foreach (var label in _damagedShipList[i])\r
282                 {\r
283                     label.Scale();\r
284                     label.PresetColor = label.BackColor = ColumnColors[(i + 1) % 2];\r
285                     label.Click += onClick;\r
286                 }\r
287             }\r
288             _panelDamagedShipList = parent;\r
289             parent.ResumeLayout();\r
290         }\r
291 \r
292         public void SetDamagedShipList(ShipStatus[] list)\r
293         {\r
294             const int fleet = 0, name = 3, time = 2, damage = 1;\r
295             var parent = _panelDamagedShipList;\r
296             var num = Min(list.Length, _damagedShipList.Length);\r
297             if (num == 0)\r
298             {\r
299                 parent.Size = new Size(parent.Width, (int)Round(ShipLabel.ScaleFactor.Height * 19));\r
300                 var labels = _damagedShipList[0];\r
301                 labels[fleet].Text = "";\r
302                 labels[name].SetName("なし");\r
303                 labels[time].Text = "";\r
304                 labels[damage].BackColor = labels[damage].PresetColor;\r
305                 return;\r
306             }\r
307             parent.Size = new Size(parent.Width, (int)Round(ShipLabel.ScaleFactor.Height * (num * 16 + 3)));\r
308             for (var i = 0; i < num; i++)\r
309             {\r
310                 var s = list[i];\r
311                 var labels = _damagedShipList[i];\r
312                 labels[fleet].SetFleet(s);\r
313                 labels[name].SetName(s, new Dictionary<string, string>\r
314                 {\r
315                     {"Graf Zeppelin", "Graf Zeppeli"},\r
316                     {"Graf Zeppelin改", "Graf Zeppeli"},\r
317                     {"千代田航改二", "千代田航改"}\r
318                 });\r
319                 labels[time].SetRepairTime(s);\r
320                 labels[damage].BackColor = ShipLabel.DamageColor(s, labels[damage].PresetColor);\r
321             }\r
322         }\r
323 \r
324         public void CreateNDockLabels(Control parent, EventHandler onClick)\r
325         {\r
326             for (var i = 0; i < _ndockLabels.Length; i++)\r
327             {\r
328                 var y = 3 + i * 15;\r
329                 parent.Controls.AddRange(\r
330                     _ndockLabels[i] = new[]\r
331                     {\r
332                         new ShipLabel\r
333                         {\r
334                             Location = new Point(138, y),\r
335                             AutoSize = true,\r
336                             AnchorRight = true\r
337                         },\r
338                         new ShipLabel {Location = new Point(29, y), AutoSize = true} // 名前のZ-orderを下に\r
339                     });\r
340                 foreach (var label in _ndockLabels[i])\r
341                 {\r
342                     label.Scale();\r
343                     label.Click += onClick;\r
344                 }\r
345             }\r
346         }\r
347 \r
348         public void SetNDockLabels(NameAndTimer[] ndock)\r
349         {\r
350             var dict = new Dictionary<string, string>\r
351             {\r
352                 {"Graf Zeppelin", "Graf Zeppeli"},\r
353                 {"Graf Zeppelin改", "Graf Zeppeli"},\r
354                 {"千代田航改二", "千代田航改"}\r
355             };\r
356             for (var i = 0; i < _ndockLabels.Length; i++)\r
357             {\r
358                 string name;\r
359                 _ndockLabels[i][1].SetName(dict.TryGetValue(ndock[i].Name, out name) ? name : ndock[i].Name);\r
360             }\r
361         }\r
362 \r
363         public void SetNDockTimer(int dock, RingTimer timer, bool finishTime)\r
364         {\r
365             var label = _ndockLabels[dock][0];\r
366             label.ForeColor = timer.IsFinished ? Color.Red : Color.Black;\r
367             label.Text = timer.ToString(finishTime);\r
368         }\r
369     }\r
370 \r
371     [System.ComponentModel.DesignerCategory("Code")]\r
372     public class ShipLabel : Label\r
373     {\r
374         public static SizeF ScaleFactor { get; set; }\r
375         public static Font LatinFont { get; set; } = new Font("Tahoma", 8f);\r
376         public Color PresetColor { get; set; }\r
377         public bool AnchorRight { get; set; }\r
378         private int _right = int.MinValue;\r
379         private int _left;\r
380 \r
381         public ShipLabel()\r
382         {\r
383             UseMnemonic = false;\r
384         }\r
385 \r
386         public void SetName(ShipStatus status, Dictionary<string, string> convDict = null)\r
387         {\r
388             string name;\r
389             if (convDict == null || !convDict.TryGetValue(status.Name, out name))\r
390                 name = status.Name;\r
391             var empty = status.Id != -1 && status.Slot.All(e => e.Id == -1) ? "▫" : "";\r
392             var dc = status.PreparedDamageControl;\r
393             var dcname = dc == 42 ? "[ダ]" : dc == 43 ? "[メ]" : "";\r
394             SetName((status.Escaped ? "[避]" : dcname) + name + empty);\r
395         }\r
396 \r
397         public void SetName(string name)\r
398         {\r
399             var lu = name != null && new Regex(@"^(?:\[.\])?\p{Lu}").IsMatch(name);\r
400             var shift = (int)Round(ScaleFactor.Height);\r
401             if (lu && Font.Equals(Parent.Font))\r
402             {\r
403                 Location += new Size(0, -shift);\r
404                 Font = LatinFont;\r
405             }\r
406             else if (!lu && !Font.Equals(Parent.Font))\r
407             {\r
408                 Location += new Size(0, shift);\r
409                 Font = Parent.Font;\r
410             }\r
411             Text = name;\r
412         }\r
413 \r
414         public void SetHp(ShipStatus status)\r
415         {\r
416             Text = $"{status.NowHp:D}/{status.MaxHp:D}";\r
417             BackColor = DamageColor(status, PresetColor);\r
418         }\r
419 \r
420         public void SetHp(int now, int max)\r
421         {\r
422             SetHp(new ShipStatus {NowHp = now, MaxHp = max});\r
423         }\r
424 \r
425         public static Color DamageColor(ShipStatus status, Color backcolor)\r
426         {\r
427             switch (status.DamageLevel)\r
428             {\r
429                 case ShipStatus.Damage.Badly:\r
430                     return Color.Red;\r
431                 case ShipStatus.Damage.Half:\r
432                     return Color.Orange;\r
433                 case ShipStatus.Damage.Small:\r
434                     return Color.FromArgb(240, 240, 0);\r
435                 default:\r
436                     return backcolor;\r
437             }\r
438         }\r
439 \r
440         public void SetCond(ShipStatus status)\r
441         {\r
442             var cond = status.Cond;\r
443             Text = cond.ToString("D");\r
444             BackColor = cond >= 50\r
445                 ? Color.Yellow\r
446                 : cond >= 30\r
447                     ? PresetColor\r
448                     : cond >= 20 ? Color.Orange : Color.Red;\r
449         }\r
450 \r
451         public void SetLevel(ShipStatus status)\r
452         {\r
453             Text = status.Level.ToString("D");\r
454         }\r
455 \r
456         public void SetExpToNext(ShipStatus status)\r
457         {\r
458             Text = status.ExpToNext.ToString("D");\r
459         }\r
460 \r
461         public void SetRepairTime(ShipStatus status)\r
462         {\r
463             SetRepairTime(status.RepairTime);\r
464         }\r
465 \r
466         public void SetRepairTime(TimeSpan span)\r
467         {\r
468             Text = $@"{(int)span.TotalHours:d2}:{span:mm\:ss}";\r
469         }\r
470 \r
471         public void SetFleet(ShipStatus status)\r
472         {\r
473             Text = new[] {"", "1", "2", "3", "4"}[status.Fleet + 1];\r
474         }\r
475 \r
476         protected override void OnLayout(LayoutEventArgs levent)\r
477         {\r
478             base.OnLayout(levent);\r
479             if (!AnchorRight)\r
480                 return;\r
481             if (_right == int.MinValue || _left != Left)\r
482             {\r
483                 _right = Right;\r
484                 _left = Left;\r
485                 return;\r
486             }\r
487             if (_right == Right)\r
488                 return;\r
489             _left -= Right - _right;\r
490             Location = new Point(_left, Top);\r
491         }\r
492 \r
493         public void Scale()\r
494         {\r
495             Scale(ScaleFactor);\r
496         }\r
497     }\r
498 }