1 // Copyright (C) 2014 Kazuhiro Fujieda <fujieda@users.sourceforge.jp>
\r
3 // This program is part of KancolleSniffer.
\r
5 // KancolleSniffer is free software: you can redistribute it and/or modify
\r
6 // it under the terms of the GNU General Public License as published by
\r
7 // the Free Software Foundation, either version 3 of the License, or
\r
8 // (at your option) any later version.
\r
10 // This program is distributed in the hope that it will be useful,
\r
11 // but WITHOUT ANY WARRANTY; without even the implied warranty of
\r
12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
\r
13 // GNU General Public License for more details.
\r
15 // You should have received a copy of the GNU General Public License
\r
16 // along with this program; if not, see <http://www.gnu.org/licenses/>.
\r
21 namespace KancolleSniffer
\r
23 public class BattleInfo
\r
25 private readonly ShipMaster _shipMaster;
\r
26 private readonly ShipInfo _shipInfo;
\r
27 private readonly ItemInfo _itemInfo;
\r
28 public bool InBattle { get; set; }
\r
29 public string Formation { get; private set; }
\r
30 public int DelayInFormation { get; private set; }
\r
31 public int EnemyAirSuperiority { get; private set; }
\r
32 public int DelayInAirSuperiority { get; private set; }
\r
33 public bool HasDamagedShip { get; set; }
\r
34 public string[] DamagedShipNames { get; private set; }
\r
36 private struct Delay
\r
38 public const int Basic = 4100;
\r
39 public const int Formation = 1100;
\r
40 public const int Tau = 200;
\r
41 public const int SearchAirSuccess = 5700;
\r
42 public const int SearchAirFailure = 4900;
\r
43 public const int SearchSuccess = 5400;
\r
44 public const int Submarine = 2500;
\r
45 public const int Emergence = 2000;
\r
46 public const int AirFight = 8700;
\r
47 public const int AirFightBoth = 2700;
\r
48 public const int Support = 9800;
\r
49 public const int OpeningAttack = 3500;
\r
50 public const int Cutin = 4900;
\r
53 public BattleInfo(ShipMaster shipMaster, ShipInfo shipInfo, ItemInfo itemInfo)
\r
55 _shipMaster = shipMaster;
\r
56 _shipInfo = shipInfo;
\r
57 _itemInfo = itemInfo;
\r
60 public void InspectBattle(dynamic json)
\r
63 Formation = FormationName(json);
\r
64 EnemyAirSuperiority = CalcEnemyAirSuperiority(json);
\r
69 private string FormationName(dynamic json)
\r
71 switch ((int)json.api_formation[2])
\r
85 private void SetDelay(dynamic json)
\r
88 var delay = Delay.Basic;
\r
89 if ((int)json.api_formation[2] >= 3)
\r
91 var subm = (SubmarineFlags)CheckSubmarine(json);
\r
93 delay += SearchDelay(json, out success) + subm.AddtionalDelay;
\r
94 DelayInAirSuperiority = delay + (success ? 0 : Delay.Emergence); // 失敗すると出現が遅れる
\r
96 delay += Delay.Emergence + Delay.Formation + SupportDelay(json) + CutinDelay(json);
\r
97 if (!subm.PreventAirFight)
\r
98 delay += AirFightDelay(json);
\r
99 if (!subm.PreventOpeningAttack)
\r
100 delay += OpeningAttackDelay(json);
\r
101 DelayInFormation = delay;
\r
104 private int SearchDelay(dynamic json, out bool success)
\r
107 switch ((int)json.api_search[0])
\r
109 case 1: // 索敵機による索敵成功
\r
110 case 2: // 索敵機未帰還あり
\r
112 return Delay.SearchAirSuccess;
\r
114 case 4: // 索敵機による索敵失敗
\r
115 return Delay.SearchAirFailure;
\r
116 case 5: // 索敵力による索敵成功
\r
118 return Delay.SearchSuccess;
\r
123 private int OpeningAttackDelay(dynamic json)
\r
125 return json.api_opening_flag == 1 ? Delay.OpeningAttack : 0;
\r
128 private class SubmarineFlags
\r
130 public bool[] Friend;
\r
131 public bool[] Enemy;
\r
133 public int AddtionalDelay
\r
135 get { return Friend.Any(x => x) || Enemy.Any(x => x) ? Delay.Submarine : 0; }
\r
139 public bool PreventAirFight
\r
141 get { return Friend.All(x => x) || Enemy.All(x => x); } // 一方がすべて潜水艦
\r
144 public bool PreventOpeningAttack
\r
146 get { return Friend.All(x => x) && Enemy.All(x => x); } // 双方すべて潜水艦
\r
150 private SubmarineFlags CheckSubmarine(dynamic json)
\r
152 return new SubmarineFlags
\r
154 Friend = (from status in _shipInfo.GetShipStatuses((int)json.api_dock_id - 1)
\r
155 select _shipMaster[status.ShipId].IsSubmarine).ToArray(),
\r
156 Enemy = (from id in (int[])json.api_ship_ke where id != -1 select _shipMaster[id].IsSubmarine).ToArray()
\r
160 private int AirFightDelay(dynamic json)
\r
162 // 僚艦が偵察機だけだと航空戦に参加しないので、
\r
163 // 艦載機の配備ではなく遭遇戦の状況を調べる。
\r
164 var f = json.api_kouku.api_stage1.api_f_count > 0;
\r
165 var e = json.api_kouku.api_stage1.api_e_count > 0;
\r
167 if (f || e) // 艦載機発艦
\r
168 result += Delay.AirFight;
\r
169 if (f && e) // 双方とも
\r
170 result += Delay.AirFightBoth;
\r
174 private int SupportDelay(dynamic json)
\r
176 return json.api_support_flag() && json.api_support_flag == 1 ? Delay.Support : 0;
\r
179 private int CutinDelay(dynamic json)
\r
182 var maxHps = (int[])json.api_nowhps;
\r
183 var nowHps = (int[])json.api_nowhps;
\r
184 var planeFrom = (int[][])json.api_kouku.api_plane_from;
\r
185 if ((planeFrom[0][0] != -1 || planeFrom[1][0] != -1) && // どちらかに艦載機あり
\r
186 json.api_kouku.api_stage3 != null) // 敵艦載機が全滅しているとnull
\r
189 var damages = (int[])json.api_kouku.api_stage3.api_fdam;
\r
190 var newHps = nowHps.Zip(damages, (hp, dmg) => hp - dmg).ToArray();
\r
191 if (IsCutinShown(nowHps, newHps, maxHps))
\r
195 if ((int)json.api_opening_flag != 0)
\r
198 var damages = (int[])json.api_opening_atack.api_fdam;
\r
199 var newHps = nowHps.Zip(damages, (hp, dmg) => hp - dmg).ToArray();
\r
200 if (IsCutinShown(nowHps, newHps, maxHps))
\r
203 return Delay.Cutin * cutin;
\r
206 private bool IsCutinShown(int[] nowHps, int[] newHps, int[] maxHps)
\r
208 for (var i = 1; i < ShipInfo.MemberCount + 1; i++)
\r
210 if (ShipStatus.CalcDamage(nowHps[i], maxHps[i]) <= ShipStatus.Damage.Small &&
\r
211 ShipStatus.CalcDamage(newHps[i], maxHps[i]) >= ShipStatus.Damage.Half)
\r
217 private int CalcEnemyAirSuperiority(dynamic json)
\r
219 var maxEq = ((int[])json.api_ship_ke).Skip(1).SelectMany(id => _shipMaster[id].MaxEq);
\r
220 var equips = ((int[][])json.api_eSlot).SelectMany(x => x);
\r
221 return (from slot in equips.Zip(maxEq, (id, max) => new {id, max})
\r
222 select (int)Math.Floor(_itemInfo.GetSpecByItemId(slot.id).TyKu * Math.Sqrt(slot.max))).Sum();
\r
225 private void CauseDamage(dynamic json)
\r
227 var ships = _shipInfo.GetShipStatuses((int)json.api_dock_id - 1);
\r
228 if (json.api_kouku.api_stage3 != null)
\r
229 CauseSimpleDamage(ships, json.api_kouku.api_stage3.api_fdam);
\r
230 if (json.api_opening_atack != null)
\r
231 CauseSimpleDamage(ships, json.api_opening_atack.api_fdam);
\r
232 if (json.api_hougeki1 != null)
\r
233 CauseHougekiDamage(ships, json.api_hougeki1.api_df_list, json.api_hougeki1.api_damage);
\r
234 if (json.api_hougeki2 != null)
\r
235 CauseHougekiDamage(ships, json.api_hougeki2.api_df_list, json.api_hougeki2.api_damage);
\r
236 if (json.api_raigeki != null)
\r
237 CauseSimpleDamage(ships, json.api_raigeki.api_fdam);
\r
239 (from ship in ships where ship.DamageLevel == ShipStatus.Damage.Badly select ship.Name).ToArray();
\r
240 HasDamagedShip = DamagedShipNames.Any();
\r
243 private void CauseSimpleDamage(ShipStatus[] ships, dynamic rawDamage)
\r
245 var damage = (int[])rawDamage;
\r
246 for (var i = 0; i < ships.Length; i++)
\r
247 ships[i].NowHp -= damage[i + 1];
\r
250 private void CauseHougekiDamage(ShipStatus[] ships, dynamic rawTergets, dynamic rawDamages)
\r
252 var targets = ((dynamic[])rawTergets).Skip(1).SelectMany(x => (int[])x);
\r
253 var damages = ((dynamic[])rawDamages).Skip(1).SelectMany(x => (double[])x);
\r
254 foreach (var hit in targets.Zip(damages, (t, d) => new {t, d}))
\r
256 if (hit.t - 1 < ships.Length)
\r
257 ships[hit.t - 1].NowHp -= (int)hit.d;
\r