1 // Copyright (C) 2017 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
\r
22 public class ShipStatus : ICloneable
\r
24 public int Id { get; set; }
\r
25 public int Fleet { get; set; }
\r
26 public ShipSpec Spec { get; set; }
\r
28 public string Name => Spec.Name;
\r
30 public int Level { get; set; }
\r
31 public int ExpToNext { get; set; }
\r
32 public int MaxHp { get; set; }
\r
33 public int NowHp { get; set; }
\r
34 public int Cond { get; set; }
\r
35 public int Fuel { get; set; }
\r
36 public int Bull { get; set; }
\r
37 public int[] OnSlot { get; set; }
\r
38 public ItemStatus[] Slot { get; set; }
\r
39 public ItemStatus SlotEx { get; set; }
\r
40 public int NdockTime { get; set; }
\r
41 public int[] NdockItem { get; set; }
\r
42 public int LoS { get; set; }
\r
43 public int Firepower { get; set; }
\r
44 public int Torpedo { get; set; }
\r
45 public int AntiSubmarine { get; set; }
\r
46 public int AntiAir { get; set; }
\r
47 public int Lucky { get; set; }
\r
48 public bool Locked { get; set; }
\r
49 public bool Escaped { get; set; }
\r
51 public Damage DamageLevel => CalcDamage(NowHp, MaxHp);
\r
53 public int CombinedFleetType { get; set; }
\r
55 public IEnumerable<ItemStatus> AllSlot => SlotEx.Id == 0 ? Slot : Slot.Concat(new[] {SlotEx});
\r
61 Spec = new ShipSpec();
\r
62 OnSlot = new int[0];
\r
63 Slot = new ItemStatus[0];
\r
64 SlotEx = new ItemStatus();
\r
76 public static Damage CalcDamage(int now, int max)
\r
78 if (now == 0 && max > 0)
\r
80 var ratio = max == 0 ? 1 : (double)now / max;
\r
81 return ratio > 0.75 ? Damage.Minor : ratio > 0.5 ? Damage.Small : ratio > 0.25 ? Damage.Half : Damage.Badly;
\r
84 public TimeSpan RepairTime => TimeSpan.FromSeconds((int)(RepairTimePerHp.TotalSeconds * (MaxHp - NowHp)) + 30);
\r
86 public TimeSpan RepairTimePerHp =>
\r
87 TimeSpan.FromSeconds(Spec.RepairWeight *
\r
90 : Level * 5 + Floor(Sqrt(Level - 11)) * 10 + 50));
\r
92 public double EffectiveFirepower
\r
96 if (Spec.IsSubmarine)
\r
98 var isRyuseiAttack = Spec.Id == 352 && // 速吸改
\r
99 Slot.Any(item => item.Spec.Type == 8); // 艦攻装備
\r
100 var levelBonus = AllSlot.Sum(item => item.FirePowerLevelBonus);
\r
101 if (!Spec.IsAircraftCarrier && !isRyuseiAttack)
\r
102 return Firepower + levelBonus + CombinedFleetFirepowerBonus + 5;
\r
103 var specs = (from item in Slot where item.Spec.IsAircraft select item.Spec).ToArray();
\r
104 var torpedo = specs.Sum(s => s.Torpedo);
\r
105 var bomber = specs.Sum(s => s.Bomber);
\r
106 if (torpedo == 0 && bomber == 0)
\r
108 return (int)((Firepower + torpedo + levelBonus +
\r
109 (int)(bomber * 1.3) + CombinedFleetFirepowerBonus) * 1.5) + 55;
\r
113 private int CombinedFleetFirepowerBonus
\r
117 switch (CombinedFleetType)
\r
122 return Fleet == 0 ? 2 : 10;
\r
124 return Fleet == 0 ? 10 : -5;
\r
126 return Fleet == 0 ? -5 : 10;
\r
133 public double EffectiveTorpedo
\r
137 if (Spec.IsAircraftCarrier || Torpedo == 0)
\r
139 return Torpedo + AllSlot.Sum(item => item.TorpedoLevelBonus) + CombinedFleetTorpedoPenalty + 5;
\r
143 private int CombinedFleetTorpedoPenalty => CombinedFleetType > 0 && Fleet == 1 ? -5 : 0;
\r
145 public double EffectiveAntiSubmarine
\r
149 if (!Spec.IsAntiSubmarine)
\r
151 // ReSharper disable once CompareOfFloatsByEqualityOperator
\r
152 if (Spec.IsAircraftCarrier && EffectiveFirepower == 0) // 砲撃戦に参加しない
\r
155 var projector = false;
\r
156 var depthCharge = false;
\r
157 var aircraft = false;
\r
159 var vanilla = AntiSubmarine;
\r
160 foreach (var spec in Slot.Select(item => item.Spec))
\r
162 vanilla -= spec.AntiSubmarine;
\r
167 else if (spec.IsDepthCharge)
\r
169 if (spec.Name.EndsWith("投射機"))
\r
171 if (spec.Name.EndsWith("爆雷"))
\r
172 depthCharge = true;
\r
174 else if (spec.IsAircraft)
\r
178 all += spec.EffectiveAntiSubmarine;
\r
180 if (vanilla == 0 && !aircraft) // 素対潜0で航空機なしは対潜攻撃なし
\r
183 if (sonar && projector)
\r
185 if (sonar && depthCharge)
\r
187 if (projector && depthCharge)
\r
189 if (sonar && projector && depthCharge)
\r
190 bonus = 1.15 * 1.25;
\r
191 var levelBonus = Slot.Sum(item => item.AntiSubmarineLevelBonus);
\r
192 return bonus * (Sqrt(vanilla) * 2 + all * 1.5 + levelBonus + (aircraft ? 8 : 13));
\r
196 public bool CanOpeningAntiSubmarineAttack =>
\r
197 Spec.Id == 141 || Slot.Any(item => item.Spec.IsSonar) &&
\r
198 (AntiSubmarine >= 100 || Spec.ShipType == 1 && AntiSubmarine >= 60);
\r
200 public double NightBattlePower
\r
204 if (Spec.IsAircraftCarrier && Spec.Id != 353 && Spec.Id != 432) // Graf Zeppelin以外の空母
\r
206 return Firepower + Torpedo + Slot.Sum(item => item.NightBattleLevelBonus);
\r
210 public int PreparedDamageControl =>
\r
211 DamageLevel != Damage.Badly
\r
213 : SlotEx.Spec.Id == 42 || SlotEx.Spec.Id == 43
\r
215 : Slot.FirstOrDefault(item => item.Spec.Id == 42 || item.Spec.Id == 43)?.Spec.Id ?? -1;
\r
217 public double TransportPoint
\r
218 => Spec.TransportPoint + AllSlot.Sum(item => item.Spec.TransportPoint);
\r
220 public int EffectiveAntiAirForShip
\r
224 if (AllSlot.All(item => item.Id == -1 || item.Id == 0))
\r
226 var vanilla = AntiAir - AllSlot.Sum(item => item.Spec.AntiAir);
\r
227 var x = vanilla + AllSlot.Sum(item => item.EffectiveAntiAirForShip);
\r
228 return (int)(x / 2) * 2;
\r
232 public int EffectiveAntiAirForFleet => (int)AllSlot.Sum(item => item.EffectiveAntiAirForFleet);
\r
234 public object Clone()
\r
236 var r = (ShipStatus)MemberwiseClone();
\r
237 r.Slot = r.Slot.ToArray(); // 戦闘中のダメコンの消費が見えないように複製する
\r