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