public static PBEBattle LoadReplay(string path) { byte[] fileBytes = File.ReadAllBytes(path); using (var s = new MemoryStream(fileBytes)) using (var r = new EndianBinaryReader(s, encoding: EncodingType.UTF16)) { byte[] hash; using (var md5 = MD5.Create()) { hash = md5.ComputeHash(fileBytes, 0, fileBytes.Length - 16); } for (int i = 0; i < 16; i++) { if (hash[i] != fileBytes[fileBytes.Length - 16 + i]) { throw new InvalidDataException(); } } ushort version = r.ReadUInt16(); // Unused for now int seed = r.ReadInt32(); // Unused for now PBEBattle b = null; int numEvents = r.ReadInt32(); for (int i = 0; i < numEvents; i++) { IPBEPacket packet = PBEPacketProcessor.CreatePacket(b, r.ReadBytes(r.ReadUInt16())); if (packet is PBEBattlePacket bp) { b = new PBEBattle(bp); } else if (packet is PBEWildPkmnAppearedPacket wpap) { PBETrainer wildTrainer = b.Teams[1].Trainers[0]; foreach (PBEPkmnAppearedInfo info in wpap.Pokemon) { PBEBattlePokemon pkmn = wildTrainer.TryGetPokemon(info.Pokemon); // Process disguise and position now pkmn.FieldPosition = info.FieldPosition; if (info.IsDisguised) { pkmn.Status2 |= PBEStatus2.Disguised; pkmn.KnownCaughtBall = info.CaughtBall; pkmn.KnownGender = info.Gender; pkmn.KnownNickname = info.Nickname; pkmn.KnownShiny = info.Shiny; pkmn.KnownSpecies = info.Species; pkmn.KnownForm = info.Form; IPBEPokemonData pData = PBEDataProvider.Instance.GetPokemonData(info); pkmn.KnownType1 = pData.Type1; pkmn.KnownType2 = pData.Type2; } b.ActiveBattlers.Add(pkmn); } } b.Events.Add(packet); } b.BattleState = PBEBattleState.Ended; return(b); } }
internal static bool AreSwitchesValid(PBETrainer trainer, IReadOnlyCollection <PBESwitchIn> switches, [NotNullWhen(false)] out string?invalidReason) { if (trainer.Battle._battleState != PBEBattleState.WaitingForSwitchIns) { throw new InvalidOperationException($"{nameof(BattleState)} must be {PBEBattleState.WaitingForSwitchIns} to validate switches."); } if (trainer.SwitchInsRequired == 0) { invalidReason = "Switches were already submitted"; return(false); } if (switches.Count != trainer.SwitchInsRequired) { invalidReason = $"Invalid amount of switches submitted; required amount is {trainer.SwitchInsRequired}"; return(false); } var verified = new List <PBEBattlePokemon>(trainer.SwitchInsRequired); foreach (PBESwitchIn s in switches) { if (s.Position == PBEFieldPosition.None || s.Position >= PBEFieldPosition.MAX || !trainer.OwnsSpot(s.Position)) { invalidReason = $"Invalid position ({s.PokemonId})"; return(false); } if (!trainer.TryGetPokemon(s.PokemonId, out PBEBattlePokemon? pkmn)) { invalidReason = $"Invalid Pokémon ID ({s.PokemonId})"; return(false); } if (pkmn.HP == 0) { invalidReason = $"Pokémon {s.PokemonId} is fainted"; return(false); } if (pkmn.PBEIgnore) { invalidReason = $"Pokémon {s.PokemonId} cannot battle"; return(false); } if (pkmn.FieldPosition != PBEFieldPosition.None) { invalidReason = $"Pokémon {s.PokemonId} is already on the field"; return(false); } if (verified.Contains(pkmn)) { invalidReason = $"Pokémon {s.PokemonId} was asked to be switched in multiple times"; return(false); } verified.Add(pkmn); } invalidReason = null; return(true); }
internal static string AreSwitchesValid(PBETrainer trainer, IReadOnlyCollection <PBESwitchIn> switches) { if (switches == null || switches.Any(s => s == null)) { throw new ArgumentNullException(nameof(switches)); } if (trainer.Battle._battleState != PBEBattleState.WaitingForSwitchIns) { throw new InvalidOperationException($"{nameof(BattleState)} must be {PBEBattleState.WaitingForSwitchIns} to validate switches."); } if (trainer.SwitchInsRequired == 0) { return("Switches were already submitted"); } if (switches.Count != trainer.SwitchInsRequired) { return($"Invalid amount of switches submitted; required amount is {trainer.SwitchInsRequired}"); } var verified = new List <PBEBattlePokemon>(trainer.SwitchInsRequired); foreach (PBESwitchIn s in switches) { if (s.Position == PBEFieldPosition.None || s.Position >= PBEFieldPosition.MAX || !trainer.OwnsSpot(s.Position)) { return($"Invalid position ({s.PokemonId})"); } PBEBattlePokemon pkmn = trainer.TryGetPokemon(s.PokemonId); if (pkmn is null) { return($"Invalid Pokémon ID ({s.PokemonId})"); } if (pkmn.HP == 0) { return($"Pokémon {s.PokemonId} is fainted"); } if (pkmn.FieldPosition != PBEFieldPosition.None) { return($"Pokémon {s.PokemonId} is already on the field"); } if (verified.Contains(pkmn)) { return($"Pokémon {s.PokemonId} was asked to be switched in multiple times"); } verified.Add(pkmn); } return(null); }
/// <summary>Selects switches if they are valid. Changes the battle state if both teams have selected valid switches.</summary> /// <param name="trainer">The trainer the inputted switches belong to.</param> /// <param name="switches">The switches the team wishes to execute.</param> /// <returns>True if the switches are valid and were selected.</returns> /// <exception cref="InvalidOperationException">Thrown when <see cref="BattleState"/> is not <see cref="PBEBattleState.WaitingForSwitchIns"/>.</exception> public static bool SelectSwitchesIfValid(PBETrainer trainer, IReadOnlyList <PBESwitchIn> switches) { if (AreSwitchesValid(trainer, switches)) { trainer.SwitchInsRequired = 0; foreach (PBESwitchIn s in switches) { PBEBattlePokemon pkmn = trainer.TryGetPokemon(s.PokemonId); trainer.SwitchInQueue.Add((pkmn, s.Position)); } if (trainer.Battle.Trainers.All(t => t.SwitchInsRequired == 0)) { trainer.Battle.BattleState = PBEBattleState.ReadyToRunSwitches; trainer.Battle.OnStateChanged?.Invoke(trainer.Battle); } return(true); } return(false); }
internal static string SelectSwitchesIfValid(PBETrainer trainer, IReadOnlyCollection <PBESwitchIn> switches) { string valid = AreSwitchesValid(trainer, switches); if (valid is null) { trainer.SwitchInsRequired = 0; foreach (PBESwitchIn s in switches) { PBEBattlePokemon pkmn = trainer.TryGetPokemon(s.PokemonId); trainer.SwitchInQueue.Add((pkmn, s.Position)); } if (trainer.Battle.Trainers.All(t => t.SwitchInsRequired == 0)) { trainer.Battle.BattleState = PBEBattleState.ReadyToRunSwitches; } } return(valid); }
/// <summary>Selects actions if they are valid. Changes the battle state if both teams have selected valid actions.</summary> /// <param name="trainer">The trainer the inputted actions belong to.</param> /// <param name="actions">The actions the team wishes to execute.</param> /// <returns>True if the actions are valid and were selected.</returns> /// <exception cref="InvalidOperationException">Thrown when <see cref="BattleState"/> is not <see cref="PBEBattleState.WaitingForActions"/>.</exception> public static bool SelectActionsIfValid(PBETrainer trainer, IReadOnlyList <PBETurnAction> actions) { if (AreActionsValid(trainer, actions)) { trainer.ActionsRequired.Clear(); foreach (PBETurnAction action in actions) { PBEBattlePokemon pkmn = trainer.TryGetPokemon(action.PokemonId); if (action.Decision == PBETurnDecision.Fight && pkmn.GetMoveTargets(action.FightMove) == PBEMoveTarget.RandomFoeSurrounding) { switch (trainer.Battle.BattleFormat) { case PBEBattleFormat.Single: case PBEBattleFormat.Rotation: { action.FightTargets = PBETurnTarget.FoeCenter; break; } case PBEBattleFormat.Double: { action.FightTargets = PBERandom.RandomBool() ? PBETurnTarget.FoeLeft : PBETurnTarget.FoeRight; break; } case PBEBattleFormat.Triple: { if (pkmn.FieldPosition == PBEFieldPosition.Left) { action.FightTargets = PBERandom.RandomBool() ? PBETurnTarget.FoeCenter : PBETurnTarget.FoeRight; } else if (pkmn.FieldPosition == PBEFieldPosition.Center) { PBETeam oppTeam = trainer.Team.OpposingTeam; int r; // Keep randomly picking until a non-fainted foe is selected roll: r = PBERandom.RandomInt(0, 2); if (r == 0) { if (oppTeam.TryGetPokemon(PBEFieldPosition.Left) != null) { action.FightTargets = PBETurnTarget.FoeLeft; } else { goto roll; } } else if (r == 1) { if (oppTeam.TryGetPokemon(PBEFieldPosition.Center) != null) { action.FightTargets = PBETurnTarget.FoeCenter; } else { goto roll; } } else { if (oppTeam.TryGetPokemon(PBEFieldPosition.Right) != null) { action.FightTargets = PBETurnTarget.FoeRight; } else { goto roll; } } } else { action.FightTargets = PBERandom.RandomBool() ? PBETurnTarget.FoeLeft : PBETurnTarget.FoeCenter; } break; } default: throw new ArgumentOutOfRangeException(nameof(trainer.Battle.BattleFormat)); } } pkmn.TurnAction = action; } if (trainer.Battle.Trainers.All(t => t.ActionsRequired.Count == 0)) { trainer.Battle.BattleState = PBEBattleState.ReadyToRunTurn; trainer.Battle.OnStateChanged?.Invoke(trainer.Battle); } return(true); } return(false); }
/// <summary>Determines whether chosen actions are valid.</summary> /// <param name="trainer">The trainer the inputted actions belong to.</param> /// <param name="actions">The actions the team wishes to execute.</param> /// <returns>False if the team already chose actions or the actions are illegal, True otherwise.</returns> /// <exception cref="InvalidOperationException">Thrown when <see cref="BattleState"/> is not <see cref="PBEBattleState.WaitingForActions"/>.</exception> public static bool AreActionsValid(PBETrainer trainer, IReadOnlyList <PBETurnAction> actions) { if (trainer == null) { throw new ArgumentNullException(nameof(trainer)); } if (actions == null || actions.Any(a => a == null)) { throw new ArgumentNullException(nameof(actions)); } if (trainer.Battle.BattleState != PBEBattleState.WaitingForActions) { throw new InvalidOperationException($"{nameof(BattleState)} must be {PBEBattleState.WaitingForActions} to validate actions."); } if (trainer.ActionsRequired.Count == 0 || actions.Count != trainer.ActionsRequired.Count) { return(false); } var verified = new List <PBEBattlePokemon>(trainer.ActionsRequired.Count); var standBy = new List <PBEBattlePokemon>(trainer.ActionsRequired.Count); foreach (PBETurnAction action in actions) { PBEBattlePokemon pkmn = trainer.TryGetPokemon(action.PokemonId); if (pkmn == null || !trainer.ActionsRequired.Contains(pkmn) || verified.Contains(pkmn)) { return(false); } switch (action.Decision) { case PBETurnDecision.Fight: { if (Array.IndexOf(pkmn.GetUsableMoves(), action.FightMove) == -1 || (action.FightMove == pkmn.TempLockedMove && action.FightTargets != pkmn.TempLockedTargets) || !AreTargetsValid(pkmn, action.FightMove, action.FightTargets) ) { return(false); } break; } case PBETurnDecision.SwitchOut: { if (!pkmn.CanSwitchOut()) { return(false); } PBEBattlePokemon switchPkmn = trainer.TryGetPokemon(action.SwitchPokemonId); if (switchPkmn == null || switchPkmn.HP == 0 || switchPkmn.FieldPosition != PBEFieldPosition.None || // Also takes care of trying to switch into yourself standBy.Contains(switchPkmn) ) { return(false); } standBy.Add(switchPkmn); break; } default: return(false); } verified.Add(pkmn); } return(true); }
internal static bool AreActionsValid(PBETrainer trainer, IReadOnlyCollection <PBETurnAction> actions, [NotNullWhen(false)] out string?invalidReason) { if (trainer.Battle._battleState != PBEBattleState.WaitingForActions) { throw new InvalidOperationException($"{nameof(BattleState)} must be {PBEBattleState.WaitingForActions} to validate actions."); } if (trainer.ActionsRequired.Count == 0) { invalidReason = "Actions were already submitted"; return(false); } if (actions.Count != trainer.ActionsRequired.Count) { invalidReason = $"Invalid amount of actions submitted; required amount is {trainer.ActionsRequired.Count}"; return(false); } var verified = new List <PBEBattlePokemon>(trainer.ActionsRequired.Count); var standBy = new List <PBEBattlePokemon>(trainer.ActionsRequired.Count); var items = new Dictionary <PBEItem, int>(trainer.ActionsRequired.Count); foreach (PBETurnAction action in actions) { if (!trainer.TryGetPokemon(action.PokemonId, out PBEBattlePokemon? pkmn)) { invalidReason = $"Invalid Pokémon ID ({action.PokemonId})"; return(false); } if (!trainer.ActionsRequired.Contains(pkmn)) { invalidReason = $"Pokémon {action.PokemonId} not looking for actions"; return(false); } if (verified.Contains(pkmn)) { invalidReason = $"Pokémon {action.PokemonId} was multiple actions"; return(false); } switch (action.Decision) { case PBETurnDecision.Fight: { if (Array.IndexOf(pkmn.GetUsableMoves(), action.FightMove) == -1) { invalidReason = $"{action.FightMove} is not usable by Pokémon {action.PokemonId}"; return(false); } if (action.FightMove == pkmn.TempLockedMove && action.FightTargets != pkmn.TempLockedTargets) { invalidReason = $"Pokémon {action.PokemonId} must target {pkmn.TempLockedTargets}"; return(false); } if (!AreTargetsValid(pkmn, action.FightMove, action.FightTargets)) { invalidReason = $"Invalid move targets for Pokémon {action.PokemonId}'s {action.FightMove}"; return(false); } break; } case PBETurnDecision.Item: { if (pkmn.TempLockedMove != PBEMove.None) { invalidReason = $"Pokémon {action.PokemonId} must use {pkmn.TempLockedMove}"; return(false); } if (!trainer.Inventory.TryGetValue(action.UseItem, out PBEBattleInventory.PBEBattleInventorySlot? slot)) { invalidReason = $"Trainer \"{trainer.Name}\" does not have any {action.UseItem}"; // Handles wild Pokémon return(false); } bool used = items.TryGetValue(action.UseItem, out int amtUsed); if (!used) { amtUsed = 0; } long newAmt = slot.Quantity - amtUsed; if (newAmt <= 0) { invalidReason = $"Tried to use too many {action.UseItem}"; return(false); } if (trainer.Battle.BattleType == PBEBattleType.Wild && trainer.Team.OpposingTeam.ActiveBattlers.Count > 1 && PBEDataUtils.AllBalls.Contains(action.UseItem)) { invalidReason = $"Cannot throw a ball at multiple wild Pokémon"; return(false); } amtUsed++; if (used) { items[action.UseItem] = amtUsed; } else { items.Add(action.UseItem, amtUsed); } break; } case PBETurnDecision.SwitchOut: { if (!pkmn.CanSwitchOut()) { invalidReason = $"Pokémon {action.PokemonId} cannot switch out"; return(false); } if (!trainer.TryGetPokemon(action.SwitchPokemonId, out PBEBattlePokemon? switchPkmn)) { invalidReason = $"Invalid switch Pokémon ID ({action.PokemonId})"; return(false); } if (switchPkmn.HP == 0) { invalidReason = $"Switch Pokémon {action.PokemonId} is fainted"; return(false); } if (switchPkmn.PBEIgnore) { invalidReason = $"Switch Pokémon {action.PokemonId} cannot battle"; return(false); } if (switchPkmn.FieldPosition != PBEFieldPosition.None) { invalidReason = $"Switch Pokémon {action.PokemonId} is already on the field"; return(false); } if (standBy.Contains(switchPkmn)) { invalidReason = $"Switch Pokémon {action.PokemonId} was asked to be switched in multiple times"; return(false); } standBy.Add(switchPkmn); break; } default: { invalidReason = $"Invalid turn decision ({action.Decision})"; return(false); } } verified.Add(pkmn); } invalidReason = null; return(true); }