public void SimulatorGetEncounters() { var set = new ShowdownSet(SetGlaceonUSUMTutor); var pk7 = new PK7 { Species = set.Species, AltForm = set.FormIndex, Moves = set.Moves }; var encs = EncounterMovesetGenerator.GenerateEncounters(pk7, set.Moves, GameVersion.MN); Assert.IsTrue(!encs.Any()); pk7.HT_Name = "PKHeX"; encs = EncounterMovesetGenerator.GenerateEncounters(pk7, set.Moves, GameVersion.MN); var first = encs.FirstOrDefault(); Assert.IsTrue(first != null); var egg = (EncounterEgg)first; var info = new SimpleTrainerInfo(); var pk = egg.ConvertToPKM(info); Assert.IsTrue(pk.Species != set.Species); var la = new LegalityAnalysis(pk); Assert.IsTrue(la.Valid); var test = EncounterMovesetGenerator.GeneratePKMs(pk7, info).ToList(); foreach (var t in test) { var la2 = new LegalityAnalysis(t); Assert.IsTrue(la2.Valid); } }
public void CanGenerateMG5Case() { const Species species = Species.Haxorus; var pk = new PK5 { Species = (int)species }; var ez = EncounterMovesetGenerator.GenerateEncounters(pk, pk.Moves, GameVersion.W2).OfType <EncounterStatic>().First(); ez.Should().NotBeNull("Shiny Haxorus stationary encounter exists for B2/W2"); var criteria = EncounterCriteria.Unrestricted; var tr = new SimpleTrainerInfo(GameVersion.B2) { TID = 57600, SID = 62446, }; for (var nature = Nature.Hardy; nature <= Nature.Quirky; nature++) { criteria = criteria with { Nature = nature }; var pkm = ez.ConvertToPKM(tr, criteria); pkm.Nature.Should().Be((int)nature, "not nature locked"); pkm.IsShiny.Should().BeTrue("encounter is shiny locked"); pkm.TID.Should().Be(tr.TID); pkm.SID.Should().Be(tr.SID); } } }
/// <summary> /// Clone trainerdata and mutate the language and then return the clone /// </summary> /// <param name="tr">Trainerdata to clone</param> /// <param name="lang">language to mutate</param> /// <returns></returns> public static ITrainerInfo MutateLanguage(this ITrainerInfo tr, LanguageID?lang) { if (lang == LanguageID.UNUSED_6 || lang == LanguageID.Hacked || lang == null) { return(tr); } if (tr is PokeTrainerDetails p) { var clone = PokeTrainerDetails.Clone(p); clone.Language = (int)lang; return(clone); } if (tr is SimpleTrainerInfo s) { var sti = new SimpleTrainerInfo((GameVersion)s.Game); sti.OT = s.OT; sti.TID = s.TID; sti.SID = s.SID; sti.Generation = s.Gender; sti.Language = (int)lang; sti.ConsoleRegion = s.ConsoleRegion; sti.Region = s.Region; sti.Country = s.Country; sti.Generation = s.Generation; return(sti); } return(tr); }
/// <summary> /// Main function that auto legalizes based on the legality /// </summary> /// <remarks>Leverages <see cref="Core"/>'s <see cref="EncounterMovesetGenerator"/> to create a <see cref="PKM"/> from a <see cref="ShowdownSet"/>.</remarks> /// <param name="dest">Destination for the generated pkm</param> /// <param name="template">rough pkm that has all the <see cref="set"/> values entered</param> /// <param name="set">Showdown set object</param> /// <param name="satisfied">If the final result is satisfactory, otherwise use deprecated bruteforce auto legality functionality</param> public static PKM GetLegalFromTemplate(this ITrainerInfo dest, PKM template, ShowdownSet set, out bool satisfied) { var Form = SanityCheckForm(template, ref set); template.ApplySetDetails(set); template.SetRecordFlags(); // Validate TR moves for the encounter var destType = template.GetType(); var destVer = (GameVersion)dest.Game; if (destVer <= 0 && dest is SaveFile s) { destVer = s.Version; } var gamelist = GameUtil.GetVersionsWithinRange(template, template.Format).OrderByDescending(c => c.GetGeneration()).ToArray(); var encounters = EncounterMovesetGenerator.GenerateEncounters(pk: template, moves: set.Moves, gamelist); foreach (var enc in encounters) { var ver = enc is IVersion v ? v.Version : destVer; var gen = enc is IGeneration g ? g.Generation : dest.Generation; ITrainerInfo tr = new SimpleTrainerInfo(ver); if (UseTrainerData) { tr = TrainerSettings.GetSavedTrainerData(ver, gen, new SimpleTrainerInfo(ver)); } var raw = SanityCheckEncounters(enc).ConvertToPKM(tr); var pk = PKMConverter.ConvertToType(raw, destType, out _); if (pk == null) { continue; } ApplySetDetails(pk, set, Form, raw, dest, enc); if (set.CanGigantamax && pk is IGigantamax gmax) { if (!gmax.CanGigantamax) { continue; } } var la = new LegalityAnalysis(pk); if (la.Valid) { satisfied = true; return(pk); } Debug.WriteLine(la.Report()); } satisfied = false; return(template); }
public static bool GetTrainerInfo(IEnumerable <string> lines, int format, out ITrainerInfo tr) { var sti = new SimpleTrainerInfo { Generation = format }; var split = Split(lines); bool any = false; foreach (var s in split) { var key = s.Key; var value = s.Value; switch (key) { case "OT": sti.OT = value; break; case "TID" when int.TryParse(value, out int tid) && tid > 0: sti.TID = tid; break; case "SID" when int.TryParse(value, out int sid) && sid > 0: sti.SID = sid; break; case "OTGender": sti.Gender = value is "Female" or "F" ? 1 : 0; break; default: continue; } any = true; } tr = sti; if (!any || format < 7) { return(any); } const int mil = 1_000_000; uint repack = ((uint)sti.SID * mil) + (uint)sti.TID; sti.TID = (int)(repack & 0xFFFF); sti.SID = (int)(repack >> 16); return(true); }
//[TestMethod] //[TestCategory(SimulatorParse)] public void TestGenerate() { for (int i = 1; i <= 807; i++) { var tr = new SimpleTrainerInfo(); var pk = new PK7 { Species = i }; var ez = EncounterMovesetGenerator.GeneratePKMs(pk, tr); Debug.WriteLine($"Starting {i:000}"); bool v = ez.Select(p => new LegalityAnalysis(p)).All(la => la.Valid); Debug.WriteLine($"Finished {i:000}"); Debug.Assert(v); } }
private static void InitializeTrainerDatabase() { var ot = GetEnvOrThrow("PKHEX_DEFAULT_OT"); var trainerId = int.Parse(GetEnvOrThrow("PKHEX_DEFAULT_TID")); var secretId = int.Parse(GetEnvOrThrow("PKHEX_DEFAULT_SID")); var languageName = GetEnvOrThrow("PKHEX_DEFAULT_LANGUAGE"); if (!Enum.TryParse <LanguageID>(languageName, true, out var language)) { throw new Exception($"Invalid default language {languageName}"); } SaveFile GetFallbackBlank(int generation) { var blankSav = SaveUtil.GetBlankSAV(generation, ot); blankSav.Language = (int)language; blankSav.TID = trainerId; blankSav.SID = secretId; blankSav.OT = ot; return(blankSav); } for (var i = 1; i < PKX.Generation + 1; i++) { var versions = GameUtil.GetVersionsInGeneration(i, PKX.Generation); foreach (var v in versions) { var fallback = new SimpleTrainerInfo(v) { Language = (int)language, TID = trainerId, SID = secretId, OT = ot, }; var exist = TrainerSettings.GetSavedTrainerData(v, i, fallback); if (exist is SimpleTrainerInfo) { TrainerSettings.Register(fallback); } } } var trainer = TrainerSettings.GetSavedTrainerData(PKX.Generation); PKMConverter.SetPrimaryTrainer(trainer); }
public void SimulatorGetVCEgg1() { var set = new ShowdownSet(SetSlowpoke12); var pk7 = new PK7 { Species = set.Species, AltForm = set.FormIndex, Moves = set.Moves, HT_Name = "PKHeX" }; var encs = EncounterMovesetGenerator.GenerateEncounters(pk7, set.Moves, GameVersion.GD).ToList(); Assert.True(encs.Count > 0); var info = new SimpleTrainerInfo(GameVersion.SN); var enc = encs[0]; var pk = enc.ConvertToPKM(info); var la = new LegalityAnalysis(pk); Assert.True(la.Valid); }
public void SimulatorGetSplitBreed() { var set = new ShowdownSet(SetMunchSnorLax); var pk7 = new PK7 { Species = set.Species, AltForm = set.FormIndex, Moves = set.Moves, HT_Name = "PKHeX" }; // !! specify the HT name, we need tutors for this one var encs = EncounterMovesetGenerator.GenerateEncounters(pk7, set.Moves, GameVersion.SN).ToList(); Assert.True(encs.Count > 0); Assert.True(encs.All(z => z.Species > 150)); var info = new SimpleTrainerInfo(GameVersion.SN); var enc = encs[0]; var pk = enc.ConvertToPKM(info); var la = new LegalityAnalysis(pk); Assert.True(la.Valid); }
public void PokemonGenerationReturnsLegalPokemon(int species) { int count = 0; var tr = new SimpleTrainerInfo(GameVersion.SN); var pk = new PK7 { Species = species }; pk.Gender = pk.GetSaneGender(); var ez = EncounterMovesetGenerator.GeneratePKMs(pk, tr); foreach (var e in ez) { var la = new LegalityAnalysis(e); la.Valid.Should().BeTrue($"Because generated Pokemon {count} for {species:000} should be valid"); Assert.True(la.Valid); count++; } }
public void SimulatorGetCelebi() { var set = new ShowdownSet(SetCelebi); var pk7 = new PK7 { Species = set.Species, AltForm = set.FormIndex, Moves = set.Moves }; var encs = EncounterMovesetGenerator.GenerateEncounters(pk7, set.Moves, GameVersion.X); Assert.IsTrue(encs.Any()); encs = EncounterMovesetGenerator.GenerateEncounters(pk7, set.Moves, GameVersion.X); var first = encs.FirstOrDefault(); Assert.IsTrue(first != null); var enc = first; var info = new SimpleTrainerInfo(); var pk = enc.ConvertToPKM(info); var la = new LegalityAnalysis(pk); Assert.IsTrue(la.Valid); }
public void SimulatorGetWC3() { var set = new ShowdownSet(SetROCKSMetang); var pk3 = new PK3 { Species = set.Species, AltForm = set.FormIndex, Moves = set.Moves }; var encs = EncounterMovesetGenerator.GenerateEncounters(pk3, set.Moves, GameVersion.R); Assert.IsTrue(encs.Any()); encs = EncounterMovesetGenerator.GenerateEncounters(pk3, set.Moves, GameVersion.R); var first = encs.FirstOrDefault(); Assert.IsTrue(first != null); var wc3 = (WC3)first; var info = new SimpleTrainerInfo(); var pk = wc3.ConvertToPKM(info); var la = new LegalityAnalysis(pk); Assert.IsTrue(la.Valid); }
private static void InitializeTrainerDatabase(LegalitySettings cfg) { // Seed the Trainer Database with enough fake save files so that we return a generation sensitive format when needed. string OT = cfg.GenerateOT; int TID = cfg.GenerateTID16; int SID = cfg.GenerateSID16; int lang = (int)cfg.GenerateLanguage; var externalSource = cfg.GeneratePathTrainerInfo; if (!string.IsNullOrWhiteSpace(externalSource) && Directory.Exists(externalSource)) { TrainerSettings.LoadTrainerDatabaseFromPath(externalSource); } for (int i = 1; i < PKX.Generation + 1; i++) { var versions = GameUtil.GetVersionsInGeneration(i, PKX.Generation); foreach (var v in versions) { var fallback = new SimpleTrainerInfo(v) { Language = lang, TID = TID, SID = SID, OT = OT, }; var exist = TrainerSettings.GetSavedTrainerData(v, i, fallback); if (exist is SimpleTrainerInfo) // not anything from files; this assumes ALM returns SimpleTrainerInfo for non-user-provided fake templates. { TrainerSettings.Register(fallback); } } } var trainer = TrainerSettings.GetSavedTrainerData(PKX.Generation); PKMConverter.SetPrimaryTrainer(trainer); }
public void SimulatorGetSmeargle() { var set = new ShowdownSet(SetSmeargle); var pk7 = new PK7 { Species = set.Species, Form = set.Form, Moves = set.Moves }; var encs = EncounterMovesetGenerator.GenerateEncounters(pk7, set.Moves, GameVersion.MN); Assert.True(encs.Any()); encs = EncounterMovesetGenerator.GenerateEncounters(pk7, set.Moves, GameVersion.MN); var first = encs.FirstOrDefault(); Assert.NotNull(first); var enc = first !; var info = new SimpleTrainerInfo(GameVersion.SN); var pk = enc.ConvertToPKM(info); var la = new LegalityAnalysis(pk); Assert.True(la.Valid); }
//[TestMethod] //[TestCategory(SimulatorParse)] public void TestGenerate() { int count = 0; var tr = new SimpleTrainerInfo(); for (int i = 1; i <= 807; i++) { var pk = new PK7 { Species = i }; pk.Gender = pk.GetSaneGender(); var ez = EncounterMovesetGenerator.GeneratePKMs(pk, tr); Debug.WriteLine($"Starting {i:000}"); foreach (var e in ez) { var la = new LegalityAnalysis(e); Assert.IsTrue(la.Valid); count++; } Debug.WriteLine($"Finished {i:000}"); } Debug.WriteLine($"Generated {count} PKMs!"); }
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); }