OSDN Git Service

IUpdatableをIUpdateContextとIUpdateTimerに分離する
[kancollesniffer/KancolleSniffer.git] / KancolleSniffer / View / FleetSpec.cs
1 // Copyright (C) 2019 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 FleetSpec\r
25     {\r
26         public static Record[] Create(Sniffer sniffer)\r
27         {\r
28             var list = new List<Record>();\r
29             foreach (var fleet in sniffer.Fleets)\r
30             {\r
31                 var shipRecords = new List<Record>();\r
32                 foreach (var ship in fleet.ActualShips)\r
33                 {\r
34                     shipRecords.Add(Record.CreateShipRecord(ship));\r
35                     shipRecords.AddRange(GetItemRecords(ship));\r
36                 }\r
37                 list.Add(Record.CreateFleetRecord(sniffer.Fleets, fleet.Number));\r
38                 list.AddRange(shipRecords);\r
39             }\r
40             if (sniffer.AirBase == null)\r
41                 return list.ToArray();\r
42             foreach (var baseInfo in sniffer.AirBase)\r
43             {\r
44                 list.Add(new Record {Fleet = baseInfo.AreaName + " 基地航空隊"});\r
45                 var i = 0;\r
46                 foreach (var airCorps in baseInfo.AirCorps)\r
47                 {\r
48                     if (i >= 3)\r
49                         break;\r
50                     list.Add(Record.CreateAirCorpsRecord(airCorps, i++));\r
51                     list.AddRange(airCorps.Planes.Select(Record.CreateCorpsPlaneRecord));\r
52                 }\r
53             }\r
54             return list.ToArray();\r
55         }\r
56 \r
57         private static IEnumerable<Record> GetItemRecords(ShipStatus ship)\r
58         {\r
59             var items = ship.Slot.TakeWhile(item => !item.Empty)\r
60                 .Select((item, i) => Record.CreateItemRecord(item, ship.OnSlot[i], ship.Spec.MaxEq[i]));\r
61             if (ship.SlotEx.Id <= 0)\r
62                 return items;\r
63             return items.Concat(new[] {Record.CreateItemRecord(ship.SlotEx, 0, 0)});\r
64         }\r
65 \r
66         public class Record\r
67         {\r
68             public string Fleet { get; set; }\r
69             public string Fleet2 { get; set; }\r
70             public string Ship { get; set; }\r
71             public string Ship2 { get; set; }\r
72             public int Id { get; set; }\r
73             public string Equip { get; set; }\r
74             public Color Color { get; set; }\r
75             public string Spec { get; set; }\r
76             public string Spec2 { get; set; }\r
77             public string AircraftSpec { get; set; }\r
78 \r
79             public Record()\r
80             {\r
81                 Color = Control.DefaultBackColor;\r
82                 Fleet = Ship = Equip = AircraftSpec = "";\r
83             }\r
84 \r
85             public static Record CreateShipRecord(ShipStatus ship)\r
86             {\r
87                 return new ShipRecord(ship);\r
88             }\r
89 \r
90             private class ShipRecord : Record\r
91             {\r
92                 public ShipRecord(ShipStatus ship)\r
93                 {\r
94                     Ship = (ship.Escaped ? "[避]" : "") + ship.Name + " Lv" + ship.Level;\r
95                     Ship2 = $"燃{ship.EffectiveFuelMax} 弾{ship.EffectiveBullMax}";\r
96                     Id = ship.Id;\r
97                     SetAttackPower(ship);\r
98                 }\r
99 \r
100                 private void SetAttackPower(ShipStatus ship)\r
101                 {\r
102                     Spec = HideIfZero("砲", ship.EffectiveFirepower) +\r
103                            HideIfZero(" 潜", ship.EffectiveAntiSubmarine) +\r
104                            (ship.CanOpeningAntiSubmarineAttack ? "*" : "");\r
105                     Spec2 = (HideIfZero("雷", ship.EffectiveTorpedo) +\r
106                              HideIfZero(" 夜", ship.NightBattlePower)).TrimStart(' ');\r
107                     if (Spec != "")\r
108                         return;\r
109                     Spec = Spec2;\r
110                     Spec2 = "";\r
111                 }\r
112             }\r
113 \r
114             public static Record CreateItemRecord(ItemStatus item, int onSlot, int maxEq)\r
115             {\r
116                 return new ItemRecord(item, onSlot, maxEq);\r
117             }\r
118 \r
119             private class ItemRecord : Record\r
120             {\r
121                 public ItemRecord(ItemStatus item, int onSlot, int maxEq)\r
122                 {\r
123                     Equip = GenEquipString(item);\r
124                     Spec = item.Spec.IsAircraft ? $"+{item.Alv} {onSlot}/{maxEq}" : "";\r
125                     AircraftSpec = GetAircraftSpec(item, onSlot);\r
126                     Color = item.Spec.Color;\r
127                 }\r
128 \r
129                 private static string GetAircraftSpec(ItemStatus item, int onSlot)\r
130                 {\r
131                     if (item.Spec.IsDiveBomber) // 爆撃\r
132                     {\r
133                         return "航空戦 " +\r
134                                (25 + (int)((item.Spec.Bomber + item.BomberLevelBonus) * Math.Sqrt(onSlot)));\r
135                     }\r
136                     if (item.Spec.IsTorpedoBomber)\r
137                     {\r
138                         var normal = 25 + item.Spec.Torpedo * Math.Sqrt(onSlot);\r
139                         return "航空戦 " + (int)(normal * 0.8) + "/" + (int)(normal * 1.5);\r
140                     }\r
141                     return "";\r
142                 }\r
143             }\r
144 \r
145             private static string GenEquipString(ItemStatus item)\r
146             {\r
147                 var level = item.Level == 0 ? "" : "★" + item.Level;\r
148                 return StringTruncator.Truncate(item.Spec.Name, level, item.Spec.IsAircraft ? 132 : 180) + level;\r
149             }\r
150 \r
151             public static Record CreateFleetRecord(IReadOnlyList<Fleet> fleets, int number)\r
152             {\r
153                 return new FleetRecord(fleets, number);\r
154             }\r
155 \r
156             private class FleetRecord : Record\r
157             {\r
158                 public FleetRecord(IReadOnlyList<Fleet> fleets, int number)\r
159                 {\r
160                     var fleet = fleets[number];\r
161                     Fleet = new[] {"第一", "第二", "第三", "第四"}[number] + " " + SpeedName(fleet) + "   " + SpecTotal(fleet);\r
162                     Fleet2 = Concat(FleetParams(fleet),\r
163                         Concat(GetTp(fleets, number), GetNightContact(fleet), " "), "\r\n");\r
164                 }\r
165 \r
166                 private static string SpeedName(Fleet fleet)\r
167                 {\r
168                     var speed = fleet.ActualShips.Min(s => (int?)s.Speed);\r
169                     return new[] {"", "低速", "高速", "高速+", "最速"}[(speed ?? 0) / 5];\r
170                 }\r
171 \r
172                 private static string Concat(string a, string b, string separator)\r
173                 {\r
174                     return a == "" ? b : b == "" ? a : a + separator + b;\r
175                 }\r
176 \r
177                 private static string GetTp(IReadOnlyList<Fleet> fleets, int number)\r
178                 {\r
179                     var tp = fleets[number].TransportPoint;\r
180                     if (fleets[0].CombinedType != CombinedType.None)\r
181                     {\r
182                         if (number == 0)\r
183                             tp += fleets[1].TransportPoint;\r
184                         else if (number == 1)\r
185                             return "";\r
186                     }\r
187                     return $"TP:S{(int)tp} A{(int)(tp * 0.7)}";\r
188                 }\r
189 \r
190                 private static string GetNightContact(Fleet fleet)\r
191                 {\r
192                     return fleet.NightContactTriggerRate > 0 ? $"夜偵:{fleet.NightContactTriggerRate}%" : "";\r
193                 }\r
194 \r
195                 private static string SpecTotal(Fleet fleet)\r
196                 {\r
197                     var total = CalcTotal(fleet);\r
198                     return "火" + CutOverFlow(total.FirePower) +\r
199                            " 空" + CutOverFlow(total.AntiAir) +\r
200                            " 潜" + CutOverFlow(total.AntiSubmarine) +\r
201                            " 索" + CutOverFlow(total.LoS);\r
202                 }\r
203 \r
204                 private static string FleetParams(Fleet fleet)\r
205                 {\r
206                     var total = CalcTotal(fleet);\r
207                     return "計:" + HideIfZero(" Lv", total.Level) +\r
208                            HideIfZero(" ド", total.Drum) + HideIfZero("(", total.DrumShips, "隻)") +\r
209                            HideIfZero(" 大", fleet.DaihatsuBonus, "%") + "\r\n" +\r
210                            $"戦闘:燃{total.Fuel / 5}弾{total.Bull / 5} 支援:燃{total.Fuel / 2}弾{(int)(total.Bull * 0.8)}";\r
211                 }\r
212 \r
213                 private static Total CalcTotal(Fleet fleet)\r
214                 {\r
215                     return fleet.Ships.Aggregate(new Total(), (sum, next) => sum.Add(next));\r
216                 }\r
217 \r
218                 private static int CutOverFlow(int value) => value > 999 ? 999 : value;\r
219 \r
220                 private class Total\r
221                 {\r
222                     public int Drum;\r
223                     public int DrumShips;\r
224                     public int Level;\r
225                     public int FirePower;\r
226                     public int AntiSubmarine;\r
227                     public int AntiAir;\r
228                     public int LoS;\r
229                     public int Fuel;\r
230                     public int Bull;\r
231 \r
232                     public Total Add(ShipStatus s)\r
233                     {\r
234                         var drum = s.Slot.Count(item => item.Spec.IsDrum);\r
235                         return new Total\r
236                         {\r
237                             DrumShips = DrumShips + (drum != 0 ? 1 : 0),\r
238                             Drum = Drum + drum,\r
239                             Level = Level + s.Level,\r
240                             FirePower = FirePower + s.Firepower,\r
241                             AntiSubmarine = AntiSubmarine + s.MissionAntiSubmarine,\r
242                             AntiAir = AntiAir + s.AntiAir,\r
243                             LoS = LoS + s.LoS,\r
244                             Fuel = Fuel + s.EffectiveFuelMax,\r
245                             Bull = Bull + s.EffectiveBullMax\r
246                         };\r
247                     }\r
248                 }\r
249             }\r
250 \r
251             private static string HideIfZero(string name, double value, string suffix = "")\r
252             {\r
253                 return value > 0 ? name + value.ToString("f1") + suffix : "";\r
254             }\r
255 \r
256             private static string HideIfZero(string name, int value, string suffix = "")\r
257             {\r
258                 return value > 0 ? name + value + suffix : "";\r
259             }\r
260 \r
261             public static Record CreateAirCorpsRecord(AirBase.AirCorpsInfo airCorps, int number)\r
262             {\r
263                 return new AirCorpsRecord(airCorps, number);\r
264             }\r
265 \r
266             private class AirCorpsRecord : Record\r
267             {\r
268                 public AirCorpsRecord(AirBase.AirCorpsInfo airCorps, int number)\r
269                 {\r
270                     var corpsFp = airCorps.CalcFighterPower();\r
271                     string spec;\r
272                     string spec2;\r
273                     if (airCorps.Action == 2)\r
274                     {\r
275                         spec = "制空:" + RangeString(corpsFp.Interception);\r
276                         spec2 = corpsFp.Difference ? "制空(出撃):" + RangeString(corpsFp.AirCombat) : "";\r
277                     }\r
278                     else\r
279                     {\r
280                         spec = "制空:" + RangeString(corpsFp.AirCombat);\r
281                         spec2 = corpsFp.Difference ? "制空(防空):" + RangeString(corpsFp.Interception) : "";\r
282                     }\r
283                     var cost = airCorps.CostForSortie;\r
284                     Ship = new[] {"第一", "第二", "第三"}[number] + " " + airCorps.ActionName;\r
285                     Ship2 = $"出撃コスト:燃{cost[0]}弾{cost[1]}";\r
286                     Spec = spec + $" 距離:{airCorps.Distance}";\r
287                     Spec2 = spec2;\r
288                 }\r
289             }\r
290 \r
291             public static Record CreateCorpsPlaneRecord(AirBase.PlaneInfo plane)\r
292             {\r
293                 return new CorpsPlaneRecord(plane);\r
294             }\r
295 \r
296             private class CorpsPlaneRecord : Record\r
297             {\r
298                 public CorpsPlaneRecord(AirBase.PlaneInfo plane)\r
299                 {\r
300                     var planeFp = plane.FighterPower;\r
301                     Equip = plane.State != 1 ? plane.StateName : GenEquipString(plane.Slot);\r
302                     Spec = plane.State != 1 ? "" : $"+{plane.Slot.Alv} {plane.Count}/{plane.MaxCount}";\r
303                     AircraftSpec =\r
304                         $"距離:{plane.Slot.Spec.Distance} 制空:{RangeString(planeFp.AirCombat)}" +\r
305                         (planeFp.Difference ? $" 防空:{RangeString(planeFp.Interception)}" : "");\r
306                     Color = plane.Slot.Spec.Color;\r
307                 }\r
308             }\r
309 \r
310             private static string RangeString(Range range) => range.Diff ? range.RangeString : range.Min.ToString();\r
311         }\r
312     }\r
313 }