OSDN Git Service

76ec6fa4c9db7fdf20be16d1c29b61655dd92bf4
[kancollesniffer/KancolleSniffer.git] / KancolleSniffer / View / FleetPanel.cs
1 // Copyright (C) 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;\r
17 using System.Collections.Generic;\r
18 using System.Drawing;\r
19 using System.Linq;\r
20 using System.Windows.Forms;\r
21 using KancolleSniffer.Model;\r
22 \r
23 namespace KancolleSniffer.View\r
24 {\r
25     public class FleetPanel : Panel\r
26     {\r
27         private const int LineHeight = 14;\r
28         private const int LabelHeight = 12;\r
29         private Record[] _table = new Record[0];\r
30         private readonly List<FleetLabels> _labelList = new List<FleetLabels>();\r
31         private readonly List<Panel> _panelList = new List<Panel>();\r
32         private readonly ResizableToolTip _toolTip = new ResizableToolTip {ShowAlways = true, AutoPopDelay = 10000};\r
33 \r
34         public class Record\r
35         {\r
36             public string Fleet { get; set; }\r
37             public string Fleet2 { get; set; }\r
38             public string Ship { get; set; }\r
39             public string Ship2 { get; set; }\r
40             public int Id { get; set; }\r
41             public string Equip { get; set; }\r
42             public Color Color { get; set; }\r
43             public string Spec { get; set; }\r
44             public string Spec2 { get; set; }\r
45             public string AircraftSpec { get; set; }\r
46 \r
47             public Record()\r
48             {\r
49                 Fleet = Ship = Equip = AircraftSpec = "";\r
50                 Color = DefaultBackColor;\r
51             }\r
52         }\r
53 \r
54         public void Update(Sniffer sniffer)\r
55         {\r
56             _table = CreateTable(sniffer);\r
57             SuspendLayout();\r
58             CreateLabels();\r
59             SetRecords();\r
60             ResumeLayout();\r
61         }\r
62 \r
63         private class Total\r
64         {\r
65             public int Drum;\r
66             public int DrumShips;\r
67             public int Level;\r
68             public int FirePower;\r
69             public int AntiSubmarine;\r
70             public int AntiAir;\r
71             public int LoS;\r
72             public int Fuel;\r
73             public int Bull;\r
74 \r
75             public void Add(ShipStatus s)\r
76             {\r
77                 var drum = s.Slot.Count(item => item.Spec.IsDrum);\r
78                 DrumShips += drum != 0 ? 1 : 0;\r
79                 Drum += drum;\r
80                 Level += s.Level;\r
81                 FirePower += s.Firepower;\r
82                 AntiSubmarine += s.MissionAntiSubmarine;\r
83                 AntiAir += s.AntiAir;\r
84                 LoS += s.LoS;\r
85                 Fuel += s.EffectiveFuelMax;\r
86                 Bull += s.EffectiveBullMax;\r
87             }\r
88         }\r
89 \r
90         public Record[] CreateTable(Sniffer sniffer)\r
91         {\r
92             var list = new List<Record>();\r
93             var fn = new[] {"第一", "第二", "第三", "第四"};\r
94             foreach (var fleet in sniffer.Fleets)\r
95             {\r
96                 var total = new Total();\r
97                 var shipRecords = new List<Record>();\r
98                 foreach (var ship in fleet.ActualShips)\r
99                 {\r
100                     var equips = new List<Record>();\r
101                     for (var i = 0; i < ship.Slot.Count; i++)\r
102                     {\r
103                         var item = ship.Slot[i];\r
104                         var onSlot = ship.OnSlot[i];\r
105                         var max = ship.Spec.MaxEq[i];\r
106                         if (item.Empty)\r
107                             continue;\r
108                         var airSpec = "";\r
109                         if (item.Spec.IsDiveBomber) // 爆撃\r
110                         {\r
111                             airSpec = "航空戦 " +\r
112                                       (25 + (int)((item.Spec.Bomber + item.BomberLevelBonus) * Math.Sqrt(onSlot)));\r
113                         }\r
114                         else if (item.Spec.IsTorpedoBomber)\r
115                         {\r
116                             var normal = 25 + item.Spec.Torpedo * Math.Sqrt(onSlot);\r
117                             airSpec = "航空戦 " + (int)(normal * 0.8) + "/" + (int)(normal * 1.5);\r
118                         }\r
119                         equips.Add(new Record\r
120                         {\r
121                             Equip = GenEquipString(item),\r
122                             Spec = item.Spec.IsAircraft ? $"+{item.Alv} {onSlot}/{max}" : "",\r
123                             AircraftSpec = airSpec,\r
124                             Color = item.Spec.Color\r
125                         });\r
126                     }\r
127                     if (ship.SlotEx.Id > 0)\r
128                     {\r
129                         var item = ship.SlotEx;\r
130                         equips.Add(new Record {Equip = GenEquipString(item), Color = item.Spec.Color});\r
131                     }\r
132                     total.Add(ship);\r
133                     var fire = ship.EffectiveFirepower;\r
134                     // ReSharper disable IdentifierTypo\r
135                     var subm = ship.EffectiveAntiSubmarine;\r
136                     var torp = ship.EffectiveTorpedo;\r
137                     var night = ship.NightBattlePower;\r
138                     var oasa = ship.CanOpeningAntiSubmarineAttack ? "*" : "";\r
139                     // ReSharper restore IdentifierTypo\r
140                     var record = new Record\r
141                     {\r
142                         Ship = (ship.Escaped ? "[避]" : "") + ship.Name + " Lv" + ship.Level,\r
143                         Ship2 = $"燃{ship.EffectiveFuelMax} 弾{ship.EffectiveBullMax}",\r
144                         Id = ship.Id,\r
145                         Spec = HideIfZero("砲", fire) + HideIfZero(" 潜", subm) + oasa,\r
146                         Spec2 = (HideIfZero("雷", torp) + HideIfZero(" 夜", night)).TrimStart(' ')\r
147                     };\r
148                     if (record.Spec == "")\r
149                     {\r
150                         record.Spec = record.Spec2;\r
151                         record.Spec2 = "";\r
152                     }\r
153                     shipRecords.Add(record);\r
154                     shipRecords.AddRange(equips);\r
155                 }\r
156                 var daihatsu = fleet.DaihatsuBonus;\r
157                 var tp = fleet.TransportPoint;\r
158                 if (sniffer.IsCombinedFleet && fleet.Number == 0)\r
159                     tp += sniffer.Fleets[1].TransportPoint;\r
160                 list.Add(new Record\r
161                 {\r
162                     Fleet = fn[fleet.Number] + HideIfZero(" Lv", total.Level) +\r
163                             HideIfZero(" ドラム缶", total.Drum) + HideIfZero("(", total.DrumShips, "隻)") +\r
164                             HideIfZero(" 大発", daihatsu * 100, "%"),\r
165                     Fleet2 = "計:" +\r
166                              "火" + CutOverFlow(total.FirePower) +\r
167                              " 空" + CutOverFlow(total.AntiAir) +\r
168                              " 潜" + CutOverFlow(total.AntiSubmarine) +\r
169                              " 索" + CutOverFlow(total.LoS) + "\r\n" +\r
170                              $"戦闘:燃{total.Fuel / 5}弾{total.Bull / 5} 支援:燃{total.Fuel / 2}弾{(int)(total.Bull * 0.8)}" +\r
171                              (sniffer.IsCombinedFleet && fleet.Number == 1\r
172                                  ? ""\r
173                                  : $"\r\nTP:S{(int)tp} A{(int)(tp * 0.7)}")\r
174                 });\r
175                 list.AddRange(shipRecords);\r
176             }\r
177             if (sniffer.BaseAirCorps != null)\r
178             {\r
179                 var name = new[] {"第一", "第二", "第三"};\r
180                 foreach (var baseInfo in sniffer.BaseAirCorps)\r
181                 {\r
182                     list.Add(new Record {Fleet = baseInfo.AreaName + " 基地航空隊"});\r
183                     var i = 0;\r
184                     foreach (var airCorps in baseInfo.AirCorps)\r
185                     {\r
186                         if (i >= name.Length)\r
187                             break;\r
188                         var corpsFp = airCorps.FighterPower;\r
189                         string spec;\r
190                         string spec2;\r
191                         if (airCorps.Action == 2)\r
192                         {\r
193                             spec = "制空:" + RangeString(corpsFp.Interception);\r
194                             spec2 = corpsFp.IsInterceptor ? "制空(出撃):" + RangeString(corpsFp.AirCombat) : "";\r
195                         }\r
196                         else\r
197                         {\r
198                             spec = "制空:" + RangeString(corpsFp.AirCombat);\r
199                             spec2 = corpsFp.IsInterceptor ? "制空(防空):" + RangeString(corpsFp.Interception) : "";\r
200                         }\r
201                         var cost = airCorps.CostForSortie;\r
202                         list.Add(new Record\r
203                         {\r
204                             Ship = name[i++] + " " + airCorps.ActionName,\r
205                             Ship2 = $"出撃コスト:燃{cost[0]}弾{cost[1]}",\r
206                             Spec = spec + " 距離:" + airCorps.Distance,\r
207                             Spec2 = spec2\r
208                         });\r
209                         list.AddRange(airCorps.Planes.Select(plane =>\r
210                         {\r
211                             var planeFp = plane.FighterPower;\r
212                             return new Record\r
213                             {\r
214                                 Equip = plane.State != 1 ? plane.StateName : GenEquipString(plane.Slot),\r
215                                 Spec = plane.State != 1 ? "" : $"+{plane.Slot.Alv} {plane.Count}/{plane.MaxCount}",\r
216                                 AircraftSpec =\r
217                                     $"距離:{plane.Slot.Spec.Distance} 制空:{RangeString(planeFp.AirCombat)}" +\r
218                                     (planeFp.IsInterceptor ? $" 防空:{RangeString(planeFp.Interception)}" : ""),\r
219                                 Color = plane.Slot.Spec.Color\r
220                             };\r
221                         }));\r
222                     }\r
223                 }\r
224             }\r
225             return list.ToArray();\r
226         }\r
227 \r
228         private string RangeString(int[] fp) => fp[0] == fp[1] ? fp[0].ToString() : $"{fp[0]}~{fp[1]}";\r
229 \r
230         private int CutOverFlow(int value) => value > 999 ? 999 : value;\r
231 \r
232         private string HideIfZero(string name, double value, string suffix = "")\r
233         {\r
234             return value > 0 ? name + value.ToString("f1") + suffix : "";\r
235         }\r
236 \r
237         private string HideIfZero(string name, int value, string suffix = "")\r
238         {\r
239             return value > 0 ? name + value + suffix : "";\r
240         }\r
241 \r
242         private string GenEquipString(ItemStatus item)\r
243         {\r
244             var name = item.Spec.Name;\r
245             var attr = item.Level == 0 ? "" : "★" + item.Level;\r
246             var proposed = new Size(int.MaxValue, int.MaxValue);\r
247             var maxWidth = item.Spec.IsAircraft ? 132 : 180;\r
248             var result = name + attr;\r
249             if (TextRenderer.MeasureText(result, Font, proposed).Width <= maxWidth)\r
250                 return result;\r
251             var truncated = "";\r
252             foreach (var ch in name)\r
253             {\r
254                 var tmp = truncated + ch;\r
255                 if (TextRenderer.MeasureText(tmp + attr, Font, proposed).Width > maxWidth)\r
256                     break;\r
257                 truncated = tmp;\r
258             }\r
259             return truncated + attr;\r
260         }\r
261 \r
262         private void CreateLabels()\r
263         {\r
264             for (var i = _labelList.Count; i < _table.Length; i++)\r
265                 CreateLabels(i);\r
266         }\r
267 \r
268         private class FleetLabels : IEnumerable<ShipLabel>\r
269         {\r
270             public ShipLabel Fleet { get; set; }\r
271             public ShipLabel Name { get; set; }\r
272             public ShipLabel Equip { get; set; }\r
273             public ShipLabel EquipColor { get; set; }\r
274             public ShipLabel Spec { get; set; }\r
275 \r
276             public IEnumerator<ShipLabel> GetEnumerator() =>\r
277                 ((IEnumerable<ShipLabel>)new[] {Fleet, Name, Equip, EquipColor, Spec}).GetEnumerator();\r
278 \r
279             IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();\r
280         }\r
281 \r
282         private void CreateLabels(int i)\r
283         {\r
284             var y = 1 + LineHeight * i;\r
285             var lbp = new Panel\r
286             {\r
287                 Location = new Point(0, y),\r
288                 Size = new Size(ListForm.PanelWidth, LineHeight),\r
289                 BackColor = ShipLabel.ColumnColors[(i + 1) % 2],\r
290                 Visible = false\r
291             };\r
292             lbp.Scale(ShipLabel.ScaleFactor);\r
293             lbp.Tag = lbp.Location.Y;\r
294             var labels = new FleetLabels\r
295             {\r
296                 Fleet = new ShipLabel {Location = new Point(1, 2), AutoSize = true},\r
297                 Name = new ShipLabel {Location = new Point(10, 2), AutoSize = true},\r
298                 Equip = new ShipLabel {Location = new Point(38, 2), AutoSize = true},\r
299                 EquipColor = new ShipLabel {Location = new Point(35, 2), Size = new Size(4, LabelHeight - 2)},\r
300                 Spec = new ShipLabel {Location = new Point(217, 2), AutoSize = true, AnchorRight = true}\r
301             };\r
302             _labelList.Add(labels);\r
303             _panelList.Add(lbp);\r
304             lbp.Controls.AddRange(labels.Cast<Control>().ToArray());\r
305             Controls.Add(lbp);\r
306             foreach (var label in labels)\r
307             {\r
308                 label.Scale();\r
309                 label.PresetColor =\r
310                     label.BackColor = ShipLabel.ColumnColors[(i + 1) % 2];\r
311             }\r
312         }\r
313 \r
314         private void SetRecords()\r
315         {\r
316             for (var i = 0; i < _table.Length; i++)\r
317                 SetRecord(i);\r
318             for (var i = _table.Length; i < _labelList.Count; i++)\r
319                 _panelList[i].Visible = false;\r
320         }\r
321 \r
322         private void SetRecord(int i)\r
323         {\r
324             var lbp = _panelList[i];\r
325             if (!lbp.Visible)\r
326                 lbp.Location = new Point(lbp.Left, (int)lbp.Tag + AutoScrollPosition.Y);\r
327             var e = _table[i];\r
328             var labels = _labelList[i];\r
329             labels.Fleet.Text = e.Fleet;\r
330             labels.Name.SetName(e.Ship);\r
331             if (e.Ship2 != "")\r
332                 _toolTip.SetToolTip(labels.Name, e.Ship2);\r
333             labels.Equip.Text = e.Equip;\r
334             labels.EquipColor.Visible = e.Equip != "";\r
335             labels.EquipColor.BackColor = e.Color;\r
336             labels.Spec.Text = e.Spec;\r
337             if (e.Fleet != "" && e.Fleet2 != "")\r
338                 _toolTip.SetToolTip(labels.Fleet, e.Fleet2);\r
339             _toolTip.SetToolTip(labels.Equip, e.AircraftSpec != "" ? e.AircraftSpec : "");\r
340             _toolTip.SetToolTip(labels.Spec, e.Spec2 != "" ? e.Spec2 : "");\r
341             lbp.Visible = true;\r
342         }\r
343 \r
344         public void ShowShip(int id)\r
345         {\r
346             var i = Array.FindIndex(_table, e => e.Id == id);\r
347             if (i == -1)\r
348                 return;\r
349             var y = (int)Math.Round(ShipLabel.ScaleFactor.Height * LineHeight * i);\r
350             AutoScrollPosition = new Point(0, y);\r
351         }\r
352 \r
353         public void ShowFleet(string fn)\r
354         {\r
355             var i = Array.FindIndex(_table, e => e.Fleet.StartsWith(fn));\r
356             if (i == -1)\r
357                 return;\r
358             var y = (int)Math.Round(ShipLabel.ScaleFactor.Height * LineHeight * i);\r
359             AutoScrollPosition = new Point(0, y);\r
360         }\r
361 \r
362         protected override void ScaleControl(SizeF factor, BoundsSpecified specified)\r
363         {\r
364             base.ScaleControl(factor, specified);\r
365             if (factor.Height > 1)\r
366                 _toolTip.Font = new Font(_toolTip.Font.FontFamily, _toolTip.Font.Size * factor.Height);\r
367         }\r
368     }\r
369 }