From 229e030e177684ca2a2880b3448cc35c1bef303a Mon Sep 17 00:00:00 2001 From: theAstrogoth Date: Wed, 15 Apr 2026 10:12:46 -0500 Subject: [PATCH 1/2] more fixes --- .../ShinyHunting/PokemonFRLG_RngHelper.cpp | 95 +++++++++++++++---- .../ShinyHunting/PokemonFRLG_RngHelper.h | 27 +++++- 2 files changed, 98 insertions(+), 24 deletions(-) diff --git a/SerialPrograms/Source/PokemonFRLG/Programs/ShinyHunting/PokemonFRLG_RngHelper.cpp b/SerialPrograms/Source/PokemonFRLG/Programs/ShinyHunting/PokemonFRLG_RngHelper.cpp index d8f9cd7f6..f5b392e64 100644 --- a/SerialPrograms/Source/PokemonFRLG/Programs/ShinyHunting/PokemonFRLG_RngHelper.cpp +++ b/SerialPrograms/Source/PokemonFRLG/Programs/ShinyHunting/PokemonFRLG_RngHelper.cpp @@ -20,6 +20,7 @@ #include "NintendoSwitch/NintendoSwitch_Settings.h" #include "NintendoSwitch/Inference/NintendoSwitch_HomeMenuDetector.h" #include "NintendoSwitch/Inference/NintendoSwitch_UpdatePopupDetector.h" +#include "NintendoSwitch/Inference/NintendoSwitch_StartGameUserSelectDetector.h" #include "NintendoSwitch/Programs/NintendoSwitch_GameEntry.h" #include "PokemonFRLG/Inference/PokemonFRLG_SelectionArrowDetector.h" #include "PokemonFRLG/Inference/PokemonFRLG_ShinySymbolDetector.h" @@ -102,7 +103,8 @@ RngHelper::RngHelper() 1, 0 // default, min ) , SEED_BUTTON( - "Seed Button:
", + "Seed Button:
" + "The button to be pressed on the title screen to set the seed.", { {SeedButton::A, "A", "A"}, {SeedButton::Start, "Start", "Start"}, @@ -112,44 +114,57 @@ RngHelper::RngHelper() SeedButton::A ) , SEED_DELAY( - "Seed Delay Time (ms):
The delay between starting the game and advancing past the title screen. Set this to match your target seed.", + "Seed Delay Time (ms):
" + "The delay between starting the game and advancing past the title screen. Set this to match your target seed.", LockMode::LOCK_WHILE_RUNNING, 35000, 28000 // default, min ) , SEED_CALIBRATION( - "Seed Calibration (ms):
Modifies the seed delay time.", + "Seed Calibration (ms):" + "
Modifies the seed delay time. This should be changed in the opposite of the direction that you missed your seed.
" + "Example: if you missed your target seed by +16ms (meaning the button press was too late), decrease your seed calibration by -16 (shortening the delay).", LockMode::UNLOCK_WHILE_RUNNING, 0 // default ) , CONTINUE_SCREEN_FRAMES( - "Continue Screen Frames:
The number of RNG advances before loading the game.
These pass at the \"normal\" rate compared to other consoles.", + "Continue Screen Frames:" + "
The number of RNG advances before loading the game.
" + "These pass at the \"normal\" rate compared to other consoles.", LockMode::LOCK_WHILE_RUNNING, 1000, 192 // default, min ) , CONTINUE_SCREEN_CALIBRATION( - "Continue Screen Frames Calibration:
A \"fine adjustment\" that modifies the RNG advances passed on the Continue Screen.
" - "Example: if your target advance was 10000 and you hit 10025, you can decrease your calibration value by 25.", + "Continue Screen Frames Calibration:" + "
A \"fine adjustment\" that modifies the RNG advances passed on the Continue Screen.
" + "Example: if your target advance was 10000 and you hit 10025, you can decrease your calibration value by 25.", LockMode::UNLOCK_WHILE_RUNNING, 0 // default ) , INGAME_ADVANCES( - "In-Game Advances:
The number of in-game RNG advances before triggering the gift/encounter.
These pass at double the rate compared to other consoles, where every frame results in 2 advances.
Warning: this needs to be long enough to accomodate all in-game button presses prior to the gift/encounter", + "In-Game Advances:" + "
The number of in-game RNG advances before triggering the gift/encounter.
" + "These pass at double the rate compared to other consoles, where every frame results in 2 advances.
" + "Warning: this needs to be long enough to accomodate all in-game button presses prior to the gift/encounter", LockMode::LOCK_WHILE_RUNNING, 12345, 480 // default, min ) , INGAME_CALIBRATION( - "In-Game Advances Calibration:
A \"coarse adjustment\" that modifies the RNG advances passed after loading the game.
" - "Example: if your target advance was 10000 and you hit 8500, you can increase your calibration value by 1500.", + "In-Game Advances Calibration:" + "
A \"coarse adjustment\" that modifies the RNG advances passed after loading the game.
" + "Example: if your target advance was 10000 and you hit 8500, you can increase your calibration value by 1500.", LockMode::UNLOCK_WHILE_RUNNING, 0 // default ) , USE_COPYRIGHT_TEXT( - "Detect Copyright Text:
Start the seed timer only after detecting the copyright text. Can be helpful if your seeds are inconsistent.", + "Detect Copyright Text:" + "
Start the seed timer only after detecting the copyright text. Can be helpful if your seeds are inconsistent.", LockMode::LOCK_WHILE_RUNNING, true // default ) , USE_TEACHY_TV( - "Use Teachy TV:
Opens the Teachy TV to quickly advance the RNG at 313x speed.
Warning: can result in larger misses.", + "Use Teachy TV:" + "
Opens the Teachy TV to quickly advance the RNG at 313x speed.
" + "Warning: can result in larger misses.", LockMode::LOCK_WHILE_RUNNING, false // default ) @@ -588,7 +603,13 @@ void RngHelper::wait_with_teachy_tv(ProControllerContext& context, const uint64_ // total non-teachy delay duration: 13700ms } -void RngHelper::check_timings(SingleSwitchProgramEnvironment& env, int64_t FIXED_SEED_OFFSET, const uint64_t& CONTINUE_SCREEN_DELAY, const uint64_t& INGAME_DELAY, bool SAFARI_ZONE){ +void RngHelper::check_timings( + SingleSwitchProgramEnvironment& env, + int64_t FIXED_SEED_OFFSET, + const uint64_t& CONTINUE_SCREEN_DELAY, + const uint64_t& INGAME_DELAY, + bool SAFARI_ZONE +){ if (CONTINUE_SCREEN_DELAY < 3200){ OperationFailedException::fire( ErrorReport::SEND_ERROR_REPORT, @@ -813,7 +834,14 @@ void RngHelper::check_timings(SingleSwitchProgramEnvironment& env, int64_t FIXED } } -void RngHelper::perform_blind_sequence(ProControllerContext& context, int64_t FIXED_SEED_OFFSET, const uint64_t& CONTINUE_SCREEN_DELAY, const uint64_t& TEACHY_DELAY, const uint64_t& INGAME_DELAY, bool SAFARI_ZONE){ +void RngHelper::perform_blind_sequence( + ProControllerContext& context, + int64_t FIXED_SEED_OFFSET, + const uint64_t& CONTINUE_SCREEN_DELAY, + const uint64_t& TEACHY_DELAY, + const uint64_t& INGAME_DELAY, + bool SAFARI_ZONE +){ pbf_press_button(context, BUTTON_A, 80ms, 0ms); // start the game from the Home screen set_seed_after_delay(context, FIXED_SEED_OFFSET); load_game_after_delay(context, CONTINUE_SCREEN_DELAY); @@ -912,26 +940,37 @@ void RngHelper::perform_blind_sequence(ProControllerContext& context, int64_t FI } } -void RngHelper::reset_and_perform_blind_sequence(SingleSwitchProgramEnvironment& env, ProControllerContext& context, int64_t FIXED_SEED_OFFSET, const uint64_t& CONTINUE_SCREEN_DELAY, const uint64_t& TEACHY_DELAY, const uint64_t& INGAME_DELAY, bool SAFARI_ZONE){ +void RngHelper::reset_and_perform_blind_sequence( + SingleSwitchProgramEnvironment& env, + ProControllerContext& context, + int64_t FIXED_SEED_OFFSET, + const uint64_t& CONTINUE_SCREEN_DELAY, + const uint64_t& TEACHY_DELAY, + const uint64_t& INGAME_DELAY, + bool SAFARI_ZONE +){ + // close the game go_home(env.console, context); close_game_from_home(env.console, context); + // start the game and quickly go back home start_game_from_home(env.console, context, ConsoleSettings::instance().TOLERATE_SYSTEM_UPDATE_MENU_FAST, uint8_t(0), uint8_t(0)); // TODO: add option for user slot if needed - go_home(env.console, context); // happens as soon as a black screen is detected + pbf_wait(context, 200ms); // wait a moment to ensure the game doesn't fail to launch + go_home(env.console, context); // attempt to resume the game and perform the blind sequence // by this point, the license check should be over, so we don't need to worry about it when resuming the game - // profile selection is also already taken care of uint8_t attempts = 0; while(true){ if (attempts >= 5){ OperationFailedException::fire( ErrorReport::SEND_ERROR_REPORT, - "RngHelper(): Failed to resume the game 5 times in a row.", + "RngHelper(): Failed to reset the game 5 times in a row.", env.console ); } env.log("Starting blind button presses..."); UpdateMenuWatcher update_detector(env.console); + StartGameUserSelectWatcher user_selection_detector(env.console); // any other fail conditions should be added here context.wait_for_all_requests(); int ret = run_until( @@ -939,7 +978,7 @@ void RngHelper::reset_and_perform_blind_sequence(SingleSwitchProgramEnvironment& [this, FIXED_SEED_OFFSET, CONTINUE_SCREEN_DELAY, TEACHY_DELAY, INGAME_DELAY, SAFARI_ZONE](ProControllerContext& context) { perform_blind_sequence(context, FIXED_SEED_OFFSET, CONTINUE_SCREEN_DELAY, TEACHY_DELAY, INGAME_DELAY, SAFARI_ZONE); }, - { update_detector } + { update_detector, user_selection_detector } ); switch (ret){ @@ -950,6 +989,12 @@ void RngHelper::reset_and_perform_blind_sequence(SingleSwitchProgramEnvironment& pbf_press_button(context, BUTTON_A, 80ms, 4000ms); context.wait_for_all_requests(); continue; + case 1: + attempts++; + env.log("Detected the user selection screen. Reattempting to start the game"); + pbf_press_button(context, BUTTON_A, 160ms, 1040ms); + go_home(env.console, context); + continue; default: return; } @@ -960,7 +1005,8 @@ void RngHelper::reset_and_detect_copyright_text(SingleSwitchProgramEnvironment& go_home(env.console, context); close_game_from_home(env.console, context); start_game_from_home(env.console, context, ConsoleSettings::instance().TOLERATE_SYSTEM_UPDATE_MENU_FAST, uint8_t(0), uint8_t(0)); // TODO: add option for user slot if needed - go_home(env.console, context); // happens as soon as a black screen is detected + pbf_wait(context, 200ms); // add an extra delay to try to ensure the game doesn't fail to launch + go_home(env.console, context); uint8_t attempts = 0; while(true){ @@ -973,6 +1019,7 @@ void RngHelper::reset_and_detect_copyright_text(SingleSwitchProgramEnvironment& } UpdateMenuWatcher update_detector(env.console); + StartGameUserSelectWatcher user_selection_detector(env.console); BlackScreenWatcher blackscreen_detector(COLOR_RED); context.wait_for_all_requests(); int ret = run_until( @@ -980,7 +1027,7 @@ void RngHelper::reset_and_detect_copyright_text(SingleSwitchProgramEnvironment& [](ProControllerContext& context) { pbf_press_button(context, BUTTON_A, 80ms, 9920ms); }, - { update_detector, blackscreen_detector }, + { update_detector, user_selection_detector, blackscreen_detector }, 1ms ); @@ -994,7 +1041,13 @@ void RngHelper::reset_and_detect_copyright_text(SingleSwitchProgramEnvironment& pbf_press_button(context, BUTTON_A, 80ms, 4000ms); context.wait_for_all_requests(); continue; - case 1: + case 1: + attempts++; + env.log("Detected the user selection screen. Reattempting to start the game"); + pbf_press_button(context, BUTTON_A, 160ms, 1040ms); + go_home(env.console, context); + continue; + case 2: context.wait_for_all_requests(); ret2 = wait_until( env.console, context, 10000ms, diff --git a/SerialPrograms/Source/PokemonFRLG/Programs/ShinyHunting/PokemonFRLG_RngHelper.h b/SerialPrograms/Source/PokemonFRLG/Programs/ShinyHunting/PokemonFRLG_RngHelper.h index e5fa50b3e..a12697f54 100644 --- a/SerialPrograms/Source/PokemonFRLG/Programs/ShinyHunting/PokemonFRLG_RngHelper.h +++ b/SerialPrograms/Source/PokemonFRLG/Programs/ShinyHunting/PokemonFRLG_RngHelper.h @@ -75,9 +75,30 @@ class RngHelper : public SingleSwitchProgramInstance{ void wait_with_teachy_tv(ProControllerContext& context, const uint64_t& TEACHY_DELAY); - void check_timings(SingleSwitchProgramEnvironment& env, int64_t FIXED_SEED_OFFSET, const uint64_t& CONTINUE_SCREEN_DELAY, const uint64_t& INGAME_DELAY, bool SAFARI_ZONE); - void perform_blind_sequence(ProControllerContext& context, int64_t FIXED_SEED_OFFSET, const uint64_t& CONTINUE_SCREEN_DELAY, const uint64_t& TEACHY_DELAY, const uint64_t& INGAME_DELAY, bool SAFARI_ZONE); - void reset_and_perform_blind_sequence(SingleSwitchProgramEnvironment& env, ProControllerContext& context, int64_t FIXED_SEED_OFFSET, const uint64_t& CONTINUE_SCREEN_DELAY, const uint64_t& TEACHY_DELAY, const uint64_t& INGAME_DELAY, bool SAFARI_ZONE); + void check_timings( + SingleSwitchProgramEnvironment& env, + int64_t FIXED_SEED_OFFSET, + const uint64_t& CONTINUE_SCREEN_DELAY, + const uint64_t& INGAME_DELAY, + bool SAFARI_ZONE + ); + void perform_blind_sequence( + ProControllerContext& context, + int64_t FIXED_SEED_OFFSET, + const uint64_t& CONTINUE_SCREEN_DELAY, + const uint64_t& TEACHY_DELAY, + const uint64_t& INGAME_DELAY, + bool SAFARI_ZONE + ); + void reset_and_perform_blind_sequence( + SingleSwitchProgramEnvironment& env, + ProControllerContext& context, + int64_t FIXED_SEED_OFFSET, + const uint64_t& CONTINUE_SCREEN_DELAY, + const uint64_t& TEACHY_DELAY, + const uint64_t& INGAME_DELAY, + bool SAFARI_ZONE + ); void reset_and_detect_copyright_text(SingleSwitchProgramEnvironment& env, ProControllerContext& context); bool check_for_shiny(SingleSwitchProgramEnvironment& env, ProControllerContext& context); From 8894be59b521248dd4ac1a8520672352e5123f2b Mon Sep 17 00:00:00 2001 From: theAstrogoth Date: Wed, 15 Apr 2026 18:58:45 -0500 Subject: [PATCH 2/2] add profile option and fix starter summary check --- .../ShinyHunting/PokemonFRLG_RngHelper.cpp | 66 +++++++++++++++---- .../ShinyHunting/PokemonFRLG_RngHelper.h | 3 +- 2 files changed, 57 insertions(+), 12 deletions(-) diff --git a/SerialPrograms/Source/PokemonFRLG/Programs/ShinyHunting/PokemonFRLG_RngHelper.cpp b/SerialPrograms/Source/PokemonFRLG/Programs/ShinyHunting/PokemonFRLG_RngHelper.cpp index f5b392e64..9a8e91ba1 100644 --- a/SerialPrograms/Source/PokemonFRLG/Programs/ShinyHunting/PokemonFRLG_RngHelper.cpp +++ b/SerialPrograms/Source/PokemonFRLG/Programs/ShinyHunting/PokemonFRLG_RngHelper.cpp @@ -25,6 +25,7 @@ #include "PokemonFRLG/Inference/PokemonFRLG_SelectionArrowDetector.h" #include "PokemonFRLG/Inference/PokemonFRLG_ShinySymbolDetector.h" #include "PokemonFRLG/Inference/Dialogs/PokemonFRLG_DialogDetector.h" +#include "PokemonFRLG/Inference/Menus/PokemonFRLG_SummaryDetector.h" #include "PokemonFRLG/Programs/PokemonFRLG_StartMenuNavigation.h" #include "PokemonFRLG/PokemonFRLG_Navigation.h" #include "PokemonFRLG_RngHelper.h" @@ -64,8 +65,15 @@ std::unique_ptr RngHelper_Descriptor::make_stats() const{ } RngHelper::RngHelper() - : TARGET( - "Target:
", + : PROFILE( + "User Profile Position:
" + "The position, from left to right, of the Switch profile with the FRLG save you'd like to use.
" + "If this is set to 0, Switch 1 defaults to the last-used profile, while Switch 2 defaults to the first profile (position 1)", + LockMode::LOCK_WHILE_RUNNING, + 0, 0, 8 // default, min, max + ) + , TARGET( + "Target:", { {Target::starters, "starters", "Bulbasaur / Squirtle / Charmander"}, {Target::magikarp, "magikarp", "Magikarp"}, @@ -98,7 +106,8 @@ RngHelper::RngHelper() Target::starters ) , NUM_RESETS( - "Max Resets:
", + "Max Resets:
" + "This program requires manual calibration, so this should usually be set to 1 while calibrating.", LockMode::UNLOCK_WHILE_RUNNING, 1, 0 // default, min ) @@ -186,6 +195,7 @@ RngHelper::RngHelper() &NOTIFICATION_PROGRAM_FINISH, }) { + PA_ADD_OPTION(PROFILE); PA_ADD_OPTION(TARGET); PA_ADD_OPTION(NUM_RESETS); PA_ADD_OPTION(SEED_BUTTON); @@ -385,9 +395,26 @@ void use_registered_fishing_rod(ProControllerContext& context, const uint64_t& I void go_to_starter_summary(SingleSwitchProgramEnvironment& env, ProControllerContext& context){ // Navigate to summary (1st party slot) - open_party_menu_from_overworld(env.console, context); - pbf_press_button(context, BUTTON_A, 200ms, 1000ms); - pbf_press_button(context, BUTTON_A, 200ms, 2300ms); + open_start_menu(env.console, context); // Don't have a Pokedex yet, so arrow will already by over POKeMON + + SummaryWatcher summary_open(COLOR_RED); + context.wait_for_all_requests(); + int ret = run_until( + env.console, context, + [](ProControllerContext& context) { + pbf_press_button(context, BUTTON_A, 200ms, 1000ms); + for (int i=0; i<3; i++){ + pbf_press_button(context, BUTTON_A, 200ms, 2800ms); + } + }, + { summary_open } + ); + + if (ret < 0){ + env.log("go_to_starter_summary(): failed to open the summary."); + }else{ + env.log("Summary opened."); + } } bool shiny_check_starter_summary(SingleSwitchProgramEnvironment& env, ProControllerContext& context){ @@ -399,13 +426,30 @@ bool shiny_check_starter_summary(SingleSwitchProgramEnvironment& env, ProControl } void go_to_last_summary(SingleSwitchProgramEnvironment& env, ProControllerContext& context){ - // navigate to summary (last party slot) + // navigate to the last occupied party slot open_party_menu_from_overworld(env.console, context); pbf_move_left_joystick(context, {0, +1}, 200ms, 300ms); pbf_move_left_joystick(context, {0, +1}, 200ms, 300ms); + // open summary - pbf_press_button(context, BUTTON_A, 200ms, 1000ms); - pbf_press_button(context, BUTTON_A, 200ms, 2300ms); + SummaryWatcher summary_open(COLOR_RED); + context.wait_for_all_requests(); + int ret = run_until( + env.console, context, + [](ProControllerContext& context) { + pbf_press_button(context, BUTTON_A, 200ms, 1000ms); + for (int i=0; i<3; i++){ + pbf_press_button(context, BUTTON_A, 200ms, 2800ms); + } + }, + { summary_open } + ); + + if (ret < 0){ + env.log("go_to_last_summary(): failed to open the summary."); + } else { + env.log("Summary opened."); + } } bool shiny_check_summary(SingleSwitchProgramEnvironment& env, ProControllerContext& context){ @@ -953,7 +997,7 @@ void RngHelper::reset_and_perform_blind_sequence( go_home(env.console, context); close_game_from_home(env.console, context); // start the game and quickly go back home - start_game_from_home(env.console, context, ConsoleSettings::instance().TOLERATE_SYSTEM_UPDATE_MENU_FAST, uint8_t(0), uint8_t(0)); // TODO: add option for user slot if needed + start_game_from_home(env.console, context, ConsoleSettings::instance().TOLERATE_SYSTEM_UPDATE_MENU_FAST, uint8_t(0), PROFILE); pbf_wait(context, 200ms); // wait a moment to ensure the game doesn't fail to launch go_home(env.console, context); @@ -1004,7 +1048,7 @@ void RngHelper::reset_and_perform_blind_sequence( void RngHelper::reset_and_detect_copyright_text(SingleSwitchProgramEnvironment& env, ProControllerContext& context){ go_home(env.console, context); close_game_from_home(env.console, context); - start_game_from_home(env.console, context, ConsoleSettings::instance().TOLERATE_SYSTEM_UPDATE_MENU_FAST, uint8_t(0), uint8_t(0)); // TODO: add option for user slot if needed + start_game_from_home(env.console, context, ConsoleSettings::instance().TOLERATE_SYSTEM_UPDATE_MENU_FAST, uint8_t(0), PROFILE); pbf_wait(context, 200ms); // add an extra delay to try to ensure the game doesn't fail to launch go_home(env.console, context); diff --git a/SerialPrograms/Source/PokemonFRLG/Programs/ShinyHunting/PokemonFRLG_RngHelper.h b/SerialPrograms/Source/PokemonFRLG/Programs/ShinyHunting/PokemonFRLG_RngHelper.h index a12697f54..27c230ac0 100644 --- a/SerialPrograms/Source/PokemonFRLG/Programs/ShinyHunting/PokemonFRLG_RngHelper.h +++ b/SerialPrograms/Source/PokemonFRLG/Programs/ShinyHunting/PokemonFRLG_RngHelper.h @@ -102,8 +102,9 @@ class RngHelper : public SingleSwitchProgramInstance{ void reset_and_detect_copyright_text(SingleSwitchProgramEnvironment& env, ProControllerContext& context); bool check_for_shiny(SingleSwitchProgramEnvironment& env, ProControllerContext& context); - EnumDropdownOption TARGET; + SimpleIntegerOption PROFILE; + EnumDropdownOption TARGET; SimpleIntegerOption NUM_RESETS; EnumDropdownOption SEED_BUTTON;