Esempio n. 1
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(true, token).ConfigureAwait(false);

                return(PokeTradeResult.RecoverStart);
            }

            if (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
            await Task.Delay(1_000, token).ConfigureAwait(false);

            Hub.Config.Stream.StartEnterCode(this);
            await Task.Delay(1_000, token).ConfigureAwait(false);

            var code = poke.Code;

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

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

            // Start a Link Trade, in case of Empty Slot/Egg/Bad Pokémon we press sometimes B to return to the Overworld and skip this Slot.
            // Confirming...
            for (int i = 0; i < 4; i++)
            {
                await Click(A, 1_000, token).ConfigureAwait(false);
            }
            Hub.Config.Stream.EndEnterCode(this);

            // Should be on overworld by now.
            if (!await IsOnOverworld(Hub.Config, token).ConfigureAwait(false))
            {
                await ExitTrade(true, token).ConfigureAwait(false);

                return(PokeTradeResult.RecoverPostLinkCode);
            }

            // Clear the shown data offset right as we start waiting on overworld.
            await Connection.WriteBytesAsync(PokeTradeBotUtil.EMPTY_SLOT, LinkTradePartnerPokemonOffset, token).ConfigureAwait(false);

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

            // Wait until search finishes
            // Wait 30 Seconds for Trainer...
            Log("Waiting for trainer ...");
            bool       partnerFound;
            const uint ofs = LinkTradePartnerPokemonOffset;

            if (Hub.Config.Trade.SpinTrade)
            {
                partnerFound = await SpinUntilChangedLink(30_000, token).ConfigureAwait(false);
            }
            else
            {
                partnerFound = await ReadUntilChanged(ofs, PokeTradeBotUtil.EMPTY_EC, 30_000, 0_200, token).ConfigureAwait(false);
            }

            // Wait 15 more seconds. First 30 seconds is for spin
            if (!partnerFound)
            {
                Log("Still no partner found.. waiting 15 more seconds");
                partnerFound =
                    await ReadUntilChanged(ofs, PokeTradeBotUtil.EMPTY_EC, 15_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
            var TrainerName = await GetTradePartnerName(TradeMethod.LinkTrade, token).ConfigureAwait(false);

            Log($"Found Trading Partner: {TrainerName} ...");
            await Task.Delay(1_500, token).ConfigureAwait(false); // necessary delay to get to the box properly

            if (!await IsCorrectScreen(CurrentScreen_Box, token).ConfigureAwait(false))
            {
                await ExitTrade(true, token).ConfigureAwait(false);

                return(PokeTradeResult.RecoverOpenBox);
            }

            // Confirm Box 1 Slot 1
            if (poke.Type == PokeTradeType.Specific)
            {
                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);

            if (pk == null)
            {
                await ExitTrade(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));
            }

            if (poke.Type == PokeTradeType.Random) // distribution
            {
                // Allow the trade partner to do a Ledy swap.
                var trade = Hub.Ledy.GetLedyTrade(pk, Hub.Config.Distribute.LedySpecies);
                pkm            = trade.Receive;
                poke.TradeData = pkm;
                if (trade.Type != LedyResponseType.Random)
                {
                    poke.SendNotification(this, "Injecting your 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)
            {
                // 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 && Hub.Config.Legality.VerifyLegality)
                {
                    Log($"Clone request 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);

                    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...");

                // Clear the shown data offset.
                await Connection.WriteBytesAsync(PokeTradeBotUtil.EMPTY_SLOT, LinkTradePartnerPokemonOffset, token).ConfigureAwait(false);

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

                if (pk2 == null || SearchUtil.HashByDetails(pk2) == SearchUtil.HashByDetails(pk))
                {
                    poke.SendNotification(this, "**HEY CHANGE IT NOW OR I AM LEAVING!!!**");

                    // They get one more chance.
                    // Clear the shown data offset.
                    await Connection.WriteBytesAsync(PokeTradeBotUtil.EMPTY_SLOT, LinkTradePartnerPokemonOffset, token).ConfigureAwait(false);

                    // Wait for User Input...
                    pk2 = await ReadUntilPresent(LinkTradePartnerPokemonOffset, 5_000, 1_000, token).ConfigureAwait(false);

                    if (pk2 == null || SearchUtil.HashByDetails(pk2) == SearchUtil.HashByDetails(pk))
                    {
                        Log("Trading partner did not change their Pokémon.");
                        await ExitTrade(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);
            }

            var delay_count = 0;

            while (!await IsCorrectScreen(CurrentScreen_Box, 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(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 (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
                {
                    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)
                    {
                        DumpPokemon(DumpSetting.DumpFolder, "traded", pkm); // sent to partner
                    }
                }
            }

            return(PokeTradeResult.Success);
        }
Esempio n. 2
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
            await Task.Delay(1_000, token).ConfigureAwait(false);

            if (poke.Type != PokeTradeType.Random)
            {
                Hub.Config.Stream.StartEnterCode(this);
            }
            await Task.Delay(1_000, 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.EggRoll)
            {
                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, Config.ConnectionType, token).ConfigureAwait(false);

            if (pk == null)
            {
                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));
            }

            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.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)").Value != "" ||
                            System.Text.RegularExpressions.Regex.Match(clone.Nickname, @"(YT$)|(YT\w*$)|(Lab$)|(\.\w*)|(TV$)|(PKHeX)|(FB:)|(SysBot)|(AuSLove)|(ShinyMart)").Value != "";

                if (adOT && clone.OT_Name != $"{TrainerName}")
                {
                    clone.OT_Name = clone.FatefulEncounter ? clone.OT_Name : $"{TrainerName}";
                    clone.ClearNickname();
                    clone.PKRS_Infected = false;
                    clone.PKRS_Cured    = false;
                    clone.PKRS_Days     = 0;
                    clone.PKRS_Strain   = 0;
                    clone.MetDate       = DateTime.Parse("2020/10/20");
                    poke.SendNotification(this, $"```fix\nDetected an ad OT/Nickname with your {(Species)clone.Species}!```");
                }
                else
                {
                    poke.SendNotification(this, "```fix\nNo ad detected in Nickname or OT. Exiting trade...```");
                    await ExitTrade(Hub.Config, true, token).ConfigureAwait(false);

                    return(PokeTradeResult.IllegalTrade);
                }

                var la = new LegalityAnalysis(clone);
                if (!la.Valid && Hub.Config.Legality.VerifyLegality)
                {
                    Log($"FixOT request 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 fixing 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\nFixed your {(Species)clone.Species}! Now confirm the trade!```");
                Log($"Fixed Nickname/OT for {(Species)clone.Species}.");

                await ReadUntilPresent(LinkTradePartnerPokemonOffset, 3_000, 1_000, token).ConfigureAwait(false);
                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 && Hub.Config.Legality.VerifyLegality)
                {
                    Log($"Clone request 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.EggRoll)
                {
                    counts.AddCompletedEggRolls();
                }
                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.EggRoll)
                    {
                        DumpPokemon(DumpSetting.DumpFolder, "traded", pkm); // sent to partner
                    }
                }
            }

            return(PokeTradeResult.Success);
        }
Esempio n. 3
0
        private async Task <PokeTradeResult> PerformLinkCodeTrade(SAV8SWSH sav, PokeTradeDetail <PK8> poke, CancellationToken token)
        {
            // Update Barrier Settings
            UpdateBarrier(poke.IsSynchronized);
            poke.TradeInitialize(this);

            var pkm = poke.TradeData;

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

            if (!await IsCorrectScreen(CurrentScreen_Overworld, token).ConfigureAwait(false))
            {
                await ExitTrade(true, token).ConfigureAwait(false);

                return(PokeTradeResult.Recover);
            }

            Connection.Log("Opening Y-Comm Menu");
            await Click(Y, 1_700, token).ConfigureAwait(false);

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

            Connection.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);
            }

            // Korean requires an extra A press at this menu.
            if (GameLang == LanguageID.Korean)
            {
                await Click(A, 1_500, token).ConfigureAwait(false);
            }

            // Loading Screen
            await Task.Delay(2_000, token).ConfigureAwait(false);

            var code = poke.Code;

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

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

            // Start a Link Trade, in case of Empty Slot/Egg/Bad Pokemon we press sometimes B to return to the Overworld and skip this Slot.
            // Confirming...
            for (int i = 0; i < 4; i++)
            {
                await Click(A, 1_000, token).ConfigureAwait(false);
            }

            poke.TradeSearching(this);
            await Task.Delay(Util.Rand.Next(0_350, 0_750), token).ConfigureAwait(false);

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

            if (!await IsCorrectScreen(CurrentScreen_Overworld, token).ConfigureAwait(false))
            {
                await ExitTrade(true, token).ConfigureAwait(false);

                return(PokeTradeResult.Recover);
            }

            // Clear the shown data offset.
            await Connection.WriteBytesAsync(PokeTradeBotUtil.EMPTY_SLOT, LinkTradePartnerPokemonOffset, token).ConfigureAwait(false);

            // Wait 40 Seconds for Trainer...
            var partnerFound = await ReadUntilChanged(LinkTradePartnerPokemonOffset, PokeTradeBotUtil.EMPTY_EC, 90_000, 0_200, token).ConfigureAwait(false);

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

                return(PokeTradeResult.NoTrainerFound);
            }

            // Select Pokemon
            // pkm already injected to b1s1
            var TrainerName = await GetTradePartnerName(TradeMethod.LinkTrade, token).ConfigureAwait(false);

            Connection.Log($"Found Trading Partner: {TrainerName} ...");
            await Task.Delay(1_500, token).ConfigureAwait(false); // necessary delay to get to the box properly

            if (!await IsCorrectScreen(CurrentScreen_Box, token).ConfigureAwait(false))
            {
                await ExitTrade(true, token).ConfigureAwait(false);

                return(PokeTradeResult.Recover);
            }

            // Confirm Box 1 Slot 1
            if (poke.Type == PokeTradeType.Specific)
            {
                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 Pokemon ...");

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

            if (pk == null)
            {
                await ExitTrade(true, token).ConfigureAwait(false);

                return(PokeTradeResult.TrainerTooSlow);
            }

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

            if (poke.Type == PokeTradeType.Random) // distribution
            {
                // Allow the trade partner to do a Ledy swap.
                var trade = Hub.Ledy.GetLedyTrade(pk, Hub.Config.DistributeLedySpecies);
                pkm = trade.Receive;
                if (trade.Type != LedyResponseType.Random)
                {
                    poke.SendNotification(this, "Injecting your requested Pokemon.");
                    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)
            {
                // Inject the shown Pokemon.
                var clone = (PK8)pk.Clone();
                if (Hub.Config.ResetHOMETracker)
                {
                    clone.Tracker = 0;
                }

                poke.SendNotification(this, "Cloned. Change what you're offering!");
                Connection.Log("Waiting for trading partner to change their Pokemon...");

                // Clear the shown data offset.
                await Connection.WriteBytesAsync(PokeTradeBotUtil.EMPTY_SLOT, LinkTradePartnerPokemonOffset, token).ConfigureAwait(false);

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

                if (pk2 == null || SearchUtil.HashByDetails(pk2) == SearchUtil.HashByDetails(pk))
                {
                    Connection.Log("Trading partner did not change their Pokemon.");
                    await ExitTrade(true, token).ConfigureAwait(false);

                    return(PokeTradeResult.TrainerTooSlow);
                }

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

                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);
            }

            var delay_count = 0;

            while (!await IsCorrectScreen(CurrentScreen_Box, token).ConfigureAwait(false))
            {
                await Click(A, 3_000, token).ConfigureAwait(false);

                delay_count++;
                if (delay_count >= 50)
                {
                    break;
                }
                if (await IsCorrectScreen(CurrentScreen_Overworld, 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(false, token).ConfigureAwait(false);

            Connection.Log("Exited Trade!");

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

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

            if (SearchUtil.HashByDetails(traded) == SearchUtil.HashByDetails(pk))
            {
                Connection.Log("User traded the initially shown Pokémon.");
            }
            else if (SearchUtil.HashByDetails(traded) == SearchUtil.HashByDetails(pkm))
            {
                Connection.Log("User did not complete the trade.");
            }
            else
            {
                Connection.Log("User completed the trade.");
            }

            poke.TradeFinished(this, traded);

            var counts = Hub.Counts;

            if (poke.Type == PokeTradeType.Random)
            {
                counts.AddCompletedDistribution();
            }
            else if (poke.Type == PokeTradeType.Clone)
            {
                counts.AddCompletedClones();
            }
            else
            {
                Hub.Counts.AddCompletedTrade();
            }

            if (DumpSetting.Dump && !string.IsNullOrEmpty(DumpSetting.DumpFolder))
            {
                DumpPokemon(DumpSetting.DumpFolder, poke.Type.ToString().ToLower(), traded);
            }

            return(PokeTradeResult.Success);
        }
Esempio n. 4
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)
            {
                if (CheckForAdOT(pkm) && !Hub.Config.Legality.AllowAds)
                {
                    pkm.OT_Name = $"{Hub.Config.Legality.GenerateOT}";
                }

                if (CheckForAdNickname(pkm) && !Hub.Config.Legality.AllowAds)
                {
                    pkm.Nickname = pkm.ClearNickname();
                }

                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
            await Task.Delay(1_000, token).ConfigureAwait(false);

            if (poke.Type != PokeTradeType.Random)
            {
                Hub.Config.Stream.StartEnterCode(this);
            }
            await Task.Delay(1_000, 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 (poke.RequestedIgn != string.Empty && TrainerName != poke.RequestedIgn && (poke.Type == PokeTradeType.LanTrade || poke.Type == PokeTradeType.LanRoll))
            {
                poke.SendNotification(this, $"Found Trading Partner: {TrainerName}. This does not match your Requested IGN ({poke.RequestedIgn}).");
                Log("IGN Requested does not match Trading Partner.");
                await ExitTrade(Hub.Config, false, token).ConfigureAwait(false);

                return(PokeTradeResult.IncorrectIGN);
            }

            if (GetAltAccount(poke, TrainerName) != "")
            {
                Log($"<@{Hub.Config.Discord.PingUserOnAltDetection}> Potential Alt Detected! I have matched an IGN with 2 different Discord accounts. IGN: {TrainerName} | New User ID: {poke.DiscordUserId} | Old User ID: {GetAltAccount(poke, 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.EggRoll || poke.Type == PokeTradeType.LanTrade || poke.Type == PokeTradeType.LanRoll)
            {
                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)
            {
                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));
            }

            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.FixOT)
            {
                var clone = (PK8)pk.Clone();

                if ((CheckForAdOT(clone) || CheckForAdNickname(clone)) && clone.OT_Name != $"{TrainerName}")
                {
                    clone.OT_Name = $"{TrainerName}";
                    clone.ClearNickname();
                    clone.PKRS_Infected = false;
                    clone.PKRS_Cured    = false;
                    clone.PKRS_Days     = 0;
                    clone.PKRS_Strain   = 0;
                    poke.SendNotification(this, $"```fix\nDetected an advertisement OT/Nickname with your {(Species)clone.Species}!```");
                }
                else
                {
                    poke.SendNotification(this, "```fix\nNo advertisement detected in Nickname or OT. Exiting trade...```");
                    await ExitTrade(Hub.Config, true, token).ConfigureAwait(false);

                    return(PokeTradeResult.IllegalTrade);
                }

                var la = new LegalityAnalysis(clone);
                if (!la.Valid)
                {
                    Log($"FixOT 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 fixing 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\nFixed your {(Species)clone.Species}! Now confirm the trade!```");
                Log($"Fixed Nickname/OT for {(Species)clone.Species}.");

                await ReadUntilPresent(LinkTradePartnerPokemonOffset, 3_000, 1_000, token).ConfigureAwait(false);
                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.PowerUp)
            {
                var clone = (PK8)pk.Clone();

                clone.MaximizeLevel();
                clone.SetRecordFlags();
                clone.SetMaximumPPUps();
                if (poke.TradeData.EVTotal != 0) // If no EVs specified in command (=0), make them the original EVs.
                {
                    clone.EVs = poke.TradeData.EVs;
                }
                clone.SetSuggestedHyperTrainingData(clone.IVs);
                if (clone.CanToggleGigantamax(clone.Species, clone.SpecForm))
                {
                    clone.CanGigantamax = true;
                }
                if (clone is IDynamaxLevel d)
                {
                    d.DynamaxLevel = (byte)(d.CanHaveDynamaxLevel(clone) ? 10 : 0);
                }

                var la = new LegalityAnalysis(clone);
                if (!la.Valid)
                {
                    Log($"PowerUp 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 modifying 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, $"```pu\nPowered up your {(Species)clone.Species}! Now confirm the trade!```");
                Log($"Powered up {(Species)clone.Species}.");

                await ReadUntilPresent(LinkTradePartnerPokemonOffset, 3_000, 1_000, token).ConfigureAwait(false);
                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);
            }

            Log("In Trade Animation...");

            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 && poke.Type != PokeTradeType.PowerUp && 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.PowerUp)
                {
                    counts.AddCompletedPowerUps();
                }
                else if (poke.Type == PokeTradeType.EggRoll)
                {
                    counts.AddCompletedEggRolls();
                }
                else if (poke.Type == PokeTradeType.LanTrade)
                {
                    counts.AddCompletedLanTrades();
                }
                else if (poke.Type == PokeTradeType.LanRoll)
                {
                    counts.AddCompletedLanRolls();
                }
                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.PowerUp || poke.Type == PokeTradeType.EggRoll || poke.Type == PokeTradeType.LanRoll)
                    {
                        DumpPokemon(DumpSetting.DumpFolder, "traded", pkm); // sent to partner
                    }
                }
            }

            return(PokeTradeResult.Success);
        }
Esempio n. 5
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);

            if (poke.Type != PokeTradeType.Random)
            {
                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}...");
                poke.SendNotification(this, $"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);

            TrainerName = TrainerName.Replace('&', '+');
            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)
            {
                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)
            {
                await ExitTrade(Hub.Config, true, token).ConfigureAwait(false);

                return(PokeTradeResult.TrainerTooSlow);
            }

            SpecialTradeType itemReq = SpecialTradeType.None;

            if (poke.Type == PokeTradeType.Seed)
            {
                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.Seed && itemReq == SpecialTradeType.None)
            {
                // Immediately exit, we aren't trading anything.
                return(await EndSeedCheckTradeAsync(poke, pk, token).ConfigureAwait(false));
            }

            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, "**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 = pk;

                if (itemReq == SpecialTradeType.WonderCard)
                {
                    poke.SendNotification(this, "SSRDistribution success!");
                }
                else if (itemReq != SpecialTradeType.None && itemReq != SpecialTradeType.Shinify)
                {
                    poke.SendNotification(this, "SSRSpecial request successful!");
                }
                else if (itemReq == SpecialTradeType.Shinify)
                {
                    poke.SendNotification(this, "SSRShinify success! Thanks for being part of the community!");
                }

                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 >= 30)
                {
                    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 (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
                {
                    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)
                    {
                        DumpPokemon(DumpSetting.DumpFolder, "traded", pkm); // sent to partner
                    }
                }
            }

            return(PokeTradeResult.Success);
        }
Esempio n. 6
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);
        }
Esempio n. 7
0
        private async Task <PokeTradeResult> PerformLinkCodeTrade(SAV8BS sav, PokeTradeDetail <PB8> poke, CancellationToken token)
        {
            sessionTradeCount++;
            Log($"Starting trade #{sessionTradeCount} for this session.");
            // Update Barrier Settings
            UpdateBarrier(poke.IsSynchronized);
            poke.TradeInitialize(this);
            await RestartGameIfCantLeaveUnionRoom(token).ConfigureAwait(false);

            Hub.Config.Stream.EndEnterCode(this);

            if (await CheckIfSoftBanned(SoftBanOffset, token).ConfigureAwait(false))
            {
                await UnSoftBan(token).ConfigureAwait(false);
            }

            var toSend = poke.TradeData;

            if (toSend.Species != 0)
            {
                await SetBoxPokemonAbsolute(BoxStartOffset, toSend, token, sav).ConfigureAwait(false);
            }

            // Enter Union Room and set ourselves up as Trading.
            if (!await EnterUnionRoomWithCode(poke.Type, poke.Code, token).ConfigureAwait(false))
            {
                // We don't know how far we made it in, so restart the game to be safe.
                await RestartGameBDSP(token).ConfigureAwait(false);

                return(PokeTradeResult.RecoverEnterUnionRoom);
            }

            poke.TradeSearching(this);
            var waitPartner = Hub.Config.Trade.TradeWaitTime;

            // Keep pressing A until we detect someone talking to us.
            while (!await IsUnionWork(UnionTalkingOffset, token).ConfigureAwait(false) && waitPartner > 0)
            {
                for (int i = 0; i < 2; ++i)
                {
                    await Click(A, 0_450, token).ConfigureAwait(false);
                }

                if (--waitPartner <= 0)
                {
                    return(PokeTradeResult.NoTrainerFound);
                }
            }

            // Keep pressing A until TargetTranerParam is loaded (when we hit the box).
            while (!await IsPartnerParamLoaded(token).ConfigureAwait(false) && waitPartner > 0)
            {
                for (int i = 0; i < 2; ++i)
                {
                    await Click(A, 0_450, token).ConfigureAwait(false);
                }

                // Can be false if they talked and quit.
                if (!await IsUnionWork(UnionTalkingOffset, token).ConfigureAwait(false))
                {
                    break;
                }
                if (--waitPartner <= 0)
                {
                    return(PokeTradeResult.TrainerTooSlow);
                }
            }

            // Still going through dialog and box opening.
            await Task.Delay(3_000, token).ConfigureAwait(false);

            // Can happen if they quit out of talking to us.
            if (!await IsPartnerParamLoaded(token).ConfigureAwait(false))
            {
                return(PokeTradeResult.TrainerTooSlow);
            }

            var tradePartner = await GetTradePartnerInfo(token).ConfigureAwait(false);

            //var trainerNID = await GetTradePartnerNID(token).ConfigureAwait(false);
            Log($"Found Link Trade partner: {tradePartner.TrainerName}-{tradePartner.TID7}");

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

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

            poke.SendNotification(this, $"Found Link Trade partner: {tradePartner.TrainerName}. Waiting for a Pokémon...");

            // Requires at least one trade for this pointer to make sense, so cache it here.
            LinkTradePokemonOffset = await SwitchConnection.PointerAll(Offsets.LinkTradePartnerPokemonPointer, token).ConfigureAwait(false);

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

            // Wait for user input... Needs to be different from the previously offered Pokémon.
            var tradeOffered = await ReadUntilChanged(LinkTradePokemonOffset, lastOffered, 25_000, 1_000, false, true, token).ConfigureAwait(false);

            if (!tradeOffered)
            {
                return(PokeTradeResult.TrainerTooSlow);
            }

            // If we detected a change, they offered something.
            var offered = await ReadPokemon(LinkTradePokemonOffset, BoxFormatSlotSize, token).ConfigureAwait(false);

            if (offered.Species == 0 || !offered.ChecksumValid)
            {
                return(PokeTradeResult.TrainerTooSlow);
            }
            lastOffered = await SwitchConnection.ReadBytesAbsoluteAsync(LinkTradePokemonOffset, 8, token).ConfigureAwait(false);

            PokeTradeResult update;
            var             trainer = new PartnerDataHolder(0, tradePartner.TrainerName, tradePartner.TID7);

            (toSend, update) = await GetEntityToSend(sav, poke, offered, toSend, trainer, token).ConfigureAwait(false);

            if (update != PokeTradeResult.Success)
            {
                return(update);
            }

            var tradeResult = await ConfirmAndStartTrading(poke, token).ConfigureAwait(false);

            if (tradeResult != PokeTradeResult.Success)
            {
                return(tradeResult);
            }

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

            // Trade was Successful!
            var received = await ReadPokemon(BoxStartOffset, BoxFormatSlotSize, token).ConfigureAwait(false);

            // Pokémon in b1s1 is same as the one they were supposed to receive (was never sent).
            if (SearchUtil.HashByDetails(received) == SearchUtil.HashByDetails(toSend) && received.Checksum == toSend.Checksum)
            {
                Log("User did not complete the trade.");
                return(PokeTradeResult.TrainerTooSlow);
            }

            // As long as we got rid of our inject in b1s1, assume the trade went through.
            Log("User completed the trade.");
            poke.TradeFinished(this, received);

            // Only log if we completed the trade.
            UpdateCountsAndExport(poke, received, toSend);

            // Still need to wait out the trade animation.
            for (var i = 0; i < 30; i++)
            {
                await Click(A, 0_500, token).ConfigureAwait(false);
            }

            Log("Trying to get out of the Union Room.");
            // Now get out of the Union Room.
            if (!await EnsureOutsideOfUnionRoom(token).ConfigureAwait(false))
            {
                return(PokeTradeResult.RecoverReturnOverworld);
            }

            // Sometimes they offered another mon, so store that immediately upon leaving Union Room.
            lastOffered = await SwitchConnection.ReadBytesAbsoluteAsync(LinkTradePokemonOffset, 8, token).ConfigureAwait(false);

            return(PokeTradeResult.Success);
        }