// These don't change per session and we access them frequently, so set these each time we start. private async Task InitializeSessionOffsets(CancellationToken token) { Log("Caching session offsets..."); BoxStartOffset = await SwitchConnection.PointerAll(Offsets.BoxStartPokemonPointer, token).ConfigureAwait(false); UnionGamingOffset = await SwitchConnection.PointerAll(Offsets.UnionWorkIsGamingPointer, token).ConfigureAwait(false); UnionTalkingOffset = await SwitchConnection.PointerAll(Offsets.UnionWorkIsTalkingPointer, token).ConfigureAwait(false); SoftBanOffset = await SwitchConnection.PointerAll(Offsets.UnionWorkPenaltyPointer, token).ConfigureAwait(false); }
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); }
protected async Task <(bool, ulong)> ValidatePointerAll(IEnumerable <long> jumps, CancellationToken token) { var solved = await SwitchConnection.PointerAll(jumps, token).ConfigureAwait(false); return(solved != 0, solved); }