OSDN Git Service

戦況で支援が来るのをわかるようにする
[kancollesniffer/KancolleSniffer.git] / KancolleSniffer / View / ShipLabel.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.ComponentModel;\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     [DesignerCategory("Code")]\r
27     public class ShipLabel : Label\r
28     {\r
29         public static Color[] ColumnColors = {SystemColors.Control, Color.FromArgb(255, 250, 250, 250)};\r
30         public static SizeF ScaleFactor { get; set; }\r
31         public static Font LatinFont { get; set; } = new Font("Tahoma", 8f);\r
32         public Color PresetColor { get; set; }\r
33         public bool AnchorRight { get; set; }\r
34         private int _right = Int32.MinValue;\r
35         private int _left;\r
36         private SlotStatus _slotStatus;\r
37         private ShipStatus _status;\r
38         private bool _hpPercent;\r
39 \r
40         public override Color BackColor\r
41         {\r
42             get => base.BackColor;\r
43             set => base.BackColor = value == DefaultBackColor ? PresetColor : value;\r
44         }\r
45 \r
46         [Flags]\r
47         private enum SlotStatus\r
48         {\r
49             Equipped = 0,\r
50             SemiEquipped = 1,\r
51             NormalEmpty = 2,\r
52             ExtraEmpty = 4\r
53         }\r
54 \r
55         public ShipLabel()\r
56         {\r
57             UseMnemonic = false;\r
58         }\r
59 \r
60         public void SetName(ShipStatus status, ShipNameWidth width = ShipNameWidth.Max)\r
61         {\r
62             if (status == null)\r
63             {\r
64                 SetName("");\r
65                 return;\r
66             }\r
67             var empty = SlotStatus.Equipped;\r
68             if (!status.Empty)\r
69             {\r
70                 var slots = status.Slot.Take(status.Spec.SlotNum).ToArray();\r
71                 if (slots.All(item => item.Empty))\r
72                 {\r
73                     empty |= SlotStatus.NormalEmpty;\r
74                 }\r
75                 else if (slots.Any(item => item.Empty))\r
76                 {\r
77                     empty |= SlotStatus.SemiEquipped;\r
78                 }\r
79                 if (status.SlotEx.Empty)\r
80                     empty |= SlotStatus.ExtraEmpty;\r
81             }\r
82             var dc = status.PreparedDamageControl;\r
83             var dcName = dc == 42 ? "[ダ]" :\r
84                 dc == 43 ? "[メ]" : "";\r
85             var sp = "";\r
86             switch (status.SpecialAttack)\r
87             {\r
88                 case ShipStatus.Attack.Fire:\r
89                     sp = "+";\r
90                     break;\r
91                 case ShipStatus.Attack.Fired:\r
92                     sp = "-";\r
93                     break;\r
94             }\r
95             SetName((status.Escaped ? "[避]" : dcName) + sp, status.Name, empty, width);\r
96         }\r
97 \r
98         public void SetName(string name)\r
99         {\r
100             SetName("", name, SlotStatus.Equipped);\r
101         }\r
102 \r
103         public void SetName(string name, ShipNameWidth width)\r
104         {\r
105             SetName("", name, SlotStatus.Equipped, width);\r
106         }\r
107 \r
108         private void SetName(string prefix, string name, SlotStatus slotStatus, ShipNameWidth width = ShipNameWidth.Max)\r
109         {\r
110             if (name == null)\r
111                 name = "";\r
112             _slotStatus = slotStatus;\r
113             var lu = new Regex(@"^\p{Lu}").IsMatch(name);\r
114             var shift = (int)Round(ScaleFactor.Height);\r
115             if (lu && Font.Equals(Parent.Font))\r
116             {\r
117                 Location += new Size(0, -shift);\r
118                 Font = LatinFont;\r
119             }\r
120             else if (!lu && !Font.Equals(Parent.Font))\r
121             {\r
122                 Location += new Size(0, shift);\r
123                 Font = Parent.Font;\r
124             }\r
125             var result = prefix + name;\r
126             var measured = TextRenderer.MeasureText(result, Font).Width;\r
127             if (measured <= (int)width)\r
128             {\r
129                 Text = result;\r
130                 Invalidate(); // 必ずOnPaintを実行させるため\r
131                 return;\r
132             }\r
133             var truncated = "";\r
134             foreach (var ch in name)\r
135             {\r
136                 var tmp = truncated + ch;\r
137                 if (TextRenderer.MeasureText(tmp, Font).Width > (int)width * ScaleFactor.Width)\r
138                     break;\r
139                 truncated = tmp;\r
140             }\r
141             Text = prefix + truncated.TrimEnd(' ');\r
142             Invalidate();\r
143         }\r
144 \r
145         public void SetHp(ShipStatus status)\r
146         {\r
147             _status = status;\r
148             if (status == null)\r
149             {\r
150                 Text = "";\r
151                 BackColor = PresetColor;\r
152                 return;\r
153             }\r
154             Text = _hpPercent\r
155                 ? $"{(int)Floor(status.NowHp * 100.0 / status.MaxHp):D}%"\r
156                 : $"{status.NowHp:D}/{status.MaxHp:D}";\r
157             BackColor = DamageColor(status, PresetColor);\r
158         }\r
159 \r
160         public void ToggleHpPercent()\r
161         {\r
162             _hpPercent = !_hpPercent;\r
163             SetHp(_status);\r
164         }\r
165 \r
166         public void SetHp(int now, int max)\r
167         {\r
168             SetHp(new ShipStatus {NowHp = now, MaxHp = max});\r
169         }\r
170 \r
171         public static Color DamageColor(ShipStatus status, Color backColor)\r
172         {\r
173             switch (status.DamageLevel)\r
174             {\r
175                 case ShipStatus.Damage.Sunk:\r
176                     return Color.CornflowerBlue;\r
177                 case ShipStatus.Damage.Badly:\r
178                     return CUDColors.Red;\r
179                 case ShipStatus.Damage.Half:\r
180                     return CUDColors.Orange;\r
181                 case ShipStatus.Damage.Small:\r
182                     return Color.FromArgb(240, 240, 0);\r
183                 default:\r
184                     return backColor;\r
185             }\r
186         }\r
187 \r
188         public void SetCond(ShipStatus status)\r
189         {\r
190             if (status == null)\r
191             {\r
192                 Text = "";\r
193                 BackColor = PresetColor;\r
194                 return;\r
195             }\r
196             var cond = status.Cond;\r
197             Text = cond.ToString("D");\r
198             BackColor = cond >= 50\r
199                 ? CUDColors.Yellow\r
200                 : cond >= 30\r
201                     ? PresetColor\r
202                     : cond >= 20\r
203                         ? CUDColors.Orange\r
204                         : CUDColors.Red;\r
205         }\r
206 \r
207         public void SetLevel(ShipStatus status)\r
208         {\r
209             Text = status?.Level.ToString("D");\r
210         }\r
211 \r
212         public void SetExpToNext(ShipStatus status)\r
213         {\r
214             Text = status?.ExpToNext.ToString("D");\r
215         }\r
216 \r
217         public void SetRepairTime(ShipStatus status)\r
218         {\r
219             if (status == null)\r
220             {\r
221                 Text = "";\r
222                 return;\r
223             }\r
224             SetRepairTime(status.RepairTime);\r
225         }\r
226 \r
227         public void SetRepairTime(TimeSpan span)\r
228         {\r
229             Text = $@"{(int)span.TotalHours:d2}:{span:mm\:ss}";\r
230         }\r
231 \r
232         public void SetFleet(ShipStatus status)\r
233         {\r
234             Text = status?.Fleet == null ? "" : new[] {"1", "2", "3", "4"}[status.Fleet.Number];\r
235         }\r
236 \r
237         protected override void OnSizeChanged(EventArgs args)\r
238         {\r
239             base.OnSizeChanged(args);\r
240             KeepAnchorRight();\r
241         }\r
242 \r
243         protected override void OnLayout(LayoutEventArgs args)\r
244         {\r
245             base.OnLayout(args);\r
246             KeepAnchorRight();\r
247         }\r
248 \r
249         private void KeepAnchorRight()\r
250         {\r
251             if (!AnchorRight)\r
252                 return;\r
253             if (_right == int.MinValue || _left != Left)\r
254             {\r
255                 _right = Right;\r
256                 _left = Left;\r
257                 return;\r
258             }\r
259             if (_right == Right)\r
260                 return;\r
261             _left -= Right - _right;\r
262             Location = new Point(_left, Top);\r
263         }\r
264 \r
265         protected override void OnPaint(PaintEventArgs e)\r
266         {\r
267             base.OnPaint(e);\r
268             if ((_slotStatus & SlotStatus.NormalEmpty) != 0)\r
269             {\r
270                 e.Graphics.DrawRectangle(\r
271                     Pens.Black,\r
272                     ClientSize.Width - 3 * ScaleFactor.Width, 0,\r
273                     2 * ScaleFactor.Width, 5 * ScaleFactor.Height);\r
274             }\r
275             else if ((_slotStatus & SlotStatus.SemiEquipped) != 0)\r
276             {\r
277                 e.Graphics.DrawLine(\r
278                     Pens.Black,\r
279                     ClientSize.Width - 1 * ScaleFactor.Width, 0,\r
280                     ClientSize.Width - 1 * ScaleFactor.Width, 5 * ScaleFactor.Height);\r
281             }\r
282             if ((_slotStatus & SlotStatus.ExtraEmpty) != 0)\r
283             {\r
284                 e.Graphics.DrawRectangle(\r
285                     Pens.Black,\r
286                     ClientSize.Width - 3 * ScaleFactor.Width, 8 * ScaleFactor.Height,\r
287                     2 * ScaleFactor.Width, 3 * ScaleFactor.Height);\r
288             }\r
289         }\r
290 \r
291         public void Scale()\r
292         {\r
293             Scale(ScaleFactor);\r
294         }\r
295     }\r
296 }