OSDN Git Service

dffb8f13b38e18de799a63c3212b8af77e7972d9
[kancollesniffer/KancolleSniffer.git] / KancolleSniffer / Model / Fleet.cs
1 // Copyright (C) 2018 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.Linq;\r
18 using static System.Math;\r
19 \r
20 namespace KancolleSniffer.Model\r
21 {\r
22     public struct ChargeStatus\r
23     {\r
24         public int Fuel { get; set; }\r
25         public int Bull { get; set; }\r
26 \r
27         public ChargeStatus(ShipStatus status) : this()\r
28         {\r
29             Fuel = CalcChargeState(status.Fuel, status.Spec.FuelMax);\r
30             Bull = CalcChargeState(status.Bull, status.Spec.BullMax);\r
31         }\r
32 \r
33         public ChargeStatus(int fuel, int bull) : this()\r
34         {\r
35             Fuel = fuel;\r
36             Bull = bull;\r
37         }\r
38 \r
39         private int CalcChargeState(int now, int full)\r
40         {\r
41             if (full == 0 || now == full)\r
42                 return 0;\r
43             var ratio = (double)now / full;\r
44             if (ratio >= 7.0 / 9)\r
45                 return 1;\r
46             if (ratio >= 3.0 / 9)\r
47                 return 2;\r
48             if (ratio > 0)\r
49                 return 3;\r
50             return 4;\r
51         }\r
52     }\r
53 \r
54     public enum FleetState\r
55     {\r
56         Port,\r
57         Mission,\r
58         Sortie,\r
59         Practice\r
60     }\r
61 \r
62     public enum CombinedType\r
63     {\r
64         None,\r
65         Carrier, // 機動\r
66         Surface, // 水上\r
67         Transport // 輸送\r
68     }\r
69 \r
70     public class Fleet\r
71     {\r
72         private readonly ShipInventry _shipInventry;\r
73         private readonly Func<int> _getHqLevel;\r
74         private int[] _deck = Enumerable.Repeat(-1, ShipInfo.MemberCount).ToArray();\r
75         public int Number { get; }\r
76         public FleetState State { get; set; }\r
77         public CombinedType CombinedType { get; set; }\r
78         public IReadOnlyList<ShipStatus> Ships { get; private set; }\r
79         public IReadOnlyList<ShipStatus> ActualShips { get; private set; }\r
80 \r
81         public Fleet(ShipInventry shipInventry, int number, Func<int> getHqLevel)\r
82         {\r
83             _shipInventry = shipInventry;\r
84             Number = number;\r
85             _getHqLevel = getHqLevel;\r
86             Ships = _deck.Select(id => new ShipStatus()).ToArray();\r
87             ActualShips = new ShipStatus[0];\r
88         }\r
89 \r
90         public IReadOnlyList<int> Deck\r
91         {\r
92             get => _deck;\r
93             set\r
94             {\r
95                 _deck = value.ToArray();\r
96                 SetDeck();\r
97             }\r
98         }\r
99 \r
100         public void SetDeck()\r
101         {\r
102             foreach (var ship in Ships)\r
103             {\r
104                 ship.Fleet = null;\r
105                 ship.DeckIndex = -1;\r
106             }\r
107             Ships = _deck.Select((id, num) =>\r
108             {\r
109                 var ship = _shipInventry[id];\r
110                 if (ship.Empty)\r
111                     return ship;\r
112                 ship.DeckIndex = num;\r
113                 ship.Fleet = this;\r
114                 return ship;\r
115             }).ToArray();\r
116             ActualShips = Ships.Where(ship => !ship.Empty).ToArray();\r
117         }\r
118 \r
119         public int SetShip(int index, int shipId)\r
120         {\r
121             var prev = _deck[index];\r
122             _deck[index] = shipId;\r
123             SetDeck();\r
124             return prev;\r
125         }\r
126 \r
127         public void WithdrowShip(int index)\r
128         {\r
129             if (index == -1) // 旗艦以外解除\r
130             {\r
131                 for (var i = 1; i < _deck.Length; i++)\r
132                     _deck[i] = -1;\r
133                 SetDeck();\r
134                 return;\r
135             }\r
136             var dst = index;\r
137             for (var src = index + 1; src < _deck.Length; src++)\r
138             {\r
139                 if (_deck[src] != -1)\r
140                     _deck[dst++] = _deck[src];\r
141             }\r
142             for (; dst < _deck.Length; dst++)\r
143                 _deck[dst] = -1;\r
144             SetDeck();\r
145         }\r
146 \r
147         public ChargeStatus ChargeStatus\r
148         {\r
149             get\r
150             {\r
151                 var fs = new ChargeStatus(Ships[0]);\r
152                 var others = (from ship in Ships.Skip(1) select new ChargeStatus(ship)).Aggregate(\r
153                     (result, next) => new ChargeStatus(Max(result.Fuel, next.Fuel), Max(result.Bull, next.Bull)));\r
154                 return new ChargeStatus(fs.Fuel != 0 ? fs.Fuel : others.Fuel + 5,\r
155                     fs.Bull != 0 ? fs.Bull : others.Bull + 5);\r
156             }\r
157         }\r
158 \r
159         public int[] FighterPower\r
160             => ActualShips.Where(ship => !ship.Escaped).SelectMany(ship =>\r
161                     ship.Slot.Zip(ship.OnSlot, (slot, onslot) => slot.CalcFighterPower(onslot)))\r
162                 .Aggregate(new[] {0, 0}, (prev, cur) => new[] {prev[0] + cur[0], prev[1] + cur[1]});\r
163 \r
164         public double ContactTriggerRate\r
165             => ActualShips.Where(ship => !ship.Escaped).SelectMany(ship =>\r
166                 ship.Slot.Zip(ship.OnSlot, (slot, onslot) =>\r
167                     slot.Spec.ContactTriggerRate * slot.Spec.LoS * Sqrt(onslot))).Sum();\r
168 \r
169         public double GetLineOfSights(int factor)\r
170         {\r
171             var result = 0.0;\r
172             var emptyBonus = 6;\r
173             foreach (var s in ActualShips.Where(s => !s.Escaped))\r
174             {\r
175                 emptyBonus--;\r
176                 var itemLoS = 0;\r
177                 foreach (var item in s.Slot)\r
178                 {\r
179                     var spec = item.Spec;\r
180                     itemLoS += spec.LoS;\r
181                     result += (spec.LoS + item.LoSLevelBonus) * spec.LoSScaleFactor * factor;\r
182                 }\r
183                 result += Sqrt(s.LoS - itemLoS);\r
184             }\r
185             return result > 0 ? result - Ceiling(_getHqLevel() * 0.4) + emptyBonus * 2 : 0.0;\r
186         }\r
187 \r
188         public double DaihatsuBonus\r
189         {\r
190             get\r
191             {\r
192                 var tokudaiBonus = new[,]\r
193                 {\r
194                     {0.00, 0.00, 0.00, 0.00, 0.00},\r
195                     {0.02, 0.02, 0.02, 0.02, 0.02},\r
196                     {0.04, 0.04, 0.04, 0.04, 0.04},\r
197                     {0.05, 0.05, 0.052, 0.054, 0.054},\r
198                     {0.054, 0.056, 0.058, 0.059, 0.06}\r
199                 };\r
200                 var daihatsu = 0;\r
201                 var tokudai = 0;\r
202                 var bonus = 0.0;\r
203                 var level = 0;\r
204                 var sum = 0;\r
205                 foreach (var ship in Ships)\r
206                 {\r
207                     if (ship.Name == "鬼怒改二")\r
208                         bonus += 0.05;\r
209                     foreach (var item in ship.Slot)\r
210                     {\r
211                         switch (item.Spec.Name)\r
212                         {\r
213                             case "大発動艇":\r
214                                 level += item.Level;\r
215                                 sum++;\r
216                                 daihatsu++;\r
217                                 bonus += 0.05;\r
218                                 break;\r
219                             case "特大発動艇":\r
220                                 level += item.Level;\r
221                                 sum++;\r
222                                 tokudai++;\r
223                                 bonus += 0.05;\r
224                                 break;\r
225                             case "大発動艇(八九式中戦車&陸戦隊)":\r
226                                 level += item.Level;\r
227                                 sum++;\r
228                                 bonus += 0.02;\r
229                                 break;\r
230                             case "特二式内火艇":\r
231                                 level += item.Level;\r
232                                 sum++;\r
233                                 bonus += 0.01;\r
234                                 break;\r
235                         }\r
236                     }\r
237                 }\r
238                 var levelAverage = sum == 0 ? 0.0 : (double)level / sum;\r
239                 bonus = Min(bonus, 0.2);\r
240                 return bonus + 0.01 * bonus * levelAverage + tokudaiBonus[Min(tokudai, 4), Min(daihatsu, 4)];\r
241             }\r
242         }\r
243 \r
244         public double TransportPoint => Ships.Where(ship => !ship.Escaped).Sum(ship => ship.TransportPoint);\r
245 \r
246         public int CombinedFirepowerBonus\r
247         {\r
248             get\r
249             {\r
250                 switch (CombinedType)\r
251                 {\r
252                     case CombinedType.None:\r
253                         return 0;\r
254                     case CombinedType.Carrier:\r
255                         return Number == 0 ? 2 : 10;\r
256                     case CombinedType.Surface:\r
257                         return Number == 0 ? 10 : -5;\r
258                     case CombinedType.Transport:\r
259                         return Number == 0 ? -5 : 10;\r
260                     default:\r
261                         return 0;\r
262                 }\r
263             }\r
264         }\r
265 \r
266         public int CombinedTorpedoPenalty => CombinedType != 0 && Number == 1 ? -5 : 0;\r
267     }\r
268 }