Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
47 changes: 47 additions & 0 deletions SerialPrograms/Source/PokemonFRLG/PokemonFRLG_Navigation.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -797,6 +797,53 @@ void open_bag_from_overworld(ConsoleHandle& console, ProControllerContext& conte
}
}

void use_sweet_scent_from_overworld(ConsoleHandle& console, ProControllerContext& context, int from_last){
uint16_t errors = 0;

while (true){
if (errors > 5){
OperationFailedException::fire(
ErrorReport::SEND_ERROR_REPORT,
"use_teleport_from_overworld(): Failed to use Teleport 5 times in a row.",
console
);
}

open_party_menu_from_overworld(console, context);
// navigate to last party slot
for (int i=0; i<(2+from_last); i++){
pbf_move_left_joystick(context, {0, +1}, 200ms, 300ms);
}

PartySelectionWatcher sweetscent_selected(COLOR_RED);

context.wait_for_all_requests();
int ret = run_until<ProControllerContext>(
console, context,
[](ProControllerContext& context){
pbf_press_button(context, BUTTON_A, 200ms, 1800ms);
},
{ sweetscent_selected }
);

if (ret < 0){
console.log("Failed to select Sweet Scent user.");
errors++;
pbf_mash_button(context, BUTTON_B, 3000ms);
continue;
}

// select Sweet Scent (2nd option, but maybe HMs could change this)
pbf_move_left_joystick(context, {0, -1}, 200ms, 300ms);
pbf_press_button(context, BUTTON_A, 200ms, 1800ms);
pbf_press_button(context, BUTTON_A, 200ms, 800ms);

context.wait_for_all_requests();
console.log("Used Sweet Scent.");
return;
}
}

void use_teleport_from_overworld(ConsoleHandle& console, ProControllerContext& context){
uint16_t errors = 0;

Expand Down
7 changes: 7 additions & 0 deletions SerialPrograms/Source/PokemonFRLG/PokemonFRLG_Navigation.h
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,9 @@ namespace NintendoSwitch{
using ProControllerContext = ControllerContext<ProController>;
namespace PokemonFRLG{

using namespace std::chrono_literals;


enum class BattleResult{
opponentfainted,
playerfainted,
Expand Down Expand Up @@ -80,6 +83,10 @@ void open_party_menu_from_overworld(ConsoleHandle& console, ProControllerContext
// Starting from the start menu, a sub-screen of the start menu, or the overworld, navigate to the bag
void open_bag_from_overworld(ConsoleHandle& console, ProControllerContext& context, StartMenuContext menu_context = StartMenuContext::STANDARD);

// Uses Sweet Scent, assuming that the specified party member has it learned
// The last argument is the distance of the Sweet Scent user from the last party slot
void use_sweet_scent_from_overworld(ConsoleHandle& console, ProControllerContext& context, int from_last = 0);

// Uses Teleport to return to a PokeCenter.
// Assumes that Teleport is usable and the last party member has it learned
void use_teleport_from_overworld(ConsoleHandle& console, ProControllerContext& context);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
#include "PokemonFRLG_RngNavigation.h"
#include "PokemonFRLG_HardReset.h"
#include "PokemonFRLG_RngCalibration.h"
#include "PokemonFRLG_RngLoopRoutines.h"
#include "PokemonFRLG_GiftRng.h"

namespace PokemonAutomation{
Expand Down Expand Up @@ -227,10 +228,6 @@ GiftRng::GiftRng()



bool GiftRng::have_hit_target(SingleSwitchProgramEnvironment& env, const uint32_t& TARGET_SEED, const AdvRngState& hit){
return (hit.seed == TARGET_SEED) && (hit.advance == ADVANCES);
}

void GiftRng::program(SingleSwitchProgramEnvironment& env, ProControllerContext& context){
/*
* Settings: Text Speed fast
Expand Down Expand Up @@ -324,8 +321,6 @@ void GiftRng::program(SingleSwitchProgramEnvironment& env, ProControllerContext&

static const uint64_t CONTINUE_SCREEN_FRAMES = 200;

static const double SEED_BUMPS[] = {0, 1, -1, 2, -2};

const uint64_t INITIAL_ADVANCES_RADIUS = USE_TEACHY_TV ? 4096 : 1024;

const uint8_t MAX_HISTORY_LENGTH = USE_TEACHY_TV ? 2 : 10;
Expand All @@ -344,24 +339,14 @@ void GiftRng::program(SingleSwitchProgramEnvironment& env, ProControllerContext&
AdvRngSearcher searcher(TARGET_SEED, ADVANCES, AdvRngMethod::Method1);
AdvPokemonResult target_result = searcher.generate_pokemon();
RNG_TARGET.set_target(target_result, GENDER_THRESHOLD);
env.log("Target PID: " + to_hex_string(target_result.pid));
env.log("Target Nature: " + nature_to_string(target_result.nature));
env.log("Target IVs:");
env.log(" HP: " + std::to_string(target_result.ivs.hp));
env.log(" Atk: " + std::to_string(target_result.ivs.attack));
env.log(" Def: " + std::to_string(target_result.ivs.defense));
env.log(" SpA: " + std::to_string(target_result.ivs.spatk));
env.log(" SpD: " + std::to_string(target_result.ivs.spdef));
env.log(" Spe: " + std::to_string(target_result.ivs.speed));
log_target_pokemon(env.console, target_result);

RngCalibrations calibrations = {
RNG_CALIBRATION.seed_calibration / FRLG_FRAME_DURATION,
RNG_CALIBRATION.csf_calibration,
RNG_CALIBRATION.advances_calibration
};
env.log("Initial Seed calibration (frames): " + std::to_string(calibrations.seed_offset));
env.log("Initial CSF calibration (frames): " + std::to_string(calibrations.csf_offset));
env.log("Initial In-game calibration (frames x2): " + std::to_string(calibrations.ingame_offset));
log_calibrations(env.console, calibrations, true);

Milliseconds launch_delay = INITIAL_LAUNCH_DELAY;

Expand All @@ -373,7 +358,7 @@ void GiftRng::program(SingleSwitchProgramEnvironment& env, ProControllerContext&
while (true){
if (calibration_history.results.size() > 0){
env.log("Checking for nonshiny target hit...");
if (have_hit_target(env, TARGET_SEED, calibration_history.results.back())){
if (have_hit_target(TARGET_SEED, ADVANCES, calibration_history.results.back())){
env.log("Target Hit!");
stats.nonshiny++;
break;
Expand Down Expand Up @@ -410,8 +395,7 @@ void GiftRng::program(SingleSwitchProgramEnvironment& env, ProControllerContext&
}

// if previous resets had uncertain advances, slightly modify the seed delay to try to hit a different target
double seed_bump = SEED_BUMPS[uncertain_history.results.size() % 5];
calibrations.seed_offset += seed_bump;
apply_seed_bump(calibrations, uncertain_history);

uint64_t ingame_advances = ADVANCES - CONTINUE_SCREEN_FRAMES;

Expand Down Expand Up @@ -459,60 +443,23 @@ void GiftRng::program(SingleSwitchProgramEnvironment& env, ProControllerContext&
AdvRngFilters filters = observation_to_filters(pokemon, BASE_STATS);
RNG_FILTERS.set(filters);

std::vector<AdvRngState> search_hits = get_search_results(env.console, searcher, filters, SEED_VALUES, ADVANCES, advances_radius, GENDER_THRESHOLD);
RNG_CALIBRATION.set_hits(search_hits);
bool force_finish = (
(MAX_RARE_CANDIES == 0)
|| all_equal(search_hits)
|| all_indistinguishable(search_hits, searcher, GENDER_THRESHOLD)
);
bool finished = update_history(
env.console, uncertain_history, calibration_history, MAX_HISTORY_LENGTH,
calibrations, search_hits, 1, 2, force_finish
);

for (uint64_t i=0; i<MAX_RARE_CANDIES; i++){
if (finished){
break;
}

bool failed = use_rare_candy(env.console, context, LANGUAGE, pokemon, filters, BASE_STATS, AdvRngMethod::Method1, false, i == 0);
if (failed){
update_history(
env.console, uncertain_history, calibration_history,
MAX_HISTORY_LENGTH, calibrations, search_hits, 1, 2, true
);
stats.errors++;
send_program_recoverable_error_notification(
env, NOTIFICATION_ERROR_RECOVERABLE,
"Failed to use Rare Candy."
);
std::vector<AdvRngState> search_hits = refine_calibration_with_rare_candy(
env, context, LANGUAGE, pokemon, filters, BASE_STATS,
uncertain_history, calibration_history, calibrations,
MAX_HISTORY_LENGTH, MAX_RARE_CANDIES, AdvRngMethod::Method1, false,
stats.errors, NOTIFICATION_ERROR_RECOVERABLE,
[&](AdvRngFilters& f){
return get_search_results(env.console, searcher, f, SEED_VALUES, ADVANCES, advances_radius, GENDER_THRESHOLD);
},
[&](const std::vector<AdvRngState>& h){ RNG_CALIBRATION.set_hits(h); },
[&](const AdvRngFilters& f){ RNG_FILTERS.set(f); },
[&](const std::vector<AdvRngState>& h){
return all_equal(h) || all_indistinguishable(h, searcher, GENDER_THRESHOLD);
}
RNG_FILTERS.set(filters);

search_hits = get_search_results(env.console, searcher, filters, SEED_VALUES, ADVANCES, advances_radius, GENDER_THRESHOLD);
RNG_CALIBRATION.set_hits(search_hits);

force_finish = (
failed
|| (i == (MAX_RARE_CANDIES - 1))
|| all_equal(search_hits)
|| all_indistinguishable(search_hits, searcher, GENDER_THRESHOLD)
);
finished = update_history(
env.console, uncertain_history,
calibration_history, MAX_HISTORY_LENGTH,
calibrations, search_hits,
1, 2, force_finish
);
}
);

env.log("RNG search finished.");
if (search_hits.size() == 0){
failed_searches++;
}else{
failed_searches = 0;
}
update_failed_searches(failed_searches, search_hits);

}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,8 +44,6 @@ class GiftRng : public SingleSwitchProgramInstance{

private:

bool have_hit_target(SingleSwitchProgramEnvironment& env, const uint32_t& TARGET_SEED, const AdvRngState& hit);

SectionDividerOption m_calibration_displays;
RngTargetDisplay RNG_TARGET;
RngFilterDisplay RNG_FILTERS;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,35 @@ std::string to_hex_string(const uint32_t& val){
return s.str();
}

bool have_hit_target(uint32_t target_seed, uint64_t target_advances, const AdvRngState& hit){
return (hit.seed == target_seed) && (hit.advance == target_advances);
}

void apply_seed_bump(RngCalibrations& calibrations, const RngUncertainHistory& uncertain_history){
static const double SEED_BUMPS[] = {0, 1, -1, 2, -2};
double seed_bump = SEED_BUMPS[uncertain_history.results.size() % 5];
calibrations.seed_offset += seed_bump;
}

void log_target_pokemon(ConsoleHandle& console, const AdvPokemonResult& result, bool normal_method_note){
console.log("Target PID: " + to_hex_string(result.pid));
console.log("Target Nature: " + nature_to_string(result.nature));
console.log(normal_method_note ? "Target IVs (assuming Normal method):" : "Target IVs:");
console.log(" HP: " + std::to_string(result.ivs.hp));
console.log(" Atk: " + std::to_string(result.ivs.attack));
console.log(" Def: " + std::to_string(result.ivs.defense));
console.log(" SpA: " + std::to_string(result.ivs.spatk));
console.log(" SpD: " + std::to_string(result.ivs.spdef));
console.log(" Spe: " + std::to_string(result.ivs.speed));
}

void log_calibrations(ConsoleHandle& console, const RngCalibrations& calibrations, bool initial){
std::string prefix = initial ? "Initial " : "";
console.log(prefix + "Seed calibration (frames): " + std::to_string(calibrations.seed_offset));
console.log(prefix + "CSF calibration (frames): " + std::to_string(calibrations.csf_offset));
console.log(prefix + "In-game calibration (frames x2): " + std::to_string(calibrations.ingame_offset));
}

RngTimings prepare_timings(
ConsoleHandle& console,
PokemonFRLG_RngTarget target,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,31 @@ int16_t seed_position_in_list(uint16_t seed, std::vector<uint16_t> list);
std::string to_hex_string(const uint16_t& val);
std::string to_hex_string(const uint32_t& val);


// true if a search hit matches the target seed and advance
bool have_hit_target(uint32_t target_seed, uint64_t target_advances, const AdvRngState& hit);

// if previous resets had uncertain advances, slightly nudge the seed offset to try
// to land on a different target. Cycles through {0, +1, -1, +2, -2} frames.
void apply_seed_bump(RngCalibrations& calibrations, const RngUncertainHistory& uncertain_history);

// increment the failed-search counter when a search returned no hits, otherwise reset it.
// templated so it accepts both plain hit vectors and the egg searcher's pair vectors.
template <typename HitType>
void update_failed_searches(uint16_t& failed_searches, const std::vector<HitType>& search_hits){
if (search_hits.size() == 0){
failed_searches++;
}else{
failed_searches = 0;
}
}

// log the PID, nature, and IVs of a target pokemon result
void log_target_pokemon(ConsoleHandle& console, const AdvPokemonResult& result, bool normal_method_note = false);

// log the three calibration offsets (seed/CSF/in-game), in frames
void log_calibrations(ConsoleHandle& console, const RngCalibrations& calibrations, bool initial = false);

RngTimings prepare_timings(
ConsoleHandle& console,
PokemonFRLG_RngTarget target,
Expand Down
Loading
Loading