Exemple #1
0
        public void SimulatorGetEncounters()
        {
            var set = new ShowdownSet(SetGlaceonUSUMTutor);
            var pk7 = new PK7 {
                Species = set.Species, AltForm = set.FormIndex, Moves = set.Moves
            };
            var encs = EncounterMovesetGenerator.GenerateEncounters(pk7, set.Moves, GameVersion.MN);

            Assert.IsTrue(!encs.Any());
            pk7.HT_Name = "PKHeX";
            encs        = EncounterMovesetGenerator.GenerateEncounters(pk7, set.Moves, GameVersion.MN);
            var first = encs.FirstOrDefault();

            Assert.IsTrue(first != null);

            var egg  = (EncounterEgg)first;
            var info = new SimpleTrainerInfo();
            var pk   = egg.ConvertToPKM(info);

            Assert.IsTrue(pk.Species != set.Species);

            var la = new LegalityAnalysis(pk);

            Assert.IsTrue(la.Valid);

            var test = EncounterMovesetGenerator.GeneratePKMs(pk7, info).ToList();

            foreach (var t in test)
            {
                var la2 = new LegalityAnalysis(t);
                Assert.IsTrue(la2.Valid);
            }
        }
Exemple #2
0
        public void CanGenerateMG5Case()
        {
            const Species species = Species.Haxorus;
            var           pk      = new PK5 {
                Species = (int)species
            };
            var ez = EncounterMovesetGenerator.GenerateEncounters(pk, pk.Moves, GameVersion.W2).OfType <EncounterStatic>().First();

            ez.Should().NotBeNull("Shiny Haxorus stationary encounter exists for B2/W2");

            var criteria = EncounterCriteria.Unrestricted;
            var tr       = new SimpleTrainerInfo(GameVersion.B2)
            {
                TID = 57600,
                SID = 62446,
            };

            for (var nature = Nature.Hardy; nature <= Nature.Quirky; nature++)
            {
                criteria = criteria with {
                    Nature = nature
                };
                var pkm = ez.ConvertToPKM(tr, criteria);
                pkm.Nature.Should().Be((int)nature, "not nature locked");
                pkm.IsShiny.Should().BeTrue("encounter is shiny locked");
                pkm.TID.Should().Be(tr.TID);
                pkm.SID.Should().Be(tr.SID);
            }
        }
    }
Exemple #3
0
 /// <summary>
 /// Clone trainerdata and mutate the language and then return the clone
 /// </summary>
 /// <param name="tr">Trainerdata to clone</param>
 /// <param name="lang">language to mutate</param>
 /// <returns></returns>
 public static ITrainerInfo MutateLanguage(this ITrainerInfo tr, LanguageID?lang)
 {
     if (lang == LanguageID.UNUSED_6 || lang == LanguageID.Hacked || lang == null)
     {
         return(tr);
     }
     if (tr is PokeTrainerDetails p)
     {
         var clone = PokeTrainerDetails.Clone(p);
         clone.Language = (int)lang;
         return(clone);
     }
     if (tr is SimpleTrainerInfo s)
     {
         var sti = new SimpleTrainerInfo((GameVersion)s.Game);
         sti.OT            = s.OT;
         sti.TID           = s.TID;
         sti.SID           = s.SID;
         sti.Generation    = s.Gender;
         sti.Language      = (int)lang;
         sti.ConsoleRegion = s.ConsoleRegion;
         sti.Region        = s.Region;
         sti.Country       = s.Country;
         sti.Generation    = s.Generation;
         return(sti);
     }
     return(tr);
 }
Exemple #4
0
        /// <summary>
        /// Main function that auto legalizes based on the legality
        /// </summary>
        /// <remarks>Leverages <see cref="Core"/>'s <see cref="EncounterMovesetGenerator"/> to create a <see cref="PKM"/> from a <see cref="ShowdownSet"/>.</remarks>
        /// <param name="dest">Destination for the generated pkm</param>
        /// <param name="template">rough pkm that has all the <see cref="set"/> values entered</param>
        /// <param name="set">Showdown set object</param>
        /// <param name="satisfied">If the final result is satisfactory, otherwise use deprecated bruteforce auto legality functionality</param>
        public static PKM GetLegalFromTemplate(this ITrainerInfo dest, PKM template, ShowdownSet set, out bool satisfied)
        {
            var Form = SanityCheckForm(template, ref set);

            template.ApplySetDetails(set);
            template.SetRecordFlags(); // Validate TR moves for the encounter
            var destType = template.GetType();
            var destVer  = (GameVersion)dest.Game;

            if (destVer <= 0 && dest is SaveFile s)
            {
                destVer = s.Version;
            }

            var gamelist   = GameUtil.GetVersionsWithinRange(template, template.Format).OrderByDescending(c => c.GetGeneration()).ToArray();
            var encounters = EncounterMovesetGenerator.GenerateEncounters(pk: template, moves: set.Moves, gamelist);

            foreach (var enc in encounters)
            {
                var          ver = enc is IVersion v ? v.Version : destVer;
                var          gen = enc is IGeneration g ? g.Generation : dest.Generation;
                ITrainerInfo tr  = new SimpleTrainerInfo(ver);
                if (UseTrainerData)
                {
                    tr = TrainerSettings.GetSavedTrainerData(ver, gen, new SimpleTrainerInfo(ver));
                }
                var raw = SanityCheckEncounters(enc).ConvertToPKM(tr);
                var pk  = PKMConverter.ConvertToType(raw, destType, out _);
                if (pk == null)
                {
                    continue;
                }

                ApplySetDetails(pk, set, Form, raw, dest, enc);
                if (set.CanGigantamax && pk is IGigantamax gmax)
                {
                    if (!gmax.CanGigantamax)
                    {
                        continue;
                    }
                }

                var la = new LegalityAnalysis(pk);
                if (la.Valid)
                {
                    satisfied = true;
                    return(pk);
                }
                Debug.WriteLine(la.Report());
            }
            satisfied = false;
            return(template);
        }
Exemple #5
0
        public static bool GetTrainerInfo(IEnumerable <string> lines, int format, out ITrainerInfo tr)
        {
            var sti = new SimpleTrainerInfo {
                Generation = format
            };

            var  split = Split(lines);
            bool any   = false;

            foreach (var s in split)
            {
                var key   = s.Key;
                var value = s.Value;
                switch (key)
                {
                case "OT":
                    sti.OT = value;
                    break;

                case "TID" when int.TryParse(value, out int tid) && tid > 0:
                    sti.TID = tid;

                    break;

                case "SID" when int.TryParse(value, out int sid) && sid > 0:
                    sti.SID = sid;

                    break;

                case "OTGender":
                    sti.Gender = value is "Female" or "F" ? 1 : 0;
                    break;

                default:
                    continue;
                }

                any = true;
            }
            tr = sti;
            if (!any || format < 7)
            {
                return(any);
            }
            const int mil    = 1_000_000;
            uint      repack = ((uint)sti.SID * mil) + (uint)sti.TID;

            sti.TID = (int)(repack & 0xFFFF);
            sti.SID = (int)(repack >> 16);
            return(true);
        }
 //[TestMethod]
 //[TestCategory(SimulatorParse)]
 public void TestGenerate()
 {
     for (int i = 1; i <= 807; i++)
     {
         var tr = new SimpleTrainerInfo();
         var pk = new PK7 {
             Species = i
         };
         var ez = EncounterMovesetGenerator.GeneratePKMs(pk, tr);
         Debug.WriteLine($"Starting {i:000}");
         bool v = ez.Select(p => new LegalityAnalysis(p)).All(la => la.Valid);
         Debug.WriteLine($"Finished {i:000}");
         Debug.Assert(v);
     }
 }
    private static void InitializeTrainerDatabase()
    {
        var ot           = GetEnvOrThrow("PKHEX_DEFAULT_OT");
        var trainerId    = int.Parse(GetEnvOrThrow("PKHEX_DEFAULT_TID"));
        var secretId     = int.Parse(GetEnvOrThrow("PKHEX_DEFAULT_SID"));
        var languageName = GetEnvOrThrow("PKHEX_DEFAULT_LANGUAGE");

        if (!Enum.TryParse <LanguageID>(languageName, true, out var language))
        {
            throw new Exception($"Invalid default language {languageName}");
        }

        SaveFile GetFallbackBlank(int generation)
        {
            var blankSav = SaveUtil.GetBlankSAV(generation, ot);

            blankSav.Language = (int)language;
            blankSav.TID      = trainerId;
            blankSav.SID      = secretId;
            blankSav.OT       = ot;
            return(blankSav);
        }

        for (var i = 1; i < PKX.Generation + 1; i++)
        {
            var versions = GameUtil.GetVersionsInGeneration(i, PKX.Generation);
            foreach (var v in versions)
            {
                var fallback = new SimpleTrainerInfo(v)
                {
                    Language = (int)language,
                    TID      = trainerId,
                    SID      = secretId,
                    OT       = ot,
                };
                var exist = TrainerSettings.GetSavedTrainerData(v, i, fallback);
                if (exist is SimpleTrainerInfo)
                {
                    TrainerSettings.Register(fallback);
                }
            }
        }

        var trainer = TrainerSettings.GetSavedTrainerData(PKX.Generation);

        PKMConverter.SetPrimaryTrainer(trainer);
    }
Exemple #8
0
        public void SimulatorGetVCEgg1()
        {
            var set = new ShowdownSet(SetSlowpoke12);
            var pk7 = new PK7 {
                Species = set.Species, AltForm = set.FormIndex, Moves = set.Moves, HT_Name = "PKHeX"
            };
            var encs = EncounterMovesetGenerator.GenerateEncounters(pk7, set.Moves, GameVersion.GD).ToList();

            Assert.True(encs.Count > 0);

            var info = new SimpleTrainerInfo(GameVersion.SN);
            var enc  = encs[0];
            var pk   = enc.ConvertToPKM(info);

            var la = new LegalityAnalysis(pk);

            Assert.True(la.Valid);
        }
Exemple #9
0
        public void SimulatorGetSplitBreed()
        {
            var set = new ShowdownSet(SetMunchSnorLax);
            var pk7 = new PK7 {
                Species = set.Species, AltForm = set.FormIndex, Moves = set.Moves, HT_Name = "PKHeX"
            };                                                                                                          // !! specify the HT name, we need tutors for this one
            var encs = EncounterMovesetGenerator.GenerateEncounters(pk7, set.Moves, GameVersion.SN).ToList();

            Assert.True(encs.Count > 0);
            Assert.True(encs.All(z => z.Species > 150));

            var info = new SimpleTrainerInfo(GameVersion.SN);
            var enc  = encs[0];
            var pk   = enc.ConvertToPKM(info);

            var la = new LegalityAnalysis(pk);

            Assert.True(la.Valid);
        }
Exemple #10
0
        public void PokemonGenerationReturnsLegalPokemon(int species)
        {
            int count = 0;
            var tr    = new SimpleTrainerInfo(GameVersion.SN);

            var pk = new PK7 {
                Species = species
            };

            pk.Gender = pk.GetSaneGender();
            var ez = EncounterMovesetGenerator.GeneratePKMs(pk, tr);

            foreach (var e in ez)
            {
                var la = new LegalityAnalysis(e);
                la.Valid.Should().BeTrue($"Because generated Pokemon {count} for {species:000} should be valid");
                Assert.True(la.Valid);
                count++;
            }
        }
Exemple #11
0
        public void SimulatorGetCelebi()
        {
            var set = new ShowdownSet(SetCelebi);
            var pk7 = new PK7 {
                Species = set.Species, AltForm = set.FormIndex, Moves = set.Moves
            };
            var encs = EncounterMovesetGenerator.GenerateEncounters(pk7, set.Moves, GameVersion.X);

            Assert.IsTrue(encs.Any());
            encs = EncounterMovesetGenerator.GenerateEncounters(pk7, set.Moves, GameVersion.X);
            var first = encs.FirstOrDefault();

            Assert.IsTrue(first != null);

            var enc  = first;
            var info = new SimpleTrainerInfo();
            var pk   = enc.ConvertToPKM(info);

            var la = new LegalityAnalysis(pk);

            Assert.IsTrue(la.Valid);
        }
Exemple #12
0
        public void SimulatorGetWC3()
        {
            var set = new ShowdownSet(SetROCKSMetang);
            var pk3 = new PK3 {
                Species = set.Species, AltForm = set.FormIndex, Moves = set.Moves
            };
            var encs = EncounterMovesetGenerator.GenerateEncounters(pk3, set.Moves, GameVersion.R);

            Assert.IsTrue(encs.Any());
            encs = EncounterMovesetGenerator.GenerateEncounters(pk3, set.Moves, GameVersion.R);
            var first = encs.FirstOrDefault();

            Assert.IsTrue(first != null);

            var wc3  = (WC3)first;
            var info = new SimpleTrainerInfo();
            var pk   = wc3.ConvertToPKM(info);

            var la = new LegalityAnalysis(pk);

            Assert.IsTrue(la.Valid);
        }
Exemple #13
0
        private static void InitializeTrainerDatabase(LegalitySettings cfg)
        {
            // Seed the Trainer Database with enough fake save files so that we return a generation sensitive format when needed.
            string OT   = cfg.GenerateOT;
            int    TID  = cfg.GenerateTID16;
            int    SID  = cfg.GenerateSID16;
            int    lang = (int)cfg.GenerateLanguage;

            var externalSource = cfg.GeneratePathTrainerInfo;

            if (!string.IsNullOrWhiteSpace(externalSource) && Directory.Exists(externalSource))
            {
                TrainerSettings.LoadTrainerDatabaseFromPath(externalSource);
            }

            for (int i = 1; i < PKX.Generation + 1; i++)
            {
                var versions = GameUtil.GetVersionsInGeneration(i, PKX.Generation);
                foreach (var v in versions)
                {
                    var fallback = new SimpleTrainerInfo(v)
                    {
                        Language = lang,
                        TID      = TID,
                        SID      = SID,
                        OT       = OT,
                    };
                    var exist = TrainerSettings.GetSavedTrainerData(v, i, fallback);
                    if (exist is SimpleTrainerInfo) // not anything from files; this assumes ALM returns SimpleTrainerInfo for non-user-provided fake templates.
                    {
                        TrainerSettings.Register(fallback);
                    }
                }
            }

            var trainer = TrainerSettings.GetSavedTrainerData(PKX.Generation);

            PKMConverter.SetPrimaryTrainer(trainer);
        }
        public void SimulatorGetSmeargle()
        {
            var set = new ShowdownSet(SetSmeargle);
            var pk7 = new PK7 {
                Species = set.Species, Form = set.Form, Moves = set.Moves
            };
            var encs = EncounterMovesetGenerator.GenerateEncounters(pk7, set.Moves, GameVersion.MN);

            Assert.True(encs.Any());
            encs = EncounterMovesetGenerator.GenerateEncounters(pk7, set.Moves, GameVersion.MN);
            var first = encs.FirstOrDefault();

            Assert.NotNull(first);

            var enc  = first !;
            var info = new SimpleTrainerInfo(GameVersion.SN);
            var pk   = enc.ConvertToPKM(info);

            var la = new LegalityAnalysis(pk);

            Assert.True(la.Valid);
        }
Exemple #15
0
        //[TestMethod]
        //[TestCategory(SimulatorParse)]
        public void TestGenerate()
        {
            int count = 0;
            var tr    = new SimpleTrainerInfo();

            for (int i = 1; i <= 807; i++)
            {
                var pk = new PK7 {
                    Species = i
                };
                pk.Gender = pk.GetSaneGender();
                var ez = EncounterMovesetGenerator.GeneratePKMs(pk, tr);
                Debug.WriteLine($"Starting {i:000}");
                foreach (var e in ez)
                {
                    var la = new LegalityAnalysis(e);
                    Assert.IsTrue(la.Valid);
                    count++;
                }
                Debug.WriteLine($"Finished {i:000}");
            }
            Debug.WriteLine($"Generated {count} PKMs!");
        }
Exemple #16
0
        private async Task <PokeTradeResult> PerformLinkCodeTrade(SAV8SWSH sav, PokeTradeDetail <PK8> poke, CancellationToken token)
        {
            // Update Barrier Settings
            UpdateBarrier(poke.IsSynchronized);
            poke.TradeInitialize(this);
            Hub.Config.Stream.EndEnterCode(this);

            if (await CheckIfSoftBanned(token).ConfigureAwait(false))
            {
                await Unban(token).ConfigureAwait(false);
            }

            var pkm = poke.TradeData;

            if (pkm.Species != 0)
            {
                await SetBoxPokemon(pkm, InjectBox, InjectSlot, token, sav).ConfigureAwait(false);
            }

            if (!await IsOnOverworld(Hub.Config, token).ConfigureAwait(false))
            {
                await ExitTrade(Hub.Config, true, token).ConfigureAwait(false);

                return(PokeTradeResult.RecoverStart);
            }

            while (await CheckIfSearchingForLinkTradePartner(token).ConfigureAwait(false))
            {
                Log("Still searching, reset bot position.");
                await ResetTradePosition(Hub.Config, token).ConfigureAwait(false);
            }

            Log("Opening Y-Comm Menu");
            await Click(Y, 2_000, token).ConfigureAwait(false);

            Log("Selecting Link Trade");
            await Click(A, 1_500, token).ConfigureAwait(false);

            Log("Selecting Link Trade Code");
            await Click(DDOWN, 500, token).ConfigureAwait(false);

            for (int i = 0; i < 2; i++)
            {
                await Click(A, 1_500, token).ConfigureAwait(false);
            }

            // All other languages require an extra A press at this menu.
            if (GameLang != LanguageID.English && GameLang != LanguageID.Spanish)
            {
                await Click(A, 1_500, token).ConfigureAwait(false);
            }

            // Loading Screen
            if (poke.Type != PokeTradeType.Random)
            {
                Hub.Config.Stream.StartEnterCode(this);
            }
            await Task.Delay(Hub.Config.Timings.ExtraTimeOpenCodeEntry, token).ConfigureAwait(false);

            var code = poke.Code;

            Log($"Entering Link Trade Code: {code:0000 0000}...");
            await EnterTradeCode(code, Hub.Config, token).ConfigureAwait(false);

            // Wait for Barrier to trigger all bots simultaneously.
            WaitAtBarrierIfApplicable(token);
            await Click(PLUS, 1_000, token).ConfigureAwait(false);

            Hub.Config.Stream.EndEnterCode(this);

            // Confirming and return to overworld.
            var delay_count = 0;

            while (!await IsOnOverworld(Hub.Config, token).ConfigureAwait(false))
            {
                if (delay_count >= 5)
                {
                    await ExitTrade(Hub.Config, true, token).ConfigureAwait(false);

                    return(PokeTradeResult.RecoverPostLinkCode);
                }

                for (int i = 0; i < 5; i++)
                {
                    await Click(A, 0_800, token).ConfigureAwait(false);
                }
                delay_count++;
            }

            poke.TradeSearching(this);
            await Task.Delay(0_500, token).ConfigureAwait(false);

            // Wait for a Trainer...
            Log("Waiting for trainer...");
            bool partnerFound = await WaitForPokemonChanged(LinkTradePartnerPokemonOffset, Hub.Config.Trade.TradeWaitTime * 1_000, 0_200, token).ConfigureAwait(false);

            if (token.IsCancellationRequested)
            {
                return(PokeTradeResult.Aborted);
            }
            if (!partnerFound)
            {
                await ResetTradePosition(Hub.Config, token).ConfigureAwait(false);

                return(PokeTradeResult.NoTrainerFound);
            }

            // Select Pokemon
            // pkm already injected to b1s1
            await Task.Delay(5_500, token).ConfigureAwait(false); // necessary delay to get to the box properly

            var TrainerName = await GetTradePartnerName(TradeMethod.LinkTrade, token).ConfigureAwait(false);

            Log($"Found Trading Partner: {TrainerName}...");

            if (!await IsInBox(token).ConfigureAwait(false))
            {
                await ExitTrade(Hub.Config, true, token).ConfigureAwait(false);

                return(PokeTradeResult.RecoverOpenBox);
            }

            // Confirm Box 1 Slot 1
            if (poke.Type == PokeTradeType.Specific || poke.Type == PokeTradeType.TradeCord || poke.Type == PokeTradeType.Giveaway)
            {
                for (int i = 0; i < 5; i++)
                {
                    await Click(A, 0_500, token).ConfigureAwait(false);
                }
            }

            poke.SendNotification(this, $"Found Trading Partner: {TrainerName}. Waiting for a Pokémon...");

            if (poke.Type == PokeTradeType.Dump)
            {
                return(await ProcessDumpTradeAsync(poke, token).ConfigureAwait(false));
            }

            // Wait for User Input...
            var pk = await ReadUntilPresent(LinkTradePartnerPokemonOffset, 25_000, 1_000, token).ConfigureAwait(false);

            var oldEC = await Connection.ReadBytesAsync(LinkTradePartnerPokemonOffset, 4, token).ConfigureAwait(false);

            if (pk == null)
            {
                if (poke.Type == PokeTradeType.Seed)
                {
                    await ExitSeedCheckTrade(Hub.Config, token).ConfigureAwait(false);
                }
                else
                {
                    await ExitTrade(Hub.Config, true, token).ConfigureAwait(false);
                }
                return(PokeTradeResult.TrainerTooSlow);
            }

            if (poke.Type == PokeTradeType.Seed)
            {
                // Immediately exit, we aren't trading anything.
                return(await EndSeedCheckTradeAsync(poke, pk, token).ConfigureAwait(false));
            }

            SpecialTradeType itemReq = SpecialTradeType.None;

            if (poke.Type == PokeTradeType.SpecialRequest)
            {
                itemReq = CheckItemRequest(ref pk, this, poke, TrainerName, sav);
            }
            if (itemReq == SpecialTradeType.FailReturn)
            {
                await ExitTrade(Hub.Config, false, token).ConfigureAwait(false);

                return(PokeTradeResult.IllegalTrade);
            }

            if (poke.Type == PokeTradeType.SpecialRequest && itemReq == SpecialTradeType.None)
            {
                // Immediately exit, we aren't trading anything.
                poke.SendNotification(this, "Held item was outside the bounds of the Array, or nothing was held.");
                await ResetTradePosition(Hub.Config, token).ConfigureAwait(false);

                return(PokeTradeResult.IncorrectHeldItem);
            }

            if (poke.Type == PokeTradeType.Random) // distribution
            {
                // Allow the trade partner to do a Ledy swap.
                var trade = Hub.Ledy.GetLedyTrade(pk, Hub.Config.Distribution.LedySpecies);
                if (trade != null)
                {
                    pkm            = trade.Receive;
                    poke.TradeData = pkm;

                    poke.SendNotification(this, "Injecting the requested Pokémon.");
                    await Click(A, 0_800, token).ConfigureAwait(false);
                    await SetBoxPokemon(pkm, InjectBox, InjectSlot, token, sav).ConfigureAwait(false);

                    await Task.Delay(2_500, token).ConfigureAwait(false);
                }

                for (int i = 0; i < 5; i++)
                {
                    await Click(A, 0_500, token).ConfigureAwait(false);
                }
            }
            else if (poke.Type == PokeTradeType.Clone || itemReq != SpecialTradeType.None)
            {
                // Inject the shown Pokémon.
                var clone = (PK8)pk.Clone();
                if (itemReq != SpecialTradeType.WonderCard)
                {
                    if (Hub.Config.Discord.ReturnPK8s)
                    {
                        poke.SendNotification(this, clone, "Here's what you showed me!");
                    }

                    var la = new LegalityAnalysis(clone);
                    if (!la.Valid)
                    {
                        Log($"Clone request has detected an invalid Pokémon: {(Species)clone.Species}");
                        if (DumpSetting.Dump)
                        {
                            DumpPokemon(DumpSetting.DumpFolder, "hacked", pk);
                        }

                        var report = la.Report();
                        Log(report);
                        poke.SendNotification(this, "This Pokémon is not legal per PKHeX's legality checks. I am forbidden from cloning this. Exiting trade.");
                        if (itemReq != SpecialTradeType.None)
                        {
                            poke.SendNotification(this, "SSRYour request isn't legal. Please try a different Pokémon or request.");
                            if (!string.IsNullOrWhiteSpace(Hub.Config.Web.URIEndpoint))
                            {
                                SpecialRequests.AddToPlayerLimit(TrainerName, -1);
                            }
                        }

                        poke.SendNotification(this, report);

                        await ExitTrade(Hub.Config, true, token).ConfigureAwait(false);

                        return(PokeTradeResult.IllegalTrade);
                    }

                    if (Hub.Config.Legality.ResetHOMETracker)
                    {
                        clone.Tracker = 0;
                    }


                    poke.SendNotification(this, $"**Cloned your {(Species)clone.Species}!**\nNow press B to cancel your offer and trade me a Pokémon you don't want.");
                    Log($"Cloned a {(Species)clone.Species}. Waiting for user to change their Pokémon...");
                }

                if (itemReq != SpecialTradeType.WonderCard)
                {
                    // Separate this out from WaitForPokemonChanged since we compare to old EC from original read.
                    partnerFound = await ReadUntilChanged(LinkTradePartnerPokemonOffset, oldEC, 15_000, 0_200, false, token).ConfigureAwait(false);

                    if (!partnerFound)
                    {
                        poke.SendNotification(this, "__**Change It Now Or I Am Leaving!**__");
                        // They get one more chance.
                        partnerFound = await ReadUntilChanged(LinkTradePartnerPokemonOffset, oldEC, 15_000, 0_200, false, token).ConfigureAwait(false);
                    }

                    var pk2 = await ReadUntilPresent(LinkTradePartnerPokemonOffset, 3_000, 1_000, token).ConfigureAwait(false);

                    if (!partnerFound || pk2 == null || SearchUtil.HashByDetails(pk2) == SearchUtil.HashByDetails(pk))
                    {
                        Log("Trading partner did not change their Pokémon.");
                        await ExitTrade(Hub.Config, true, token).ConfigureAwait(false);

                        return(PokeTradeResult.TrainerTooSlow);
                    }
                }

                await Click(A, 0_800, token).ConfigureAwait(false);
                await SetBoxPokemon(clone, InjectBox, InjectSlot, token, sav).ConfigureAwait(false);

                pkm = pk;

                if (itemReq == SpecialTradeType.WonderCard)
                {
                    poke.SendNotification(this, "SSRDistribution success!");
                    Log($"{(Species)clone.Species} Distribution Success!");
                }
                else if (itemReq != SpecialTradeType.None && itemReq != SpecialTradeType.Shinify)
                {
                    poke.SendNotification(this, "SSRSpecial request successful!");
                    Log($"{(Species)clone.Species} modification successful!");
                }
                else if (itemReq == SpecialTradeType.Shinify)
                {
                    poke.SendNotification(this, "Shinify success!");
                    Log($"Successfully Shinified a {(Species)clone.Species}!");


                    for (int i = 0; i < 5; i++)
                    {
                        await Click(A, 0_500, token).ConfigureAwait(false);
                    }
                }
            }
            else if (poke.Type == PokeTradeType.FixOT)
            {
                var clone = (PK8)pk.Clone();
                var adOT  = System.Text.RegularExpressions.Regex.Match(clone.OT_Name, @"(YT$)|(YT\w*$)|(Lab$)|(\.\w*)|(TV$)|(PKHeX)|(FB:)|(SysBot)|(AuSLove)|(ShinyMart)|(Blainette)").Value != "" ||
                            System.Text.RegularExpressions.Regex.Match(clone.Nickname, @"(YT$)|(YT\w*$)|(Lab$)|(\.\w*)|(TV$)|(PKHeX)|(FB:)|(SysBot)|(AuSLove)|(ShinyMart)|(Blainette)").Value != "";

                var extraInfo = $"\nBall: {(Ball)clone.Ball}\nShiny: {(clone.ShinyXor == 0 ? "Square" : clone.ShinyXor <= 16 ? "Star" : "No")}{(clone.FatefulEncounter ? "" : $"\nOT: {TrainerName}")}";
                var laInit    = new LegalityAnalysis(clone);
                if (laInit.Valid && adOT)
                {
                    clone.OT_Name       = clone.FatefulEncounter ? clone.OT_Name : $"{TrainerName}";
                    clone.PKRS_Infected = false;
                    clone.PKRS_Cured    = false;
                    clone.PKRS_Days     = 0;
                    clone.PKRS_Strain   = 0;
                }
                else if (!laInit.Valid)
                {
                    Log($"FixOT request has detected an invalid Pokémon from {poke.Trainer.TrainerName}: {(Species)clone.Species}");
                    if (DumpSetting.Dump)
                    {
                        DumpPokemon(DumpSetting.DumpFolder, "hacked", clone);
                    }

                    poke.SendNotification(this, $"```fix\nShown Pokémon is invalid. Attempting to regenerate... \n{laInit.Report()}```");
                    if (clone.FatefulEncounter)
                    {
                        clone.SetDefaultNickname(laInit);
                        var info = new SimpleTrainerInfo {
                            Gender = clone.OT_Gender, Language = clone.Language, OT = TrainerName, TID = clone.TID, SID = clone.SID
                        };
                        var mg = EncounterEvent.GetAllEvents().Where(x => x.Species == clone.Species && x.Form == clone.Form && x.IsShiny == clone.IsShiny && x.OT_Name == clone.OT_Name).ToList();
                        if (mg.Count > 0)
                        {
                            clone = TradeExtensions.CherishHandler(mg.First(), info);
                        }
                        else
                        {
                            clone = (PK8)AutoLegalityWrapper.GetTrainerInfo(8).GetLegal(AutoLegalityWrapper.GetTemplate(new ShowdownSet(ShowdownParsing.GetShowdownText(clone) + extraInfo)), out _);
                        }
                    }
                    else
                    {
                        clone = (PK8)AutoLegalityWrapper.GetTrainerInfo(8).GetLegal(AutoLegalityWrapper.GetTemplate(new ShowdownSet(ShowdownParsing.GetShowdownText(clone) + extraInfo)), out _);
                    }
                    var laRegen = new LegalityAnalysis(clone);
                    if (laRegen.Valid)
                    {
                        poke.SendNotification(this, $"```fix\nRegenerated and legalized your {(Species)clone.Species}!```");
                    }
                }
                else if (!adOT && laInit.Valid)
                {
                    poke.SendNotification(this, "```fix\nNo ad detected in Nickname or OT, and the Pokémon is legal. Exiting trade.```");
                    await ExitTrade(Hub.Config, true, token).ConfigureAwait(false);

                    return(PokeTradeResult.Aborted);
                }

                clone = (PK8)TradeExtensions.TrashBytes(clone, new LegalityAnalysis(clone));
                var la = new LegalityAnalysis(clone);
                if (!la.Valid)
                {
                    var report = la.Report();
                    Log(report);
                    poke.SendNotification(this, "This Pokémon is not legal per PKHeX's legality checks. I was unable to fix this. Exiting trade.");
                    poke.SendNotification(this, report);
                    await ExitTrade(Hub.Config, true, token).ConfigureAwait(false);

                    return(PokeTradeResult.IllegalTrade);
                }

                if (Hub.Config.Legality.ResetHOMETracker)
                {
                    clone.Tracker = 0;
                }

                poke.SendNotification(this, $"```fix\nNow confirm the trade!```");
                Log($"{(!laInit.Valid ? "Legalized" : "Fixed Nickname/OT for")} {(Species)clone.Species}!");

                bool changed = await ReadUntilChanged(LinkTradePartnerPokemonOffset, oldEC, 10_000, 0_200, false, token).ConfigureAwait(false);

                if (changed)
                {
                    Log($"{poke.Trainer.TrainerName} changed the shown Pokémon ({(Species)clone.Species})");
                    poke.SendNotification(this, $"```fix\nSend away the originally shown Pokémon, please.```");
                    changed = !await ReadUntilChanged(LinkTradePartnerPokemonOffset, oldEC, 15_000, 0_200, true, token).ConfigureAwait(false);
                }

                var pk2 = await ReadUntilPresent(LinkTradePartnerPokemonOffset, 3_000, 1_000, token).ConfigureAwait(false);

                if (changed || pk2 == null || SearchUtil.HashByDetails(pk2) != SearchUtil.HashByDetails(pk))
                {
                    Log("Trading partner did not wish to send away their ad-mon.");
                    await ExitTrade(Hub.Config, true, token).ConfigureAwait(false);

                    return(PokeTradeResult.TrainerTooSlow);
                }

                await Click(A, 0_800, token).ConfigureAwait(false);
                await SetBoxPokemon(clone, InjectBox, InjectSlot, token, sav).ConfigureAwait(false);

                pkm = clone;

                for (int i = 0; i < 5; i++)
                {
                    await Click(A, 0_500, token).ConfigureAwait(false);
                }
            }
            else if (poke.Type == PokeTradeType.Clone)
            {
                // Inject the shown Pokémon.
                var clone = (PK8)pk.Clone();

                if (Hub.Config.Discord.ReturnPK8s)
                {
                    poke.SendNotification(this, clone, "Here's what you showed me!");
                }

                var la = new LegalityAnalysis(clone);
                if (!la.Valid)
                {
                    Log($"Clone request (from {poke.Trainer.TrainerName}) has detected an invalid Pokémon: {(Species)clone.Species}.");
                    if (DumpSetting.Dump)
                    {
                        DumpPokemon(DumpSetting.DumpFolder, "hacked", clone);
                    }

                    var report = la.Report();
                    Log(report);
                    poke.SendNotification(this, "This Pokémon is not legal per PKHeX's legality checks. I am forbidden from cloning this. Exiting trade.");
                    poke.SendNotification(this, report);

                    await ExitTrade(Hub.Config, true, token).ConfigureAwait(false);

                    return(PokeTradeResult.IllegalTrade);
                }

                if (Hub.Config.Legality.ResetHOMETracker)
                {
                    clone.Tracker = 0;
                }

                poke.SendNotification(this, $"**Cloned your {(Species)clone.Species}!**\nNow press B to cancel your offer and trade me a Pokémon you don't want.");
                Log($"Cloned a {(Species)clone.Species}. Waiting for user to change their Pokémon...");

                // Separate this out from WaitForPokemonChanged since we compare to old EC from original read.
                partnerFound = await ReadUntilChanged(LinkTradePartnerPokemonOffset, oldEC, 15_000, 0_200, false, token).ConfigureAwait(false);

                if (!partnerFound)
                {
                    poke.SendNotification(this, "**HEY CHANGE IT NOW OR I AM LEAVING!!!**");
                    // They get one more chance.
                    partnerFound = await ReadUntilChanged(LinkTradePartnerPokemonOffset, oldEC, 15_000, 0_200, false, token).ConfigureAwait(false);
                }

                var pk2 = await ReadUntilPresent(LinkTradePartnerPokemonOffset, 3_000, 1_000, token).ConfigureAwait(false);

                if (!partnerFound || pk2 == null || SearchUtil.HashByDetails(pk2) == SearchUtil.HashByDetails(pk))
                {
                    Log("Trading partner did not change their Pokémon.");
                    await ExitTrade(Hub.Config, true, token).ConfigureAwait(false);

                    return(PokeTradeResult.TrainerTooSlow);
                }

                await Click(A, 0_800, token).ConfigureAwait(false);
                await SetBoxPokemon(clone, InjectBox, InjectSlot, token, sav).ConfigureAwait(false);

                pkm = clone;

                for (int i = 0; i < 5; i++)
                {
                    await Click(A, 0_500, token).ConfigureAwait(false);
                }
            }

            await Click(A, 3_000, token).ConfigureAwait(false);

            for (int i = 0; i < 5; i++)
            {
                await Click(A, 1_500, token).ConfigureAwait(false);
            }

            delay_count = 0;
            while (!await IsInBox(token).ConfigureAwait(false))
            {
                await Click(A, 3_000, token).ConfigureAwait(false);

                delay_count++;
                if (delay_count >= 50)
                {
                    break;
                }
                if (await IsOnOverworld(Hub.Config, token).ConfigureAwait(false)) // In case we are in a Trade Evolution/PokeDex Entry and the Trade Partner quits we land on the Overworld
                {
                    break;
                }
            }

            await Task.Delay(1_000 + Util.Rand.Next(0_700, 1_000), token).ConfigureAwait(false);

            await ExitTrade(Hub.Config, false, token).ConfigureAwait(false);

            Log("Exited Trade!");

            if (token.IsCancellationRequested)
            {
                return(PokeTradeResult.Aborted);
            }

            // Trade was Successful!
            var traded = await ReadBoxPokemon(InjectBox, InjectSlot, token).ConfigureAwait(false);

            // Pokémon in b1s1 is same as the one they were supposed to receive (was never sent).
            if (poke.Type != PokeTradeType.FixOT && SearchUtil.HashByDetails(traded) == SearchUtil.HashByDetails(pkm))
            {
                Log("User did not complete the trade.");
                return(PokeTradeResult.TrainerTooSlow);
            }
            else
            {
                // As long as we got rid of our inject in b1s1, assume the trade went through.
                Log("User completed the trade.");
                poke.TradeFinished(this, traded);

                // Only log if we completed the trade.
                var counts = Hub.Counts;
                if (poke.Type == PokeTradeType.Random)
                {
                    counts.AddCompletedDistribution();
                }
                else if (poke.Type == PokeTradeType.Clone)
                {
                    counts.AddCompletedClones();
                }
                else if (poke.Type == PokeTradeType.FixOT)
                {
                    counts.AddCompletedFixOTs();
                }
                else if (poke.Type == PokeTradeType.SpecialRequest)
                {
                    counts.AddCompletedSpecialRequests();
                }
                else if (poke.Type == PokeTradeType.TradeCord)
                {
                    counts.AddCompletedTradeCords();
                }
                else if (poke.Type == PokeTradeType.Giveaway)
                {
                    counts.AddCompletedGiveaways();
                }
                else
                {
                    Hub.Counts.AddCompletedTrade();
                }

                if (DumpSetting.Dump && !string.IsNullOrEmpty(DumpSetting.DumpFolder))
                {
                    var subfolder = poke.Type.ToString().ToLower();
                    DumpPokemon(DumpSetting.DumpFolder, subfolder, traded); // received
                    if (poke.Type == PokeTradeType.Specific || poke.Type == PokeTradeType.Clone || poke.Type == PokeTradeType.FixOT || poke.Type == PokeTradeType.TradeCord || poke.Type == PokeTradeType.Giveaway)
                    {
                        DumpPokemon(DumpSetting.DumpFolder, "traded", pkm); // sent to partner
                    }
                }
            }

            return(PokeTradeResult.Success);
        }