1 // Copyright (C) 2018 Kazuhiro Fujieda <fujieda@users.osdn.me>
\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
7 // http://www.apache.org/licenses/LICENSE-2.0
\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
16 using System.Collections.Generic;
\r
18 using static System.Math;
\r
20 namespace KancolleSniffer.Model
\r
22 public struct ChargeStatus
\r
24 public int Fuel { get; set; }
\r
25 public int Bull { get; set; }
\r
27 public ChargeStatus(ShipStatus status) : this()
\r
29 Fuel = CalcChargeState(status.Fuel, status.Spec.FuelMax);
\r
30 Bull = CalcChargeState(status.Bull, status.Spec.BullMax);
\r
33 public ChargeStatus(int fuel, int bull) : this()
\r
39 private int CalcChargeState(int now, int full)
\r
41 if (full == 0 || now == full)
\r
43 var ratio = (double)now / full;
\r
44 if (ratio >= 7.0 / 9)
\r
46 if (ratio >= 3.0 / 9)
\r
54 public enum FleetState
\r
62 public enum CombinedType
\r
72 private readonly ShipInventory _shipInventory;
\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
81 public Fleet(ShipInventory shipInventory, int number, Func<int> getHqLevel)
\r
83 _shipInventory = shipInventory;
\r
85 _getHqLevel = getHqLevel;
\r
86 Ships = _deck.Select(id => new ShipStatus()).ToArray();
\r
87 ActualShips = new ShipStatus[0];
\r
90 public IReadOnlyList<int> Deck
\r
92 get => _deck.ToArray();
\r
95 _deck = value.ToArray();
\r
100 public void SetDeck()
\r
102 foreach (var ship in Ships)
\r
104 if (ship.Fleet != this) // 入れ替え操作で他の艦隊に移動しているときには触らない。
\r
107 ship.DeckIndex = -1;
\r
109 Ships = _deck.Select((id, num) =>
\r
111 var ship = _shipInventory[id];
\r
114 ship.DeckIndex = num;
\r
118 ActualShips = Ships.Where(ship => !ship.Empty).ToArray();
\r
121 public int SetShip(int index, int shipId)
\r
123 var prev = _deck[index];
\r
124 _deck[index] = shipId;
\r
129 public void WithdrawAccompanyingShips()
\r
131 for (var i = 1; i < _deck.Length; i++)
\r
136 public void WithdrawShip(int index)
\r
139 for (var src = index + 1; src < _deck.Length; src++)
\r
141 if (_deck[src] != -1)
\r
142 _deck[dst++] = _deck[src];
\r
144 for (; dst < _deck.Length; dst++)
\r
149 public ChargeStatus ChargeStatus
\r
153 var fs = new ChargeStatus(Ships[0]);
\r
154 var others = (from ship in Ships.Skip(1) select new ChargeStatus(ship)).Aggregate(
\r
155 (result, next) => new ChargeStatus(Max(result.Fuel, next.Fuel), Max(result.Bull, next.Bull)));
\r
156 return new ChargeStatus(fs.Fuel != 0 ? fs.Fuel : others.Fuel + 5,
\r
157 fs.Bull != 0 ? fs.Bull : others.Bull + 5);
\r
161 public int[] FighterPower
\r
162 => ActualShips.Where(ship => !ship.Escaped).SelectMany(ship =>
\r
163 ship.Slot.Zip(ship.OnSlot, (slot, onSlot) => slot.CalcFighterPower(onSlot)))
\r
164 .Aggregate(new[] {0, 0}, (prev, cur) => new[] {prev[0] + cur[0], prev[1] + cur[1]});
\r
166 public double ContactTriggerRate
\r
167 => ActualShips.Where(ship => !ship.Escaped).SelectMany(ship =>
\r
168 ship.Slot.Zip(ship.OnSlot, (slot, onSlot) =>
\r
169 slot.Spec.ContactTriggerRate * slot.Spec.LoS * Sqrt(onSlot))).Sum();
\r
171 public double GetLineOfSights(int factor)
\r
174 var emptyBonus = 6;
\r
175 foreach (var s in ActualShips.Where(s => !s.Escaped))
\r
179 foreach (var item in s.Slot)
\r
181 var spec = item.Spec;
\r
182 itemLoS += spec.LoS;
\r
183 result += (spec.LoS + item.LoSLevelBonus) * spec.LoSScaleFactor * factor;
\r
185 result += Sqrt(s.LoS - itemLoS);
\r
187 return result > 0 ? result - Ceiling(_getHqLevel() * 0.4) + emptyBonus * 2 : 0.0;
\r
190 public double DaihatsuBonus
\r
194 // ReSharper disable IdentifierTypo
\r
195 var tokudaiBonus = new[,]
\r
197 {0.00, 0.00, 0.00, 0.00, 0.00},
\r
198 {0.02, 0.02, 0.02, 0.02, 0.02},
\r
199 {0.04, 0.04, 0.04, 0.04, 0.04},
\r
200 {0.05, 0.05, 0.052, 0.054, 0.054},
\r
201 {0.054, 0.056, 0.058, 0.059, 0.06}
\r
208 // ReSharper restore IdentifierTypo
\r
209 foreach (var ship in Ships)
\r
211 if (ship.Name == "鬼怒改二")
\r
213 foreach (var item in ship.Slot)
\r
215 switch (item.Spec.Name)
\r
218 level += item.Level;
\r
224 level += item.Level;
\r
229 case "大発動艇(八九式中戦車&陸戦隊)":
\r
230 level += item.Level;
\r
235 level += item.Level;
\r
242 var levelAverage = sum == 0 ? 0.0 : (double)level / sum;
\r
243 bonus = Min(bonus, 0.2);
\r
244 return bonus + 0.01 * bonus * levelAverage + tokudaiBonus[Min(tokudai, 4), Min(daihatsu, 4)];
\r
248 public double TransportPoint => Ships.Where(ship => !ship.Escaped).Sum(ship => ship.TransportPoint);
\r
250 public int CombinedFirepowerBonus
\r
254 switch (CombinedType)
\r
256 case CombinedType.None:
\r
258 case CombinedType.Carrier:
\r
259 return Number == 0 ? 2 : 10;
\r
260 case CombinedType.Surface:
\r
261 return Number == 0 ? 10 : -5;
\r
262 case CombinedType.Transport:
\r
263 return Number == 0 ? -5 : 10;
\r
270 public int CombinedTorpedoPenalty => CombinedType != 0 && Number == 1 ? -5 : 0;
\r