OSDN Git Service

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