Пример #1
0
 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);
         }
 }
Пример #2
0
        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);
        }
Пример #3
0
        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);
 }
Пример #5
0
        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);
        }
Пример #8
0
        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);
        }