X-Git-Url: http://git.osdn.net/view?a=blobdiff_plain;f=KancolleSniffer%2FBattleInfo.cs;h=8f9d53fbd62a816fcd7be57aa3c7deb90b802911;hb=460df58348e0c1f027a1e3f64358c4c161ff9590;hp=d93be51287abfc8511db5999d154e78e03911fcd;hpb=a4bb5b32c97e8818031851f225ff46b8c977f6ac;p=kancollesniffer%2FKancolleSniffer.git diff --git a/KancolleSniffer/BattleInfo.cs b/KancolleSniffer/BattleInfo.cs index d93be51..8f9d53f 100644 --- a/KancolleSniffer/BattleInfo.cs +++ b/KancolleSniffer/BattleInfo.cs @@ -12,6 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. +using System; using System.Collections.Generic; using System.Linq; using static System.Math; @@ -26,7 +27,24 @@ namespace KancolleSniffer B, C, D, - E, + E + } + + public enum BattleState + { + None, + Day, + Night, + Result, + Unknown + } + + public class EnemyFighterPower + { + public bool HasUnknown { get; set; } + public string UnknownMark => HasUnknown ? "+" : ""; + public int AirCombat { get; set; } + public int Interception { get; set; } } public class BattleInfo @@ -36,126 +54,119 @@ namespace KancolleSniffer private int _fleet; private Record[] _friend; private Record[] _guard; - private int[] _enemyHp; - private int[] _enemyGuardHp; - private int[] _enemyStartHp; - private int[] _enemyGuardStartHp; + private Record[] _enemy; + private Record[] _enemyGuard; private readonly List _escapingShips = new List(); - private int _flagshipRecoveryType; + private bool _lastCell; - public bool InBattle { get; set; } - public string Formation { get; private set; } - public string EnemyFighterPower { get; private set; } + public BattleState BattleState { get; set; } + public int[] Formation { get; private set; } + public int[] FighterPower { get; private set; } + public EnemyFighterPower EnemyFighterPower { get; private set; } public int AirControlLevel { get; private set; } public BattleResultRank ResultRank { get; private set; } - public ShipStatus[] EnemyResultStatus { get; private set; } + public RankPair DisplayedResultRank { get; } = new RankPair(); + public BattleResult Result { get; set; } + public bool EnemyIsCombined => _enemyGuard.Length > 0; + public List AirBattleResults { get; } = new List(); - public BattleInfo(ShipInfo shipInfo, ItemInfo itemInfo) + public class RankPair { - _shipInfo = shipInfo; - _itemInfo = itemInfo; + public char Assumed { get; set; } + public char Actual { get; set; } + public bool IsError => Assumed != Actual; } - public void InspectBattle(dynamic json, string url) + public class BattleResult { - InBattle = true; - Formation = FormationName(json); - EnemyFighterPower = CalcEnemyFighterPower(json); - AirControlLevel = CheckAirControlLevel(json); - ShowResult(false); // 昼戦の結果を夜戦のときに表示する - SetupResult(json); - if (IsNightBattle(json)) - { - CalcHougekiDamage(json.api_hougeki, - _guard.Length > 0 ? _guard : _friend, - json.api_active_deck() && json.api_active_deck[1] != 1 ? _enemyGuardHp : _enemyHp); - } - else + public class Combined { - CalcDamage(json, url.EndsWith("battle_water")); + public ShipStatus[] Main { get; set; } + public ShipStatus[] Guard { get; set; } } - ClearEnemyOverKill(); - ResultRank = url.EndsWith("ld_airbattle") ? CalcLdAirBattleRank() : CalcResultRank(); + + public Combined Friend { get; set; } + public Combined Enemy { get; set; } } - private void ClearEnemyOverKill() + public BattleInfo(ShipInfo shipInfo, ItemInfo itemInfo) { - _enemyHp = _enemyHp.Select(hp => hp < 0 ? 0 : hp).ToArray(); - _enemyGuardHp = _enemyGuardHp.Select(hp => hp < 0 ? 0 : hp).ToArray(); + _shipInfo = shipInfo; + _itemInfo = itemInfo; } - public void InspectMapNext(string request) + public void InspectBattle(string url, string request, dynamic json) { - var type = HttpUtility.ParseQueryString(request)["api_recovery_type"]; - if (type == null) - return; - _flagshipRecoveryType = int.Parse(type); + if (json.api_formation()) + Formation = ((dynamic[])json.api_formation).Select(f => f is string ? (int)int.Parse(f) : (int)f) + .ToArray(); + AirControlLevel = CheckAirControlLevel(json); + ShowResult(false); // 昼戦の結果を夜戦のときに表示する + SetupResult(request, json, url.Contains("practice")); + FighterPower = CalcFighterPower(); + EnemyFighterPower = CalcEnemyFighterPower(json); + BattleState = IsNightBattle(json) ? BattleState.Night : BattleState.Day; + CalcDamage(json); + ResultRank = url.EndsWith("ld_airbattle") ? CalcLdAirBattleRank() : CalcResultRank(); + SetResult(); } private bool IsNightBattle(dynamic json) => json.api_hougeki(); - private int DeckId(dynamic json) + public static int DeckId(dynamic json) { if (json.api_dock_id()) // 昼戦はtypoしている return (int)json.api_dock_id - 1; - if (json.api_deck_id is string) // 通常の夜戦では文字列 + if (json.api_deck_id is string) // 通常の夜戦と連合艦隊(味方のみ)では文字列 return int.Parse(json.api_deck_id) - 1; return (int)json.api_deck_id - 1; } - private string FormationName(dynamic json) - { - if (!json.api_formation()) // 演習の夜戦 - return ""; - switch ((int)json.api_formation[2]) - { - case 1: - return "同航戦"; - case 2: - return "反航戦"; - case 3: - return "T字有利"; - case 4: - return "T字不利"; - } - return ""; - } - - private void SetupResult(dynamic json) + private void SetupResult(string request, dynamic json, bool practice) { if (_friend != null) return; - var nowhps = (int[])json.api_nowhps; + _shipInfo.SaveBattleStartStatus(); _fleet = DeckId(json); var fstats = _shipInfo.GetShipStatuses(_fleet); - FlagshipRecovery(fstats[0]); - _friend = Record.Setup(fstats); - _enemyHp = nowhps.Skip(7).TakeWhile(hp => hp != -1).ToArray(); - _enemyStartHp = (int[])_enemyHp.Clone(); - EnemyResultStatus = - (from id in (int[])json.api_ship_ke - where id != -1 - select new ShipStatus {Id = id, Spec = _shipInfo.GetSpec(id)}).ToArray(); - _guard = new Record[0]; - _enemyGuardHp = new int[0]; - _enemyGuardStartHp = new int[0]; - if (!json.api_nowhps_combined()) - return; - var combined = (int[])json.api_nowhps_combined; - if (combined[1] != -1) // 味方が連合艦隊 - _guard = Record.Setup(_shipInfo.GetShipStatuses(1)); - if (combined.Length > 7) // 敵が連合艦隊 + FlagshipRecovery(request, fstats[0]); + _friend = Record.Setup(fstats, practice); + _guard = json.api_f_nowhps_combined() + ? Record.Setup(_shipInfo.GetShipStatuses(1), practice) + : new Record[0]; + _enemy = Record.Setup((int[])json.api_e_nowhps, + ((int[])json.api_ship_ke).Select(_shipInfo.GetSpec).ToArray(), + ((int[][])json.api_eSlot).Select(slot => slot.Select(_itemInfo.GetSpecByItemId).ToArray()).ToArray(), + practice); + _enemyGuard = json.api_ship_ke_combined() + ? Record.Setup((int[])json.api_e_nowhps_combined, + ((int[])json.api_ship_ke_combined).Select(_shipInfo.GetSpec).ToArray(), + ((int[][])json.api_eSlot).Select(slot => slot.Select(_itemInfo.GetSpecByItemId).ToArray()) + .ToArray(), practice) + : new Record[0]; + } + + private void SetResult() + { + Result = new BattleResult { - _enemyGuardHp = - ((int[])json.api_nowhps_combined). - Skip(7).TakeWhile(hp => hp != -1).ToArray(); - _enemyGuardStartHp = (int[])_enemyGuardHp.Clone(); - } + Friend = new BattleResult.Combined + { + Main = _friend.Select(r => r.SnapShot).ToArray(), + Guard = _guard.Select(r => r.SnapShot).ToArray() + }, + Enemy = new BattleResult.Combined + { + Main = _enemy.Select(r => r.SnapShot).ToArray(), + Guard = _enemyGuard.Select(r => r.SnapShot).ToArray() + } + }; } - private void FlagshipRecovery(ShipStatus flagship) + private void FlagshipRecovery(string request, ShipStatus flagship) { - switch (_flagshipRecoveryType) + var type = int.Parse(HttpUtility.ParseQueryString(request)["api_recovery_type"] ?? "0"); + switch (type) { case 0: return; @@ -168,9 +179,8 @@ namespace KancolleSniffer ConsumeSlotItem(flagship, 43); // 女神 break; } - if (_flagshipRecoveryType != 0) + if (type != 0) _shipInfo.SetBadlyDamagedShips(); - _flagshipRecoveryType = 0; } private static void ConsumeSlotItem(ShipStatus ship, int id) @@ -193,6 +203,7 @@ namespace KancolleSniffer public void CleanupResult() { _friend = null; + _lastCell = false; } private int CheckAirControlLevel(dynamic json) @@ -207,110 +218,120 @@ namespace KancolleSniffer return (int)stage1.api_disp_seiku; } - private string CalcEnemyFighterPower(dynamic json) + private int[] CalcFighterPower() { - var missing = ""; - var maxEq = ((int[])json.api_ship_ke).Skip(1).SelectMany(id => + if (_guard.Length > 0 && _enemyGuard.Length > 0) + return _shipInfo.GetFighterPower(0).Zip(_shipInfo.GetFighterPower(1), (a, b) => a + b).ToArray(); + return _shipInfo.GetFighterPower(_fleet); + } + + private EnemyFighterPower CalcEnemyFighterPower(dynamic json) + { + var result = new EnemyFighterPower(); + var ships = (int[])json.api_ship_ke; + if (json.api_ship_ke_combined() && _guard.Length > 0) + ships = ships.Concat((int[])json.api_ship_ke_combined).ToArray(); + var maxEq = ships.SelectMany(id => { var r = _shipInfo.GetSpec(id).MaxEq; if (r != null) return r; - missing = "+"; + result.HasUnknown = true; return new int[5]; - }).ToArray(); + }); var equips = ((int[][])json.api_eSlot).SelectMany(x => x); - return (from slot in equips.Zip(maxEq, (id, max) => new {id, max}) - let spec = _itemInfo.GetSpecByItemId(slot.id) - where spec.CanAirCombat - select (int)Floor(spec.AntiAir * Sqrt(slot.max))).DefaultIfEmpty().Sum() + missing; - } - - private void CalcDamage(dynamic json, bool surfaceFleet = false) - { - var fc = _guard.Length > 0; - var ec = _enemyGuardHp.Length > 0; - var both = fc && ec; - if (json.api_air_base_attack()) - CalcAirBaseAttackDamage(json.api_air_base_attack); - if (json.api_kouku.api_stage3 != null) - CalcSimpleDamage(json.api_kouku.api_stage3, _friend, _enemyHp); - if (json.api_kouku.api_stage3_combined() && json.api_kouku.api_stage3_combined != null) - CalcSimpleDamage(json.api_kouku.api_stage3_combined, _guard, _enemyGuardHp); - if (json.api_kouku2()) // 航空戦2回目 - { - if (json.api_kouku2.api_stage3 != null) - CalcSimpleDamage(json.api_kouku2.api_stage3, _friend, _enemyHp); - if (json.api_kouku2.api_stage3_combined() && json.api_kouku2.api_stage3_combined != null) - CalcSimpleDamage(json.api_kouku2.api_stage3_combined, _guard, _enemyGuardHp); - } - if (!json.api_opening_atack()) // 航空戦のみ - return; - if (json.api_support_info() && json.api_support_info != null) - CalcSupportDamage(json.api_support_info); - if (json.api_opening_taisen() && json.api_opening_taisen != null) + if (json.api_eSlot_combined() && _guard.Length > 0) + equips = equips.Concat(((int[][])json.api_eSlot_combined).SelectMany(x => x)); + foreach (var entry in from slot in equips.Zip(maxEq, (id, max) => new {id, max}) + let spec = _itemInfo.GetSpecByItemId(slot.id) + let perSlot = (int)Floor(spec.AntiAir * Sqrt(slot.max)) + select new {spec, perSlot}) { - CalcHougekiDamage(json.api_opening_taisen, - fc ? _guard : _friend, // 先制対潜攻撃の対象は護衛(たぶん) - _enemyHp); + if (entry.spec.CanAirCombat) + result.AirCombat += entry.perSlot; + if (entry.spec.IsAircraft) + result.Interception += entry.perSlot; } - if (json.api_opening_atack != null) - { - if (both) - { - CalcSimpleDamage(json.api_opening_atack, _friend, _guard, _enemyHp, _enemyGuardHp); - } - else - { - CalcSimpleDamage(json.api_opening_atack, - fc ? _guard : _friend, // 雷撃の対象は護衛 - _enemyHp, _enemyGuardHp); - } - } - if (json.api_hougeki1() && json.api_hougeki1 != null) - { - if (json.api_hougeki1.api_at_eflag()) - { - CalcCombinedHougekiDamage(json.api_hougeki1, _friend, _guard, _enemyHp, _enemyGuardHp); - } - else - { - CalcHougekiDamage(json.api_hougeki1, - fc && !surfaceFleet ? _guard : _friend, // 空母機動部隊は一巡目が護衛 - ec ? _enemyGuardHp : _enemyHp); // 敵連合艦隊は一巡目が護衛 - } - } - if (json.api_hougeki2() && json.api_hougeki2 != null) + return result; + } + + private enum CombatType + { + AtOnce, + ByTurn, + Support, + Aircraft, + AirBase, + Friend + } + + private class Phase + { + public string Api { get; } + public CombatType Type { get; } + public string Name { get; } + + public Phase(string api, CombatType type, string name = "") { - if (json.api_hougeki2.api_at_eflag()) - CalcCombinedHougekiDamage(json.api_hougeki2, _friend, _guard, _enemyHp, _enemyGuardHp); - else - CalcHougekiDamage(json.api_hougeki2, _friend, _enemyHp); + Api = api; + Type = type; + Name = name; } - if (json.api_hougeki3() && json.api_hougeki3 != null) + } + + private void CalcDamage(dynamic json) + { + AirBattleResults.Clear(); + var phases = new[] { - if (json.api_hougeki3.api_at_eflag()) - { - CalcCombinedHougekiDamage(json.api_hougeki3, _friend, _guard, _enemyHp, _enemyGuardHp); - } - else - { - CalcHougekiDamage(json.api_hougeki3, - fc && surfaceFleet ? _guard : _friend, // 水上打撃部隊は三順目が護衛 - _enemyHp); - } - } - if (json.api_raigeki() && json.api_raigeki != null) + new Phase("air_base_injection", CombatType.Aircraft, "AB噴式"), + new Phase("injection_kouku", CombatType.Aircraft, "噴式"), + new Phase("air_base_attack", CombatType.AirBase), + new Phase("n_support_info", CombatType.Support), + new Phase("n_hougeki1", CombatType.ByTurn), + new Phase("n_hougeki2", CombatType.ByTurn), + new Phase("kouku", CombatType.Aircraft, "航空戦"), + new Phase("kouku2", CombatType.Aircraft, "航空戦2"), + new Phase("support_info", CombatType.Support), + new Phase("opening_taisen", CombatType.ByTurn), + new Phase("opening_atack", CombatType.AtOnce), + new Phase("friendly_battle", CombatType.Friend), + new Phase("hougeki", CombatType.ByTurn), + new Phase("hougeki1", CombatType.ByTurn), + new Phase("hougeki2", CombatType.ByTurn), + new Phase("hougeki3", CombatType.ByTurn), + new Phase("raigeki", CombatType.AtOnce) + }; + foreach (var phase in phases) + CalcDamageByType(json, phase); + } + + private void CalcDamageByType(dynamic json, Phase phase) + { + var api = "api_" + phase.Api; + if (!json.IsDefined(api) || json[api] == null) + return; + switch (phase.Type) { - if (both) - { - CalcSimpleDamage(json.api_raigeki, _friend, _guard, _enemyHp, _enemyGuardHp); - } - else - { - CalcSimpleDamage(json.api_raigeki, - fc ? _guard : _friend, // 雷撃の対象は護衛 - _enemyHp, _enemyGuardHp); - } + case CombatType.AtOnce: + CalcDamageAtOnce(json[api]); + break; + case CombatType.ByTurn: + CalcDamageByTurn(json[api]); + break; + case CombatType.Support: + CalcSupportDamage(json[api]); + break; + case CombatType.Aircraft: + AddAirBattleResult(json[api], phase.Name); + CalcKoukuDamage(json[api]); + break; + case CombatType.AirBase: + CalcAirBaseAttackDamage(json[api]); + break; + case CombatType.Friend: + CalcFriendAttackDamage(json[api]); + break; } } @@ -318,137 +339,179 @@ namespace KancolleSniffer { if (json.api_support_hourai != null) { - CalcSimpleDamage(json.api_support_hourai.api_damage, _enemyHp, _enemyGuardHp); + CalcRawDamageAtOnce(json.api_support_hourai.api_damage, _enemy, _enemyGuard); } else if (json.api_support_airatack != null) { - var airattack = json.api_support_airatack; - if (airattack.api_stage3 != null) - CalcSimpleDamage(airattack.api_stage3.api_edam, _enemyHp); - if (airattack.api_stage3_combined() && airattack.api_stage3_combined != null) - CalcSimpleDamage(airattack.api_stage3_combined.api_edam, _enemyGuardHp); + CalcRawDamageAtOnce(json.api_support_airatack.api_stage3.api_edam, _enemy, _enemyGuard); } } private void CalcAirBaseAttackDamage(dynamic json) { + var i = 1; foreach (var entry in json) { - if (!entry.api_stage3() || entry.api_stage3 == null) - continue; - CalcSimpleDamage(entry.api_stage3.api_edam, _enemyHp); - if (entry.api_stage3_combined()) - CalcSimpleDamage(entry.api_stage3_combined.api_edam, _enemyGuardHp); + AddAirBattleResult(entry, "基地" + i++); + CalcKoukuDamage(entry); } } - private void CalcSimpleDamage(dynamic json, Record[] friend, int[] enemy) + private void CalcFriendAttackDamage(dynamic json) { - if (json.api_fdam()) - CalcSimpleDamage(json.api_fdam, friend); - if (json.api_edam()) - CalcSimpleDamage(json.api_edam, enemy); + CalcDamageByTurn(json.api_hougeki, true); } - private void CalcSimpleDamage(dynamic json, Record[] friend, int[] enemy, int[] enemyGuard) + private void AddAirBattleResult(dynamic json, string phaseName) { - CalcSimpleDamage(json.api_fdam, friend); - CalcSimpleDamage(json.api_edam, enemy, enemyGuard); + var stage1 = json.api_stage1; + if (stage1 == null || (stage1.api_f_count == 0 && stage1.api_e_count == 0)) + return; + var result = new AirBattleResult + { + PhaseName = phaseName, + AirControlLevel = json.api_stage1.api_disp_seiku() ? (int)json.api_stage1.api_disp_seiku : 0, + Stage1 = new AirBattleResult.StageResult + { + FriendCount = (int)json.api_stage1.api_f_count, + FriendLost = (int)json.api_stage1.api_f_lostcount, + EnemyCount = (int)json.api_stage1.api_e_count, + EnemyLost = (int)json.api_stage1.api_e_lostcount + }, + Stage2 = json.api_stage2 == null + ? new AirBattleResult.StageResult + { + FriendCount = 0, + FriendLost = 0, + EnemyCount = 0, + EnemyLost = 0 + } + : new AirBattleResult.StageResult + { + FriendCount = (int)json.api_stage2.api_f_count, + FriendLost = (int)json.api_stage2.api_f_lostcount, + EnemyCount = (int)json.api_stage2.api_e_count, + EnemyLost = (int)json.api_stage2.api_e_lostcount + } + }; + if (json.api_stage2 != null && json.api_stage2.api_air_fire()) + { + var airfire = json.api_stage2.api_air_fire; + var idx = (int)airfire.api_idx; + result.AirFire = new AirBattleResult.AirFireResult + { + ShipName = idx < _friend.Length ? _friend[idx].Name : _guard[idx - 6].Name, + Kind = (int)airfire.api_kind, + Items = ((int[])airfire.api_use_items).Select(id => _itemInfo.GetSpecByItemId(id).Name).ToArray() + }; + } + AirBattleResults.Add(result); } - private void CalcSimpleDamage(dynamic json, Record[] friend, Record[] guard, int[] enemy, int[] enemyGuard) + private void CalcKoukuDamage(dynamic json) { - CalcSimpleDamage(json.api_fdam, friend, guard); - CalcSimpleDamage(json.api_edam, enemy, enemyGuard); + if (json.api_stage3() && json.api_stage3 != null) + CalcDamageAtOnce(json.api_stage3, _friend, _enemy); + if (json.api_stage3_combined() && json.api_stage3_combined != null) + CalcDamageAtOnce(json.api_stage3_combined, _guard, _enemyGuard); } - private void CalcSimpleDamage(dynamic rawDamage, Record[] friend, Record[] guard) + private void CalcDamageAtOnce(dynamic json) { - var damage = (int[])rawDamage; - for (var i = 0; i < friend.Length; i++) - friend[i].ApplyDamage(damage[i + 1]); - for (var i = 0; i < guard.Length; i++) - friend[i].ApplyDamage(damage[i + 6 + 1]); + CalcDamageAtOnce(json, _friend, _guard, _enemy, _enemyGuard); } - private void CalcSimpleDamage(dynamic rawDamage, Record[] friend) + private void CalcDamageAtOnce(dynamic json, Record[] friend, Record[] enemy) { - var damage = (int[])rawDamage; - for (var i = 0; i < friend.Length; i++) - friend[i].ApplyDamage(damage[i + 1]); + CalcDamageAtOnce(json, friend, null, enemy, null); } - private void CalcSimpleDamage(dynamic rawDamage, int[] enemy, int[] enemyGuard) + private void CalcDamageAtOnce(dynamic json, + Record[] friend, Record[] guard, Record[] enemy, Record[] enemyGuard) { - var damage = (int[])rawDamage; - for (var i = 0; i < enemy.Length; i++) - enemy[i] -= damage[i + 1]; - for (var i = 0; i < enemyGuard.Length; i++) - enemyGuard[i] -= damage[i + 6 + 1]; + if (json.api_fdam() && json.api_fdam != null) + CalcRawDamageAtOnce(json.api_fdam, friend, guard); + if (json.api_edam() && json.api_edam != null) + CalcRawDamageAtOnce(json.api_edam, enemy, enemyGuard); } - private void CalcSimpleDamage(dynamic rawDamage, int[] result) + private void CalcRawDamageAtOnce(dynamic rawDamage, Record[] friend, Record[] guard = null) { var damage = (int[])rawDamage; - for (var i = 0; i < result.Length; i++) - result[i] -= damage[i + 1]; + for (var i = 0; i < friend.Length; i++) + friend[i].ApplyDamage(damage[i]); + if (guard == null) + return; + for (var i = 0; i < guard.Length; i++) + guard[i].ApplyDamage(damage[i + 6]); } - private void CalcHougekiDamage(dynamic hougeki, Record[] friend, int[] enemy) + private void CalcDamageByTurn(dynamic json, bool ignoreFriendDamage = false) { - var targets = ((dynamic[])hougeki.api_df_list).Skip(1).SelectMany(x => (int[])x); - var damages = ((dynamic[])hougeki.api_damage).Skip(1).SelectMany(x => (int[])x); - foreach (var hit in targets.Zip(damages, (t, d) => new {t, d})) + if (!(json.api_df_list() && json.api_df_list != null && + json.api_damage() && json.api_damage != null && + json.api_at_eflag() && json.api_at_eflag != null)) + return; + + var targets = (int[][])json.api_df_list; + var damages = (int[][])json.api_damage; + var eflags = (int[])json.api_at_eflag; + var records = new[] {new Record[12], new Record[12]}; + Array.Copy(_friend, records[1], _friend.Length); + Array.Copy(_guard, 0, records[1], 6, _guard.Length); + Array.Copy(_enemy, records[0], _enemy.Length); + Array.Copy(_enemyGuard, 0, records[0], 6, _enemyGuard.Length); + for (var i = 0; i < eflags.Length; i++) { + // 一度に複数の目標を狙う攻撃はないものと仮定する + var hit = new {t = targets[i][0], d = damages[i].Sum(d => d >= 0 ? d : 0)}; if (hit.t == -1) continue; - if (hit.t <= 6) - friend[hit.t - 1].ApplyDamage(hit.d); - else - enemy[(hit.t - 1) % 6] -= hit.d; + if (ignoreFriendDamage && eflags[i] == 1) + continue; + records[eflags[i]][hit.t].ApplyDamage(hit.d); } } - private void CalcCombinedHougekiDamage(dynamic hougeki, Record[] friend, Record[] guard, - int[] enemy, int[] enemyGuard) + public void InspectMapStart(dynamic json) { - var targets = ((dynamic[])hougeki.api_df_list).Skip(1).Select(x => (int[])x); - var damages = ((dynamic[])hougeki.api_damage).Skip(1).Select(x => (int[])x); - var eflags = ((int[])hougeki.api_at_eflag).Skip(1); - foreach (var turn in - targets.Zip(damages, (t, d) => new {t, d}). - Zip(eflags, (td, e) => new {e, td.t, td.d})) - { - foreach (var hit in turn.t.Zip(turn.d, (t, d) => new {t, d})) - { - if (turn.e == 1) - { - if (hit.t <= 6) - friend[hit.t - 1].ApplyDamage(hit.d); - else - guard[(hit.t - 1) % 6].ApplyDamage(hit.d); - } - else - { - if (hit.t <= 6) - enemy[hit.t - 1] -= hit.d; - else - enemyGuard[(hit.t - 1) % 6] -= hit.d; - } - } - } + InspectMapNext(json); + } + + public void InspectMapNext(dynamic json) + { + _lastCell = (int)json.api_next == 0; } public void InspectBattleResult(dynamic json) { - ShowResult(); + BattleState = BattleState.Result; + ShowResult(!_lastCell); + _shipInfo.SaveBattleResult(); + VerifyResultRank(json); CleanupResult(); SetEscapeShips(json); } + private void VerifyResultRank(dynamic json) + { + if (_friend == null) + return; + if (!json.api_win_rank()) + return; + var assumed = "PSABCDE"[(int)ResultRank]; + if (assumed == 'P') + assumed = 'S'; + var actual = ((string)json.api_win_rank)[0]; + DisplayedResultRank.Assumed = assumed; + DisplayedResultRank.Actual = actual; + } + public void InspectPracticeResult(dynamic json) { + BattleState = BattleState.Result; ShowResult(false); + VerifyResultRank(json); CleanupResult(); } @@ -465,16 +528,6 @@ namespace KancolleSniffer _shipInfo.SetBadlyDamagedShips(); else _shipInfo.ClearBadlyDamagedShips(); - SetEnemyResultStatus(); - } - - private void SetEnemyResultStatus() - { - for (var i = 0; i < EnemyResultStatus.Length; i++) - { - EnemyResultStatus[i].MaxHp = _enemyStartHp[i]; - EnemyResultStatus[i].NowHp = _enemyHp[i]; - } } public void SetEscapeShips(dynamic json) @@ -483,12 +536,19 @@ namespace KancolleSniffer if (!json.api_escape_flag() || (int)json.api_escape_flag == 0) return; var damaged = (int)json.api_escape.api_escape_idx[0] - 1; - _escapingShips.Add(_shipInfo.GetDeck(damaged / 6)[damaged % 6]); - var escort = (int)json.api_escape.api_tow_idx[0] - 1; - _escapingShips.Add(_shipInfo.GetDeck(escort / 6)[escort % 6]); + if (json.api_escape.api_tow_idx()) + { + _escapingShips.Add(_shipInfo.GetDeck(damaged / 6)[damaged % 6]); + var escort = (int)json.api_escape.api_tow_idx[0] - 1; + _escapingShips.Add(_shipInfo.GetDeck(escort / 6)[escort % 6]); + } + else + { + _escapingShips.Add(_shipInfo.GetDeck(2)[damaged]); + } } - public void CauseCombinedBattleEscape() + public void CauseEscape() { _shipInfo.SetEscapedShips(_escapingShips); _shipInfo.SetBadlyDamagedShips(); @@ -497,13 +557,36 @@ namespace KancolleSniffer private class Record { private ShipStatus _status; + private bool _practice; + public ShipStatus SnapShot => (ShipStatus)_status.Clone(); public int NowHp => _status.NowHp; public bool Escaped => _status.Escaped; public ShipStatus.Damage DamageLevel => _status.DamageLevel; - public int StartHp; + public string Name => _status.Name; + public int StartHp { get; private set; } - public static Record[] Setup(ShipStatus[] ships) => - (from s in ships select new Record {_status = (ShipStatus)s.Clone(), StartHp = s.NowHp}).ToArray(); + public static Record[] Setup(ShipStatus[] ships, bool practice) => + (from s in ships + select new Record {_status = (ShipStatus)s.Clone(), _practice = practice, StartHp = s.NowHp}).ToArray(); + + public static Record[] Setup(int[] nowhps, ShipSpec[] ships, ItemSpec[][] slots, bool practice) + { + return Enumerable.Range(0, nowhps.Length).Select(i => + new Record + { + StartHp = nowhps[i], + _status = new ShipStatus + { + Id = ships[i].Id, + NowHp = nowhps[i], + MaxHp = nowhps[i], + Spec = ships[i], + Slot = slots[i].Select(spec => new ItemStatus {Id = spec.Id, Spec = spec}).ToArray(), + SlotEx = new ItemStatus(0) + }, + _practice = practice + }).ToArray(); + } public void ApplyDamage(int damage) { @@ -513,6 +596,8 @@ namespace KancolleSniffer return; } _status.NowHp = 0; + if (_practice) + return; foreach (var item in new[] {_status.SlotEx}.Concat(_status.Slot)) { if (item.Spec.Id == 42) @@ -540,25 +625,19 @@ namespace KancolleSniffer private BattleResultRank CalcLdAirBattleRank() { - var combined = _friend.Concat(_guard).ToArray(); - var friendNowShips = combined.Count(r => r.NowHp > 0); + var combined = _friend.Concat(_guard).Where(r => !r.Escaped).ToArray(); var friendGauge = combined.Sum(r => r.StartHp - r.NowHp); - var friendSunk = combined.Count(r => r.NowHp == 0); var friendGaugeRate = Floor((double)friendGauge / combined.Sum(r => r.StartHp) * 100); - if (friendSunk == 0) - { - if (friendGauge == 0) - return BattleResultRank.P; - if (friendGaugeRate < 10) - return BattleResultRank.A; - if (friendGaugeRate < 20) - return BattleResultRank.B; - if (friendGaugeRate < 50) - return BattleResultRank.C; - return BattleResultRank.D; - } - if (friendSunk < friendNowShips) + if (friendGauge <= 0) + return BattleResultRank.P; + if (friendGaugeRate < 10) + return BattleResultRank.A; + if (friendGaugeRate < 20) + return BattleResultRank.B; + if (friendGaugeRate < 50) + return BattleResultRank.C; + if (friendGaugeRate < 80) return BattleResultRank.D; return BattleResultRank.E; } @@ -566,8 +645,7 @@ namespace KancolleSniffer private BattleResultRank CalcResultRank() { var friend = _friend.Concat(_guard).ToArray(); - var enemyHp = _enemyHp.Concat(_enemyGuardHp).ToArray(); - var enemyStartHp = _enemyStartHp.Concat(_enemyGuardStartHp).ToArray(); + var enemy = _enemy.Concat(_enemyGuard).ToArray(); var friendCount = friend.Length; var friendStartHpTotal = 0; @@ -584,10 +662,10 @@ namespace KancolleSniffer } var friendGaugeRate = (int)((double)(friendStartHpTotal - friendNowHpTotal) / friendStartHpTotal * 100); - var enemyCount = enemyHp.Length; - var enemyStartHpTotal = enemyStartHp.Sum(); - var enemyNowHpTotal = enemyHp.Sum(); - var enemySunk = enemyHp.Count(hp => hp == 0); + var enemyCount = enemy.Length; + var enemyStartHpTotal = enemy.Sum(r => r.StartHp); + var enemyNowHpTotal = enemy.Sum(r => r.NowHp); + var enemySunk = enemy.Count(r => r.NowHp == 0); var enemyGaugeRate = (int)((double)(enemyStartHpTotal - enemyNowHpTotal) / enemyStartHpTotal * 100); if (friendSunk == 0 && enemySunk == enemyCount) @@ -598,7 +676,7 @@ namespace KancolleSniffer } if (friendSunk == 0 && enemySunk >= (int)(enemyCount * 0.7) && enemyCount > 1) return BattleResultRank.A; - if (friendSunk < enemySunk && enemyHp[0] == 0) + if (friendSunk < enemySunk && enemy[0].NowHp == 0) return BattleResultRank.B; if (friendCount == 1 && friend[0].DamageLevel == ShipStatus.Damage.Badly) return BattleResultRank.D; @@ -610,5 +688,17 @@ namespace KancolleSniffer return BattleResultRank.E; return BattleResultRank.D; } + + /// + /// テスト専用 + /// + public void InjectResultStatus(ShipStatus[] main, ShipStatus[] guard, ShipStatus[] enemy, ShipStatus[] enemyGuard) + { + Result = new BattleResult + { + Friend = new BattleResult.Combined { Main = main, Guard = guard}, + Enemy = new BattleResult.Combined {Main = enemy, Guard = enemyGuard} + }; + } } } \ No newline at end of file