private async Task <PokeTradeResult> ProcessDumpTradeAsync(PokeTradeDetail <PB8> detail, CancellationToken token) { int ctr = 0; var time = TimeSpan.FromSeconds(Hub.Config.Trade.MaxDumpTradeTime); var start = DateTime.Now; while (ctr < Hub.Config.Trade.MaxDumpsPerTrade && DateTime.Now - start < time) { // Wait for user input... Needs to be different from the previously offered Pokémon. var tradeOffered = await ReadUntilChanged(LinkTradePokemonOffset, lastOffered, 3_000, 1_000, false, true, token).ConfigureAwait(false); if (!tradeOffered) { continue; } // If we detected a change, they offered something. var pk = await ReadPokemon(LinkTradePokemonOffset, BoxFormatSlotSize, token).ConfigureAwait(false); var newEC = await SwitchConnection.ReadBytesAbsoluteAsync(LinkTradePokemonOffset, 8, token).ConfigureAwait(false); if (pk == null || pk.Species < 1 || !pk.ChecksumValid || lastOffered == newEC) { continue; } lastOffered = newEC; // Send results from separate thread; the bot doesn't need to wait for things to be calculated. if (DumpSetting.Dump) { var subfolder = detail.Type.ToString().ToLower(); DumpPokemon(DumpSetting.DumpFolder, subfolder, pk); // received } var la = new LegalityAnalysis(pk); var verbose = la.Report(true); Log($"Shown Pokémon is: {(la.Valid ? "Valid" : "Invalid")}."); detail.SendNotification(this, pk, verbose); ctr++; } Log($"Ended Dump loop after processing {ctr} Pokémon."); if (ctr == 0) { return(PokeTradeResult.TrainerTooSlow); } TradeSettings.AddCompletedDumps(); detail.Notifier.SendNotification(this, detail, $"Dumped {ctr} Pokémon."); detail.Notifier.TradeFinished(this, detail, detail.TradeData); // blank pk8 return(PokeTradeResult.Success); }
private async Task GetCoordinatesForSpin(string name, CancellationToken token) { if (TradeExtensions <PK8> .Coordinates.TryGetValue(name, out _)) { return; } TradeExtensions <PK8> .CoordinatesOffset = await ParsePointer("[[[[[[main+26365B8]+88]+1F8]+E0]+10]+E0]+60", token).ConfigureAwait(false); // Thank you for the pointer, Zyro <3 TradeExtensions <PK8> .Coordinates.Add(name, ( await SwitchConnection.ReadBytesAbsoluteAsync(TradeExtensions <PK8> .CoordinatesOffset, 4, token).ConfigureAwait(false), await SwitchConnection.ReadBytesAbsoluteAsync(TradeExtensions <PK8> .CoordinatesOffset + 0x4, 4, token).ConfigureAwait(false), await SwitchConnection.ReadBytesAbsoluteAsync(TradeExtensions <PK8> .CoordinatesOffset + 0x8, 4, token).ConfigureAwait(false) )); }
private async Task <PokeTradeResult> ConfirmAndStartTrading(PokeTradeDetail <PB8> detail, CancellationToken token) { // We'll keep watching B1S1 for a change to indicate a trade started -> should try quitting at that point. var oldEC = await SwitchConnection.ReadBytesAbsoluteAsync(BoxStartOffset, 8, token).ConfigureAwait(false); await Click(A, 3_000, token).ConfigureAwait(false); for (int i = 0; i < 10; i++) { if (await IsUserBeingShifty(detail, token).ConfigureAwait(false)) { return(PokeTradeResult.SuspiciousActivity); } await Click(A, 1_500, token).ConfigureAwait(false); } var tradeCounter = 0; while (await IsUnionWork(UnionTalkingOffset, token).ConfigureAwait(false)) { await Click(A, 1_000, token).ConfigureAwait(false); tradeCounter++; var newEC = await SwitchConnection.ReadBytesAbsoluteAsync(BoxStartOffset, 8, token).ConfigureAwait(false); if (!newEC.SequenceEqual(oldEC)) { await Task.Delay(5_000, token).ConfigureAwait(false); return(PokeTradeResult.Success); } // We've somehow failed out of the Union Room -- can happen with connection error. if (!await IsUnionWork(UnionGamingOffset, token).ConfigureAwait(false)) { return(PokeTradeResult.TrainerTooSlow); } if (tradeCounter >= Hub.Config.Trade.TradeAnimationMaxDelaySeconds) { break; } } // If we don't detect a B1S1 change, the trade didn't go through in that time. return(PokeTradeResult.TrainerTooSlow); }
public async Task <PK8?> ReadUntilPresentAbsolute(ulong offset, int waitms, int waitInterval, CancellationToken token, int size = BoxFormatSlotSize) // Need to eliminate duplicate code, currently a hack { int msWaited = 0; while (msWaited < waitms) { var data = await SwitchConnection.ReadBytesAbsoluteAsync(offset, size, token).ConfigureAwait(false); var pk = new PK8(data); if (pk.Species != 0 && pk.ChecksumValid) { return(pk); } await Task.Delay(waitInterval, token).ConfigureAwait(false); msWaited += waitInterval; } return(null); }
//Pointer parser, code from ALM public async Task <ulong> ParsePointer(String pointer, CancellationToken token) { var ptr = pointer; uint finadd = 0; if (!ptr.EndsWith("]")) { finadd = Util.GetHexValue(ptr.Split('+').Last()); } var jumps = ptr.Replace("main", "").Replace("[", "").Replace("]", "").Split(new[] { "+" }, StringSplitOptions.RemoveEmptyEntries); if (jumps.Length == 0) { Log("Invalid Pointer"); return(0); } var initaddress = Util.GetHexValue(jumps[0].Trim()); ulong address = BitConverter.ToUInt64(await SwitchConnection.ReadBytesMainAsync(initaddress, 0x8, token).ConfigureAwait(false), 0); foreach (var j in jumps) { var val = Util.GetHexValue(j.Trim()); if (val == initaddress) { continue; } if (val == finadd) { address += val; break; } address = BitConverter.ToUInt64(await SwitchConnection.ReadBytesAbsoluteAsync(address + val, 0x8, token).ConfigureAwait(false), 0); } return(address); }
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 Unban(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 is null) { 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); }
public async Task <PB7> LGReadPokemon(ulong offset, CancellationToken token, int size = EncryptedSize) { var data = await SwitchConnection.ReadBytesAbsoluteAsync(offset, size, token).ConfigureAwait(false); return(new PB7(data)); }
public async Task <uint> LGReadComboCount(CancellationToken token) => BitConverter.ToUInt16(await SwitchConnection.ReadBytesAbsoluteAsync(await ParsePointer(CatchComboPointer, token).ConfigureAwait(false), 2, token).ConfigureAwait(false), 0);
public async Task <PK8> ReadPokemon(ulong offset, CancellationToken token, int size = BoxFormatSlotSize) { var data = await SwitchConnection.ReadBytesAbsoluteAsync(offset, size, token).ConfigureAwait(false); return(new PK8(data)); }
public async Task <bool> LairStatusCheckMain(ushort val, ulong ofs, CancellationToken token) => BitConverter.GetBytes(val).SequenceEqual(await SwitchConnection.ReadBytesAbsoluteAsync(ofs, 2, token).ConfigureAwait(false));
// Uses absolute offset which is set each session. Checks for IsGaming or IsTalking. public async Task <bool> IsUnionWork(ulong offset, CancellationToken token) { var data = await SwitchConnection.ReadBytesAbsoluteAsync(offset, 1, token).ConfigureAwait(false); return(data[0] == 1); }
public override async Task <PB8> ReadPokemon(ulong offset, int size, CancellationToken token) { var data = await SwitchConnection.ReadBytesAbsoluteAsync(offset, size, token).ConfigureAwait(false); return(new PB8(data)); }
public async Task <bool> CheckIfSoftBanned(ulong offset, CancellationToken token) { var data = await SwitchConnection.ReadBytesAbsoluteAsync(offset, 4, token).ConfigureAwait(false); return(BitConverter.ToUInt32(data, 0) != 0); }
private async Task Overworld(CancellationToken token, bool birds = false) { GameVersion version = await LGWhichGameVersion(token).ConfigureAwait(false); List <int[]> movementslist = ParseMovements(); bool firstrun = movementslist.Count > 0; Stopwatch stopwatch = new Stopwatch(); uint prev = 0; uint newspawn; uint catchcombo; uint speciescombo; int i = 0; int j = 0; bool freeze = false; bool searchforshiny = Hub.Config.LGPE_OverworldScan.OnlyShiny; bool found = false; if (movementslist.Count > 0) { Log($"ATTENTION!{Environment.NewLine}Any wild encounter will broke the movement routine, resulting in the pg moving to unwanted places!{Environment.NewLine}" + $"----------------------------------------{Environment.NewLine}" + $"ATTENTION!\nUnexpected behaviour can occur if a shiny is detected while changing area. It his higlhy recommended to avoid that.{Environment.NewLine}" + $"-----------------------------------------{Environment.NewLine}"); } //Catch combo to increment spawn quality and shiny rate (Thanks to Lincoln-LM for the offsets) if ((int)Hub.Config.LGPE_OverworldScan.ChainSpecies > 0) { speciescombo = BitConverter.ToUInt16(await SwitchConnection.ReadBytesAbsoluteAsync(await ParsePointer(SpeciesComboPointer, token).ConfigureAwait(false), 2, token).ConfigureAwait(false), 0); if ((speciescombo != (uint)Hub.Config.LGPE_OverworldScan.ChainSpecies) && (Hub.Config.LGPE_OverworldScan.ChainSpecies != 0)) { Log($"Current catch combo being on {(speciescombo == 0 ? "None" : SpeciesName.GetSpeciesName((int)speciescombo, 2))}, changing to {Hub.Config.LGPE_OverworldScan.ChainSpecies}."); await SwitchConnection.WriteBytesAbsoluteAsync(BitConverter.GetBytes((uint)Hub.Config.LGPE_OverworldScan.ChainSpecies), await ParsePointer(SpeciesComboPointer, token).ConfigureAwait(false), token).ConfigureAwait(false); speciescombo = BitConverter.ToUInt16(await SwitchConnection.ReadBytesAbsoluteAsync(await ParsePointer(SpeciesComboPointer, token).ConfigureAwait(false), 2, token).ConfigureAwait(false), 0); Log($"Current catch combo being now on {(speciescombo == 0 ? "None" : SpeciesName.GetSpeciesName((int)speciescombo, 2))}."); } } if (Hub.Config.LGPE_OverworldScan.ChainCount > 0) { catchcombo = BitConverter.ToUInt16(await SwitchConnection.ReadBytesAbsoluteAsync(await ParsePointer(CatchComboPointer, token).ConfigureAwait(false), 2, token).ConfigureAwait(false), 0); if (catchcombo < (uint)Hub.Config.LGPE_OverworldScan.ChainCount) { Log($"Current catch combo being {catchcombo}, incrementing to {Hub.Config.LGPE_OverworldScan.ChainCount}."); await SwitchConnection.WriteBytesAbsoluteAsync(BitConverter.GetBytes((uint)Hub.Config.LGPE_OverworldScan.ChainCount), await ParsePointer(CatchComboPointer, token).ConfigureAwait(false), token).ConfigureAwait(false); catchcombo = BitConverter.ToUInt16(await SwitchConnection.ReadBytesAbsoluteAsync(await ParsePointer(CatchComboPointer, token).ConfigureAwait(false), 2, token).ConfigureAwait(false), 0); Log($"Current catch combo being now {catchcombo}."); } } while (!token.IsCancellationRequested) { if (searchforshiny) { await LGZaksabeast(token, version).ConfigureAwait(false); } while (freeze == false && !token.IsCancellationRequested && !found) { if (await LGCountMilliseconds(Hub.Config, token).ConfigureAwait(false) > 0 || !searchforshiny) { //The routine need to continue and check the overworld spawns, cannot be stuck at changing stick position. if (movementslist.Count > 0) { if (stopwatch.ElapsedMilliseconds >= movementslist.ElementAt(j)[2] || firstrun) { if (firstrun) { firstrun = false; } //Log($"Moved for {stopwatch.ElapsedMilliseconds}ms."); await ResetStick(token).ConfigureAwait(false); await SetStick(RIGHT, (short)(movementslist.ElementAt(j)[0]), (short)(movementslist.ElementAt(j)[1]), 0_001, token).ConfigureAwait(false); j++; if (j == movementslist.Count) { j = 0; } stopwatch.Restart(); } } //Check is inside an unwanted encounter if (await LGIsInCatchScreen(token).ConfigureAwait(false)) { await ResetStick(token).ConfigureAwait(false); Log($"Unwanted encounter detected!"); int y = 0; while (await LGIsInCatchScreen(token).ConfigureAwait(false) && !token.IsCancellationRequested) { y++; await Task.Delay(8_000, token).ConfigureAwait(false); if (y > 2) { await Click(B, 1_200, token).ConfigureAwait(false); } await Click(B, 1_200, token).ConfigureAwait(false); await Click(A, 1_000, token).ConfigureAwait(false); await Task.Delay(6_500, token).ConfigureAwait(false); } Log($"Exited wild encounter."); } //Check new spawns newspawn = BitConverter.ToUInt16(await Connection.ReadBytesAsync(LastSpawn1, 2, token).ConfigureAwait(false), 0); if (newspawn != prev) { if (newspawn != 0) { i++; if (IsPKLegendary((int)newspawn)) { Counts.AddCompletedLegends(); } else { Counts.AddCompletedEncounters(); } Log($"New spawn ({i}): {newspawn} {SpeciesName.GetSpeciesName((int)newspawn, 4)}"); } prev = newspawn; if (!searchforshiny && ((!birds && (int)newspawn == (int)Hub.Config.LGPE_OverworldScan.StopOnSpecies) || (!birds && (int)Hub.Config.LGPE_OverworldScan.StopOnSpecies == 0) || (birds && ((int)newspawn == 144 || (int)newspawn == 145 || (int)newspawn == 146)))) { await Click(X, 1_000, token).ConfigureAwait(false); await Click(HOME, 1_000, token).ConfigureAwait(false); if (!String.IsNullOrEmpty(Hub.Config.Discord.UserTag)) { Log($"<@{Hub.Config.Discord.UserTag}> stop conditions met, restart the bot(s) to search again."); } else { Log("Stop conditions met, restart the bot(s) to search again."); } return; } } } else if (searchforshiny) { freeze = true; } } if (searchforshiny && !token.IsCancellationRequested) { Log("A Shiny has been detected."); } //Unfreeze to restart the routine, or log the Shiny species. await LGUnfreeze(token, version).ConfigureAwait(false); newspawn = BitConverter.ToUInt16(await Connection.ReadBytesAsync(LastSpawn1, 2, token).ConfigureAwait(false), 0); //Stop Conditions logic if (birds && ((int)newspawn == 144 || (int)newspawn == 145 || (int)newspawn == 146) && !token.IsCancellationRequested) { found = true; } else if ((!birds && (int)Hub.Config.LGPE_OverworldScan.StopOnSpecies > 0 && (int)newspawn == (int)Hub.Config.LGPE_OverworldScan.StopOnSpecies) || (!birds && (int)Hub.Config.LGPE_OverworldScan.StopOnSpecies == 0)) { found = true; } else { found = false; } if (!found && !token.IsCancellationRequested) { freeze = false; Log($"Shiny {SpeciesName.GetSpeciesName((int)newspawn, 4)} is not the target, the routine will continue."); } else if (!token.IsCancellationRequested) { await ResetStick(token).ConfigureAwait(false); if (!String.IsNullOrEmpty(Hub.Config.Discord.UserTag)) { Log($"<@{Hub.Config.Discord.UserTag}> Shiny {SpeciesName.GetSpeciesName((int)newspawn, 4)} found!"); } else { Log($"Shiny {SpeciesName.GetSpeciesName((int)newspawn, 4)} found!"); } await Click(X, 1_000, token).ConfigureAwait(false); await Click(HOME, 1_000, token).ConfigureAwait(false); return; } } await ResetStick(token).ConfigureAwait(false); if (searchforshiny) { await LGUnfreeze(token, version).ConfigureAwait(false); } }