Exemple #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);
         }
 }
 public static bool HasAbility(this IPBEPokemonData pData, PBEAbility ability)
 {
     if (ability == PBEAbility.None || ability >= PBEAbility.MAX)
     {
         throw new ArgumentOutOfRangeException(nameof(ability));
     }
     return(pData.Abilities.Contains(ability));
 }
        public void Illusion_Works_Wild()
        {
            #region Setup
            PBEDataProvider.GlobalRandom.Seed = 0;
            PBESettings settings = PBESettings.DefaultSettings;

            var p0 = new TestPokemonCollection(1);
            p0[0] = new TestPokemon(settings, PBESpecies.Magikarp, 0, 1, PBEMove.Tackle);

            var p1 = new TestPokemonCollection(1);
            p1[0] = new TestPokemon(settings, PBESpecies.Zoroark, 0, 100, PBEMove.Splash)
            {
                Ability    = PBEAbility.Illusion,
                CaughtBall = PBEItem.None
            };

            var battle = PBEBattle.CreateWildBattle(PBEBattleFormat.Single, settings, new PBETrainerInfo(p0, "Trainer 0", false), new PBEWildInfo(p1));
            battle.OnNewEvent += PBEBattle.ConsoleBattleEventHandler;

            PBETrainer       t0       = battle.Trainers[0];
            PBETrainer       t1       = battle.Trainers[1];
            PBEBattlePokemon magikarp = t0.Party[0];
            PBEBattlePokemon zoroark  = t1.Party[0];

            zoroark.Status2        |= PBEStatus2.Disguised;
            zoroark.KnownGender     = PBEGender.Genderless;
            zoroark.KnownCaughtBall = PBEItem.None;
            zoroark.KnownShiny      = false;
            zoroark.KnownSpecies    = PBESpecies.Entei;
            zoroark.KnownForm       = 0;
            zoroark.KnownNickname   = zoroark.KnownSpecies.ToString();
            IPBEPokemonData pData = PBEDataProvider.Instance.GetPokemonData(zoroark.KnownSpecies, zoroark.KnownForm);
            zoroark.KnownType1 = pData.Type1;
            zoroark.KnownType2 = pData.Type2;

            battle.Begin();
            #endregion

            #region Check that the disguise works
            Assert.True(zoroark.Status2.HasFlag(PBEStatus2.Disguised) &&
                        ((PBEWildPkmnAppearedPacket)battle.Events.Single(p => p is PBEWildPkmnAppearedPacket)).Pokemon[0].IsDisguised);
            #endregion

            #region Break the disguise and check
            Assert.True(t0.SelectActionsIfValid(out _, new PBETurnAction(magikarp, PBEMove.Tackle, PBETurnTarget.FoeCenter)));
            Assert.True(t1.SelectActionsIfValid(out _, new PBETurnAction(zoroark, PBEMove.Splash, PBETurnTarget.AllyCenter)));

            battle.RunTurn();

            Assert.True(!zoroark.Status2.HasFlag(PBEStatus2.Disguised) &&
                        zoroark.KnownSpecies == PBESpecies.Zoroark);
            #endregion

            #region Cleanup
            battle.OnNewEvent -= PBEBattle.ConsoleBattleEventHandler;
            #endregion
        }
Exemple #4
0
        // Wild Pokémon always select a random usable move (unless they are forced to use a move)
        // They will flee randomly based on their PBEPokemonData.FleeRate only if it's a single battle and they are allowed to flee
        public static void CreateWildAIActions(this PBETrainer trainer)
        {
            if (trainer is null)
            {
                throw new ArgumentNullException(nameof(trainer));
            }
            if (trainer.Battle.BattleState != PBEBattleState.WaitingForActions)
            {
                throw new InvalidOperationException($"{nameof(trainer.Battle.BattleState)} must be {PBEBattleState.WaitingForActions} to create actions.");
            }
            // Try to flee if it's a single wild battle and the Pokémon is a runner
            if (trainer.IsWild && trainer.Battle.BattleFormat == PBEBattleFormat.Single && trainer.IsFleeValid() is null)
            {
                PBEBattlePokemon user  = trainer.ActionsRequired[0];
                IPBEPokemonData  pData = PBEDataProvider.Instance.GetPokemonData(user);
                if (PBEDataProvider.GlobalRandom.RandomBool(pData.FleeRate, 255))
                {
                    string valid = trainer.SelectFleeIfValid();
                    if (valid != null)
                    {
                        throw new Exception("Wild AI tried to flee but couldn't. - " + valid);
                    }
                    return;
                }
            }
            var actions = new PBETurnAction[trainer.ActionsRequired.Count];

            for (int i = 0; i < actions.Length; i++)
            {
                PBEBattlePokemon user = trainer.ActionsRequired[i];
                // If a Pokémon is forced to struggle, it must use Struggle
                if (user.IsForcedToStruggle())
                {
                    actions[i] = new PBETurnAction(user, PBEMove.Struggle, PBEBattleUtils.GetPossibleTargets(user, user.GetMoveTargets(PBEMove.Struggle))[0]);
                    continue;
                }
                // If a Pokémon has a temp locked move (Dig, Dive, ShadowForce) it must be used
                if (user.TempLockedMove != PBEMove.None)
                {
                    actions[i] = new PBETurnAction(user, user.TempLockedMove, user.TempLockedTargets);
                    continue;
                }
                // The Pokémon is free to fight
                PBEMove[] usableMoves = user.GetUsableMoves();
                PBEMove   move        = PBEDataProvider.GlobalRandom.RandomElement(usableMoves);
                actions[i] = new PBETurnAction(user, move, PBEBattle.GetRandomTargetForMetronome(user, move, PBEDataProvider.GlobalRandom));
            }
            string valid2 = trainer.SelectActionsIfValid(actions);

            if (valid2 != null)
            {
                throw new Exception("Wild AI created bad actions. - " + valid2);
            }
        }
Exemple #5
0
 private void DoDisguisedAppearance(PBEBattlePokemon pkmn, PBEPkmnAppearedInfo info)
 {
     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;
     }
 }
Exemple #6
0
        public static IReadOnlyCollection <PBEMove> GetLegalMoves(PBESpecies species, PBEForm form, byte level, PBESettings settings)
        {
            if (settings == null)
            {
                throw new ArgumentNullException(nameof(settings));
            }
            if (!settings.IsReadOnly)
            {
                throw new ArgumentException("Settings must be read-only.", nameof(settings));
            }
            ValidateSpecies(species, form, true);
            ValidateLevel(level, settings);
            List <(PBESpecies, PBEForm)> speciesToStealFrom = GetSpecies(species, form);

            var moves = new List <PBEMove>();

            foreach ((PBESpecies spe, PBEForm fo) in speciesToStealFrom)
            {
                IPBEPokemonData pData = PBEDataProvider.Instance.GetPokemonData(spe, fo);
                // Disallow moves learned after the current level
                moves.AddRange(pData.LevelUpMoves.Where(t => t.Level <= level).Select(t => t.Move));
                // Disallow form-specific moves from other forms (Rotom)
                moves.AddRange(pData.OtherMoves.Where(t => (spe == species && fo == form) || t.ObtainMethod != PBEMoveObtainMethod.Form).Select(t => t.Move));
                // Event Pokémon checking is extremely basic and wrong, but the goal is not to be super restricting or accurate
                if (PBEEventPokemon.Events.TryGetValue(spe, out ReadOnlyCollection <PBEEventPokemon> events))
                {
                    // Disallow moves learned after the current level
                    moves.AddRange(events.Where(e => e.Level <= level).SelectMany(e => e.Moves).Where(m => m != PBEMove.None));
                }
                if (moves.Any(m => PBEDataProvider.Instance.GetMoveData(m, cache: false).Effect == PBEMoveEffect.Sketch))
                {
                    return(PBEDataUtils.SketchLegalMoves);
                }
            }
            return(moves.Distinct().Where(m => PBEDataUtils.IsMoveUsable(m)).ToArray());
        }
Exemple #7
0
            public async Task Info([Remainder] string input)
            {
                // Inputs for forms should be like "Giratina (Origin Forme)"
                Match  m = Regex.Match(input, @"^(\S+) \((.+)\)$");
                string speciesName;
                string?formName;

                if (m.Success)
                {
                    speciesName = m.Groups[1].Value;
                    formName    = m.Groups[2].Value;
                }
                else
                {
                    speciesName = input;
                    formName    = null;
                }
                if (!PBEDataProvider.Instance.GetSpeciesByName(speciesName, out PBESpecies? nSpecies))
                {
                    await Context.Channel.SendMessageAsync($"{Context.User.Mention} ― Invalid species!");

                    return;
                }
                PBESpecies species = nSpecies.Value;

                speciesName = PBEDataProvider.Instance.GetSpeciesName(species).English;
                PBEForm form;

                if (formName is null)
                {
                    form = 0;
                }
                else
                {
                    if (!PBEDataProvider.Instance.GetFormByName(species, formName, out PBEForm? nForm))
                    {
                        IReadOnlyList <PBEForm> forms = PBEDataUtils.GetForms(species, false);
                        string str = $"{Context.User.Mention} ― Invalid form for {speciesName}";
                        if (forms.Count > 0)
                        {
                            str += ", valid forms are:\n**" + string.Join("\n", forms.Select(f => PBEDataProvider.Instance.GetFormName(species, f).English)) + "**";
                        }
                        else
                        {
                            str += "! It has no forms!";
                        }
                        await Context.Channel.SendMessageAsync(str);

                        return;
                    }
                    form = nForm.Value;
                }
                formName = PBEDataUtils.HasForms(species, false) ? $" ({PBEDataProvider.Instance.GetFormName(species, form).English})" : string.Empty;
                IPBEPokemonData pData = PBEDataProvider.Instance.GetPokemonData(species, form);
                string          types = $"{Utils.TypeEmotes[pData.Type1]}";

                if (pData.Type2 != PBEType.None)
                {
                    types += $" {Utils.TypeEmotes[pData.Type2]}";
                }
                string ratio;

                switch (pData.GenderRatio)
                {
                case PBEGenderRatio.M7_F1: ratio = "87.5% Male, 12.5% Female"; break;

                case PBEGenderRatio.M3_F1: ratio = "75% Male, 25% Female"; break;

                case PBEGenderRatio.M1_F1: ratio = "50% Male, 50% Female"; break;

                case PBEGenderRatio.M1_F3: ratio = "25% Male, 75% Female"; break;

                case PBEGenderRatio.M0_F1: ratio = "100% Female"; break;

                case PBEGenderRatio.M1_F0: ratio = "100% Male"; break;

                case PBEGenderRatio.M0_F0: ratio = "Genderless Species"; break;

                default: throw new InvalidDataException(nameof(pData.GenderRatio));
                }
                string weaknesses  = string.Empty,
                       resistances = string.Empty,
                       immunities  = string.Empty;

                for (PBEType atk = PBEType.None + 1; atk < PBEType.MAX; atk++)
                {
                    float d = PBETypeEffectiveness.GetEffectiveness(atk, pData);
                    if (d <= 0)
                    {
                        if (immunities != string.Empty)
                        {
                            immunities += ' ';
                        }
                        immunities += Utils.TypeEmotes[atk];
                    }
                    else if (d < 1)
                    {
                        if (resistances != string.Empty)
                        {
                            resistances += ' ';
                        }
                        resistances += Utils.TypeEmotes[atk];
                    }
                    if (d > 1)
                    {
                        if (weaknesses != string.Empty)
                        {
                            weaknesses += ' ';
                        }
                        weaknesses += Utils.TypeEmotes[atk];
                    }
                }
                if (weaknesses == string.Empty)
                {
                    weaknesses = "No Weaknesses";
                }
                if (resistances == string.Empty)
                {
                    resistances = "No Resistances";
                }
                if (immunities == string.Empty)
                {
                    immunities = "No Immunities";
                }

                EmbedBuilder embed = new EmbedBuilder()
                                     .WithAuthor(Context.User)
                                     .WithColor(Utils.GetColor(pData.Type1, pData.Type2))
                                     .WithTitle($"{speciesName}{formName} ― {PBEDefaultDataProvider.Instance.GetSpeciesCategory(species).English}")
                                     .WithUrl(Utils.URL)
                                     .WithDescription(PBEDefaultDataProvider.Instance.GetSpeciesEntry(species).English.Replace('\n', ' '))
                                     .AddField("Types", types, true)
                                     .AddField("Gender Ratio", ratio, true)
                                     .AddField("Weight", $"{pData.Weight:N1} kg", true)
                                     .AddField("Abilities", string.Join(", ", pData.Abilities.Select(a => PBEDataProvider.Instance.GetAbilityName(a).English)), false)
                                     .AddField("HP", pData.BaseStats.HP, true)
                                     .AddField("Attack", pData.BaseStats.Attack, true)
                                     .AddField("Defense", pData.BaseStats.Defense, true)
                                     .AddField("Special Attack", pData.BaseStats.SpAttack, true)
                                     .AddField("Special Defense", pData.BaseStats.SpDefense, true)
                                     .AddField("Speed", pData.BaseStats.Speed, true)
                                     .AddField("Type Weaknesses", weaknesses, true)
                                     .AddField("Type Resistances", resistances, true)
                                     .AddField("Type Immunities", immunities, true)
                                     .WithImageUrl(Utils.GetPokemonSprite(species, form, PBEDataProvider.GlobalRandom.RandomShiny(), PBEDataProvider.GlobalRandom.RandomGender(pData.GenderRatio), false, false));
                await Context.Channel.SendMessageAsync(string.Empty, embed : embed.Build());
            }
        private string CreateKnownPokemonEmbed(PBEBattlePokemon pkmn)
        {
            IPBEPokemonData pData   = PBEDataProvider.Instance.GetPokemonData(pkmn.KnownSpecies, pkmn.KnownForm);
            var             sb      = new StringBuilder();
            string          formStr = PBEDataUtils.HasForms(pkmn.KnownSpecies, false) ? $" ({PBEDataProvider.Instance.GetFormName(pkmn.KnownSpecies, pkmn.KnownForm).English})" : string.Empty;

            sb.AppendLine($"{GetTrainerName(pkmn.Trainer)}'s {pkmn.KnownNickname}/{pkmn.KnownSpecies}{formStr} {(pkmn.KnownStatus2.HasFlag(PBEStatus2.Transformed) ? pkmn.Gender.ToSymbol() : pkmn.KnownGender.ToSymbol())} Lv.{pkmn.Level}{(pkmn.KnownShiny ? $" {_shinyEmoji}" : string.Empty)}");
            sb.AppendLine($"**HP:** {pkmn.HPPercentage:P2}");
            sb.Append($"**Known types:** {Utils.TypeEmotes[pkmn.KnownType1]}");
            if (pkmn.KnownType2 != PBEType.None)
            {
                sb.Append($" {Utils.TypeEmotes[pkmn.KnownType2]}");
            }
            sb.AppendLine();
            if (pkmn.Status1 != PBEStatus1.None)
            {
                sb.AppendLine($"**Main status:** {Utils.Status1Emotes[pkmn.Status1]}");
                if (pkmn.Status1 == PBEStatus1.Asleep)
                {
                    sb.AppendLine($"**{Utils.Status1Emotes[PBEStatus1.Asleep]} turns:** {pkmn.Status1Counter}");
                }
                else if (pkmn.Status1 == PBEStatus1.BadlyPoisoned)
                {
                    sb.AppendLine($"**{Utils.Status1Emotes[PBEStatus1.BadlyPoisoned]} counter:** {pkmn.Status1Counter}");
                }
            }
            if (pkmn.KnownStatus2 != PBEStatus2.None)
            {
                sb.AppendLine($"**Volatile status:** {pkmn.KnownStatus2}");
                if (pkmn.KnownStatus2.HasFlag(PBEStatus2.Confused))
                {
                    sb.AppendLine($"**Confusion turns:** {pkmn.ConfusionCounter}");
                }
            }
            PBEDataUtils.GetStatRange(pData, PBEStat.HP, pkmn.Level, PBESettings.DefaultSettings, out ushort lowHP, out ushort highHP);
            PBEDataUtils.GetStatRange(pData, PBEStat.Attack, pkmn.Level, PBESettings.DefaultSettings, out ushort lowAttack, out ushort highAttack);
            PBEDataUtils.GetStatRange(pData, PBEStat.Defense, pkmn.Level, PBESettings.DefaultSettings, out ushort lowDefense, out ushort highDefense);
            PBEDataUtils.GetStatRange(pData, PBEStat.SpAttack, pkmn.Level, PBESettings.DefaultSettings, out ushort lowSpAttack, out ushort highSpAttack);
            PBEDataUtils.GetStatRange(pData, PBEStat.SpDefense, pkmn.Level, PBESettings.DefaultSettings, out ushort lowSpDefense, out ushort highSpDefense);
            PBEDataUtils.GetStatRange(pData, PBEStat.Speed, pkmn.Level, PBESettings.DefaultSettings, out ushort lowSpeed, out ushort highSpeed);
            sb.AppendLine($"**Stat range:** [HP] {lowHP}-{highHP}, [A] {lowAttack}-{highAttack}, [D] {lowDefense}-{highDefense}, [SA] {lowSpAttack}-{highSpAttack}, [SD] {lowSpDefense}-{highSpDefense}, [S] {lowSpeed}-{highSpeed}, [W] {pkmn.KnownWeight:0.0}");
            AddStatChanges(pkmn, sb);
            if (pkmn.KnownAbility == PBEAbility.MAX)
            {
                sb.AppendLine($"**Possible abilities:** {string.Join(", ", pData.Abilities.Select(a => PBEDataProvider.Instance.GetAbilityName(a).English))}");
            }
            else
            {
                sb.AppendLine($"**Known ability:** {PBEDataProvider.Instance.GetAbilityName(pkmn.KnownAbility).English}");
            }
            sb.AppendLine($"**Known item:** {(pkmn.KnownItem == (PBEItem)ushort.MaxValue ? "???" : PBEDataProvider.Instance.GetItemName(pkmn.KnownItem).English)}");
            sb.Append("**Known moves:** ");
            for (int i = 0; i < PBESettings.DefaultNumMoves; i++)
            {
                PBEBattleMoveset.PBEBattleMovesetSlot slot = pkmn.KnownMoves[i];
                PBEMove move = slot.Move;
                if (move != PBEMove.None)
                {
                    int pp    = slot.PP;
                    int maxPP = slot.MaxPP;
                    if (i > 0)
                    {
                        sb.Append(", ");
                    }
                    if (move == PBEMove.MAX)
                    {
                        sb.Append("???");
                    }
                    else
                    {
                        sb.Append($"{Utils.TypeEmotes[pkmn.GetMoveType(move, useKnownInfo: true)]} {PBEDataProvider.Instance.GetMoveName(move).English} ({pp}{(maxPP == 0 ? ")" : $"/{maxPP})")}");
                    }
                }
            }
Exemple #9
0
        public static string CustomPokemonToString(PBEBattlePokemon pkmn, bool useKnownInfo)
        {
            var sb = new StringBuilder();

            string GetTeamNickname(PBEBattlePokemon p)
            {
                return($"{p.Trainer.Name}'s {(useKnownInfo ? p.KnownNickname : p.Nickname)}");
            }

            void AddStatChanges()
            {
                PBEStat[] statChanges = pkmn.GetChangedStats();
                if (statChanges.Length > 0)
                {
                    var statStrs = new List <string>(7);
                    if (Array.IndexOf(statChanges, PBEStat.Attack) != -1)
                    {
                        statStrs.Add($"[A] x{PBEBattle.GetStatChangeModifier(pkmn.AttackChange, false):0.00}");
                    }
                    if (Array.IndexOf(statChanges, PBEStat.Defense) != -1)
                    {
                        statStrs.Add($"[D] x{PBEBattle.GetStatChangeModifier(pkmn.DefenseChange, false):0.00}");
                    }
                    if (Array.IndexOf(statChanges, PBEStat.SpAttack) != -1)
                    {
                        statStrs.Add($"[SA] x{PBEBattle.GetStatChangeModifier(pkmn.SpAttackChange, false):0.00}");
                    }
                    if (Array.IndexOf(statChanges, PBEStat.SpDefense) != -1)
                    {
                        statStrs.Add($"[SD] x{PBEBattle.GetStatChangeModifier(pkmn.SpDefenseChange, false):0.00}");
                    }
                    if (Array.IndexOf(statChanges, PBEStat.Speed) != -1)
                    {
                        statStrs.Add($"[S] x{PBEBattle.GetStatChangeModifier(pkmn.SpeedChange, false):0.00}");
                    }
                    if (Array.IndexOf(statChanges, PBEStat.Accuracy) != -1)
                    {
                        statStrs.Add($"[AC] x{PBEBattle.GetStatChangeModifier(pkmn.AccuracyChange, true):0.00}");
                    }
                    if (Array.IndexOf(statChanges, PBEStat.Evasion) != -1)
                    {
                        statStrs.Add($"[E] x{PBEBattle.GetStatChangeModifier(pkmn.EvasionChange, true):0.00}");
                    }
                    sb.AppendLine($"Stat changes: {string.Join(", ", statStrs)}");
                }
            }

            void AddStatus1()
            {
                if (pkmn.Status1 != PBEStatus1.None)
                {
                    sb.AppendLine($"Main status: {pkmn.Status1}");
                    if (pkmn.Status1 == PBEStatus1.Asleep)
                    {
                        sb.AppendLine($"Asleep turns: {pkmn.Status1Counter}");
                    }
                    else if (pkmn.Status1 == PBEStatus1.BadlyPoisoned)
                    {
                        sb.AppendLine($"Toxic counter: {pkmn.Status1Counter}");
                    }
                }
            }

            void AddStatus2(PBEStatus2 status2)
            {
                status2 &= ~PBEStatus2.Flinching; // Don't show flinching
                sb.AppendLine($"Volatile status: {status2}");
                if (status2.HasFlag(PBEStatus2.Disguised))
                {
                    string formStr = PBEDataUtils.HasForms(pkmn.KnownSpecies, false) ? $" ({PBEDataProvider.Instance.GetFormName(pkmn.KnownSpecies, pkmn.KnownForm).FromPBECultureInfo()})" : string.Empty;
                    sb.AppendLine($"Disguised as: {pkmn.KnownNickname}/{PBEDataProvider.Instance.GetSpeciesName(pkmn.KnownSpecies).FromPBECultureInfo()}{formStr} {pkmn.KnownGender.ToSymbol()}");
                }
                if (pkmn.Battle.BattleFormat != PBEBattleFormat.Single)
                {
                    if (status2.HasFlag(PBEStatus2.Infatuated))
                    {
                        sb.AppendLine($"Infatuated with: {GetTeamNickname(pkmn.InfatuatedWithPokemon)}");
                    }
                    if (status2.HasFlag(PBEStatus2.LeechSeed))
                    {
                        sb.AppendLine($"Seeded position: {pkmn.SeededTeam.CombinedName}'s {pkmn.SeededPosition}");
                    }
                    if (status2.HasFlag(PBEStatus2.LockOn))
                    {
                        sb.AppendLine($"Taking aim at: {GetTeamNickname(pkmn.LockOnPokemon)}");
                    }
                }
            }

            if (useKnownInfo)
            {
                IPBEPokemonData pData   = PBEDataProvider.Instance.GetPokemonData(pkmn.KnownSpecies, pkmn.KnownForm);
                string          formStr = PBEDataUtils.HasForms(pkmn.KnownSpecies, false) ? $" ({PBEDataProvider.Instance.GetFormName(pkmn.KnownSpecies, pkmn.KnownForm).FromPBECultureInfo()})" : string.Empty;
                sb.AppendLine($"{pkmn.KnownNickname}/{PBEDataProvider.Instance.GetSpeciesName(pkmn.KnownSpecies).FromPBECultureInfo()}{formStr} {(pkmn.KnownStatus2.HasFlag(PBEStatus2.Transformed) ? pkmn.Gender.ToSymbol() : pkmn.KnownGender.ToSymbol())} Lv.{pkmn.Level}");
                sb.AppendLine($"HP: {pkmn.HPPercentage:P2}");
                sb.Append($"Known types: {PBEDataProvider.Instance.GetTypeName(pkmn.KnownType1).FromPBECultureInfo()}");
                if (pkmn.KnownType2 != PBEType.None)
                {
                    sb.Append($"/{PBEDataProvider.Instance.GetTypeName(pkmn.KnownType2).FromPBECultureInfo()}");
                }
                sb.AppendLine();
                if (pkmn.FieldPosition != PBEFieldPosition.None)
                {
                    sb.AppendLine($"Position: {pkmn.Team.CombinedName}'s {pkmn.FieldPosition}");
                }
                AddStatus1();
                if (pkmn.FieldPosition != PBEFieldPosition.None)
                {
                    if (pkmn.KnownStatus2 != PBEStatus2.None)
                    {
                        AddStatus2(pkmn.KnownStatus2);
                    }
                }
                PBEDataUtils.GetStatRange(pData, PBEStat.HP, pkmn.Level, pkmn.Battle.Settings, out ushort lowHP, out ushort highHP);
                PBEDataUtils.GetStatRange(pData, PBEStat.Attack, pkmn.Level, pkmn.Battle.Settings, out ushort lowAttack, out ushort highAttack);
                PBEDataUtils.GetStatRange(pData, PBEStat.Defense, pkmn.Level, pkmn.Battle.Settings, out ushort lowDefense, out ushort highDefense);
                PBEDataUtils.GetStatRange(pData, PBEStat.SpAttack, pkmn.Level, pkmn.Battle.Settings, out ushort lowSpAttack, out ushort highSpAttack);
                PBEDataUtils.GetStatRange(pData, PBEStat.SpDefense, pkmn.Level, pkmn.Battle.Settings, out ushort lowSpDefense, out ushort highSpDefense);
                PBEDataUtils.GetStatRange(pData, PBEStat.Speed, pkmn.Level, pkmn.Battle.Settings, out ushort lowSpeed, out ushort highSpeed);
                sb.AppendLine($"Stat range: [HP] {lowHP}-{highHP}, [A] {lowAttack}-{highAttack}, [D] {lowDefense}-{highDefense}, [SA] {lowSpAttack}-{highSpAttack}, [SD] {lowSpDefense}-{highSpDefense}, [S] {lowSpeed}-{highSpeed}, [W] {pkmn.KnownWeight:0.0}");
                if (pkmn.FieldPosition != PBEFieldPosition.None)
                {
                    AddStatChanges();
                }
                if (pkmn.KnownAbility == PBEAbility.MAX)
                {
                    sb.AppendLine($"Possible abilities: {string.Join(", ", pData.Abilities.Select(a => PBEDataProvider.Instance.GetAbilityName(a).FromPBECultureInfo()))}");
                }
                else
                {
                    sb.AppendLine($"Known ability: {PBEDataProvider.Instance.GetAbilityName(pkmn.KnownAbility).FromPBECultureInfo()}");
                }
                sb.AppendLine($"Known item: {(pkmn.KnownItem == (PBEItem)ushort.MaxValue ? "???" : PBEDataProvider.Instance.GetItemName(pkmn.KnownItem).FromPBECultureInfo())}");
                sb.Append("Known moves: ");
                for (int i = 0; i < pkmn.Battle.Settings.NumMoves; i++)
                {
                    PBEBattleMoveset.PBEBattleMovesetSlot slot = pkmn.KnownMoves[i];
                    PBEMove move  = slot.Move;
                    int     pp    = slot.PP;
                    int     maxPP = slot.MaxPP;
                    if (i > 0)
                    {
                        sb.Append(", ");
                    }
                    sb.Append(move == PBEMove.MAX ? "???" : PBEDataProvider.Instance.GetMoveName(move).FromPBECultureInfo());
                    if (move != PBEMove.None && move != PBEMove.MAX)
                    {
                        sb.Append($" ({pp}{(maxPP == 0 ? ")" : $"/{maxPP})")}");
                    }
                }
            }