OSDN Git Service

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