OSDN Git Service

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